Anchor
Escrow avec Anchor

Escrow avec Anchor

75 Graduates

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_a appartenant à 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 le mint_a dans l'Escrow

  • escrow: le compte où sont stockés tous les termes de cet échange

  • mint_a: le jeton que le maker dépose

  • mint_b: le jeton que le maker veut en échange

  • maker_ata_a: le compte de jetons associé (Associated Token Account ou ATA) au maker et au mint_a utilisé pour déposer des jetons dans le vault

  • vault: le compte de jetons associé à l'escrow et au mint_a où les jetons seront déposés

  • associated_token_program: le programme de jetons associé utilisé pour créer les comptes de jetons associés

  • token_program: le programme de jetons utilisé pour réaliser le transfert grâce à un CPI

  • system_program: le programme système utilisé pour créer l'Escrow

Et avec toutes les contraintes, cela ressemblera à quelque chose comme ceci :

rust
#[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 :

rust
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 :

rust
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_a et mint_b appartiennent 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.

Next PageAccepter
OU PASSER AU CHALLENGE
Prêt à relever le challenge ?
Blueshift © 2025Commit: e573eab