Créer
Nous pouvons maintenant passer à l'instruction make qui se trouve dans le fichier make.rs et qui effectuera ces actions :
Initialise le compte Escrow et stocke tous les paramètres.
Crée le vault (un ATA pour le
mint_aappartenant à l'escrow).Déplace le jeton A du créateur dans ce vault graàce à un CPI vers le programme SPL-Token.
Comptes
Les comptes nécessaires dans ce contexte sont :
maker: l'utilisateur qui décide des conditions et dépose lemint_adans l'Escrowescrow: le compte où sont stockés tous les termes de cet échangemint_a: le jeton que lemakerdéposemint_b: le jeton que lemakerveut en échangemaker_ata_a: le compte de jetons associé (Associated Token Account ou ATA) aumakeret aumint_autilisé pour déposer des jetons dans levaultvault: le compte de jetons associé à l'escrowet aumint_aoù les jetons seront déposésassociated_token_program: le programme de jetons associé utilisé pour créer les comptes de jetons associéstoken_program: le programme de jetons utilisé pour réaliser le transfert grâce à un CPIsystem_program: le programme système utilisé pour créer l'Escrow
Et avec toutes les contraintes, cela ressemblera à quelque chose comme ceci :
#[derive(Accounts)]
#[instruction(seed: u64)]
pub struct Make<'info> {
#[account(mut)]
pub maker: Signer<'info>,
#[account(
init,
payer = maker,
space = Escrow::INIT_SPACE + Escrow::DISCRIMINATOR.len(),
seeds = [b"escrow", maker.key().as_ref(), seed.to_le_bytes().as_ref()],
bump,
)]
pub escrow: Account<'info, Escrow>,
/// Token Accounts
#[account(
mint::token_program = token_program
)]
pub mint_a: InterfaceAccount<'info, Mint>,
#[account(
mint::token_program = token_program
)]
pub mint_b: InterfaceAccount<'info, Mint>,
#[account(
mut,
associated_token::mint = mint_a,
associated_token::authority = maker,
associated_token::token_program = token_program
)]
pub maker_ata_a: InterfaceAccount<'info, TokenAccount>,
#[account(
init,
payer = maker,
associated_token::mint = mint_a,
associated_token::authority = escrow,
associated_token::token_program = token_program
)]
pub vault: InterfaceAccount<'info, TokenAccount>,
/// Programs
pub associated_token_program: Program<'info, AssociatedToken>,
pub token_program: Interface<'info, TokenInterface>,
pub system_program: Program<'info, System>,
}Remarque : Puisque nous ne fournissons qu'un seul token_program, pour s'assurer que le CPI n'échoue pas dans l'instruction take (dans laquelle nous effectuons un transfert avec les deux mints) nous devons vérifier que les deux mints sont la propriété du même programme.
Logique
Après avoir initialisé les Comptes, nous pouvons enfin gérer la logique en créant une petite fonction d'aide en tant qu'implémentation de la structure du compte.
Nous commençons par remplir l'Escrow en utilisant l'aide set_inner(), puis nous déposons les tokens via le CPI transfer :
impl<'info> Make<'info> {
/// # Create the Escrow
fn populate_escrow(&mut self, seed: u64, amount: u64, bump: u8) -> Result<()> {
self.escrow.set_inner(Escrow {
seed,
maker: self.maker.key(),
mint_a: self.mint_a.key(),
mint_b: self.mint_b.key(),
receive: amount,
bump,
});
Ok(())
}
/// # Deposit the tokens
fn deposit_tokens(&self, amount: u64) -> Result<()> {
transfer_checked(
CpiContext::new(
self.token_program.to_account_info(),
TransferChecked {
from: self.maker_ata_a.to_account_info(),
mint: self.mint_a.to_account_info(),
to: self.vault.to_account_info(),
authority: self.maker.to_account_info(),
},
),
amount,
self.mint_a.decimals,
)?;
Ok(())
}
}Nous pouvons constater qu'Anchor nous aide de multiples façons :
set_inner(): garantit que tous les champs sont remplis.transfer_checked: enveloppe le CPI du Jeton de la même manière que les fonctions d'aide du Système que nous avons utilisés précédemment.
Et maintenant, nous pouvons créer une fonction handler où nous effectuons quelques vérifications avant d'utiliser les fonctions d'aide :
pub fn handler(ctx: Context<Make>, seed: u64, receive: u64, amount: u64) -> Result<()> {
// Validate the amount
require_gt!(receive, 0, EscrowError::InvalidAmount);
require_gt!(amount, 0, EscrowError::InvalidAmount);
// Save the Escrow Data
ctx.accounts.populate_escrow(seed, receive, ctx.bumps.escrow)?;
// Deposit Tokens
ctx.accounts.deposit_tokens(amount)?;
Ok(())
}Ici, nous ajoutons deux contrôles de validation. Un sur l'argument amount et un sur l'argument receive pour s'assurer que nous ne passons pas une valeur nulle pour l'un ou l'autre.
Avertissement
Certaines extensions de SPL Token-2022, par exemple les hooks de transfert, les transferts confidentiels, les états de compte par défaut, peuvent introduire des vulnérabilités telles que le blocage des transferts, le verrouillage des fonds et provoquer des rug pulls dans la logique des escrows, des vaults ou des CPIs.
Assurez-vous que
mint_aetmint_bappartiennent au même programme de jetons afin d'éviter les échecs de CPI.Utilisez des jetons correctement audités (par exemple, USDC, wSOL) issus du programme standard SPL-Token.
Évitez les mints de Token-2022 non vérifiées ou complexes.