Take
Bây giờ chúng ta có thể chuyển đến lệnh take, nằm trong take.rs và sẽ thực hiện các hành động sau:
Đóng bản ghi escrow, gửi rent lamports của nó trở lại cho maker.
Chuyển Token A từ vault đến taker, sau đó đóng vault.
Chuyển số lượng Token B đã thỏa thuận từ taker đến maker.
Tài khoản
Các tài khoản cần thiết trong ngữ cảnh này là:
taker: người dùng chấp nhận các điều khoản củamakervà đang thực hiện trao đổimaker: người dùng ban đầu đặt ra các điều khoảnescrow: 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đã gửimint_b: token màmakermuốn đổi lấyvault: tài khoản token liên kết vớiescrowvàmint_asẽ gửi token đếntakertaker_ata_a: tài khoản token liên kết vớitakervàmint_asẽ nhận token từvaulttaker_ata_b: tài khoản token liên kết vớitakervàmint_bsẽ gửi token đếnmakermaker_ata_b: tài khoản token liên kết vớimakervàmint_bsẽ nhận token từtakerassociated_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 để CPI việc chuyểnsystem_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)]
pub struct Take<'info> {
#[account(mut)]
pub taker: Signer<'info>,
#[account(mut)]
pub maker: SystemAccount<'info>,
#[account(
mut,
close = maker,
seeds = [b"escrow", maker.key().as_ref(), escrow.seed.to_le_bytes().as_ref()],
bump = escrow.bump,
has_one = maker @ EscrowError::InvalidMaker,
has_one = mint_a @ EscrowError::InvalidMintA,
has_one = mint_b @ EscrowError::InvalidMintB,
)]
pub escrow: Box<Account<'info, Escrow>>,
/// Token Accounts
pub mint_a: Box<InterfaceAccount<'info, Mint>>,
pub mint_b: Box<InterfaceAccount<'info, Mint>>,
#[account(
mut,
associated_token::mint = mint_a,
associated_token::authority = escrow,
associated_token::token_program = token_program
)]
pub vault: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
init_if_needed,
payer = taker,
associated_token::mint = mint_a,
associated_token::authority = taker,
associated_token::token_program = token_program
)]
pub taker_ata_a: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
associated_token::mint = mint_b,
associated_token::authority = taker,
associated_token::token_program = token_program
)]
pub taker_ata_b: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
init_if_needed,
payer = taker,
associated_token::mint = mint_b,
associated_token::authority = maker,
associated_token::token_program = token_program
)]
pub maker_ata_b: Box<InterfaceAccount<'info, TokenAccount>>,
/// Programs
pub associated_token_program: Program<'info, AssociatedToken>,
pub token_program: Interface<'info, TokenInterface>,
pub system_program: Program<'info, System>,
}Logic
Trong logic, chúng ta bắt đầu bằng cách chuyển token từ taker_ata_b đến maker_ata_b; sau đó chúng ta chuyển sang chuyển token từ vault đến taker_ata_a trước khi đóng vault hiện đã trống như thế này:
impl<'info> Take<'info> {
fn transfer_to_maker(&mut self) -> Result<()> {
transfer_checked(
CpiContext::new(
self.token_program.to_account_info(),
TransferChecked {
from: self.taker_ata_b.to_account_info(),
to: self.maker_ata_b.to_account_info(),
mint: self.mint_b.to_account_info(),
authority: self.taker.to_account_info(),
},
),
self.escrow.receive,
self.mint_b.decimals,
)?;
Ok(())
}
fn withdraw_and_close_vault(&mut self) -> Result<()> {
// Create the signer seeds for the Vault
let signer_seeds: [&[&[u8]]; 1] = [&[
b"escrow",
self.maker.to_account_info().key.as_ref(),
&self.escrow.seed.to_le_bytes()[..],
&[self.escrow.bump],
]];
// Transfer Token A (Vault -> Taker)
transfer_checked(
CpiContext::new_with_signer(
self.token_program.to_account_info(),
TransferChecked {
from: self.vault.to_account_info(),
to: self.taker_ata_a.to_account_info(),
mint: self.mint_a.to_account_info(),
authority: self.escrow.to_account_info(),
},
&signer_seeds,
),
self.vault.amount,
self.mint_a.decimals,
)?;
// Close the Vault
close_account(CpiContext::new_with_signer(
self.token_program.to_account_info(),
CloseAccount {
account: self.vault.to_account_info(),
authority: self.escrow.to_account_info(),
destination: self.maker.to_account_info(),
},
&signer_seeds,
))?;
Ok(())
}
}Bây giờ chúng ta tạo hàm handler và lần này may mắn là chúng ta không cần thực hiện thêm kiểm tra nào nên nó sẽ trông như thế này:
pub fn handler(ctx: Context<Take>) -> Result<()> {
// Transfer Token B to Maker
ctx.accounts.transfer_to_maker()?;
// Withdraw and close the Vault
ctx.accounts.withdraw_and_close_vault()?;
Ok(())
}