Anchor
Anchor 托管

Anchor 托管

75 Graduates

Make

现在我们可以转到 make 指令,该指令位于 make.rs 中,并将执行以下操作:

  • 初始化托管记录并存储所有条款。

  • 创建金库(一个由 escrow 拥有的 mint_a 的关联代币账户 (ATA))。

  • 使用 CPI 调用 SPL-Token 程序,将创建者的 Token A 转移到该金库中。

账户

在此上下文中需要的账户包括:

  • maker:决定条款并将 mint_a 存入 Escrow 的用户

  • escrow:持有交换条款(创建者、代币铸造、数量)的账户

  • mint_amaker 存入的代币

  • mint_bmaker 想要交换的代币

  • maker_ata_a:与 makermint_a 关联的代币账户,用于将代币存入 vault

  • vault:与 escrowmint_a 关联的代币账户,用于存放存入的代币

  • associated_token_program:用于创建关联代币账户的关联代币程序

  • token_program:用于 CPI 转账的代币程序

  • system_program:用于创建 Escrow 的系统程序

结合所有约束条件,它看起来会是这样的:

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

注意:此指令仅传递一个 token_program。由于 take 操作会转移两个代币铸造的代币,我们必须确保这两个代币铸造都由同一个程序(SPL Token 或 Token-2022)拥有,否则 CPI 将失败。

逻辑

初始化账户后,我们可以通过创建更小的辅助函数作为账户结构的实现,最终处理逻辑。

我们首先使用 set_inner() 辅助工具填充 Escrow,然后通过 transfer CPI 存入代币,如下所示:

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

我们可以看到 Anchor 在多个方面为我们提供了帮助:

  • set_inner():确保每个字段都已填充。

  • transfer_checked:像我们之前使用的系统辅助工具一样封装了 Token CPI。

现在我们可以继续创建一个 handler 函数,在使用辅助工具之前执行一些检查,如下所示:

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

这里我们添加了两个验证检查;一个针对 amount,另一个针对 receive 参数,以确保我们不会为任一参数传递零值。

警告

SPL Token-2022 的某些扩展功能,例如转账钩子、保密转账、默认账户状态,可能会引入漏洞,例如阻止转账、锁定资金以及在托管逻辑、金库或 CPI 中导致资金被抽走。

  • 确保 mint_amint_b 由同一个代币程序拥有,以防止 CPI 失败。

  • 使用经过充分审计的代币(例如 USDC、wSOL)来自标准 SPL Token 程序。

  • 避免使用未经验证或复杂的 Token-2022 铸币。

Next Page接受
或者跳到挑战
准备接受挑战了吗?
Blueshift © 2025Commit: e573eab