Skip to main content

goldrush_sdk/models/
balances.rs

1use serde::Deserialize;
2
3/// Represents a token balance item returned by the API.
4#[derive(Debug, Clone, Deserialize)]
5pub struct BalanceItem {
6    /// The contract address of the token.
7    pub contract_address: String,
8    
9    /// The token symbol/ticker.
10    #[serde(rename = "contract_ticker_symbol")]
11    pub contract_ticker_symbol: Option<String>,
12    
13    /// The token name.
14    #[serde(rename = "contract_name")]
15    pub contract_name: Option<String>,
16    
17    /// The raw balance amount as a string (to handle large numbers).
18    pub balance: String,
19    
20    /// The number of decimal places for this token.
21    #[serde(rename = "contract_decimals")]
22    pub contract_decimals: Option<u32>,
23    
24    /// The current quote rate for the token.
25    pub quote_rate: Option<f64>,
26    
27    /// The quote value (balance * quote_rate).
28    pub quote: Option<f64>,
29    
30    /// The type of token (e.g., "cryptocurrency", "stablecoin", etc.).
31    #[serde(rename = "type")]
32    pub token_type: Option<String>,
33    
34    /// Whether this token is spam.
35    pub is_spam: Option<bool>,
36    
37    /// The logo URL for the token.
38    pub logo_url: Option<String>,
39    
40    /// Last transferred timestamp.
41    pub last_transferred_at: Option<String>,
42    
43    /// Whether this token is native to the chain.
44    pub native_token: Option<bool>,
45    
46    /// Additional metadata.
47    #[serde(flatten)]
48    pub metadata: Option<serde_json::Value>,
49}
50
51impl BalanceItem {
52    /// Get the display symbol for this token (ticker symbol or "Unknown").
53    pub fn symbol(&self) -> &str {
54        self.contract_ticker_symbol.as_deref().unwrap_or("Unknown")
55    }
56    
57    /// Get the display name for this token (name or ticker symbol or "Unknown").
58    pub fn name(&self) -> &str {
59        self.contract_name.as_deref()
60            .or(self.contract_ticker_symbol.as_deref())
61            .unwrap_or("Unknown")
62    }
63    
64    /// Parse the balance as a floating point number, accounting for decimals.
65    pub fn balance_as_float(&self) -> Option<f64> {
66        let balance = self.balance.parse::<f64>().ok()?;
67        let decimals = self.contract_decimals.unwrap_or(18) as u32;
68        Some(balance / 10f64.powi(decimals as i32))
69    }
70    
71    /// Check if this token has a non-zero balance.
72    pub fn has_balance(&self) -> bool {
73        self.balance.parse::<f64>().unwrap_or(0.0) > 0.0
74    }
75    
76    /// Check if this token has quote value information.
77    pub fn has_quote_value(&self) -> bool {
78        self.quote.unwrap_or(0.0) > 0.0
79    }
80    
81    /// Check if this token is marked as spam.
82    pub fn is_spam(&self) -> bool {
83        self.is_spam.unwrap_or(false)
84    }
85}
86
87/// Container for balance items.
88#[derive(Debug, Clone, Deserialize)]
89pub struct BalancesData {
90    /// The address these balances belong to.
91    pub address: Option<String>,
92    
93    /// The chain ID.
94    pub chain_id: Option<u64>,
95    
96    /// The chain name.
97    pub chain_name: Option<String>,
98    
99    /// List of token balance items.
100    pub items: Vec<BalanceItem>,
101    
102    /// Quote currency used for calculations.
103    pub quote_currency: Option<String>,
104    
105    /// Total quote value across all tokens.
106    pub total_quote: Option<f64>,
107}
108
109impl BalancesData {
110    /// Calculate the total portfolio value from all balance items.
111    pub fn total_value(&self) -> f64 {
112        self.items.iter()
113            .filter_map(|item| item.quote)
114            .sum()
115    }
116    
117    /// Get tokens with non-zero quote value, sorted by value (highest first).
118    pub fn tokens_by_value(&self) -> Vec<&BalanceItem> {
119        let mut tokens: Vec<_> = self.items.iter()
120            .filter(|item| item.quote.unwrap_or(0.0) > 0.0)
121            .collect();
122        tokens.sort_by(|a, b| 
123            b.quote.partial_cmp(&a.quote).unwrap_or(std::cmp::Ordering::Equal)
124        );
125        tokens
126    }
127    
128    /// Filter tokens by minimum quote value.
129    pub fn tokens_above_value(&self, min_value: f64) -> Vec<&BalanceItem> {
130        self.items.iter()
131            .filter(|item| item.quote.unwrap_or(0.0) >= min_value)
132            .collect()
133    }
134    
135    /// Get count of non-spam tokens.
136    pub fn non_spam_count(&self) -> usize {
137        self.items.iter()
138            .filter(|item| !item.is_spam.unwrap_or(false))
139            .count()
140    }
141}
142
143/// Response structure for balance queries.
144pub type BalancesResponse = crate::models::ApiResponse<BalancesData>;