1use {
4 crate::{
5 collect_token_upgrade_authority_signer_seeds, error::TokenUpgradeError,
6 get_token_upgrade_authority_address_and_bump_seed, instruction::TokenUpgradeInstruction,
7 },
8 solana_program::{
9 account_info::{next_account_info, AccountInfo},
10 entrypoint::ProgramResult,
11 msg,
12 program::{invoke, invoke_signed},
13 program_error::ProgramError,
14 pubkey::Pubkey,
15 },
16 spl_token_2022::{
17 extension::StateWithExtensions,
18 instruction::decode_instruction_type,
19 state::{Account, Mint},
20 },
21};
22
23fn check_owner(account_info: &AccountInfo, expected_owner: &Pubkey) -> ProgramResult {
24 if account_info.owner != expected_owner {
25 Err(ProgramError::IllegalOwner)
26 } else {
27 Ok(())
28 }
29}
30
31fn burn_original_tokens<'a>(
32 original_token_program: AccountInfo<'a>,
33 source: AccountInfo<'a>,
34 mint: AccountInfo<'a>,
35 authority: AccountInfo<'a>,
36 multisig_signers: &[AccountInfo<'a>],
37 amount: u64,
38 decimals: u8,
39) -> ProgramResult {
40 let multisig_pubkeys = multisig_signers.iter().map(|s| s.key).collect::<Vec<_>>();
41 let ix = spl_token_2022::instruction::burn_checked(
42 original_token_program.key,
43 source.key,
44 mint.key,
45 authority.key,
46 &multisig_pubkeys,
47 amount,
48 decimals,
49 )?;
50 let mut account_infos = vec![source, mint, authority];
51 account_infos.extend_from_slice(multisig_signers);
52 invoke(&ix, &account_infos)
53}
54
55#[allow(clippy::too_many_arguments)]
56fn transfer_new_tokens<'a>(
57 new_token_program: AccountInfo<'a>,
58 source: AccountInfo<'a>,
59 mint: AccountInfo<'a>,
60 destination: AccountInfo<'a>,
61 authority: AccountInfo<'a>,
62 authority_seeds: &[&[u8]],
63 amount: u64,
64 decimals: u8,
65) -> Result<(), ProgramError> {
66 let ix = spl_token_2022::instruction::transfer_checked(
67 new_token_program.key,
68 source.key,
69 mint.key,
70 destination.key,
71 authority.key,
72 &[],
73 amount,
74 decimals,
75 )?;
76 invoke_signed(
77 &ix,
78 &[source, mint, destination, authority],
79 &[authority_seeds],
80 )
81}
82
83fn process_exchange(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
84 let account_info_iter = &mut accounts.iter();
85
86 let original_account_info = next_account_info(account_info_iter)?;
87 let original_mint_info = next_account_info(account_info_iter)?;
88 let new_escrow_info = next_account_info(account_info_iter)?;
89 let new_account_info = next_account_info(account_info_iter)?;
90 let new_mint_info = next_account_info(account_info_iter)?;
91 let new_transfer_authority_info = next_account_info(account_info_iter)?;
92 let original_token_program = next_account_info(account_info_iter)?;
93 let new_token_program = next_account_info(account_info_iter)?;
94 let original_transfer_authority_info = next_account_info(account_info_iter)?;
95
96 check_owner(original_account_info, original_token_program.key)?;
98 check_owner(original_mint_info, original_token_program.key)?;
99 check_owner(new_escrow_info, new_token_program.key)?;
100 check_owner(new_account_info, new_token_program.key)?;
101 check_owner(new_mint_info, new_token_program.key)?;
102
103 let (expected_escrow_authority, bump_seed) = get_token_upgrade_authority_address_and_bump_seed(
105 original_mint_info.key,
106 new_mint_info.key,
107 program_id,
108 );
109 let bump_seed = [bump_seed];
110 let authority_seeds = collect_token_upgrade_authority_signer_seeds(
111 original_mint_info.key,
112 new_mint_info.key,
113 &bump_seed,
114 );
115 if expected_escrow_authority != *new_transfer_authority_info.key {
116 msg!(
117 "Expected escrow authority {}, received {}",
118 &expected_escrow_authority,
119 new_transfer_authority_info.key
120 );
121 return Err(TokenUpgradeError::InvalidOwner.into());
122 }
123
124 let (token_amount, decimals) = {
126 let original_mint_data = original_mint_info.try_borrow_data()?;
128 let original_mint = StateWithExtensions::<Mint>::unpack(&original_mint_data)?;
129 let new_mint_data = new_mint_info.try_borrow_data()?;
130 let new_mint = StateWithExtensions::<Mint>::unpack(&new_mint_data)?;
131
132 let original_account_data = original_account_info.try_borrow_data()?;
134 let original_account = StateWithExtensions::<Account>::unpack(&original_account_data)?;
135 let new_escrow_data = new_escrow_info.try_borrow_data()?;
136 let new_escrow = StateWithExtensions::<Account>::unpack(&new_escrow_data)?;
137 let new_account_data = new_account_info.try_borrow_data()?;
138 let _ = StateWithExtensions::<Account>::unpack(&new_account_data)?;
139
140 let token_amount = original_account.base.amount;
141 if new_escrow.base.amount < token_amount {
142 msg!(
143 "Escrow only has {} tokens, needs at least {}",
144 new_escrow.base.amount,
145 token_amount
146 );
147 return Err(ProgramError::InsufficientFunds);
148 }
149 if original_mint.base.decimals != new_mint.base.decimals {
150 msg!(
151 "Original and new token mint decimals mismatch: original has {} decimals, and new has {}",
152 original_mint.base.decimals,
153 new_mint.base.decimals,
154 );
155 return Err(TokenUpgradeError::DecimalsMismatch.into());
156 }
157
158 (original_account.base.amount, original_mint.base.decimals)
159 };
160
161 burn_original_tokens(
162 original_token_program.clone(),
163 original_account_info.clone(),
164 original_mint_info.clone(),
165 original_transfer_authority_info.clone(),
166 account_info_iter.as_slice(),
167 token_amount,
168 decimals,
169 )?;
170
171 transfer_new_tokens(
172 new_token_program.clone(),
173 new_escrow_info.clone(),
174 new_mint_info.clone(),
175 new_account_info.clone(),
176 new_transfer_authority_info.clone(),
177 &authority_seeds,
178 token_amount,
179 decimals,
180 )?;
181
182 Ok(())
183}
184
185pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
187 match decode_instruction_type(input)? {
188 TokenUpgradeInstruction::Exchange => process_exchange(program_id, accounts),
189 }
190}