1solana_program::declare_id!("CMTQqjzH6Anr9XcPVt73EFDTjWkJWPzH7H6DtvhHcyzV");
2
3use borsh::BorshDeserialize;
4use solana_program::{
5 account_info::AccountInfo, entrypoint::ProgramResult, msg, program::invoke,
6 program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, rent::Rent,
7 system_instruction, sysvar::Sysvar,
8};
9use spl_associated_token_account::instruction::create_associated_token_account;
10
11#[track_caller]
12#[inline(always)]
13pub fn assert_with_msg(v: bool, err: impl Into<ProgramError>, msg: &str) -> ProgramResult {
14 if v {
15 Ok(())
16 } else {
17 let caller = std::panic::Location::caller();
18 msg!("{}. \n{}", msg, caller);
19 Err(err.into())
20 }
21}
22
23pub mod accounts;
24pub mod instruction;
25pub mod token;
26use accounts::{
27 Approve, Burn, Close, InitializeAccount, InitializeMint, MigrateAuthority, Mint, Revoke,
28 Transfer,
29};
30use instruction::ManagedTokenInstruction;
31use spl_token::instruction::AuthorityType;
32use token::{
33 approve, burn, close, freeze, initialize_mint, mint_to, revoke, set_authority, thaw, transfer,
34};
35
36#[cfg(not(feature = "no-entrypoint"))]
37solana_program::entrypoint!(process_instruction);
38
39#[inline]
40fn get_authority_seeds_checked(
41 upstream_authority: &Pubkey,
42 expected_key: &Pubkey,
43) -> Result<Vec<Vec<u8>>, ProgramError> {
44 let (key, seeds) = get_authority(upstream_authority);
45 assert_with_msg(
46 expected_key == &key,
47 ProgramError::InvalidInstructionData,
48 "Invalid authority",
49 )?;
50 Ok(seeds)
51}
52
53#[inline]
54fn get_authority(upstream_authority: &Pubkey) -> (Pubkey, Vec<Vec<u8>>) {
55 let mut seeds = vec![upstream_authority.as_ref().to_vec()];
56 let (key, bump) = Pubkey::find_program_address(
57 &seeds.iter().map(|s| s.as_slice()).collect::<Vec<&[u8]>>(),
58 &crate::id(),
59 );
60 seeds.push(vec![bump]);
61 (key, seeds)
62}
63
64pub fn process_instruction(
65 _program_id: &Pubkey,
66 accounts: &[AccountInfo],
67 instruction_data: &[u8],
68) -> ProgramResult {
69 let instruction = ManagedTokenInstruction::try_from_slice(instruction_data)?;
70 match instruction {
71 ManagedTokenInstruction::InitializeMint { decimals } => {
72 msg!("ManagedTokenInstruction::InitializeMint");
73 process_initialize_mint(accounts, decimals)
74 }
75 ManagedTokenInstruction::InitializeAccount => {
76 msg!("ManagedTokenInstruction::InitializeAccount");
77 process_initialize_account(accounts)
78 }
79 ManagedTokenInstruction::Transfer { amount } => {
80 msg!("ManagedTokenInstruction::Transfer");
81 process_transfer(accounts, amount)
82 }
83 ManagedTokenInstruction::MintTo { amount } => {
84 msg!("ManagedTokenInstruction::MintTo");
85 process_mint_to(accounts, amount)
86 }
87 ManagedTokenInstruction::Burn { amount } => {
88 msg!("ManagedTokenInstruction::Burn");
89 process_burn(accounts, amount)
90 }
91 ManagedTokenInstruction::CloseAccount => {
92 msg!("ManagedTokenInstruction::CloseAccount");
93 process_close(accounts)
94 }
95 ManagedTokenInstruction::Approve { amount } => {
96 msg!("ManagedTokenInstruction::Approve");
97 process_approve(accounts, amount)
98 }
99 ManagedTokenInstruction::Revoke => {
100 msg!("ManagedTokenInstruction::Revoke");
101 process_revoke(accounts)
102 }
103 ManagedTokenInstruction::MigrateAuthority => {
104 msg!("ManagedTokenInstruction::MigrationAuthority");
105 process_migrate_authority(accounts)
106 }
107 }
108}
109
110pub fn process_initialize_mint(accounts: &[AccountInfo], decimals: u8) -> ProgramResult {
111 let InitializeMint {
112 mint,
113 payer,
114 upstream_authority,
115 system_program,
116 token_program,
117 } = InitializeMint::load(accounts)?;
118 let space = spl_token::state::Mint::LEN;
119 invoke(
120 &system_instruction::create_account(
121 payer.key,
122 mint.key,
123 Rent::get()?.minimum_balance(space),
124 space as u64,
125 token_program.key,
126 ),
127 &[payer.clone(), mint.clone(), system_program.clone()],
128 )?;
129 let (authority, _) = get_authority(upstream_authority.key);
130 initialize_mint(&authority, &authority, mint, token_program, decimals)
131}
132
133pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
134 let InitializeAccount {
135 token_account,
136 owner,
137 payer,
138 upstream_authority,
139 freeze_authority,
140 mint,
141 system_program,
142 associated_token_program,
143 token_program,
144 } = InitializeAccount::load(accounts)?;
145 invoke(
146 &create_associated_token_account(payer.key, owner.key, mint.key, token_program.key),
147 &[
148 associated_token_program.clone(),
149 payer.clone(),
150 owner.clone(),
151 token_account.clone(),
152 mint.clone(),
153 system_program.clone(),
154 token_program.clone(),
155 ],
156 )?;
157 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
158 freeze(freeze_authority, mint, token_account, token_program, &seeds)
159}
160
161pub fn process_transfer(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
162 let Transfer {
163 src_account,
164 dst_account,
165 mint,
166 owner,
167 upstream_authority,
168 freeze_authority,
169 token_program,
170 } = Transfer::load(accounts)?;
171 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
172 thaw(freeze_authority, mint, src_account, token_program, &seeds)?;
173 thaw(freeze_authority, mint, dst_account, token_program, &seeds)?;
174 transfer(src_account, dst_account, owner, token_program, amount)?;
175 freeze(freeze_authority, mint, dst_account, token_program, &seeds)?;
176 freeze(freeze_authority, mint, src_account, token_program, &seeds)
177}
178
179pub fn process_mint_to(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
180 let Mint {
181 mint,
182 token_account,
183 upstream_authority,
184 freeze_and_mint_authority: authority,
185 token_program,
186 } = Mint::load(accounts)?;
187 let authority_seeds = get_authority_seeds_checked(upstream_authority.key, authority.key)?;
188 thaw(
189 authority,
190 mint,
191 token_account,
192 token_program,
193 &authority_seeds,
194 )?;
195 mint_to(
196 mint,
197 token_account,
198 authority,
199 token_program,
200 amount,
201 &authority_seeds,
202 )?;
203 freeze(
204 authority,
205 mint,
206 token_account,
207 token_program,
208 &authority_seeds,
209 )
210}
211
212pub fn process_burn(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
213 let Burn {
214 mint,
215 token_account,
216 owner,
217 upstream_authority,
218 freeze_authority,
219 token_program,
220 } = Burn::load(accounts)?;
221 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
222 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
223 burn(mint, token_account, owner, token_program, amount)?;
224 freeze(freeze_authority, mint, token_account, token_program, &seeds)
225}
226
227pub fn process_close(accounts: &[AccountInfo]) -> ProgramResult {
228 let Close {
229 token_account,
230 dst_account,
231 mint,
232 owner,
233 upstream_authority,
234 freeze_authority,
235 token_program,
236 } = Close::load(accounts)?;
237 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
238 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
239 close(token_account, dst_account, owner, token_program)
240}
241
242pub fn process_approve(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
243 let Approve {
244 mint,
245 token_account,
246 owner,
247 upstream_authority,
248 delegate,
249 freeze_authority,
250 token_program,
251 } = Approve::load(accounts)?;
252 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
253 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
254 approve(token_account, owner, delegate, token_program, amount)?;
255 freeze(freeze_authority, mint, token_account, token_program, &seeds)
256}
257
258pub fn process_revoke(accounts: &[AccountInfo]) -> ProgramResult {
259 let Revoke {
260 mint,
261 token_account,
262 owner,
263 upstream_authority,
264 freeze_authority,
265 token_program,
266 } = Revoke::load(accounts)?;
267 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
268 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
269 revoke(token_account, owner, token_program)?;
270 freeze(freeze_authority, mint, token_account, token_program, &seeds)
271}
272
273pub fn process_migrate_authority(accounts: &[AccountInfo]) -> ProgramResult {
274 let MigrateAuthority {
275 mint,
276 upstream_authority,
277 authority,
278 new_freeze_authority,
279 new_mint_authority,
280 token_program,
281 } = MigrateAuthority::load(accounts)?;
282 let seeds = get_authority_seeds_checked(upstream_authority.key, authority.key)?;
283 set_authority(
284 mint,
285 new_mint_authority.key,
286 AuthorityType::MintTokens,
287 authority,
288 token_program,
289 &seeds,
290 )?;
291 set_authority(
292 mint,
293 new_freeze_authority.key,
294 AuthorityType::FreezeAccount,
295 authority,
296 token_program,
297 &seeds,
298 )
299}