Skip to main content

apl_associated_token_account/
lib.rs

1//! Program state processor
2
3mod tools;
4
5use arch_program::{
6    account::{next_account_info, AccountInfo, AccountMeta},
7    entrypoint::ProgramResult,
8    instruction::Instruction,
9    msg,
10    program::invoke,
11    program_error::ProgramError,
12    program_pack::Pack,
13    pubkey::Pubkey,
14};
15use tools::{create_pda_account, create_pda_account_with_anchor};
16
17#[cfg(not(feature = "no-entrypoint"))]
18use arch_program::entrypoint;
19
20arch_program::declare_id!("ATok9pxLsNzM5zJJ3UQpXBrMriHpZiY5Yio3GKYU4we3");
21
22#[cfg(not(feature = "no-entrypoint"))]
23entrypoint!(process_instruction);
24
25/// Instruction processor
26pub fn process_instruction(
27    program_id: &Pubkey,
28    accounts: &[AccountInfo],
29    input: &[u8],
30) -> ProgramResult {
31    let account_info_iter = &mut accounts.iter();
32
33    let funder_info = next_account_info(account_info_iter)?;
34    let associated_token_account_info = next_account_info(account_info_iter)?;
35    let wallet_account_info = next_account_info(account_info_iter)?;
36    let spl_token_mint_info = next_account_info(account_info_iter)?;
37    let system_program_info = next_account_info(account_info_iter)?;
38    let spl_token_program_info = next_account_info(account_info_iter)?;
39    let spl_token_program_id = spl_token_program_info.key;
40
41    let (associated_token_address, bump_seed) = get_associated_token_address_and_bump_seed(
42        wallet_account_info.key,
43        spl_token_mint_info.key,
44        program_id,
45    );
46    if associated_token_address != *associated_token_account_info.key {
47        msg!("Error: Associated address does not match seed derivation");
48        return Err(ProgramError::InvalidSeeds);
49    }
50
51    let associated_token_account_signer_seeds: &[&[_]] = &[
52        &wallet_account_info.key.serialize(),
53        &apl_token::id().serialize(),
54        &spl_token_mint_info.key.serialize(),
55        &[bump_seed],
56    ];
57
58    if input.len() == 36 {
59        let txid: [u8; 32] = input[..32].try_into().unwrap();
60        let vout = u32::from_le_bytes(input[32..36].try_into().unwrap());
61
62        create_pda_account_with_anchor(
63            funder_info,
64            apl_token::state::Account::LEN,
65            spl_token_program_info.key,
66            txid,
67            vout,
68            system_program_info,
69            associated_token_account_info,
70            associated_token_account_signer_seeds,
71        )?;
72    } else {
73        create_pda_account(
74            funder_info,
75            apl_token::state::Account::LEN,
76            spl_token_program_info.key,
77            system_program_info,
78            associated_token_account_info,
79            associated_token_account_signer_seeds,
80        )?;
81    }
82
83    msg!("Initialize the associated token account");
84    invoke(
85        &apl_token::instruction::initialize_account3(
86            spl_token_program_id,
87            associated_token_account_info.key,
88            spl_token_mint_info.key,
89            wallet_account_info.key,
90        )?,
91        &[
92            associated_token_account_info.clone(),
93            spl_token_mint_info.clone(),
94            wallet_account_info.clone(),
95            spl_token_program_info.clone(),
96        ],
97    )
98}
99
100pub fn get_associated_token_address_and_bump_seed(
101    wallet_address: &Pubkey,
102    spl_token_mint_address: &Pubkey,
103    program_id: &Pubkey,
104) -> (Pubkey, u8) {
105    Pubkey::find_program_address(
106        &[
107            &wallet_address.serialize(),
108            &apl_token::id().serialize(),
109            &spl_token_mint_address.serialize(),
110        ],
111        program_id,
112    )
113}
114
115pub fn create_associated_token_account(
116    funder: &Pubkey,
117    associated_token_account: &Pubkey,
118    wallet: &Pubkey,
119    mint: &Pubkey,
120    spl_token_program: &Pubkey,
121    system_program: &Pubkey,
122) -> Instruction {
123    Instruction::new(
124        id(),
125        vec![],
126        vec![
127            AccountMeta::new(*funder, true),
128            AccountMeta::new(*associated_token_account, false),
129            AccountMeta::new_readonly(*wallet, false),
130            AccountMeta::new_readonly(*mint, false),
131            AccountMeta::new_readonly(*system_program, false),
132            AccountMeta::new_readonly(*spl_token_program, false),
133        ],
134    )
135}
136
137#[allow(clippy::too_many_arguments)]
138pub fn create_associated_token_account_with_anchor(
139    funder: &Pubkey,
140    associated_token_account: &Pubkey,
141    wallet: &Pubkey,
142    mint: &Pubkey,
143    spl_token_program: &Pubkey,
144    system_program: &Pubkey,
145    txid: [u8; 32],
146    vout: u32,
147) -> Instruction {
148    let mut data = txid.to_vec();
149    data.extend_from_slice(&vout.to_le_bytes());
150
151    Instruction::new(
152        id(),
153        data,
154        vec![
155            AccountMeta::new(*funder, true),
156            AccountMeta::new(*associated_token_account, false),
157            AccountMeta::new_readonly(*wallet, false),
158            AccountMeta::new_readonly(*mint, false),
159            AccountMeta::new_readonly(*system_program, false),
160            AccountMeta::new_readonly(*spl_token_program, false),
161        ],
162    )
163}