Rust
Pinocchio Escrow

Pinocchio Escrow

143 Graduates

Take

Instruction take hoàn thiện việc hoán đổi:

  • Chuyển Token A từ vault đến taker.

  • Đóng tài khoản token vault hiện đã trống.

  • Chuyển số lượng Token B đã thỏa thuận từ taker đến maker.

  • Đóng tài khoản chương trình escrow.

Các tài khoản cần thiết

Dưới đây là các tài khoản mà context cần:

  • taker: người muốn nhận giao dịch. Phải là signer và mutable.

  • maker: người tạo escrow. Phải là mutable.

  • escrow: tài khoản escrow mà chúng ta đang khởi tạo. Phải là mutable.

  • mint_a: token mà chúng ta đang gửi vào escrow

  • mint_b: token mà chúng ta muốn nhận

  • vault: tài khoản token liên kết thuộc sở hữu của escrow. Phải là mutable

  • taker_ata_a: tài khoản token liên kết thuộc sở hữu của taker cho mint_a. Phải là mutable

  • taker_ata_b: tài khoản token liên kết thuộc sở hữu của taker cho mint_b. Phải là mutable

  • maker_ata_b: tài khoản token liên kết thuộc sở hữu của maker cho mint_b. Phải là mutable

  • system_program: chương trình hệ thống. Phải là executable

  • token_program: chương trình token. Phải là executable

Và chúng ta thực hiện các kiểm tra này trên nó:

rust
pub struct TakeAccounts<'a> {
  pub taker: &'a AccountInfo,
  pub maker: &'a AccountInfo,
  pub escrow: &'a AccountInfo,
  pub mint_a: &'a AccountInfo,
  pub mint_b: &'a AccountInfo,
  pub vault: &'a AccountInfo,
  pub taker_ata_a: &'a AccountInfo,
  pub taker_ata_b: &'a AccountInfo,
  pub maker_ata_b: &'a AccountInfo,
  pub system_program: &'a AccountInfo,
  pub token_program: &'a AccountInfo,
}

impl<'a> TryFrom<&'a [AccountInfo]> for TakeAccounts<'a> {
  type Error = ProgramError;

  fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
    let [taker, maker, escrow, mint_a, mint_b, vault, taker_ata_a, taker_ata_b, maker_ata_b, system_program, token_program, _] = accounts else {
      return Err(ProgramError::NotEnoughAccountKeys);
    };

    // Basic Accounts Checks
    SignerAccount::check(taker)?;
    ProgramAccount::check(escrow)?;
    MintInterface::check(mint_a)?;
    MintInterface::check(mint_b)?;
    AssociatedTokenAccount::check(taker_ata_b, taker, mint_b, token_program)?;
    AssociatedTokenAccount::check(vault, escrow, mint_a, token_program)?;

    // Return the accounts
    Ok(Self {
      taker,
      maker,
      escrow,
      mint_a,
      mint_b,
      taker_ata_a,
      taker_ata_b,
      maker_ata_b,
      vault,
      system_program,
      token_program,
    })
  }
}
Expand
[31 more lines]

Dữ liệu cho Instruction

Tất cả dữ liệu mà chúng ta cần để thực hiện logic đã tồn tại trong tài khoản Escrow hoặc trên các tài khoản mà chúng ta đang deserialize. Vì lý do này, chúng ta không cần bất kỳ instruction_data nào cho instruction này.

Instruction Logic

Chúng ta bắt đầu bằng cách khởi tạo các tài khoản cần thiết trong việc triển khai TryFrom, sau khi chúng ta đã deserialize các tài khoản.

Đối với bước này, chúng ta đảm bảo cả tài khoản Token A của taker và tài khoản Token B của maker đều được khởi tạo bằng cách sử dụng AssociatedTokenAccount::init_if_needed vì chúng ta không chắc chắn rằng chúng đã tồn tại

rust
pub struct Take<'a> {
  pub accounts: TakeAccounts<'a>,
}

