express_relay/
token.rs

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}