1use crate::types::HashValue;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone)]
8pub struct AptosResponse<T> {
9 pub data: T,
11 pub ledger_version: Option<u64>,
13 pub ledger_timestamp: Option<u64>,
15 pub epoch: Option<u64>,
17 pub block_height: Option<u64>,
19 pub oldest_ledger_version: Option<u64>,
21 pub cursor: Option<String>,
23}
24
25impl<T> AptosResponse<T> {
26 pub fn new(data: T) -> Self {
28 Self {
29 data,
30 ledger_version: None,
31 ledger_timestamp: None,
32 epoch: None,
33 block_height: None,
34 oldest_ledger_version: None,
35 cursor: None,
36 }
37 }
38
39 pub fn into_inner(self) -> T {
41 self.data
42 }
43
44 pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> AptosResponse<U> {
46 AptosResponse {
47 data: f(self.data),
48 ledger_version: self.ledger_version,
49 ledger_timestamp: self.ledger_timestamp,
50 epoch: self.epoch,
51 block_height: self.block_height,
52 oldest_ledger_version: self.oldest_ledger_version,
53 cursor: self.cursor,
54 }
55 }
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct PendingTransaction {
61 pub hash: HashValue,
63 pub sender: String,
65 pub sequence_number: String,
67 pub max_gas_amount: String,
69 pub gas_unit_price: String,
71 pub expiration_timestamp_secs: String,
73}
74
75impl PendingTransaction {
76 pub fn hash(&self) -> &HashValue {
78 &self.hash
79 }
80
81 pub fn sender(&self) -> &str {
83 &self.sender
84 }
85
86 pub fn sequence_number(&self) -> Result<u64, std::num::ParseIntError> {
91 self.sequence_number.parse()
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct LedgerInfo {
98 pub chain_id: u8,
100 pub epoch: String,
102 pub ledger_version: String,
104 pub oldest_ledger_version: String,
106 pub ledger_timestamp: String,
108 pub node_role: String,
110 pub oldest_block_height: String,
112 pub block_height: String,
114 pub git_hash: Option<String>,
116}
117
118impl LedgerInfo {
119 pub fn version(&self) -> Result<u64, std::num::ParseIntError> {
124 self.ledger_version.parse()
125 }
126
127 pub fn height(&self) -> Result<u64, std::num::ParseIntError> {
132 self.block_height.parse()
133 }
134
135 pub fn epoch_num(&self) -> Result<u64, std::num::ParseIntError> {
140 self.epoch.parse()
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct GasEstimation {
147 pub deprioritized_gas_estimate: Option<u64>,
149 pub gas_estimate: u64,
151 pub prioritized_gas_estimate: Option<u64>,
153}
154
155impl GasEstimation {
156 pub fn recommended(&self) -> u64 {
158 self.gas_estimate
159 }
160
161 pub fn low(&self) -> u64 {
163 self.deprioritized_gas_estimate.unwrap_or(self.gas_estimate)
164 }
165
166 pub fn high(&self) -> u64 {
168 self.prioritized_gas_estimate.unwrap_or(self.gas_estimate)
169 }
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct AccountData {
175 pub sequence_number: String,
177 pub authentication_key: String,
179}
180
181impl AccountData {
182 pub fn sequence_number(&self) -> Result<u64, std::num::ParseIntError> {
187 self.sequence_number.parse()
188 }
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct Resource {
194 #[serde(rename = "type")]
196 pub typ: String,
197 pub data: serde_json::Value,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct MoveModule {
204 pub bytecode: String,
206 pub abi: Option<MoveModuleABI>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct MoveModuleABI {
213 pub address: String,
215 pub name: String,
217 pub exposed_functions: Vec<MoveFunction>,
219 pub structs: Vec<MoveStructDef>,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct MoveFunction {
226 pub name: String,
228 pub visibility: String,
230 pub is_entry: bool,
232 pub is_view: bool,
234 pub generic_type_params: Vec<MoveFunctionGenericTypeParam>,
236 pub params: Vec<String>,
238 #[serde(rename = "return")]
240 pub returns: Vec<String>,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct MoveFunctionGenericTypeParam {
246 pub constraints: Vec<String>,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct MoveStructDef {
253 pub name: String,
255 pub is_native: bool,
257 pub abilities: Vec<String>,
259 pub generic_type_params: Vec<MoveStructGenericTypeParam>,
261 pub fields: Vec<MoveStructField>,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct MoveStructGenericTypeParam {
268 pub constraints: Vec<String>,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct MoveStructField {
275 pub name: String,
277 #[serde(rename = "type")]
279 pub typ: String,
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn test_aptos_response() {
288 let response = AptosResponse::new(42);
289 assert_eq!(response.into_inner(), 42);
290 }
291
292 #[test]
293 fn test_aptos_response_map() {
294 let response = AptosResponse::new(42);
295 let mapped = response.map(|x| x.to_string());
296 assert_eq!(mapped.into_inner(), "42");
297 }
298
299 #[test]
300 fn test_aptos_response_preserves_metadata() {
301 let mut response = AptosResponse::new(42);
302 response.ledger_version = Some(100);
303 response.epoch = Some(5);
304 response.block_height = Some(1000);
305 response.cursor = Some("abc".to_string());
306
307 let mapped = response.map(|x| x * 2);
308 assert_eq!(mapped.data, 84);
309 assert_eq!(mapped.ledger_version, Some(100));
310 assert_eq!(mapped.epoch, Some(5));
311 assert_eq!(mapped.block_height, Some(1000));
312 assert_eq!(mapped.cursor, Some("abc".to_string()));
313 }
314
315 #[test]
316 fn test_gas_estimation() {
317 let gas = GasEstimation {
318 deprioritized_gas_estimate: Some(50),
319 gas_estimate: 100,
320 prioritized_gas_estimate: Some(150),
321 };
322 assert_eq!(gas.low(), 50);
323 assert_eq!(gas.recommended(), 100);
324 assert_eq!(gas.high(), 150);
325 }
326
327 #[test]
328 fn test_gas_estimation_defaults() {
329 let gas = GasEstimation {
330 deprioritized_gas_estimate: None,
331 gas_estimate: 100,
332 prioritized_gas_estimate: None,
333 };
334 assert_eq!(gas.low(), 100);
335 assert_eq!(gas.recommended(), 100);
336 assert_eq!(gas.high(), 100);
337 }
338
339 #[test]
340 fn test_pending_transaction_deserialization() {
341 let json = r#"{
342 "hash": "0x0000000000000000000000000000000000000000000000000000000000000001",
343 "sender": "0x1",
344 "sequence_number": "42",
345 "max_gas_amount": "100000",
346 "gas_unit_price": "100",
347 "expiration_timestamp_secs": "1000000000"
348 }"#;
349 let pending: PendingTransaction = serde_json::from_str(json).unwrap();
350 assert_eq!(pending.sender(), "0x1");
351 assert_eq!(pending.sequence_number().unwrap(), 42);
352 }
353
354 #[test]
355 fn test_ledger_info_deserialization() {
356 let json = r#"{
357 "chain_id": 2,
358 "epoch": "100",
359 "ledger_version": "12345",
360 "oldest_ledger_version": "0",
361 "ledger_timestamp": "1000000000",
362 "node_role": "full_node",
363 "oldest_block_height": "0",
364 "block_height": "5000"
365 }"#;
366 let info: LedgerInfo = serde_json::from_str(json).unwrap();
367 assert_eq!(info.chain_id, 2);
368 assert_eq!(info.version().unwrap(), 12345);
369 assert_eq!(info.height().unwrap(), 5000);
370 assert_eq!(info.epoch_num().unwrap(), 100);
371 }
372
373 #[test]
374 fn test_account_data_deserialization() {
375 let json = r#"{
376 "sequence_number": "10",
377 "authentication_key": "0x1234"
378 }"#;
379 let account: AccountData = serde_json::from_str(json).unwrap();
380 assert_eq!(account.sequence_number().unwrap(), 10);
381 }
382
383 #[test]
384 fn test_resource_deserialization() {
385 let json = r#"{
386 "type": "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
387 "data": {"coin": {"value": "1000"}}
388 }"#;
389 let resource: Resource = serde_json::from_str(json).unwrap();
390 assert_eq!(
391 resource.typ,
392 "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"
393 );
394 }
395
396 #[test]
397 fn test_move_module_abi_deserialization() {
398 let json = r#"{
399 "address": "0x1",
400 "name": "coin",
401 "exposed_functions": [
402 {
403 "name": "transfer",
404 "visibility": "public",
405 "is_entry": true,
406 "is_view": false,
407 "generic_type_params": [],
408 "params": ["&signer", "address", "u64"],
409 "return": []
410 }
411 ],
412 "structs": []
413 }"#;
414 let abi: MoveModuleABI = serde_json::from_str(json).unwrap();
415 assert_eq!(abi.name, "coin");
416 assert_eq!(abi.exposed_functions.len(), 1);
417 assert!(abi.exposed_functions[0].is_entry);
418 }
419
420 #[test]
421 fn test_move_struct_def_deserialization() {
422 let json = r#"{
423 "name": "CoinStore",
424 "is_native": false,
425 "abilities": ["key"],
426 "generic_type_params": [
427 {"constraints": []}
428 ],
429 "fields": [
430 {"name": "coin", "type": "0x1::coin::Coin<T0>"}
431 ]
432 }"#;
433 let struct_def: MoveStructDef = serde_json::from_str(json).unwrap();
434 assert_eq!(struct_def.name, "CoinStore");
435 assert!(!struct_def.is_native);
436 assert_eq!(struct_def.abilities, vec!["key"]);
437 assert_eq!(struct_def.fields.len(), 1);
438 }
439}