use {
crate::{TokenProgramDecoder, PROGRAM_ID},
solana_program_pack::Pack,
};
#[cfg(feature = "postgres")]
pub mod postgres;
#[cfg(feature = "graphql")]
pub mod graphql;
pub mod mint;
pub mod multisig;
pub mod token;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", content = "data"))]
pub enum TokenProgramAccount {
Mint(Box<mint::Mint>),
Multisig(Box<multisig::Multisig>),
Token(Box<token::Token>),
}
impl<'a> carbon_core::account::AccountDecoder<'a> for TokenProgramDecoder {
type AccountType = TokenProgramAccount;
fn decode_account(
&self,
account: &'a solana_account::Account,
) -> Option<carbon_core::account::DecodedAccount<Self::AccountType>> {
if account.owner != PROGRAM_ID {
return None;
}
if let Ok(data) = spl_token_interface::state::Mint::unpack(&account.data) {
return Some(carbon_core::account::DecodedAccount {
lamports: account.lamports,
data: TokenProgramAccount::Mint(Box::new(mint::Mint::from(data))),
owner: account.owner,
executable: account.executable,
rent_epoch: account.rent_epoch,
});
}
if let Ok(data) = spl_token_interface::state::Account::unpack(&account.data) {
return Some(carbon_core::account::DecodedAccount {
lamports: account.lamports,
data: TokenProgramAccount::Token(Box::new(token::Token::from(data))),
owner: account.owner,
executable: account.executable,
rent_epoch: account.rent_epoch,
});
}
if let Ok(data) = spl_token_interface::state::Multisig::unpack(&account.data) {
return Some(carbon_core::account::DecodedAccount {
lamports: account.lamports,
data: TokenProgramAccount::Multisig(Box::new(multisig::Multisig::from(data))),
owner: account.owner,
executable: account.executable,
rent_epoch: account.rent_epoch,
});
}
None
}
}
#[cfg(test)]
mod tests {
use {
super::*, carbon_core::account::AccountDecoder, solana_program_pack::Pack,
solana_pubkey::Pubkey,
};
const TOKEN_PROGRAM_ID: Pubkey =
solana_pubkey::Pubkey::from_str_const("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
#[test]
fn decode_token_account_uses_spl_token_pack_layout() {
let mint = Pubkey::new_from_array([1; 32]);
let owner = Pubkey::new_from_array([2; 32]);
let source = spl_token_interface::state::Account {
mint,
owner,
amount: 58_727_131,
state: spl_token_interface::state::AccountState::Initialized,
..Default::default()
};
let mut data = vec![0u8; spl_token_interface::state::Account::LEN];
spl_token_interface::state::Account::pack(source, &mut data).expect("pack account");
let account = solana_account::Account {
lamports: 2_039_280,
data,
owner: TOKEN_PROGRAM_ID,
executable: false,
rent_epoch: 0,
};
let decoded = TokenProgramDecoder
.decode_account(&account)
.expect("account should decode");
match decoded.data {
TokenProgramAccount::Token(token) => {
assert_eq!(token.mint, mint);
assert_eq!(token.owner, owner);
assert_eq!(token.amount, 58_727_131);
assert_eq!(token.state, crate::types::AccountState::Initialized);
}
_ => panic!("expected token account"),
}
}
}