spl_managed_token/
lib.rs

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}