2020-07-24 17:45:21 -06:00
|
|
|
use crate::parse_account_data::{ParsableAccount, ParseAccountError};
|
2020-07-28 23:00:48 -06:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
|
|
|
use spl_token_v1_0::{
|
2020-07-24 17:45:21 -06:00
|
|
|
option::COption,
|
2020-07-30 14:52:28 -07:00
|
|
|
solana_sdk::pubkey::Pubkey as SplTokenPubkey,
|
2020-08-01 23:31:22 -07:00
|
|
|
state::{unpack, Account, Mint, Multisig},
|
2020-07-24 17:45:21 -06:00
|
|
|
};
|
2020-07-28 23:00:48 -06:00
|
|
|
use std::{mem::size_of, str::FromStr};
|
|
|
|
|
|
|
|
// A helper function to convert spl_token_v1_0::id() as spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey
|
|
|
|
pub fn spl_token_id_v1_0() -> Pubkey {
|
|
|
|
Pubkey::from_str(&spl_token_v1_0::id().to_string()).unwrap()
|
|
|
|
}
|
2020-07-24 17:45:21 -06:00
|
|
|
|
|
|
|
pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
|
|
|
|
let mut data = data.to_vec();
|
|
|
|
if data.len() == size_of::<Account>() {
|
2020-08-01 23:31:22 -07:00
|
|
|
let account: Account = *unpack(&mut data)
|
2020-07-31 13:26:09 -06:00
|
|
|
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
2020-07-24 17:45:21 -06:00
|
|
|
Ok(TokenAccountType::Account(UiTokenAccount {
|
|
|
|
mint: account.mint.to_string(),
|
|
|
|
owner: account.owner.to_string(),
|
|
|
|
amount: account.amount,
|
|
|
|
delegate: match account.delegate {
|
|
|
|
COption::Some(pubkey) => Some(pubkey.to_string()),
|
|
|
|
COption::None => None,
|
|
|
|
},
|
|
|
|
is_initialized: account.is_initialized,
|
|
|
|
is_native: account.is_native,
|
|
|
|
delegated_amount: account.delegated_amount,
|
|
|
|
}))
|
|
|
|
} else if data.len() == size_of::<Mint>() {
|
2020-08-01 23:31:22 -07:00
|
|
|
let mint: Mint = *unpack(&mut data)
|
2020-07-31 13:26:09 -06:00
|
|
|
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
2020-07-24 17:45:21 -06:00
|
|
|
Ok(TokenAccountType::Mint(UiMint {
|
|
|
|
owner: match mint.owner {
|
|
|
|
COption::Some(pubkey) => Some(pubkey.to_string()),
|
|
|
|
COption::None => None,
|
|
|
|
},
|
|
|
|
decimals: mint.decimals,
|
|
|
|
is_initialized: mint.is_initialized,
|
|
|
|
}))
|
|
|
|
} else if data.len() == size_of::<Multisig>() {
|
2020-08-01 23:31:22 -07:00
|
|
|
let multisig: Multisig = *unpack(&mut data)
|
2020-07-31 13:26:09 -06:00
|
|
|
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
2020-07-24 17:45:21 -06:00
|
|
|
Ok(TokenAccountType::Multisig(UiMultisig {
|
|
|
|
num_required_signers: multisig.m,
|
|
|
|
num_valid_signers: multisig.n,
|
|
|
|
is_initialized: multisig.is_initialized,
|
|
|
|
signers: multisig
|
|
|
|
.signers
|
|
|
|
.iter()
|
|
|
|
.filter_map(|pubkey| {
|
2020-07-30 14:52:28 -07:00
|
|
|
if pubkey != &SplTokenPubkey::default() {
|
2020-07-24 17:45:21 -06:00
|
|
|
Some(pubkey.to_string())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
Err(ParseAccountError::AccountNotParsable(
|
2020-07-31 13:26:09 -06:00
|
|
|
ParsableAccount::SplToken,
|
2020-07-24 17:45:21 -06:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub enum TokenAccountType {
|
|
|
|
Account(UiTokenAccount),
|
|
|
|
Mint(UiMint),
|
|
|
|
Multisig(UiMultisig),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct UiTokenAccount {
|
|
|
|
pub mint: String,
|
|
|
|
pub owner: String,
|
|
|
|
pub amount: u64,
|
|
|
|
pub delegate: Option<String>,
|
|
|
|
pub is_initialized: bool,
|
|
|
|
pub is_native: bool,
|
|
|
|
pub delegated_amount: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct UiMint {
|
|
|
|
pub owner: Option<String>,
|
|
|
|
pub decimals: u8,
|
|
|
|
pub is_initialized: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct UiMultisig {
|
|
|
|
pub num_required_signers: u8,
|
|
|
|
pub num_valid_signers: u8,
|
|
|
|
pub is_initialized: bool,
|
|
|
|
pub signers: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2020-08-01 23:31:22 -07:00
|
|
|
use spl_token_v1_0::state::unpack_unchecked;
|
2020-07-24 17:45:21 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_token() {
|
2020-07-30 14:52:28 -07:00
|
|
|
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
|
|
|
|
let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
|
2020-07-24 17:45:21 -06:00
|
|
|
let mut account_data = [0; size_of::<Account>()];
|
2020-08-01 23:31:22 -07:00
|
|
|
let mut account: &mut Account = unpack_unchecked(&mut account_data).unwrap();
|
2020-07-24 17:45:21 -06:00
|
|
|
account.mint = mint_pubkey;
|
|
|
|
account.owner = owner_pubkey;
|
|
|
|
account.amount = 42;
|
|
|
|
account.is_initialized = true;
|
|
|
|
assert_eq!(
|
|
|
|
parse_token(&account_data).unwrap(),
|
|
|
|
TokenAccountType::Account(UiTokenAccount {
|
|
|
|
mint: mint_pubkey.to_string(),
|
|
|
|
owner: owner_pubkey.to_string(),
|
|
|
|
amount: 42,
|
|
|
|
delegate: None,
|
|
|
|
is_initialized: true,
|
|
|
|
is_native: false,
|
|
|
|
delegated_amount: 0,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut mint_data = [0; size_of::<Mint>()];
|
2020-08-01 23:31:22 -07:00
|
|
|
let mut mint: &mut Mint = unpack_unchecked(&mut mint_data).unwrap();
|
2020-07-24 17:45:21 -06:00
|
|
|
mint.owner = COption::Some(owner_pubkey);
|
|
|
|
mint.decimals = 3;
|
|
|
|
mint.is_initialized = true;
|
|
|
|
assert_eq!(
|
|
|
|
parse_token(&mint_data).unwrap(),
|
|
|
|
TokenAccountType::Mint(UiMint {
|
|
|
|
owner: Some(owner_pubkey.to_string()),
|
|
|
|
decimals: 3,
|
|
|
|
is_initialized: true,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
2020-07-30 14:52:28 -07:00
|
|
|
let signer1 = SplTokenPubkey::new(&[1; 32]);
|
|
|
|
let signer2 = SplTokenPubkey::new(&[2; 32]);
|
|
|
|
let signer3 = SplTokenPubkey::new(&[3; 32]);
|
2020-07-24 17:45:21 -06:00
|
|
|
let mut multisig_data = [0; size_of::<Multisig>()];
|
2020-08-01 23:31:22 -07:00
|
|
|
let mut multisig: &mut Multisig = unpack_unchecked(&mut multisig_data).unwrap();
|
2020-07-30 14:52:28 -07:00
|
|
|
let mut signers = [SplTokenPubkey::default(); 11];
|
2020-07-24 17:45:21 -06:00
|
|
|
signers[0] = signer1;
|
|
|
|
signers[1] = signer2;
|
|
|
|
signers[2] = signer3;
|
|
|
|
multisig.m = 2;
|
|
|
|
multisig.n = 3;
|
|
|
|
multisig.is_initialized = true;
|
|
|
|
multisig.signers = signers;
|
|
|
|
assert_eq!(
|
|
|
|
parse_token(&multisig_data).unwrap(),
|
|
|
|
TokenAccountType::Multisig(UiMultisig {
|
|
|
|
num_required_signers: 2,
|
|
|
|
num_valid_signers: 3,
|
|
|
|
is_initialized: true,
|
|
|
|
signers: vec![
|
|
|
|
signer1.to_string(),
|
|
|
|
signer2.to_string(),
|
|
|
|
signer3.to_string()
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
let bad_data = vec![0; 4];
|
|
|
|
assert!(parse_token(&bad_data).is_err());
|
|
|
|
}
|
|
|
|
}
|