onemoney_protocol/api/
tokens.rs

1//! Token-related API operations.
2
3use crate::Result;
4use crate::client::Client;
5use crate::client::config::api_path;
6use crate::client::config::endpoints::tokens::{
7    BURN, GRANT_AUTHORITY, MANAGE_BLACKLIST, MANAGE_WHITELIST, MINT, PAUSE, TOKEN_METADATA,
8    UPDATE_METADATA,
9};
10use crate::crypto::sign_transaction_payload;
11use crate::requests::{
12    BlacklistTokenRequest, BurnTokenRequest, MintTokenRequest, PauseTokenRequest,
13    TokenAuthorityPayload, TokenAuthorityRequest, TokenBlacklistPayload, TokenBurnPayload,
14    TokenMetadataUpdatePayload, TokenMintPayload, TokenPausePayload, TokenWhitelistPayload,
15    UpdateMetadataRequest, WhitelistTokenRequest,
16};
17use crate::responses::MintInfo;
18use crate::responses::TransactionResponse;
19use alloy_primitives::Address;
20
21impl Client {
22    /// Mint tokens to an account.
23    ///
24    /// # Arguments
25    ///
26    /// * `payload` - Token mint parameters
27    /// * `private_key` - Private key for signing the transaction (must have mint authority)
28    ///
29    /// # Returns
30    ///
31    /// The transaction result.
32    pub async fn mint_token(
33        &self,
34        payload: TokenMintPayload,
35        private_key: &str,
36    ) -> Result<TransactionResponse> {
37        let signature = sign_transaction_payload(&payload, private_key)?;
38        let request = MintTokenRequest { payload, signature };
39
40        self.post(&api_path(MINT), &request).await
41    }
42
43    /// Burn tokens from an account.
44    ///
45    /// # Arguments
46    ///
47    /// * `payload` - Token burn parameters
48    /// * `private_key` - Private key for signing the transaction (must have burn authority)
49    ///
50    /// # Returns
51    ///
52    /// The transaction result.
53    pub async fn burn_token(
54        &self,
55        payload: TokenBurnPayload,
56        private_key: &str,
57    ) -> Result<TransactionResponse> {
58        let signature = sign_transaction_payload(&payload, private_key)?;
59        let request = BurnTokenRequest { payload, signature };
60
61        self.post(&api_path(BURN), &request).await
62    }
63
64    /// Grant authority for a token to an address.
65    ///
66    /// # Arguments
67    ///
68    /// * `payload` - Authority grant parameters
69    /// * `private_key` - Private key for signing the transaction (must have master authority)
70    ///
71    /// # Returns
72    ///
73    /// The transaction result.
74    pub async fn grant_authority(
75        &self,
76        payload: TokenAuthorityPayload,
77        private_key: &str,
78    ) -> Result<TransactionResponse> {
79        let signature = sign_transaction_payload(&payload, private_key)?;
80        let request = TokenAuthorityRequest { payload, signature };
81
82        self.post(&api_path(GRANT_AUTHORITY), &request).await
83    }
84
85    /// Revoke authority for a token from an address.
86    ///
87    /// Note: This method uses the same `/v1/tokens/grant_authority` endpoint as grant_authority(),
88    /// but with `AuthorityAction::Revoke` in the payload to indicate a revoke operation.
89    ///
90    /// # Arguments
91    ///
92    /// * `payload` - Authority revoke parameters (with action set to AuthorityAction::Revoke)
93    /// * `private_key` - Private key for signing the transaction (must have master authority)
94    ///
95    /// # Returns
96    ///
97    /// The transaction result.
98    pub async fn revoke_authority(
99        &self,
100        payload: TokenAuthorityPayload,
101        private_key: &str,
102    ) -> Result<TransactionResponse> {
103        let signature = sign_transaction_payload(&payload, private_key)?;
104        let request = TokenAuthorityRequest { payload, signature };
105
106        self.post(&api_path(GRANT_AUTHORITY), &request).await
107    }
108
109    /// Get token metadata by mint address.
110    ///
111    /// # Arguments
112    ///
113    /// * `mint_address` - The token mint address
114    ///
115    /// # Returns
116    ///
117    /// The token metadata.
118    ///
119    /// # Example
120    ///
121    /// ```rust,no_run
122    /// use onemoney_protocol::Client;
123    /// use alloy_primitives::Address;
124    /// use std::str::FromStr;
125    ///
126    /// #[tokio::main]
127    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
128    ///     let client = Client::mainnet()?;
129    ///     let mint = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")?;
130    ///
131    ///     let mint_info = client.get_token_metadata(mint).await?;
132    ///     println!("Token: {}", mint_info.symbol);
133    ///
134    ///     Ok(())
135    /// }
136    /// ```
137    pub async fn get_token_metadata(&self, mint_address: Address) -> Result<MintInfo> {
138        let path = api_path(&format!("{}?token={}", TOKEN_METADATA, mint_address));
139        let response: MintInfo = self.get(&path).await?;
140        Ok(response)
141    }
142
143    /// Pause or unpause a token.
144    ///
145    /// # Arguments
146    ///
147    /// * `payload` - Token pause parameters
148    /// * `private_key` - Private key for signing the transaction (must have pause authority)
149    ///
150    /// # Returns
151    ///
152    /// The transaction result.
153    pub async fn pause_token(
154        &self,
155        payload: TokenPausePayload,
156        private_key: &str,
157    ) -> Result<TransactionResponse> {
158        let signature = sign_transaction_payload(&payload, private_key)?;
159        let request = PauseTokenRequest { payload, signature };
160
161        self.post(&api_path(PAUSE), &request).await
162    }
163
164    /// Manage token blacklist (add or remove addresses).
165    ///
166    /// # Arguments
167    ///
168    /// * `payload` - Token blacklist management parameters
169    /// * `private_key` - Private key for signing the transaction (must have manage list authority)
170    ///
171    /// # Returns
172    ///
173    /// The transaction result.
174    pub async fn manage_blacklist(
175        &self,
176        payload: TokenBlacklistPayload,
177        private_key: &str,
178    ) -> Result<TransactionResponse> {
179        let signature = sign_transaction_payload(&payload, private_key)?;
180        let request = BlacklistTokenRequest { payload, signature };
181
182        self.post(&api_path(MANAGE_BLACKLIST), &request).await
183    }
184
185    /// Manage token whitelist (add or remove addresses).
186    ///
187    /// # Arguments
188    ///
189    /// * `payload` - Token whitelist management parameters
190    /// * `private_key` - Private key for signing the transaction (must have manage list authority)
191    ///
192    /// # Returns
193    ///
194    /// The transaction result.
195    pub async fn manage_whitelist(
196        &self,
197        payload: TokenWhitelistPayload,
198        private_key: &str,
199    ) -> Result<TransactionResponse> {
200        let signature = sign_transaction_payload(&payload, private_key)?;
201        let request = WhitelistTokenRequest { payload, signature };
202
203        self.post(&api_path(MANAGE_WHITELIST), &request).await
204    }
205
206    /// Update token metadata.
207    ///
208    /// # Arguments
209    ///
210    /// * `payload` - Token metadata update parameters
211    /// * `private_key` - Private key for signing the transaction (must have update metadata authority)
212    ///
213    /// # Returns
214    ///
215    /// The transaction result.
216    pub async fn update_token_metadata(
217        &self,
218        payload: TokenMetadataUpdatePayload,
219        private_key: &str,
220    ) -> Result<TransactionResponse> {
221        let signature = sign_transaction_payload(&payload, private_key)?;
222        let request = UpdateMetadataRequest { payload, signature };
223
224        self.post(&api_path(UPDATE_METADATA), &request).await
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231    use crate::{Authority, AuthorityAction, BlacklistAction, PauseAction, WhitelistAction};
232    use alloy_primitives::{Address, U256};
233    use std::str::FromStr;
234
235    #[test]
236    fn test_authority_values() {
237        assert_eq!(
238            serde_json::to_string(&Authority::MasterMintBurn).expect("Test data should be valid"),
239            "\"MasterMintBurn\""
240        );
241        assert_eq!(
242            serde_json::to_string(&Authority::MintBurnTokens).expect("Test data should be valid"),
243            "\"MintBurnTokens\""
244        );
245        assert_eq!(
246            serde_json::to_string(&Authority::Pause).expect("Test data should be valid"),
247            "\"Pause\""
248        );
249        assert_eq!(
250            serde_json::to_string(&Authority::ManageList).expect("Test data should be valid"),
251            "\"ManageList\""
252        );
253        assert_eq!(
254            serde_json::to_string(&Authority::UpdateMetadata).expect("Test data should be valid"),
255            "\"UpdateMetadata\""
256        );
257    }
258
259    #[test]
260    fn test_token_mint_payload_structure() {
261        let address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")
262            .expect("Test data should be valid");
263        let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")
264            .expect("Test data should be valid");
265
266        let payload = TokenMintPayload {
267            recent_epoch: 100,
268            recent_checkpoint: 200,
269            chain_id: 1212101,
270            nonce: 5,
271            recipient: address,
272            value: U256::from(1000000000000000000u64),
273            token,
274        };
275
276        assert_eq!(payload.recent_epoch, 100);
277        assert_eq!(payload.recent_checkpoint, 200);
278        assert_eq!(payload.chain_id, 1212101);
279        assert_eq!(payload.nonce, 5);
280        assert_eq!(payload.recipient, address);
281        assert_eq!(payload.value, U256::from(1000000000000000000u64));
282        assert_eq!(payload.token, token);
283    }
284
285    #[test]
286    fn test_token_burn_payload_structure() {
287        let address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")
288            .expect("Test data should be valid");
289        let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")
290            .expect("Test data should be valid");
291
292        let payload = TokenBurnPayload {
293            recent_epoch: 100,
294            recent_checkpoint: 200,
295            chain_id: 1212101,
296            nonce: 5,
297            recipient: address,
298            value: U256::from(500000000000000000u64),
299            token,
300        };
301
302        assert_eq!(payload.recent_epoch, 100);
303        assert_eq!(payload.recipient, address);
304        assert_eq!(payload.value, U256::from(500000000000000000u64));
305    }
306
307    #[test]
308    fn test_authority_action_serialization() {
309        assert_eq!(
310            serde_json::to_string(&AuthorityAction::Grant).expect("Test data should be valid"),
311            "\"Grant\""
312        );
313        assert_eq!(
314            serde_json::to_string(&AuthorityAction::Revoke).expect("Test data should be valid"),
315            "\"Revoke\""
316        );
317    }
318
319    #[test]
320    fn test_pause_action_serialization() {
321        assert_eq!(
322            serde_json::to_string(&PauseAction::Pause).expect("Test data should be valid"),
323            "\"Pause\""
324        );
325        assert_eq!(
326            serde_json::to_string(&PauseAction::Unpause).expect("Test data should be valid"),
327            "\"Unpause\""
328        );
329    }
330
331    #[test]
332    fn test_blacklist_action_serialization() {
333        assert_eq!(
334            serde_json::to_string(&BlacklistAction::Add).expect("Test data should be valid"),
335            "\"Add\""
336        );
337        assert_eq!(
338            serde_json::to_string(&BlacklistAction::Remove).expect("Test data should be valid"),
339            "\"Remove\""
340        );
341    }
342
343    #[test]
344    fn test_whitelist_action_serialization() {
345        assert_eq!(
346            serde_json::to_string(&WhitelistAction::Add).expect("Test data should be valid"),
347            "\"Add\""
348        );
349        assert_eq!(
350            serde_json::to_string(&WhitelistAction::Remove).expect("Test data should be valid"),
351            "\"Remove\""
352        );
353    }
354
355    #[test]
356    fn test_token_authority_payload_structure() {
357        let authority_address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")
358            .expect("Test data should be valid");
359        let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")
360            .expect("Test data should be valid");
361
362        let payload = TokenAuthorityPayload {
363            recent_epoch: 100,
364            recent_checkpoint: 200,
365            chain_id: 1212101,
366            nonce: 5,
367            action: AuthorityAction::Grant,
368            authority_type: Authority::MintBurnTokens,
369            authority_address,
370            token,
371            value: U256::from(1000000000000000000u64),
372        };
373
374        assert_eq!(payload.action, AuthorityAction::Grant);
375        assert_eq!(payload.authority_type, Authority::MintBurnTokens);
376        assert_eq!(payload.authority_address, authority_address);
377    }
378
379    #[test]
380    fn test_token_pause_payload_structure() {
381        let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")
382            .expect("Test data should be valid");
383
384        let payload = TokenPausePayload {
385            recent_epoch: 100,
386            recent_checkpoint: 200,
387            chain_id: 1212101,
388            nonce: 5,
389            action: PauseAction::Pause,
390            token,
391        };
392
393        assert_eq!(payload.action, PauseAction::Pause);
394        assert_eq!(payload.token, token);
395    }
396
397    #[test]
398    fn test_token_blacklist_payload_structure() {
399        let address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")
400            .expect("Test data should be valid");
401        let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")
402            .expect("Test data should be valid");
403
404        let payload = TokenBlacklistPayload {
405            recent_epoch: 100,
406            recent_checkpoint: 200,
407            chain_id: 1212101,
408            nonce: 5,
409            action: BlacklistAction::Add,
410            address,
411            token,
412        };
413
414        assert_eq!(payload.action, BlacklistAction::Add);
415        assert_eq!(payload.address, address);
416        assert_eq!(payload.token, token);
417    }
418
419    #[test]
420    fn test_token_whitelist_payload_structure() {
421        let address = Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")
422            .expect("Test data should be valid");
423        let token = Address::from_str("0x1234567890abcdef1234567890abcdef12345678")
424            .expect("Test data should be valid");
425
426        let payload = TokenWhitelistPayload {
427            recent_epoch: 100,
428            recent_checkpoint: 200,
429            chain_id: 1212101,
430            nonce: 5,
431            action: WhitelistAction::Add,
432            address,
433            token,
434        };
435
436        assert_eq!(payload.action, WhitelistAction::Add);
437        assert_eq!(payload.address, address);
438        assert_eq!(payload.token, token);
439    }
440}