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 :
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à.
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_bversmaker_ata_b.Transférer les jetons du
vaultverstaker_ata_a.Fermer le
vaultdésormais vide et récupérer la rente du compte.
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(())
}
}