Rust
Escrow avec Pinocchio

Escrow avec Pinocchio

47 Graduates

Take

L'instruction take finalise l'échange :

  • Ferme le compte d'escrow en retournant la rente au créateur.

  • Transfére le Jeton A du coffre au preneur puis ferme le vault.

  • Transférer le montant convenu du Jeton B du preneur au créateur.

Comptes Nécessaires

Voici les comptes dont le contexte a besoin :

  • taker: la personne qui veut accepter l'offre. Doit être un signataire et mutable

  • maker: le créateur de l'escrow. Doit être mutable.

  • escrow: le compte d'escrow que nous initialisons. Doit être mutable

  • mint_a: le jeton que nous déposons dans l'escrow

  • mint_b: le jeton que nous souhaitons recevoir

  • vault: le compte de jetons associé appartenant à l'escrow. Doit être mutable

  • taker_ata_a: le compte de jetons associé appartenant au preneur pour le mint_a. Doit être mutable

  • taker_ata_b: le compte de jetons associé appartenant au preneur pour le mint_b. Doit être mutable

  • maker_ata_b: le compte de jetons associé appartenant au créateur pour le mint_b. Doit être mutable

  • system_program: le programme système. Doit être executable

  • token_program: le programme de jeton. Doit être executable

Et nous effectuons ces vérifications :

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,
    })
  }
}

Données d'Instruction

Toutes les données dont nous avons besoin pour exécuter la logique se trouvent déjà dans le compte Escrow ou dans les comptes que nous désérialisons. C'est pourquoi nous n'avons pas besoin de instruction_data pour cette instruction.

Logique d'Instruction

Tout d'abord, après avoir désérialisé les accounts dans l'implémentation du TryFrom, nous initialisons les comptes nécessaires.

Pour cette étape, nous nous assurons que le compte de Jeton A du preneur et que le compte de Jeton B du créateur sont initialisés en utilisant AssociatedTokenAccount::init_if_needed puisque nous ne sommes pas sûrs qu'ils existent déjà.

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,
    })
  }
}

Nous pouvons maintenant nous concentrer sur la logique elle-même :

  • Transférer les jetons de taker_ata_b vers maker_ata_b.

  • Transférer les jetons du vault vers taker_ata_a.

  • Fermer le vault désormais vide et récupérer la rente du compte.

rust
impl<'a> Take<'a> {
  pub const DISCRIMINATOR: &'a u8 = &1;
  
  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::get_amount(self.accounts.vault);
    
    // Transfer from the Vault to the Taker
    Transfer {
      from: self.accounts.vault,
      to: self.accounts.taker_ata_a,
      authority: self.accounts.escrow,
      amount,
    }.invoke_signed(&[signer.clone()])?;

    // Close the Vault
    CloseAccount {
      account: self.accounts.vault,
      destination: self.accounts.maker,
      authority: self.accounts.escrow,
    }.invoke_signed(&[signer.clone()])?;

    // Transfer from the Taker to the Maker
    Transfer {
      from: self.accounts.taker_ata_b,
      to: self.accounts.maker_ata_b,
      authority: self.accounts.taker,
      amount: escrow.receive,
    }.invoke()?;

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

    Ok(())
  }
}
Next PageRembourser
OU PASSER AU CHALLENGE
Prêt à relever le challenge ?
Blueshift © 2025Commit: e573eab