community_managed_token/
lib.rs

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}