Anchor
Anchor Escrow

Anchor Escrow

42 Graduates

Take

We can now move to the take instruction, that lives in the take.rs and will perform this actions:

  • Close the escrow record, sending its rent lamports back to the maker.
  • Move Token A from the vault to the taker, then close the vault.
  • Move the agreed amount of Token B from the taker to the maker.

Accounts

The accounts needed in this context are:

  • taker: the user that accepted the terms of the maker` and is making the exchange
  • maker: the user that initially set the terms
  • escrow: the account where all the terms of this exchange lives
  • mint_a: the token that the maker has deposited
  • mint_b: the token that the maker wants in exchange
  • vault: the token account associated with the escrow and mint_a that will send the tokens to the taker
  • taker_ata_a: the token account associated with the taker and mint_a that will receive the tokens from the vault
  • taker_ata_b: the token account associated with the taker and mint_b that will send the tokens to the maker
  • maker_ata_b: the token account associated with the maker and mint_b that will receive the tokens to the taker
  • associated_token_program: the associated token program used to create the associated token accounts
  • token_program: the token program used to CPI the transfer
  • system_program: the system program used to create the Escrow

And with all the constraint it will look something like this:

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

In the logic we then start by transfering the tokens from the taker_ata_b to the maker_ata_b; we then move onto transferring the tokens from the vault to the taker_ata_a before closing the now empty vault like this:

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

We know create the handler function and this time luckily we don't need to perform any additional like checks so it will look like this:

rust
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(())
}
Next PageRefund
OR SKIP TO THE CHALLENGE
Ready to take the challenge?
Contents
View Source
Blueshift © 2025Commit: cc5f933