Rust
Escrow Pinocchio

Escrow Pinocchio

143 Graduates

Take

Instruksi take menyelesaikan pertukaran:

  • Memindahkan Token A dari vault ke pengambil.

  • Menutup akun token vault yang sekarang kosong.

  • Memindahkan jumlah Token B yang disepakati dari pengambil ke pembuat.

  • Menutup akun program escrow.

Required Accounts

Berikut adalah akun yang dibutuhkan oleh konteks:

  • taker: orang yang ingin mengambil penawaran. Harus menjadi penandatangan dan dapat diubah.

  • maker: pembuat escrow. Harus dapat diubah.

  • escrow: akun escrow yang kita inisialisasi. Harus dapat diubah.

  • mint_a: token yang kita setorkan ke dalam escrow

  • mint_b: token yang ingin kita terima

  • vault: akun token terkait yang dimiliki oleh escrow. Harus dapat diubah

  • taker_ata_a: akun token terkait yang dimiliki oleh pengambil untuk mint_a. Harus dapat diubah

  • taker_ata_b: akun token terkait yang dimiliki oleh pengambil untuk mint_b. Harus dapat diubah

  • maker_ata_b: akun token terkait yang dimiliki oleh pembuat untuk mint_b. Harus dapat diubah

  • system_program: program sistem. Harus dapat dieksekusi

  • token_program: program token. Harus dapat dieksekusi

Dan kita melakukan pemeriksaan berikut padanya:

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]

Instruction Data

Semua data yang kita butuhkan untuk melakukan logika sudah ada di akun Escrow atau pada akun yang kita deserialisasi. Karena alasan ini kita tidak memerlukan instruction_data untuk instruksi ini.

Instruction Logic

Kita mulai dengan menginisialisasi akun yang diperlukan dalam implementasi TryFrom, setelah kita mendeserialisasi akun.

Untuk langkah ini, kita memastikan bahwa akun Token A milik taker dan akun Token B milik maker sudah diinisialisasi menggunakan AssociatedTokenAccount::init_if_needed karena kita tidak yakin apakah akun-akun tersebut sudah ada

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]

Sekarang kita bisa fokus pada logika itu sendiri yang akan:

  • Mentransfer token dari vault ke taker_ata_a.

  • Menutup akun token vault yang sekarang kosong dan menarik biaya sewanya.

  • Mentransfer token dari taker_ata_b ke maker_ata_b.

  • Menutup akun program escrow setelah pertukaran selesai.

Daripada melakukan tiga panggilan CPI token terpisah untuk menyelesaikan pertukaran, kita bisa memanfaatkan instruksi batch di p-token untuk menjalankan semua operasi token secara efisien dalam satu panggilan 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]

Pastikan pinocchio-token setidaknya versi 0.6.0 untuk mendukung operasi batch p-token.

Daftar Isi
Lihat Sumber
Blueshift © 2026Commit: 3c44267