onemoney_protocol/api/
transactions.rs

1//! Transaction-related API operations.
2
3use crate::client::Client;
4use crate::client::config::api_path;
5use crate::client::config::endpoints::transactions::{
6    BY_HASH, ESTIMATE_FEE, PAYMENT, RECEIPT_BY_HASH,
7};
8use crate::crypto::sign_transaction_payload;
9use crate::requests::{FeeEstimateRequest, PaymentPayload, PaymentRequest};
10use crate::responses::FeeEstimate;
11use crate::responses::TransactionReceipt;
12use crate::responses::TransactionResponse;
13use crate::{Result, Transaction};
14#[cfg(test)]
15use rlp::encode as rlp_encode;
16
17impl Client {
18    /// Send a payment transaction.
19    ///
20    /// # Arguments
21    ///
22    /// * `payload` - Payment transaction parameters
23    /// * `private_key` - Private key for signing the transaction
24    ///
25    /// # Returns
26    ///
27    /// The payment response containing the transaction hash.
28    ///
29    /// # Example
30    ///
31    /// ```rust,no_run
32    /// use onemoney_protocol::{Client, PaymentPayload};
33    /// use alloy_primitives::{Address, U256};
34    /// use std::str::FromStr;
35    ///
36    /// #[tokio::main]
37    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
38    ///     let client = Client::mainnet()?;
39    ///
40    ///     let payload = PaymentPayload {
41    ///         recent_epoch: 123,
42    ///         recent_checkpoint: 456,
43    ///         chain_id: 1212101,
44    ///         nonce: 0,
45    ///         recipient: Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")?,
46    ///         value: U256::from(1000000000000000000u64), // 1 token
47    ///         token: Address::from_str("0x1234567890abcdef1234567890abcdef12345678")?,
48    ///     };
49    ///
50    ///     let private_key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
51    ///     let result = client.send_payment(payload, private_key).await?;
52    ///     println!("Transaction hash: {}", result.hash);
53    ///
54    ///     Ok(())
55    /// }
56    /// ```
57    pub async fn send_payment(
58        &self,
59        payload: PaymentPayload,
60        private_key: &str,
61    ) -> Result<TransactionResponse> {
62        // Use the L1-compatible signing method
63        let signature = sign_transaction_payload(&payload, private_key)?;
64        let request = PaymentRequest { payload, signature };
65
66        self.post(&api_path(PAYMENT), &request).await
67    }
68
69    /// Get transaction by hash.
70    ///
71    /// # Arguments
72    ///
73    /// * `hash` - The transaction hash to query
74    ///
75    /// # Returns
76    ///
77    /// The transaction information.
78    ///
79    /// # Example
80    ///
81    /// ```rust,no_run
82    /// use onemoney_protocol::Client;
83    ///
84    /// #[tokio::main]
85    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
86    ///     let client = Client::mainnet()?;
87    ///
88    ///     let tx_hash = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
89    ///     let transaction = client.get_transaction_by_hash(tx_hash).await?;
90    ///
91    ///     println!("Transaction hash: {}", transaction.hash);
92    ///
93    ///     Ok(())
94    /// }
95    /// ```
96    pub async fn get_transaction_by_hash(&self, hash: &str) -> Result<Transaction> {
97        let path = api_path(&format!("{}?hash={}", BY_HASH, hash));
98        self.get(&path).await
99    }
100
101    /// Estimate fees for a transaction.
102    ///
103    /// # Arguments
104    ///
105    /// * `request` - Fee estimation request parameters
106    ///
107    /// # Returns
108    ///
109    /// The fee estimate information.
110    ///
111    /// # Example
112    ///
113    /// ```rust,no_run
114    /// use onemoney_protocol::{Client, FeeEstimateRequest};
115    ///
116    /// #[tokio::main]
117    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
118    ///     let client = Client::mainnet()?;
119    ///
120    ///     let request = FeeEstimateRequest {
121    ///         from: "0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0".to_string(),
122    ///         value: "1000000000000000000".to_string(),
123    ///         token: None,
124    ///     };
125    ///
126    ///     let estimate = client.estimate_fee(request).await?;
127    ///     println!("Estimated fee: {}", estimate.fee);
128    ///
129    ///     Ok(())
130    /// }
131    /// ```
132    pub async fn estimate_fee(&self, request: FeeEstimateRequest) -> Result<FeeEstimate> {
133        let mut path = ESTIMATE_FEE.to_string();
134        let mut query_params = Vec::new();
135
136        query_params.push(format!("from={}", request.from));
137        query_params.push(format!("value={}", request.value));
138        if let Some(token) = request.token {
139            query_params.push(format!("token={}", token));
140        }
141
142        if !query_params.is_empty() {
143            path.push('?');
144            path.push_str(&query_params.join("&"));
145        }
146
147        self.get(&api_path(&path)).await
148    }
149
150    /// Get transaction receipt by hash.
151    ///
152    /// # Arguments
153    ///
154    /// * `hash` - The transaction hash to query
155    ///
156    /// # Returns
157    ///
158    /// The transaction receipt information.
159    ///
160    /// # Example
161    ///
162    /// ```rust,no_run
163    /// use onemoney_protocol::Client;
164    ///
165    /// #[tokio::main]
166    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
167    ///     let client = Client::mainnet()?;
168    ///
169    ///     let tx_hash = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
170    ///     let receipt = client.get_transaction_receipt_by_hash(tx_hash).await?;
171    ///
172    ///     println!("Transaction success: {}", receipt.success);
173    ///     println!("Fee used: {}", receipt.fee_used);
174    ///
175    ///     Ok(())
176    /// }
177    /// ```
178    pub async fn get_transaction_receipt_by_hash(&self, hash: &str) -> Result<TransactionReceipt> {
179        let path = api_path(&format!("{}?hash={}", RECEIPT_BY_HASH, hash));
180        self.get(&path).await
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use alloy_primitives::{Address, U256};
188    use std::str::FromStr;
189
190    #[test]
191    fn test_payment_payload_rlp() {
192        let payload = PaymentPayload {
193            recent_epoch: 123,
194            recent_checkpoint: 456,
195            chain_id: 1212101,
196            nonce: 0,
197            recipient: Address::from_str("0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0")
198                .expect("Test data should be valid"),
199            value: U256::from(1000000000000000000u64),
200            token: Address::from_str("0x1234567890abcdef1234567890abcdef12345678")
201                .expect("Test data should be valid"),
202        };
203
204        let encoded = rlp_encode(&payload);
205        assert!(!encoded.is_empty());
206    }
207
208    #[test]
209    fn test_fee_estimate_request() {
210        let request = FeeEstimateRequest {
211            from: "0x742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0".to_string(),
212            value: "1000000000000000000".to_string(),
213            token: None,
214        };
215
216        let json = serde_json::to_string(&request).expect("Test data should be valid");
217        assert!(json.contains("742d35Cc6634C0532925a3b8D91D6F4A81B8Cbc0"));
218    }
219}