spl_generic_token/
generic_token.rs

1//! Minimum viable SPL Token parsers to avoid a dependency on the spl-token and spl-token-2022 crates.
2//! Users may use the generic traits directly, but this requires them to select the correct implementation
3//! based on the account's program id. `generic_token::Account` and `generic_token::Mint` abstract over
4//! this and require no knowledge of the different token programs on the part of the caller at all.
5//!
6//! We provide the minimum viable interface to determine balances and ownership. For more advanced use-cases,
7//! it is recommended to use to full token program crates instead.
8
9use {
10    crate::{
11        token::{self, GenericTokenAccount, GenericTokenMint},
12        token_2022,
13    },
14    solana_pubkey::Pubkey,
15};
16
17#[derive(Debug, Clone, Default, PartialEq, Eq)]
18pub struct Account {
19    pub mint: Pubkey,
20    pub owner: Pubkey,
21    pub amount: u64,
22}
23
24impl Account {
25    pub fn unpack(account_data: &[u8], program_id: &Pubkey) -> Option<Self> {
26        let (mint, owner, amount) = if *program_id == token::id() {
27            token::Account::valid_account_data(account_data).then_some(())?;
28
29            let mint = token::Account::unpack_account_mint_unchecked(account_data);
30            let owner = token::Account::unpack_account_owner_unchecked(account_data);
31            let amount = token::Account::unpack_account_amount_unchecked(account_data);
32
33            (*mint, *owner, amount)
34        } else if *program_id == token_2022::id() {
35            token_2022::Account::valid_account_data(account_data).then_some(())?;
36
37            let mint = token_2022::Account::unpack_account_mint_unchecked(account_data);
38            let owner = token_2022::Account::unpack_account_owner_unchecked(account_data);
39            let amount = token_2022::Account::unpack_account_amount_unchecked(account_data);
40
41            (*mint, *owner, amount)
42        } else {
43            return None;
44        };
45
46        Some(Self {
47            mint,
48            owner,
49            amount,
50        })
51    }
52}
53
54#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
55pub struct Mint {
56    pub supply: u64,
57    pub decimals: u8,
58}
59
60impl Mint {
61    pub fn unpack(account_data: &[u8], program_id: &Pubkey) -> Option<Self> {
62        let (supply, decimals) = if *program_id == token::id() {
63            token::Mint::valid_account_data(account_data).then_some(())?;
64
65            let supply = token::Mint::unpack_mint_supply_unchecked(account_data);
66            let decimals = token::Mint::unpack_mint_decimals_unchecked(account_data);
67
68            (supply, decimals)
69        } else if *program_id == token_2022::id() {
70            token_2022::Mint::valid_account_data(account_data).then_some(())?;
71
72            let supply = token_2022::Mint::unpack_mint_supply_unchecked(account_data);
73            let decimals = token_2022::Mint::unpack_mint_decimals_unchecked(account_data);
74
75            (supply, decimals)
76        } else {
77            return None;
78        };
79
80        Some(Self { supply, decimals })
81    }
82}