Skip to main content

onemoney_protocol/api/
accounts.rs

1//! Account-related API operations.
2
3use alloy_primitives::Address;
4use om_rest_types::responses::{AccountBBNonce, AccountNonce, AssociatedTokenAccount};
5
6use crate::{
7    client::{
8        Client,
9        config::{
10            api_path,
11            endpoints::accounts::{BBNONCE, NONCE, TOKEN_ACCOUNT},
12        },
13    },
14    error::Result,
15};
16
17impl Client {
18    /// Get the nonce for an account.
19    ///
20    /// # Arguments
21    ///
22    /// * `address` - The account address to query
23    ///
24    /// # Returns
25    ///
26    /// The account nonce information.
27    ///
28    /// # Example
29    ///
30    /// ```rust,no_run
31    /// use std::str::FromStr;
32    ///
33    /// use alloy_primitives::Address;
34    /// use onemoney_protocol::Client;
35    ///
36    /// #[tokio::main]
37    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
38    ///     let client = Client::mainnet()?;
39    ///     let address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")?;
40    ///
41    ///     let nonce = client.get_account_nonce(address).await?;
42    ///     println!("Account nonce: {}", nonce.nonce);
43    ///
44    ///     Ok(())
45    /// }
46    /// ```
47    pub async fn get_account_nonce(&self, address: Address) -> Result<AccountNonce> {
48        let path = api_path(&format!("{NONCE}?address={address}"));
49        self.get(&path).await
50    }
51
52    /// Get the BB nonce for an account.
53    ///
54    /// # Arguments
55    ///
56    /// * `address` - The account address to query
57    ///
58    /// # Returns
59    ///
60    /// The account BB nonce information.
61    ///
62    /// # Example
63    ///
64    /// ```rust,no_run
65    /// use std::str::FromStr;
66    ///
67    /// use alloy_primitives::Address;
68    /// use onemoney_protocol::Client;
69    ///
70    /// #[tokio::main]
71    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
72    ///     let client = Client::mainnet()?;
73    ///     let address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")?;
74    ///
75    ///     let bbnonce = client.get_account_bbonce(address).await?;
76    ///     println!("Account BB nonce: {}", bbnonce.bbnonce);
77    ///
78    ///     Ok(())
79    /// }
80    /// ```
81    pub async fn get_account_bbonce(&self, address: Address) -> Result<AccountBBNonce> {
82        let path = api_path(&format!("{BBNONCE}?address={address}"));
83        self.get(&path).await
84    }
85
86    /// Get associated token account information for a specific address and
87    /// token.
88    ///
89    /// This method queries the L1 server's `/v1/accounts/token_account`
90    /// endpoint to retrieve token account details including balance and
91    /// nonce.
92    ///
93    /// # Arguments
94    ///
95    /// * `address` - The wallet owner address
96    /// * `token` - The token mint address
97    ///
98    /// # Returns
99    ///
100    /// The associated token account information.
101    ///
102    /// # Example
103    ///
104    /// ```rust,no_run
105    /// use std::str::FromStr;
106    ///
107    /// use alloy_primitives::Address;
108    /// use onemoney_protocol::Client;
109    ///
110    /// #[tokio::main]
111    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
112    ///     let client = Client::mainnet()?;
113    ///     let address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")?;
114    ///     let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")?;
115    ///
116    ///     let account = client.get_associated_token_account(address, token).await?;
117    ///     println!("Token balance: {}", account.balance);
118    ///
119    ///     Ok(())
120    /// }
121    /// ```
122    pub async fn get_associated_token_account(
123        &self,
124        address: Address,
125        token: Address,
126    ) -> Result<AssociatedTokenAccount> {
127        let path = api_path(&format!("{TOKEN_ACCOUNT}?address={address}&token={token}"));
128        self.get(&path).await
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use std::str::FromStr;
135
136    use alloy_primitives::Address;
137
138    use super::*;
139    use crate::client::config::api_path;
140
141    #[test]
142    fn test_nonce_api_path_construction() {
143        let address =
144            Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0").expect("Test data should be valid");
145        let expected_path = api_path(&format!("{NONCE}?address={address}"));
146
147        assert!(expected_path.contains("/accounts/nonce"));
148        assert!(expected_path.contains("address=0x742d35Cc6634c0532925a3b8D91D6f4a81B8cbc0"));
149    }
150
151    #[test]
152    fn test_bbnonce_api_path_construction() {
153        let address =
154            Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0").expect("Test data should be valid");
155        let expected_path = api_path(&format!("{BBNONCE}?address={address}"));
156
157        assert!(expected_path.contains("/accounts/bbnonce"));
158        assert!(expected_path.contains("address=0x742d35Cc6634c0532925a3b8D91D6f4a81B8cbc0"));
159    }
160
161    #[test]
162    fn test_token_account_api_path_construction() {
163        let address =
164            Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0").expect("Test data should be valid");
165        let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678").expect("Test data should be valid");
166        let expected_path = api_path(&format!("{TOKEN_ACCOUNT}?address={address}&token={token}"));
167
168        assert!(expected_path.contains("/accounts/token_account"));
169        assert!(expected_path.contains("address=0x742d35Cc6634c0532925a3b8D91D6f4a81B8cbc0"));
170        assert!(expected_path.contains("token=0x1234567890AbcdEF1234567890aBcdef12345678"));
171    }
172
173    #[test]
174    fn test_account_nonce_display() {
175        let nonce = AccountNonce { nonce: 42 };
176        let display_str = format!("{}", nonce);
177        assert_eq!(display_str, "Account Nonce: 42");
178    }
179
180    #[test]
181    fn test_account_bbnonce_display() {
182        let bbnonce = AccountBBNonce { bbnonce: 42 };
183        let display_str = format!("{}", bbnonce);
184        assert_eq!(display_str, "Account BB Nonce: 42");
185    }
186
187    #[test]
188    fn test_associated_token_account_display() {
189        let account = AssociatedTokenAccount {
190            balance: "1000000000000000000".to_string(),
191            nonce: 5,
192        };
193
194        let display_str = format!("{}", account);
195        assert!(display_str.contains("Associated Token Account"));
196        assert!(display_str.contains("Balance: 1000000000000000000"));
197        assert!(display_str.contains("Nonce: 5"));
198    }
199
200    #[test]
201    fn test_associated_token_account_equality() {
202        let account1 = AssociatedTokenAccount {
203            balance: "1000000000000000000".to_string(),
204            nonce: 5,
205        };
206
207        let account2 = AssociatedTokenAccount {
208            balance: "1000000000000000000".to_string(),
209            nonce: 5,
210        };
211
212        assert_eq!(account1, account2);
213    }
214
215    #[test]
216    fn test_associated_token_account_clone() {
217        let account = AssociatedTokenAccount {
218            balance: "1000000000000000000".to_string(),
219            nonce: 5,
220        };
221
222        let cloned = account.clone();
223        assert_eq!(account, cloned);
224    }
225
226    #[test]
227    fn test_associated_token_account_default() {
228        let account = AssociatedTokenAccount::default();
229        assert_eq!(account.balance, String::default());
230        assert_eq!(account.nonce, 0);
231    }
232}