Rust
Pinocchio Escrow

Pinocchio Escrow

143 Graduates

Take

Die take Anweisung schließt den Tausch ab:

  • Überträgt Token A vom Tresor zum Nehmer.

  • Schließt das nun leere Vault-Token-Konto.

  • Überträgt die vereinbarte Menge von Token B vom Nehmer zum Ersteller.

  • Schließt das Escrow-Programmkonto.

Required Accounts

Nachfolgend sind die Konten aufgeführt, die der Kontext benötigt:

  • taker: die Person, die das Angebot annehmen möchte. Muss ein Unterzeichner und veränderbar sein.

  • maker: der Ersteller des Escrows. Muss veränderbar sein.

  • escrow: das Escrow-Konto, das wir initialisieren. Muss veränderbar sein.

  • mint_a: der Token, den wir im Escrow hinterlegen

  • mint_b: der Token, den wir erhalten möchten

  • vault: das zugehörige Token-Konto im Besitz des Escrows. Muss veränderbar sein

  • taker_ata_a: das zugehörige Token-Konto im Besitz des Nehmers für mint_a. Muss veränderbar sein

  • taker_ata_b: das zugehörige Token-Konto im Besitz des Nehmers für mint_b. Muss veränderbar sein

  • maker_ata_b: das zugehörige Token-Konto im Besitz des Erstellers für mint_b. Muss veränderbar sein

  • system_program: das System-Programm. Muss ausführbar sein

  • token_program: das Token-Programm. Muss ausführbar sein

Und wir führen folgende Prüfungen daran durch:

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

Alle Daten, die wir zur Durchführung der Logik benötigen, befinden sich bereits im Escrow-Konto oder in den Konten, die wir deserialisieren. Aus diesem Grund benötigen wir keine instruction_data für diese Anweisung.

Instruction Logic

Wir beginnen mit der Initialisierung der erforderlichen Konten in der TryFrom Implementierung, nachdem wir die Konten deserialisiert haben.

Für diesen Schritt stellen wir sicher, dass sowohl das Token-A-Konto des Nehmers als auch das Token-B-Konto des Erstellers mit AssociatedTokenAccount::init_if_needed initialisiert sind, da wir nicht sicher sind, ob diese bereits existieren

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]

Wir können uns jetzt auf die eigentliche Logik konzentrieren, die:

  • Die Token vom vault zum taker_ata_a überträgt.

  • Das nun leere vault Token-Konto schließt und die Miete vom Konto abhebt.

  • Die Token vom taker_ata_b zum maker_ata_b überträgt.

  • Das escrow Programmkonto schließt, sobald der Tausch abgeschlossen ist.

Anstatt den Tausch mit drei separaten Token-CPI-Aufrufen abzuschließen, können wir die Batch-Instruktion in p-token nutzen, um alle Token-Operationen effizient in einem einzigen CPI-Aufruf auszuführen.

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]

Stelle sicher, dass pinocchio-token mindestens Version 0.6.0 hat, um p-token Batch-Operationen zu unterstützen.

Blueshift © 2026Commit: 3c44267