impl<'a> TryFrom<&'a [AccountInfo]> for Take<'a> {
  type Error = ProgramError;

  fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
    let accounts = TakeAccounts::try_from(accounts)?;

    // Initialize necessary accounts
    AssociatedTokenAccount::init_if_needed(
      accounts.taker_ata_a,
      accounts.mint_a,
      accounts.taker,
      accounts.taker,
      accounts.system_program,
      accounts.token_program,
    )?;

    AssociatedTokenAccount::init_if_needed(
      accounts.maker_ata_b,
      accounts.mint_b,
      accounts.taker,
      accounts.maker,
      accounts.system_program,
      accounts.token_program,
    )?;

    Ok(Self {
      accounts,
    })
  }
}
Expand
[19 more lines]

Bây giờ chúng ta có thể tập trung vào logic chính sẽ:

  • Chuyển token từ vault đến taker_ata_a.

  • Đóng tài khoản token vault hiện đã trống và thu lại tiền thuê của nó.

  • Chuyển token từ taker_ata_b đến maker_ata_b.

  • Đóng tài khoản chương trình escrow sau khi việc hoán đổi hoàn tất.

Thay vì thực hiện ba lệnh gọi CPI token riêng biệt để hoàn tất việc hoán đổi, chúng ta có thể tận dụng batch instruction trong p-token để thực hiện hiệu quả tất cả các thao tác token chỉ trong một lần gọi CPI.

rust
impl<'a> Take<'a> {
  pub const DISCRIMINATOR: &'a u8 = &1;

  const MAX_ACCOUNTS_LEN: usize = Transfer::MAX_ACCOUNTS_LEN * 2 + CloseAccount::MAX_ACCOUNTS_LEN;
  const MAX_DATA_LEN: usize = Batch::header_data_len(3) + Transfer::DATA_LEN * 2 + CloseAccount::DATA_LEN;

  pub fn process(&mut self) -> ProgramResult {
    let data = self.accounts.escrow.try_borrow_data()?;
    let escrow = Escrow::load(&data)?;

    // Check if the escrow is valid
    let escrow_key = create_program_address(&[b"escrow", self.accounts.maker.key(), &escrow.seed.to_le_bytes(), &escrow.bump], &crate::ID)?;
    if &escrow_key != self.accounts.escrow.key() {
      return Err(ProgramError::InvalidAccountOwner);
    }

    let seed_binding = escrow.seed.to_le_bytes();
    let bump_binding = escrow.bump;
    let escrow_seeds = [
      Seed::from(b"escrow"),
      Seed::from(self.accounts.maker.key().as_ref()),
      Seed::from(&seed_binding),
      Seed::from(&bump_binding),
    ];
    let signer = Signer::from(&escrow_seeds);

    let amount = TokenAccount::from_account_info(self.accounts.vault)?.amount();

    let batch_state = BatchState::new(Self::MAX_ACCOUNTS_LEN, Self::MAX_DATA_LEN);
    let mut batch = batch_state.as_batch()?;

    Transfer::new(
      self.accounts.vault,
      self.accounts.taker_ata_a,
      self.accounts.escrow,
      amount,
    )
    .into_batch(&mut batch)?;

    CloseAccount::new(
      self.accounts.vault,
      self.accounts.maker,
      self.accounts.escrow,
    )
    .into_batch(&mut batch)?;

    Transfer::new(
      self.accounts.taker_ata_b,
      self.accounts.maker_ata_b,
      self.accounts.taker,
      escrow.receive,
    )
    .into_batch(&mut batch)?;

    batch.invoke_signed(&[signer])?;

    // Close the Escrow program account
    drop(data);
    ProgramAccount::close(self.accounts.escrow, self.accounts.taker)?;

    Ok(())
  }
}
Expand
[48 more lines]

Hãy đảm bảo pinocchio-token có phiên bản ít nhất là 0.6.0 để hỗ trợ các thao tác batch của p-token.

Hoặc bỏ qua để làm thử thách
Nội dung
Xem mã nguồn
Blueshift © 2026Commit: 3c44267