solana_extra_wasm/account_decoder/
parse_token.rs

1use std::str::FromStr;
2
3use super::{parse_token_extension::UiExtension, StringAmount, StringDecimals};
4use crate::program::spl_token_2022::state::AccountState;
5
6#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "camelCase")]
8pub enum UiAccountState {
9    Uninitialized,
10    Initialized,
11    Frozen,
12}
13
14impl From<AccountState> for UiAccountState {
15    fn from(state: AccountState) -> Self {
16        match state {
17            AccountState::Uninitialized => UiAccountState::Uninitialized,
18            AccountState::Initialized => UiAccountState::Initialized,
19            AccountState::Frozen => UiAccountState::Frozen,
20        }
21    }
22}
23
24pub fn real_number_string(amount: u64, decimals: u8) -> StringDecimals {
25    let decimals = decimals as usize;
26    if decimals > 0 {
27        // Left-pad zeros to decimals + 1, so we at least have an integer zero
28        let mut s = format!("{:01$}", amount, decimals + 1);
29        // Add the decimal point (Sorry, "," locales!)
30        s.insert(s.len() - decimals, '.');
31        s
32    } else {
33        amount.to_string()
34    }
35}
36
37pub fn real_number_string_trimmed(amount: u64, decimals: u8) -> StringDecimals {
38    let mut s = real_number_string(amount, decimals);
39    if decimals > 0 {
40        let zeros_trimmed = s.trim_end_matches('0');
41        s = zeros_trimmed.trim_end_matches('.').to_string();
42    }
43    s
44}
45
46#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
47#[serde(rename_all = "camelCase")]
48pub struct UiTokenAmount {
49    pub ui_amount: Option<f64>,
50    pub decimals: u8,
51    pub amount: StringAmount,
52    pub ui_amount_string: StringDecimals,
53}
54
55impl UiTokenAmount {
56    pub fn real_number_string(&self) -> String {
57        real_number_string(
58            u64::from_str(&self.amount).unwrap_or_default(),
59            self.decimals,
60        )
61    }
62
63    pub fn real_number_string_trimmed(&self) -> String {
64        if !self.ui_amount_string.is_empty() {
65            self.ui_amount_string.clone()
66        } else {
67            real_number_string_trimmed(
68                u64::from_str(&self.amount).unwrap_or_default(),
69                self.decimals,
70            )
71        }
72    }
73}
74
75#[derive(Debug, Serialize, Deserialize, PartialEq)]
76#[serde(rename_all = "camelCase")]
77pub struct UiTokenAccount {
78    pub mint: String,
79    pub owner: String,
80    pub token_amount: UiTokenAmount,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub delegate: Option<String>,
83    pub state: UiAccountState,
84    pub is_native: bool,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub rent_exempt_reserve: Option<UiTokenAmount>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub delegated_amount: Option<UiTokenAmount>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub close_authority: Option<String>,
91    #[serde(skip_serializing_if = "Vec::is_empty", default)]
92    pub extensions: Vec<UiExtension>,
93}
94
95#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
96#[serde(rename_all = "camelCase")]
97pub struct UiMint {
98    pub mint_authority: Option<String>,
99    pub supply: StringAmount,
100    pub decimals: u8,
101    pub is_initialized: bool,
102    pub freeze_authority: Option<String>,
103    #[serde(skip_serializing_if = "Vec::is_empty", default)]
104    pub extensions: Vec<UiExtension>,
105}
106
107#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
108#[serde(rename_all = "camelCase")]
109pub struct UiMultisig {
110    pub num_required_signers: u8,
111    pub num_valid_signers: u8,
112    pub is_initialized: bool,
113    pub signers: Vec<String>,
114}
115
116#[derive(Debug, Serialize, Deserialize, PartialEq)]
117#[serde(rename_all = "camelCase", tag = "type", content = "info")]
118#[allow(clippy::large_enum_variant)]
119pub enum TokenAccountType {
120    Account(UiTokenAccount),
121    Mint(UiMint),
122    Multisig(UiMultisig),
123}