solana_extra_wasm/account_decoder/
parse_token.rs1use 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 let mut s = format!("{:01$}", amount, decimals + 1);
29 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}