Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ use anchor_lang::prelude::*;

use anchor_spl::{
associated_token::AssociatedToken,
token_interface::{
close_account, transfer_checked, CloseAccount, Mint, TokenAccount, TokenInterface,
TransferChecked,
},
token_interface::{Mint, TokenAccount, TokenInterface},
};

use crate::Offer;

use super::{close_token_account, transfer_tokens};

// Cancel an outstanding offer. Without this handler, an abandoned offer would
// keep the maker's token-A locked in the vault forever (and the offer
// account's rent unclaimed). The maker signs, the vault tokens flow back to
Expand Down Expand Up @@ -55,45 +54,28 @@ pub struct CancelOffer<'info> {
pub fn handle_cancel_offer(context: Context<CancelOffer>) -> Result<()> {
let maker_key = context.accounts.maker.key();
let id_bytes = context.accounts.offer.id.to_le_bytes();
let seeds = &[
b"offer".as_ref(),
maker_key.as_ref(),
id_bytes.as_ref(),
&[context.accounts.offer.bump],
];
let signer_seeds = [&seeds[..]];
let bump = [context.accounts.offer.bump];
let offer_seeds: &[&[u8]] = &[b"offer", maker_key.as_ref(), id_bytes.as_ref(), &bump];

// Move all tokens back from the vault to the maker.
let vault_amount = context.accounts.vault.amount;
let transfer_accounts = TransferChecked {
from: context.accounts.vault.to_account_info(),
mint: context.accounts.token_mint_a.to_account_info(),
to: context.accounts.maker_token_account_a.to_account_info(),
authority: context.accounts.offer.to_account_info(),
};
let cpi_context = CpiContext::new_with_signer(
context.accounts.token_program.key(),
transfer_accounts,
&signer_seeds,
);
transfer_checked(
cpi_context,
vault_amount,
context.accounts.token_mint_a.decimals,
transfer_tokens(
&context.accounts.vault,
&context.accounts.maker_token_account_a,
&context.accounts.vault.amount,
&context.accounts.token_mint_a,
&context.accounts.offer.to_account_info(),
&context.accounts.token_program,
Some(offer_seeds),
)?;

// Close the vault, sending its rent lamports back to the maker.
let close_accounts = CloseAccount {
account: context.accounts.vault.to_account_info(),
destination: context.accounts.maker.to_account_info(),
authority: context.accounts.offer.to_account_info(),
};
let cpi_context = CpiContext::new_with_signer(
context.accounts.token_program.key(),
close_accounts,
&signer_seeds,
);
close_account(cpi_context)?;
close_token_account(
&context.accounts.vault,
&context.accounts.maker.to_account_info(),
&context.accounts.offer.to_account_info(),
&context.accounts.token_program,
Some(offer_seeds),
)?;

// The offer account itself is closed by the `close = maker` constraint
// above, which refunds its rent to the maker.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ pub fn handle_send_offered_tokens_to_vault(
&context.accounts.vault,
&token_a_offered_amount,
&context.accounts.token_mint_a,
&context.accounts.maker,
&context.accounts.maker.to_account_info(),
&context.accounts.token_program,
None,
)
}

Expand Down
44 changes: 41 additions & 3 deletions finance/escrow/anchor/programs/escrow/src/instructions/shared.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
use anchor_lang::prelude::*;

use anchor_spl::token_interface::{
transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked,
close_account, transfer_checked, CloseAccount, Mint, TokenAccount, TokenInterface,
TransferChecked,
};

// Transfer tokens from one token account to another.
// When transferring out of a token account owned by a PDA, pass the PDA's
// signer seeds via owning_pda_seeds; otherwise pass None.
pub fn transfer_tokens<'info>(
from: &InterfaceAccount<'info, TokenAccount>,
to: &InterfaceAccount<'info, TokenAccount>,
amount: &u64,
mint: &InterfaceAccount<'info, Mint>,
authority: &Signer<'info>,
authority: &AccountInfo<'info>,
token_program: &Interface<'info, TokenInterface>,
owning_pda_seeds: Option<&[&[u8]]>,
) -> Result<()> {
let transfer_accounts = TransferChecked {
from: from.to_account_info(),
Expand All @@ -19,7 +24,40 @@ pub fn transfer_tokens<'info>(
authority: authority.to_account_info(),
};

let cpi_context = CpiContext::new(token_program.key(), transfer_accounts);
let signer_seeds = owning_pda_seeds.map(|seeds| [seeds]);
let cpi_context = match signer_seeds.as_ref() {
Some(signer_seeds) => {
CpiContext::new_with_signer(token_program.key(), transfer_accounts, signer_seeds)
}
None => CpiContext::new(token_program.key(), transfer_accounts),
};

transfer_checked(cpi_context, *amount, mint.decimals)
}

