1use {
2 anchor_lang::prelude::*,
3 anchor_spl::{
4 associated_token::get_associated_token_address_with_program_id,
5 token_interface::{
6 self,
7 Mint,
8 TokenAccount,
9 TokenInterface,
10 TransferChecked,
11 },
12 },
13};
14
15pub fn get_token_account_checked(
16 account: &AccountInfo,
17 expected_program_id: &Pubkey,
18) -> Result<TokenAccount> {
19 if account.data_len() == 0 {
20 return Err(ErrorCode::AccountNotInitialized.into());
21 }
22
23 if account.owner != expected_program_id {
24 return Err(ErrorCode::ConstraintTokenTokenProgram.into());
25 }
26
27 let token_account = match TokenAccount::try_deserialize(&mut &account.data.borrow()[..]) {
28 Ok(ta) => ta,
29 Err(_) => return Err(ErrorCode::AccountDidNotDeserialize.into()),
30 };
31
32 Ok(token_account)
33}
34pub fn transfer_token_if_needed<'info>(
35 from: &InterfaceAccount<'info, TokenAccount>,
36 to: AccountInfo<'info>,
37 token_program: &Interface<'info, TokenInterface>,
38 authority: &Signer<'info>,
39 mint: &InterfaceAccount<'info, Mint>,
40 amount: u64,
41) -> Result<()> {
42 if amount > 0 {
43 let cpi_accounts = TransferChecked {
44 from: from.to_account_info(),
45 to,
46 mint: mint.to_account_info(),
47 authority: authority.to_account_info(),
48 };
49
50 token_interface::transfer_checked(
51 CpiContext::new(token_program.to_account_info(), cpi_accounts),
52 amount,
53 mint.decimals,
54 )?;
55 }
56 Ok(())
57}
58
59pub fn check_receiver_and_transfer_token_if_needed<'info>(
60 from: &InterfaceAccount<'info, TokenAccount>,
61 recipient_token_account: &UncheckedAccount<'info>,
62 recipient: Option<&Pubkey>,
63 token_program: &Interface<'info, TokenInterface>,
64 authority: &Signer<'info>,
65 mint: &InterfaceAccount<'info, Mint>,
66 amount: u64,
67) -> Result<()> {
68 if amount > 0 {
69 let to = get_token_account_checked(
70 &recipient_token_account.to_account_info(),
71 &token_program.key(),
72 )?;
73
74 if let Some(recipient) = recipient {
75 if !to.owner.eq(recipient) {
76 return Err(ErrorCode::ConstraintTokenOwner.into());
77 }
78 if get_associated_token_address_with_program_id(
79 recipient,
80 &mint.key(),
81 &token_program.key(),
82 ) != recipient_token_account.key()
83 {
84 return Err(ErrorCode::AccountNotAssociatedTokenAccount.into());
85 }
86 }
87 if !to.mint.eq(&mint.key()) {
88 return Err(ErrorCode::ConstraintTokenMint.into());
89 }
90 transfer_token_if_needed(
91 from,
92 recipient_token_account.to_account_info(),
93 token_program,
94 authority,
95 mint,
96 amount,
97 )?;
98 }
99 Ok(())
100}