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}