carbon_token_program_decoder/accounts/
mod.rs1use {
3 crate::{TokenProgramDecoder, PROGRAM_ID},
4 solana_program_pack::Pack,
5};
6
7#[cfg(feature = "postgres")]
8pub mod postgres;
9
10#[cfg(feature = "graphql")]
11pub mod graphql;
12
13pub mod mint;
14pub mod multisig;
15pub mod token;
16
17#[derive(Debug, Clone, PartialEq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "serde", serde(tag = "type", content = "data"))]
20pub enum TokenProgramAccount {
21 Mint(Box<mint::Mint>),
22 Multisig(Box<multisig::Multisig>),
23 Token(Box<token::Token>),
24}
25
26impl<'a> carbon_core::account::AccountDecoder<'a> for TokenProgramDecoder {
27 type AccountType = TokenProgramAccount;
28
29 fn decode_account(
30 &self,
31 account: &'a solana_account::Account,
32 ) -> Option<carbon_core::account::DecodedAccount<Self::AccountType>> {
33 if account.owner != PROGRAM_ID {
34 return None;
35 }
36
37 if let Ok(data) = spl_token_interface::state::Mint::unpack(&account.data) {
38 return Some(carbon_core::account::DecodedAccount {
39 lamports: account.lamports,
40 data: TokenProgramAccount::Mint(Box::new(mint::Mint::from(data))),
41 owner: account.owner,
42 executable: account.executable,
43 rent_epoch: account.rent_epoch,
44 });
45 }
46 if let Ok(data) = spl_token_interface::state::Account::unpack(&account.data) {
47 return Some(carbon_core::account::DecodedAccount {
48 lamports: account.lamports,
49 data: TokenProgramAccount::Token(Box::new(token::Token::from(data))),
50 owner: account.owner,
51 executable: account.executable,
52 rent_epoch: account.rent_epoch,
53 });
54 }
55 if let Ok(data) = spl_token_interface::state::Multisig::unpack(&account.data) {
56 return Some(carbon_core::account::DecodedAccount {
57 lamports: account.lamports,
58 data: TokenProgramAccount::Multisig(Box::new(multisig::Multisig::from(data))),
59 owner: account.owner,
60 executable: account.executable,
61 rent_epoch: account.rent_epoch,
62 });
63 }
64
65 None
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use {
72 super::*, carbon_core::account::AccountDecoder, solana_program_pack::Pack,
73 solana_pubkey::Pubkey,
74 };
75
76 const TOKEN_PROGRAM_ID: Pubkey =
77 solana_pubkey::Pubkey::from_str_const("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
78
79 #[test]
80 fn decode_token_account_uses_spl_token_pack_layout() {
81 let mint = Pubkey::new_from_array([1; 32]);
82 let owner = Pubkey::new_from_array([2; 32]);
83 let source = spl_token_interface::state::Account {
84 mint,
85 owner,
86 amount: 58_727_131,
87 state: spl_token_interface::state::AccountState::Initialized,
88 ..Default::default()
89 };
90 let mut data = vec![0u8; spl_token_interface::state::Account::LEN];
91 spl_token_interface::state::Account::pack(source, &mut data).expect("pack account");
92
93 let account = solana_account::Account {
94 lamports: 2_039_280,
95 data,
96 owner: TOKEN_PROGRAM_ID,
97 executable: false,
98 rent_epoch: 0,
99 };
100
101 let decoded = TokenProgramDecoder
102 .decode_account(&account)
103 .expect("account should decode");
104
105 match decoded.data {
106 TokenProgramAccount::Token(token) => {
107 assert_eq!(token.mint, mint);
108 assert_eq!(token.owner, owner);
109 assert_eq!(token.amount, 58_727_131);
110 assert_eq!(token.state, crate::types::AccountState::Initialized);
111 }
112 _ => panic!("expected token account"),
113 }
114 }
115}