Make
Bây giờ chúng ta có thể chuyển đến lệnh make, nằm trong make.rs và sẽ thực hiện các hành động sau:
Khởi tạo bản ghi Escrow và lưu trữ tất cả các điều khoản.
Tạo Vault (một ATA cho
mint_athuộc sở hữu củaescrow).Chuyển Token A của maker vào vault đó bằng cách tạo một CPI đến chương trình SPL-Token.
Tài khoản
Các tài khoản cần thiết trong ngữ cảnh này là:
maker: người dùng quyết định các điều khoản và gửimint_avàoEscrowescrow: tài khoản nơi tất cả các điều khoản của việc trao đổi này tồn tạimint_a: token màmakerđang gửimint_b: token màmakermuốn đổi lấymaker_ata_a: tài khoản token liên kết vớimakervàmint_ađược sử dụng để gửi token vàovaultvault: tài khoản token liên kết vớiescrowvàmint_anơi các token gửi được lưu trữassociated_token_program: chương trình associated token được sử dụng để tạo các tài khoản associated tokentoken_program: chương trình token được sử dụng để gọi CPI chuyển tokensystem_program: chương trình hệ thống được sử dụng để tạoEscrow
Và với tất cả các ràng buộc, nó sẽ trông như thế này:
#[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>,
}Lưu ý: instruction này chỉ truyền vào 1 token program. Bởi vì để chuyển token cho cả hai mint, chúng ta phải đảm bảo rằng cả 2 mint đều được sở hữu bởi cùng token program (SPL Token hoặc Token-2022), hoặc lời gọi CPI sẽ thất bại.
Logic
Sau khi khởi tạo các Tài khoản, cuối cùng chúng ta có thể xử lý logic bằng cách tạo các hàm helper nhỏ hơn như một implementation của account struct.
Chúng ta bắt đầu bằng cách điền vào Escrow bằng helper set_inner(), và sau đó chuyển sang gửi token thông qua CPI transfer như thế này:
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(())
}
}Chúng ta có thể thấy rằng Anchor giúp chúng ta bằng nhiều cách:
set_inner(): đảm bảo mọi trường đều được điền.transfer_checked: bao bọc Token CPI giống như các hàm hỗ trợ của System mà chúng ta đã sử dụng trước đó.
Và bây giờ chúng ta có thể chuyển sang tạo hàm handler nơi chúng ta thực hiện một số kiểm tra trước khi sử dụng các hàm hỗ trợ, như thế này:
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(())
}Ở đây chúng ta thêm hai kiểm tra xác thực cho các đối số; một cho amount và một cho receive để đảm bảo chúng ta không truyền giá trị zero cho cả hai.
Lưu ý
Một số extension từ SPL Token-2022 ví dụ như transfer hooks, confidential transfers, default account states có thể gây ra các lỗ hổng như chặn chuyển, khóa tiền và gây ra rug pulls trong logic escrow, vault, hoặc CPI.
Đảm bảo
mint_avàmint_bđược sở hữu bởi cùng một token program để ngăn chặn lỗi khi gọi CPI.Sử dụng các token đã được xác minh kỹ (ví dụ: USDC, wSOL) từ chương trình SPL Token tiêu chuẩn.
Tránh các mint Token-2022 phức tạp hoặc chưa được xác minh.