Skip to main content

anchor_spl/
token.rs

1// Avoiding AccountInfo deprecated msg in anchor context
2#![allow(deprecated)]
3use {
4    anchor_lang::{
5        context::CpiContext,
6        solana_program::{account_info::AccountInfo, program_pack::Pack, pubkey::Pubkey},
7        Accounts, Result,
8    },
9    std::ops::Deref,
10};
11pub use {spl_token::ID, spl_token_interface as spl_token};
12
13pub fn transfer<'info>(
14    ctx: CpiContext<'_, '_, '_, 'info, Transfer<'info>>,
15    amount: u64,
16) -> Result<()> {
17    let ix = spl_token::instruction::transfer(
18        &spl_token::ID,
19        ctx.accounts.from.key,
20        ctx.accounts.to.key,
21        ctx.accounts.authority.key,
22        &[],
23        amount,
24    )?;
25    anchor_lang::solana_program::program::invoke_signed(
26        &ix,
27        &[ctx.accounts.from, ctx.accounts.to, ctx.accounts.authority],
28        ctx.signer_seeds,
29    )
30    .map_err(Into::into)
31}
32
33pub fn transfer_checked<'info>(
34    ctx: CpiContext<'_, '_, '_, 'info, TransferChecked<'info>>,
35    amount: u64,
36    decimals: u8,
37) -> Result<()> {
38    let ix = spl_token::instruction::transfer_checked(
39        &spl_token::ID,
40        ctx.accounts.from.key,
41        ctx.accounts.mint.key,
42        ctx.accounts.to.key,
43        ctx.accounts.authority.key,
44        &[],
45        amount,
46        decimals,
47    )?;
48    anchor_lang::solana_program::program::invoke_signed(
49        &ix,
50        &[
51            ctx.accounts.from,
52            ctx.accounts.mint,
53            ctx.accounts.to,
54            ctx.accounts.authority,
55        ],
56        ctx.signer_seeds,
57    )
58    .map_err(Into::into)
59}
60
61pub fn mint_to<'info>(
62    ctx: CpiContext<'_, '_, '_, 'info, MintTo<'info>>,
63    amount: u64,
64) -> Result<()> {
65    let ix = spl_token::instruction::mint_to(
66        &spl_token::ID,
67        ctx.accounts.mint.key,
68        ctx.accounts.to.key,
69        ctx.accounts.authority.key,
70        &[],
71        amount,
72    )?;
73    anchor_lang::solana_program::program::invoke_signed(
74        &ix,
75        &[ctx.accounts.to, ctx.accounts.mint, ctx.accounts.authority],
76        ctx.signer_seeds,
77    )
78    .map_err(Into::into)
79}
80
81pub fn burn<'info>(ctx: CpiContext<'_, '_, '_, 'info, Burn<'info>>, amount: u64) -> Result<()> {
82    let ix = spl_token::instruction::burn(
83        &spl_token::ID,
84        ctx.accounts.from.key,
85        ctx.accounts.mint.key,
86        ctx.accounts.authority.key,
87        &[],
88        amount,
89    )?;
90    anchor_lang::solana_program::program::invoke_signed(
91        &ix,
92        &[ctx.accounts.from, ctx.accounts.mint, ctx.accounts.authority],
93        ctx.signer_seeds,
94    )
95    .map_err(Into::into)
96}
97
98pub fn approve<'info>(
99    ctx: CpiContext<'_, '_, '_, 'info, Approve<'info>>,
100    amount: u64,
101) -> Result<()> {
102    let ix = spl_token::instruction::approve(
103        &spl_token::ID,
104        ctx.accounts.to.key,
105        ctx.accounts.delegate.key,
106        ctx.accounts.authority.key,
107        &[],
108        amount,
109    )?;
110    anchor_lang::solana_program::program::invoke_signed(
111        &ix,
112        &[
113            ctx.accounts.to,
114            ctx.accounts.delegate,
115            ctx.accounts.authority,
116        ],
117        ctx.signer_seeds,
118    )
119    .map_err(Into::into)
120}
121
122pub fn approve_checked<'info>(
123    ctx: CpiContext<'_, '_, '_, 'info, ApproveChecked<'info>>,
124    amount: u64,
125    decimals: u8,
126) -> Result<()> {
127    let ix = spl_token::instruction::approve_checked(
128        &spl_token::ID,
129        ctx.accounts.to.key,
130        ctx.accounts.mint.key,
131        ctx.accounts.delegate.key,
132        ctx.accounts.authority.key,
133        &[],
134        amount,
135        decimals,
136    )?;
137    anchor_lang::solana_program::program::invoke_signed(
138        &ix,
139        &[
140            ctx.accounts.to,
141            ctx.accounts.mint,
142            ctx.accounts.delegate,
143            ctx.accounts.authority,
144        ],
145        ctx.signer_seeds,
146    )
147    .map_err(Into::into)
148}
149
150pub fn revoke<'info>(ctx: CpiContext<'_, '_, '_, 'info, Revoke<'info>>) -> Result<()> {
151    let ix = spl_token::instruction::revoke(
152        &spl_token::ID,
153        ctx.accounts.source.key,
154        ctx.accounts.authority.key,
155        &[],
156    )?;
157    anchor_lang::solana_program::program::invoke_signed(
158        &ix,
159        &[ctx.accounts.source, ctx.accounts.authority],
160        ctx.signer_seeds,
161    )
162    .map_err(Into::into)
163}
164
165pub fn initialize_account<'info>(
166    ctx: CpiContext<'_, '_, '_, 'info, InitializeAccount<'info>>,
167) -> Result<()> {
168    let ix = spl_token::instruction::initialize_account(
169        &spl_token::ID,
170        ctx.accounts.account.key,
171        ctx.accounts.mint.key,
172        ctx.accounts.authority.key,
173    )?;
174    anchor_lang::solana_program::program::invoke_signed(
175        &ix,
176        &[
177            ctx.accounts.account,
178            ctx.accounts.mint,
179            ctx.accounts.authority,
180            ctx.accounts.rent,
181        ],
182        ctx.signer_seeds,
183    )
184    .map_err(Into::into)
185}
186
187pub fn initialize_account3<'info>(
188    ctx: CpiContext<'_, '_, '_, 'info, InitializeAccount3<'info>>,
189) -> Result<()> {
190    let ix = spl_token::instruction::initialize_account3(
191        &spl_token::ID,
192        ctx.accounts.account.key,
193        ctx.accounts.mint.key,
194        ctx.accounts.authority.key,
195    )?;
196    anchor_lang::solana_program::program::invoke_signed(
197        &ix,
198        &[ctx.accounts.account, ctx.accounts.mint],
199        ctx.signer_seeds,
200    )
201    .map_err(Into::into)
202}
203
204pub fn close_account<'info>(ctx: CpiContext<'_, '_, '_, 'info, CloseAccount<'info>>) -> Result<()> {
205    let ix = spl_token::instruction::close_account(
206        &spl_token::ID,
207        ctx.accounts.account.key,
208        ctx.accounts.destination.key,
209        ctx.accounts.authority.key,
210        &[], // TODO: support multisig
211    )?;
212    anchor_lang::solana_program::program::invoke_signed(
213        &ix,
214        &[
215            ctx.accounts.account,
216            ctx.accounts.destination,
217            ctx.accounts.authority,
218        ],
219        ctx.signer_seeds,
220    )
221    .map_err(Into::into)
222}
223
224pub fn freeze_account<'info>(
225    ctx: CpiContext<'_, '_, '_, 'info, FreezeAccount<'info>>,
226) -> Result<()> {
227    let ix = spl_token::instruction::freeze_account(
228        &spl_token::ID,
229        ctx.accounts.account.key,
230        ctx.accounts.mint.key,
231        ctx.accounts.authority.key,
232        &[], // TODO: Support multisig signers.
233    )?;
234    anchor_lang::solana_program::program::invoke_signed(
235        &ix,
236        &[
237            ctx.accounts.account,
238            ctx.accounts.mint,
239            ctx.accounts.authority,
240        ],
241        ctx.signer_seeds,
242    )
243    .map_err(Into::into)
244}
245
246pub fn thaw_account<'info>(ctx: CpiContext<'_, '_, '_, 'info, ThawAccount<'info>>) -> Result<()> {
247    let ix = spl_token::instruction::thaw_account(
248        &spl_token::ID,
249        ctx.accounts.account.key,
250        ctx.accounts.mint.key,
251        ctx.accounts.authority.key,
252        &[], // TODO: Support multisig signers.
253    )?;
254    anchor_lang::solana_program::program::invoke_signed(
255        &ix,
256        &[
257            ctx.accounts.account,
258            ctx.accounts.mint,
259            ctx.accounts.authority,
260        ],
261        ctx.signer_seeds,
262    )
263    .map_err(Into::into)
264}
265
266pub fn initialize_mint<'info>(
267    ctx: CpiContext<'_, '_, '_, 'info, InitializeMint<'info>>,
268    decimals: u8,
269    authority: &Pubkey,
270    freeze_authority: Option<&Pubkey>,
271) -> Result<()> {
272    let ix = spl_token::instruction::initialize_mint(
273        &spl_token::ID,
274        ctx.accounts.mint.key,
275        authority,
276        freeze_authority,
277        decimals,
278    )?;
279    anchor_lang::solana_program::program::invoke_signed(
280        &ix,
281        &[ctx.accounts.mint, ctx.accounts.rent],
282        ctx.signer_seeds,
283    )
284    .map_err(Into::into)
285}
286
287pub fn initialize_mint2<'info>(
288    ctx: CpiContext<'_, '_, '_, 'info, InitializeMint2<'info>>,
289    decimals: u8,
290    authority: &Pubkey,
291    freeze_authority: Option<&Pubkey>,
292) -> Result<()> {
293    let ix = spl_token::instruction::initialize_mint2(
294        &spl_token::ID,
295        ctx.accounts.mint.key,
296        authority,
297        freeze_authority,
298        decimals,
299    )?;
300    anchor_lang::solana_program::program::invoke_signed(&ix, &[ctx.accounts.mint], ctx.signer_seeds)
301        .map_err(Into::into)
302}
303
304pub fn set_authority<'info>(
305    ctx: CpiContext<'_, '_, '_, 'info, SetAuthority<'info>>,
306    authority_type: spl_token::instruction::AuthorityType,
307    new_authority: Option<Pubkey>,
308) -> Result<()> {
309    let ix = spl_token::instruction::set_authority(
310        &spl_token::ID,
311        ctx.accounts.account_or_mint.key,
312        new_authority.as_ref(),
313        authority_type,
314        ctx.accounts.current_authority.key,
315        &[], // TODO: Support multisig signers.
316    )?;
317    anchor_lang::solana_program::program::invoke_signed(
318        &ix,
319        &[ctx.accounts.account_or_mint, ctx.accounts.current_authority],
320        ctx.signer_seeds,
321    )
322    .map_err(Into::into)
323}
324
325pub fn sync_native<'info>(ctx: CpiContext<'_, '_, '_, 'info, SyncNative<'info>>) -> Result<()> {
326    let ix = spl_token::instruction::sync_native(&spl_token::ID, ctx.accounts.account.key)?;
327    anchor_lang::solana_program::program::invoke_signed(
328        &ix,
329        &[ctx.accounts.account],
330        ctx.signer_seeds,
331    )
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, Copy)]
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#[derive(Clone, Debug, Default, PartialEq, Copy)]
475pub struct Mint(spl_token::state::Mint);
476
477impl Mint {
478    pub const LEN: usize = spl_token::state::Mint::LEN;
479}
480
481impl anchor_lang::AccountDeserialize for Mint {
482    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
483        spl_token::state::Mint::unpack(buf)
484            .map(Mint)
485            .map_err(Into::into)
486    }
487}
488
489impl anchor_lang::AccountSerialize for Mint {}
490
491impl anchor_lang::Owner for Mint {
492    fn owner() -> Pubkey {
493        ID
494    }
495}
496
497impl Deref for Mint {
498    type Target = spl_token::state::Mint;
499
500    fn deref(&self) -> &Self::Target {
501        &self.0
502    }
503}
504
505#[derive(Clone)]
506pub struct Token;
507
508impl anchor_lang::Id for Token {
509    fn id() -> Pubkey {
510        ID
511    }
512}
513
514// Field parsers to save compute. All account validation is assumed to be done
515// outside of these methods.
516pub mod accessor {
517    use super::*;
518
519    pub fn amount(account: &AccountInfo) -> Result<u64> {
520        let bytes = account.try_borrow_data()?;
521        let mut amount_bytes = [0u8; 8];
522        amount_bytes.copy_from_slice(&bytes[64..72]);
523        Ok(u64::from_le_bytes(amount_bytes))
524    }
525
526    pub fn mint(account: &AccountInfo) -> Result<Pubkey> {
527        let bytes = account.try_borrow_data()?;
528        let mut mint_bytes = [0u8; 32];
529        mint_bytes.copy_from_slice(&bytes[..32]);
530        Ok(Pubkey::new_from_array(mint_bytes))
531    }
532
533    pub fn authority(account: &AccountInfo) -> Result<Pubkey> {
534        let bytes = account.try_borrow_data()?;
535        let mut owner_bytes = [0u8; 32];
536        owner_bytes.copy_from_slice(&bytes[32..64]);
537        Ok(Pubkey::new_from_array(owner_bytes))
538    }
539}