// Close a token account, sending its rent lamports to destination.
// When the token account is owned by a PDA, pass the PDA's signer seeds via
// owning_pda_seeds; otherwise pass None.
pub fn close_token_account<'info>(
token_account: &InterfaceAccount<'info, TokenAccount>,
destination: &AccountInfo<'info>,
authority: &AccountInfo<'info>,
token_program: &Interface<'info, TokenInterface>,
owning_pda_seeds: Option<&[&[u8]]>,
) -> Result<()> {
let close_accounts = CloseAccount {
account: token_account.to_account_info(),
destination: destination.to_account_info(),
authority: authority.to_account_info(),
};

let signer_seeds = owning_pda_seeds.map(|seeds| [seeds]);
let cpi_context = match signer_seeds.as_ref() {
Some(signer_seeds) => {
CpiContext::new_with_signer(token_program.key(), close_accounts, signer_seeds)
}
None => CpiContext::new(token_program.key(), close_accounts),
};

close_account(cpi_context)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ use anchor_lang::prelude::*;

use anchor_spl::{
associated_token::AssociatedToken,
token_interface::{
close_account, transfer_checked, CloseAccount, Mint, TokenAccount, TokenInterface,
TransferChecked,
},
token_interface::{Mint, TokenAccount, TokenInterface},
};

use crate::Offer;

use super::transfer_tokens;
use super::{close_token_account, transfer_tokens};

#[derive(Accounts)]
pub struct TakeOffer<'info> {
Expand Down Expand Up @@ -82,50 +79,33 @@ pub fn handle_send_wanted_tokens_to_maker(context: &Context<TakeOffer>) -> Resul
&context.accounts.maker_token_account_b,
&context.accounts.offer.token_b_wanted_amount,
&context.accounts.token_mint_b,
&context.accounts.taker,
&context.accounts.taker.to_account_info(),
&context.accounts.token_program,
None,
)
}

pub fn handle_withdraw_and_close_vault(context: Context<TakeOffer>) -> Result<()> {
let seeds = &[
b"offer",
context.accounts.maker.to_account_info().key.as_ref(),
&context.accounts.offer.id.to_le_bytes()[..],
&[context.accounts.offer.bump],
];
let signer_seeds = [&seeds[..]];

let accounts = TransferChecked {
from: context.accounts.vault.to_account_info(),
mint: context.accounts.token_mint_a.to_account_info(),
to: context.accounts.taker_token_account_a.to_account_info(),
authority: context.accounts.offer.to_account_info(),
};

let cpi_context = CpiContext::new_with_signer(
context.accounts.token_program.key(),
accounts,
&signer_seeds,
);

transfer_checked(
cpi_context,
context.accounts.vault.amount,
context.accounts.token_mint_a.decimals,
)?;

let accounts = CloseAccount {
account: context.accounts.vault.to_account_info(),
destination: context.accounts.taker.to_account_info(),
authority: context.accounts.offer.to_account_info(),
};
let maker_key = context.accounts.maker.key();
let id_bytes = context.accounts.offer.id.to_le_bytes();
let bump = [context.accounts.offer.bump];
let offer_seeds: &[&[u8]] = &[b"offer", maker_key.as_ref(), id_bytes.as_ref(), &bump];

let cpi_context = CpiContext::new_with_signer(
context.accounts.token_program.key(),
accounts,
&signer_seeds,
);
transfer_tokens(
&context.accounts.vault,
&context.accounts.taker_token_account_a,
&context.accounts.vault.amount,
&context.accounts.token_mint_a,
&context.accounts.offer.to_account_info(),
&context.accounts.token_program,
Some(offer_seeds),
)?;

close_account(cpi_context)
close_token_account(
&context.accounts.vault,
&context.accounts.taker.to_account_info(),
&context.accounts.offer.to_account_info(),
&context.accounts.token_program,
Some(offer_seeds),
)
}
Loading