1solana_program::declare_id!("MgTkZ2uns1oBn688aY1xJ6TJ1sVnEmcvNGdrpT7c9r2");
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::{Approve, Burn, Close, InitializeAccount, InitializeMint, Mint, Revoke, Transfer};
27use instruction::ManagedTokenInstruction;
28use token::{approve, burn, close, freeze, initialize_mint, mint_to, revoke, thaw, transfer};
29
30#[cfg(not(feature = "no-entrypoint"))]
31solana_program::entrypoint!(process_instruction);
32
33#[inline]
34fn get_authority_seeds_checked(
35 upstream_authority: &Pubkey,
36 expected_key: &Pubkey,
37) -> Result<Vec<Vec<u8>>, ProgramError> {
38 let (key, seeds) = get_authority(upstream_authority);
39 assert_with_msg(
40 expected_key == &key,
41 ProgramError::InvalidInstructionData,
42 "Invalid authority",
43 )?;
44 Ok(seeds)
45}
46
47#[inline]
48fn get_authority(upstream_authority: &Pubkey) -> (Pubkey, Vec<Vec<u8>>) {
49 let mut seeds = vec![upstream_authority.as_ref().to_vec()];
50 let (key, bump) = Pubkey::find_program_address(
51 &seeds.iter().map(|s| s.as_slice()).collect::<Vec<&[u8]>>(),
52 &crate::id(),
53 );
54 seeds.push(vec![bump]);
55 (key, seeds)
56}
57
58pub fn process_instruction(
59 _program_id: &Pubkey,
60 accounts: &[AccountInfo],
61 instruction_data: &[u8],
62) -> ProgramResult {
63 let instruction = ManagedTokenInstruction::try_from_slice(instruction_data)?;
64 match instruction {
65 ManagedTokenInstruction::InitializeMint { decimals } => {
66 msg!("ManagedTokenInstruction::InitializeMint");
67 process_initialize_mint(accounts, decimals)
68 }
69 ManagedTokenInstruction::InitializeAccount => {
70 msg!("ManagedTokenInstruction::InitializeAccount");
71 process_initialize_account(accounts)
72 }
73 ManagedTokenInstruction::Transfer { amount } => {
74 msg!("ManagedTokenInstruction::Transfer");
75 process_transfer(accounts, amount)
76 }
77 ManagedTokenInstruction::MintTo { amount } => {
78 msg!("ManagedTokenInstruction::MintTo");
79 process_mint_to(accounts, amount)
80 }
81 ManagedTokenInstruction::Burn { amount } => {
82 msg!("ManagedTokenInstruction::Burn");
83 process_burn(accounts, amount)
84 }
85 ManagedTokenInstruction::CloseAccount => {
86 msg!("ManagedTokenInstruction::CloseAccount");
87 process_close(accounts)
88 }
89 ManagedTokenInstruction::Approve { amount } => {
90 msg!("ManagedTokenInstruction::Approve");
91 process_approve(accounts, amount)
92 }
93 ManagedTokenInstruction::Revoke => {
94 msg!("ManagedTokenInstruction::Revoke");
95 process_revoke(accounts)
96 }
97 }
98}
99
100pub fn process_initialize_mint(accounts: &[AccountInfo], decimals: u8) -> ProgramResult {
101 let InitializeMint {
102 mint,
103 payer,
104 upstream_authority,
105 system_program,
106 token_program,
107 } = InitializeMint::load(accounts)?;
108 let space = spl_token::state::Mint::LEN;
109 invoke(
110 &system_instruction::create_account(
111 payer.key,
112 mint.key,
113 Rent::get()?.minimum_balance(space),
114 space as u64,
115 token_program.key,
116 ),
117 &[payer.clone(), mint.clone(), system_program.clone()],
118 )?;
119 let (authority, _) = get_authority(upstream_authority.key);
120 initialize_mint(&authority, &authority, mint, token_program, decimals)
121}
122
123pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
124 let InitializeAccount {
125 token_account,
126 owner,
127 payer,
128 upstream_authority,
129 freeze_authority,
130 mint,
131 system_program,
132 associated_token_program,
133 token_program,
134 } = InitializeAccount::load(accounts)?;
135 invoke(
136 &create_associated_token_account(payer.key, owner.key, mint.key, token_program.key),
137 &[
138 associated_token_program.clone(),
139 payer.clone(),
140 owner.clone(),
141 token_account.clone(),
142 mint.clone(),
143 system_program.clone(),
144 token_program.clone(),
145 ],
146 )?;
147 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
148 freeze(freeze_authority, mint, token_account, token_program, &seeds)
149}
150
151pub fn process_transfer(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
152 let Transfer {
153 src_account,
154 dst_account,
155 mint,
156 owner,
157 upstream_authority,
158 freeze_authority,
159 token_program,
160 } = Transfer::load(accounts)?;
161 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
162 thaw(freeze_authority, mint, src_account, token_program, &seeds)?;
163 thaw(freeze_authority, mint, dst_account, token_program, &seeds)?;
164 transfer(src_account, dst_account, owner, token_program, amount)?;
165 freeze(freeze_authority, mint, dst_account, token_program, &seeds)?;
166 freeze(freeze_authority, mint, src_account, token_program, &seeds)
167}
168
169pub fn process_mint_to(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
170 let Mint {
171 mint,
172 token_account,
173 upstream_authority,
174 freeze_and_mint_authority: authority,
175 token_program,
176 } = Mint::load(accounts)?;
177 let authority_seeds = get_authority_seeds_checked(upstream_authority.key, authority.key)?;
178 thaw(
179 authority,
180 mint,
181 token_account,
182 token_program,
183 &authority_seeds,
184 )?;
185 mint_to(
186 mint,
187 token_account,
188 authority,
189 token_program,
190 amount,
191 &authority_seeds,
192 )?;
193 freeze(
194 authority,
195 mint,
196 token_account,
197 token_program,
198 &authority_seeds,
199 )
200}
201
202pub fn process_burn(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
203 let Burn {
204 mint,
205 token_account,
206 owner,
207 upstream_authority,
208 freeze_authority,
209 token_program,
210 } = Burn::load(accounts)?;
211 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
212 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
213 burn(mint, token_account, owner, token_program, amount)?;
214 freeze(freeze_authority, mint, token_account, token_program, &seeds)
215}
216
217pub fn process_close(accounts: &[AccountInfo]) -> ProgramResult {
218 let Close {
219 token_account,
220 dst_account,
221 mint,
222 owner,
223 upstream_authority,
224 freeze_authority,
225 token_program,
226 } = Close::load(accounts)?;
227 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
228 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
229 close(token_account, dst_account, owner, token_program)
230}
231
232pub fn process_approve(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
233 let Approve {
234 mint,
235 token_account,
236 owner,
237 upstream_authority,
238 delegate,
239 freeze_authority,
240 token_program,
241 } = Approve::load(accounts)?;
242 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
243 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
244 approve(token_account, owner, delegate, token_program, amount)?;
245 freeze(freeze_authority, mint, token_account, token_program, &seeds)
246}
247
248pub fn process_revoke(accounts: &[AccountInfo]) -> ProgramResult {
249 let Revoke {
250 mint,
251 token_account,
252 owner,
253 upstream_authority,
254 freeze_authority,
255 token_program,
256 } = Revoke::load(accounts)?;
257 let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
258 thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
259 revoke(token_account, owner, token_program)?;
260 freeze(freeze_authority, mint, token_account, token_program, &seeds)
261}