spl_simplified/
token.rs

1use anchor_lang::solana_program::account_info::AccountInfo;
2
3use anchor_lang::solana_program::program_pack::Pack;
4use anchor_lang::solana_program::pubkey::Pubkey;
5use anchor_lang::{context::CpiContext, Accounts};
6use anchor_lang::{solana_program, Result};
7use std::ops::Deref;
8
9pub use spl_token;
10pub use spl_token::ID;
11
12pub fn transfer<'info>(
13    ctx: CpiContext<'_, '_, '_, 'info, Transfer<'info>>,
14    amount: u64,
15) -> Result<()> {
16    let ix = spl_token::instruction::transfer(
17        &spl_token::ID,
18        ctx.accounts.from.key,
19        ctx.accounts.to.key,
20        ctx.accounts.authority.key,
21        &[],
22        amount,
23    )?;
24    solana_program::program::invoke_signed(
25        &ix,
26        &[ctx.accounts.from, ctx.accounts.to, ctx.accounts.authority],
27        ctx.signer_seeds,
28    )
29    .map_err(Into::into)
30}
31
32pub fn transfer_checked<'info>(
33    ctx: CpiContext<'_, '_, '_, 'info, TransferChecked<'info>>,
34    amount: u64,
35    decimals: u8,
36) -> Result<()> {
37    let ix = spl_token::instruction::transfer_checked(
38        &spl_token::ID,
39        ctx.accounts.from.key,
40        ctx.accounts.mint.key,
41        ctx.accounts.to.key,
42        ctx.accounts.authority.key,
43        &[],
44        amount,
45        decimals,
46    )?;
47    solana_program::program::invoke_signed(
48        &ix,
49        &[
50            ctx.accounts.from,
51            ctx.accounts.mint,
52            ctx.accounts.to,
53            ctx.accounts.authority,
54        ],
55        ctx.signer_seeds,
56    )
57    .map_err(Into::into)
58}
59
60pub fn mint_to<'info>(
61    ctx: CpiContext<'_, '_, '_, 'info, MintTo<'info>>,
62    amount: u64,
63) -> Result<()> {
64    let ix = spl_token::instruction::mint_to(
65        &spl_token::ID,
66        ctx.accounts.mint.key,
67        ctx.accounts.to.key,
68        ctx.accounts.authority.key,
69        &[],
70        amount,
71    )?;
72    solana_program::program::invoke_signed(
73        &ix,
74        &[ctx.accounts.to, ctx.accounts.mint, ctx.accounts.authority],
75        ctx.signer_seeds,
76    )
77    .map_err(Into::into)
78}
79
80pub fn burn<'info>(ctx: CpiContext<'_, '_, '_, 'info, Burn<'info>>, amount: u64) -> Result<()> {
81    let ix = spl_token::instruction::burn(
82        &spl_token::ID,
83        ctx.accounts.from.key,
84        ctx.accounts.mint.key,
85        ctx.accounts.authority.key,
86        &[],
87        amount,
88    )?;
89    solana_program::program::invoke_signed(
90        &ix,
91        &[ctx.accounts.from, ctx.accounts.mint, ctx.accounts.authority],
92        ctx.signer_seeds,
93    )
94    .map_err(Into::into)
95}
96
97pub fn approve<'info>(
98    ctx: CpiContext<'_, '_, '_, 'info, Approve<'info>>,
99    amount: u64,
100) -> Result<()> {
101    let ix = spl_token::instruction::approve(
102        &spl_token::ID,
103        ctx.accounts.to.key,
104        ctx.accounts.delegate.key,
105        ctx.accounts.authority.key,
106        &[],
107        amount,
108    )?;
109    solana_program::program::invoke_signed(
110        &ix,
111        &[
112            ctx.accounts.to,
113            ctx.accounts.delegate,
114            ctx.accounts.authority,
115        ],
116        ctx.signer_seeds,
117    )
118    .map_err(Into::into)
119}
120
121pub fn approve_checked<'info>(
122    ctx: CpiContext<'_, '_, '_, 'info, ApproveChecked<'info>>,
123    amount: u64,
124    decimals: u8,
125) -> Result<()> {
126    let ix = spl_token::instruction::approve_checked(
127        &spl_token::ID,
128        ctx.accounts.to.key,
129        ctx.accounts.mint.key,
130        ctx.accounts.delegate.key,
131        ctx.accounts.authority.key,
132        &[],
133        amount,
134        decimals,
135    )?;
136    solana_program::program::invoke_signed(
137        &ix,
138        &[
139            ctx.accounts.to,
140            ctx.accounts.mint,
141            ctx.accounts.delegate,
142            ctx.accounts.authority,
143        ],
144        ctx.signer_seeds,
145    )
146    .map_err(Into::into)
147}
148
149pub fn revoke<'info>(ctx: CpiContext<'_, '_, '_, 'info, Revoke<'info>>) -> Result<()> {
150    let ix = spl_token::instruction::revoke(
151        &spl_token::ID,
152        ctx.accounts.source.key,
153        ctx.accounts.authority.key,
154        &[],
155    )?;
156    solana_program::program::invoke_signed(
157        &ix,
158        &[ctx.accounts.source, ctx.accounts.authority],
159        ctx.signer_seeds,
160    )
161    .map_err(Into::into)
162}
163
164pub fn initialize_account<'info>(
165    ctx: CpiContext<'_, '_, '_, 'info, InitializeAccount<'info>>,
166) -> Result<()> {
167    let ix = spl_token::instruction::initialize_account(
168        &spl_token::ID,
169        ctx.accounts.account.key,
170        ctx.accounts.mint.key,
171        ctx.accounts.authority.key,
172    )?;
173    solana_program::program::invoke_signed(
174        &ix,
175        &[
176            ctx.accounts.account,
177            ctx.accounts.mint,
178            ctx.accounts.authority,
179            ctx.accounts.rent,
180        ],
181        ctx.signer_seeds,
182    )
183    .map_err(Into::into)
184}
185
186pub fn initialize_account3<'info>(
187    ctx: CpiContext<'_, '_, '_, 'info, InitializeAccount3<'info>>,
188) -> Result<()> {
189    let ix = spl_token::instruction::initialize_account3(
190        &spl_token::ID,
191        ctx.accounts.account.key,
192        ctx.accounts.mint.key,
193        ctx.accounts.authority.key,
194    )?;
195    solana_program::program::invoke_signed(
196        &ix,
197        &[ctx.accounts.account, ctx.accounts.mint],
198        ctx.signer_seeds,
199    )
200    .map_err(Into::into)
201}
202
203pub fn close_account<'info>(ctx: CpiContext<'_, '_, '_, 'info, CloseAccount<'info>>) -> Result<()> {
204    let ix = spl_token::instruction::close_account(
205        &spl_token::ID,
206        ctx.accounts.account.key,
207        ctx.accounts.destination.key,
208        ctx.accounts.authority.key,
209        &[], // TODO: support multisig
210    )?;
211    solana_program::program::invoke_signed(
212        &ix,
213        &[
214            ctx.accounts.account,
215            ctx.accounts.destination,
216            ctx.accounts.authority,
217        ],
218        ctx.signer_seeds,
219    )
220    .map_err(Into::into)
221}
222
223pub fn freeze_account<'info>(
224    ctx: CpiContext<'_, '_, '_, 'info, FreezeAccount<'info>>,
225) -> Result<()> {
226    let ix = spl_token::instruction::freeze_account(
227        &spl_token::ID,
228        ctx.accounts.account.key,
229        ctx.accounts.mint.key,
230        ctx.accounts.authority.key,
231        &[], // TODO: Support multisig signers.
232    )?;
233    solana_program::program::invoke_signed(
234        &ix,
235        &[
236            ctx.accounts.account,
237            ctx.accounts.mint,
238            ctx.accounts.authority,
239        ],
240        ctx.signer_seeds,
241    )
242    .map_err(Into::into)
243}
244
245pub fn thaw_account<'info>(ctx: CpiContext<'_, '_, '_, 'info, ThawAccount<'info>>) -> Result<()> {
246    let ix = spl_token::instruction::thaw_account(
247        &spl_token::ID,
248        ctx.accounts.account.key,
249        ctx.accounts.mint.key,
250        ctx.accounts.authority.key,
251        &[], // TODO: Support multisig signers.
252    )?;
253    solana_program::program::invoke_signed(
254        &ix,
255        &[
256            ctx.accounts.account,
257            ctx.accounts.mint,
258            ctx.accounts.authority,
259        ],
260        ctx.signer_seeds,
261    )
262    .map_err(Into::into)
263}
264
265pub fn initialize_mint<'info>(
266    ctx: CpiContext<'_, '_, '_, 'info, InitializeMint<'info>>,
267    decimals: u8,
268    authority: &Pubkey,
269    freeze_authority: Option<&Pubkey>,
270) -> Result<()> {
271    let ix = spl_token::instruction::initialize_mint(
272        &spl_token::ID,
273        ctx.accounts.mint.key,
274        authority,
275        freeze_authority,
276        decimals,
277    )?;
278    solana_program::program::invoke_signed(
279        &ix,
280        &[ctx.accounts.mint, ctx.accounts.rent],
281        ctx.signer_seeds,
282    )
283    .map_err(Into::into)
284}
285
286pub fn initialize_mint2<'info>(
287    ctx: CpiContext<'_, '_, '_, 'info, InitializeMint2<'info>>,
288    decimals: u8,
289    authority: &Pubkey,
290    freeze_authority: Option<&Pubkey>,
291) -> Result<()> {
292    let ix = spl_token::instruction::initialize_mint2(
293        &spl_token::ID,
294        ctx.accounts.mint.key,
295        authority,
296        freeze_authority,
297        decimals,
298    )?;
299    solana_program::program::invoke_signed(&ix, &[ctx.accounts.mint], ctx.signer_seeds)
300        .map_err(Into::into)
301}
302
303pub fn set_authority<'info>(
304    ctx: CpiContext<'_, '_, '_, 'info, SetAuthority<'info>>,
305    authority_type: spl_token::instruction::AuthorityType,
306    new_authority: Option<Pubkey>,
307) -> Result<()> {
308    let mut spl_new_authority: Option<&Pubkey> = None;
309    if new_authority.is_some() {
310        spl_new_authority = new_authority.as_ref()
311    }
312
313    let ix = spl_token::instruction::set_authority(
314        &spl_token::ID,
315        ctx.accounts.account_or_mint.key,
316        spl_new_authority,
317        authority_type,
318        ctx.accounts.current_authority.key,
319        &[], // TODO: Support multisig signers.
320    )?;
321    solana_program::program::invoke_signed(
322        &ix,
323        &[ctx.accounts.account_or_mint, ctx.accounts.current_authority],
324        ctx.signer_seeds,
325    )
326    .map_err(Into::into)
327}
328
329pub fn sync_native<'info>(ctx: CpiContext<'_, '_, '_, 'info, SyncNative<'info>>) -> Result<()> {
330    let ix = spl_token::instruction::sync_native(&spl_token::ID, ctx.accounts.account.key)?;
331    solana_program::program::invoke_signed(&ix, &[ctx.accounts.account], ctx.signer_seeds)
332        .map_err(Into::into)
333}
334
335#[derive(Accounts)]
336pub struct Transfer<'info> {
337    pub from: AccountInfo<'info>,
338    pub to: AccountInfo<'info>,
339    pub authority: AccountInfo<'info>,
340}
341
342#[derive(Accounts)]
343pub struct TransferChecked<'info> {
344    pub from: AccountInfo<'info>,
345    pub mint: AccountInfo<'info>,
346    pub to: AccountInfo<'info>,
347    pub authority: AccountInfo<'info>,
348}
349
350#[derive(Accounts)]
351pub struct MintTo<'info> {
352    pub mint: AccountInfo<'info>,
353    pub to: AccountInfo<'info>,
354    pub authority: AccountInfo<'info>,
355}
356
357#[derive(Accounts)]
358pub struct Burn<'info> {
359    pub mint: AccountInfo<'info>,
360    pub from: AccountInfo<'info>,
361    pub authority: AccountInfo<'info>,
362}
363
364#[derive(Accounts)]
365pub struct Approve<'info> {
366    pub to: AccountInfo<'info>,
367    pub delegate: AccountInfo<'info>,
368    pub authority: AccountInfo<'info>,
369}
370
371#[derive(Accounts)]
372pub struct ApproveChecked<'info> {
373    pub to: AccountInfo<'info>,
374    pub mint: AccountInfo<'info>,
375    pub delegate: AccountInfo<'info>,
376    pub authority: AccountInfo<'info>,
377}
378
379#[derive(Accounts)]
380pub struct Revoke<'info> {
381    pub source: AccountInfo<'info>,
382    pub authority: AccountInfo<'info>,
383}
384
385#[derive(Accounts)]
386pub struct InitializeAccount<'info> {
387    pub account: AccountInfo<'info>,
388    pub mint: AccountInfo<'info>,
389    pub authority: AccountInfo<'info>,
390    pub rent: AccountInfo<'info>,
391}
392
393#[derive(Accounts)]
394pub struct InitializeAccount3<'info> {
395    pub account: AccountInfo<'info>,
396    pub mint: AccountInfo<'info>,
397    pub authority: AccountInfo<'info>,
398}
399
400#[derive(Accounts)]
401pub struct CloseAccount<'info> {
402    pub account: AccountInfo<'info>,
403    pub destination: AccountInfo<'info>,
404    pub authority: AccountInfo<'info>,
405}
406
407#[derive(Accounts)]
408pub struct FreezeAccount<'info> {
409    pub account: AccountInfo<'info>,
410    pub mint: AccountInfo<'info>,
411    pub authority: AccountInfo<'info>,
412}
413
414#[derive(Accounts)]
415pub struct ThawAccount<'info> {
416    pub account: AccountInfo<'info>,
417    pub mint: AccountInfo<'info>,
418    pub authority: AccountInfo<'info>,
419}
420
421#[derive(Accounts)]
422pub struct InitializeMint<'info> {
423    pub mint: AccountInfo<'info>,
424    pub rent: AccountInfo<'info>,
425}
426
427#[derive(Accounts)]
428pub struct InitializeMint2<'info> {
429    pub mint: AccountInfo<'info>,
430}
431
432#[derive(Accounts)]
433pub struct SetAuthority<'info> {
434    pub current_authority: AccountInfo<'info>,
435    pub account_or_mint: AccountInfo<'info>,
436}
437
438#[derive(Accounts)]
439pub struct SyncNative<'info> {
440    pub account: AccountInfo<'info>,
441}
442
443#[derive(Clone, Debug, Default, PartialEq)]
444pub struct TokenAccount(spl_token::state::Account);
445
446impl TokenAccount {
447    pub const LEN: usize = spl_token::state::Account::LEN;
448}
449
450impl anchor_lang::AccountDeserialize for TokenAccount {
451    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
452        spl_token::state::Account::unpack(buf)
453            .map(TokenAccount)
454            .map_err(Into::into)
455    }
456}
457
458impl anchor_lang::AccountSerialize for TokenAccount {}
459
460impl anchor_lang::Owner for TokenAccount {
461    fn owner() -> Pubkey {
462        ID
463    }
464}
465
466impl Deref for TokenAccount {
467    type Target = spl_token::state::Account;
468
469    fn deref(&self) -> &Self::Target {
470        &self.0
471    }
472}
473
474#[cfg(feature = "idl-build")]
475impl anchor_lang::IdlBuild for TokenAccount {}
476
477#[derive(Clone, Debug, Default, PartialEq)]
478pub struct Mint(spl_token::state::Mint);
479
480impl Mint {
481    pub const LEN: usize = spl_token::state::Mint::LEN;
482}
483
484impl anchor_lang::AccountDeserialize for Mint {
485    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
486        spl_token::state::Mint::unpack(buf)
487            .map(Mint)
488            .map_err(Into::into)
489    }
490}
491
492impl anchor_lang::AccountSerialize for Mint {}
493
494impl anchor_lang::Owner for Mint {
495    fn owner() -> Pubkey {
496        ID
497    }
498}
499
500impl Deref for Mint {
501    type Target = spl_token::state::Mint;
502
503    fn deref(&self) -> &Self::Target {
504        &self.0
505    }
506}
507
508#[cfg(feature = "idl-build")]
509impl anchor_lang::IdlBuild for Mint {}
510
511#[derive(Clone)]
512pub struct Token;
513
514impl anchor_lang::Id for Token {
515    fn id() -> Pubkey {
516        ID
517    }
518}
519
520// Field parsers to save compute. All account validation is assumed to be done
521// outside of these methods.
522pub mod accessor {
523    use super::*;
524
525    pub fn amount(account: &AccountInfo) -> Result<u64> {
526        let bytes = account.try_borrow_data()?;
527        let mut amount_bytes = [0u8; 8];
528        amount_bytes.copy_from_slice(&bytes[64..72]);
529        Ok(u64::from_le_bytes(amount_bytes))
530    }
531
532    pub fn mint(account: &AccountInfo) -> Result<Pubkey> {
533        let bytes = account.try_borrow_data()?;
534        let mut mint_bytes = [0u8; 32];
535        mint_bytes.copy_from_slice(&bytes[..32]);
536        Ok(Pubkey::new_from_array(mint_bytes))
537    }
538
539    pub fn authority(account: &AccountInfo) -> Result<Pubkey> {
540        let bytes = account.try_borrow_data()?;
541        let mut owner_bytes = [0u8; 32];
542        owner_bytes.copy_from_slice(&bytes[32..64]);
543        Ok(Pubkey::new_from_array(owner_bytes))
544    }
545}