anchor_spl/
token.rs

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