1pub mod bridge;
2pub mod contract;
3pub mod dex;
4pub mod event;
5pub mod global;
6pub mod multicall;
7pub mod nft;
8pub mod nft_market;
9pub mod staking;
10pub mod token;
11pub mod tool;
12pub mod trade;
13pub mod types;
14pub mod wallet;
15
16use crate::{
17 global::rpc::{APTOS_DEVNET_URL, APTOS_MAINNET_URL, APTOS_TESTNET_URL},
18 trade::TransactionInfo,
19 types::*,
20};
21use reqwest::Client;
22use serde_json::Value;
23use std::time::Duration;
24
25const WAITING_TRANSACTION_DELAY_TIME: u64 = 500;
27
28#[derive(Debug, Clone)]
30pub enum AptosType {
31 Mainnet,
32 Testnet,
33 Devnet,
34}
35
36#[derive(Debug, Clone)]
37pub struct Aptos {
38 client: Client,
39 base_url: String,
40}
41
42impl Aptos {
43 pub fn new(network: AptosType) -> Self {
44 let base_url = match network {
45 AptosType::Mainnet => APTOS_MAINNET_URL.to_string(),
46 AptosType::Testnet => APTOS_TESTNET_URL.to_string(),
47 AptosType::Devnet => APTOS_DEVNET_URL.to_string(),
48 };
49 Aptos {
50 client: Client::new(),
51 base_url,
52 }
53 }
54
55 pub async fn get_chain_height(&self) -> Result<u64, String> {
57 let chain_info = self.get_chain_info().await?;
58 Ok(chain_info.block_height.parse::<u64>().unwrap_or(0))
59 }
60
61 pub async fn get_ledger_version(&self) -> Result<u64, String> {
63 let chain_info = self.get_chain_info().await?;
64 Ok(chain_info.ledger_version.parse::<u64>().unwrap_or(0))
65 }
66
67 pub async fn get_account_info(&self, address: &str) -> Result<AccountInfo, String> {
69 let url: String = format!("{}/accounts/{}", self.base_url, address);
70 let response = self.client.get(&url).send().await.unwrap();
71 if !response.status().is_success() {
72 let error_msg = response.text().await.unwrap();
73 return Err(format!("api error: {}", error_msg).to_string());
74 }
75
76 let account_info: AccountInfo = response.json().await.unwrap();
77 Ok(account_info)
78 }
79
80 pub async fn get_account_resource_vec(&self, address: &str) -> Result<Vec<Resource>, String> {
82 let url = format!("{}/accounts/{}/resources", self.base_url, address);
83 let response = self.client.get(&url).send().await.unwrap();
84 if !response.status().is_success() {
85 let error_msg = response.text().await.unwrap();
86 return Err(format!("api error: {}", error_msg).to_string());
87 }
88 let resources: Vec<Resource> = response.json().await.unwrap();
89 Ok(resources)
90 }
91
92 pub async fn get_account_resource(
94 &self,
95 address: &str,
96 resource_type: &str,
97 ) -> Result<Option<Resource>, String> {
98 let url = format!(
99 "{}/accounts/{}/resource/{}",
100 self.base_url, address, resource_type
101 );
102 let response = self.client.get(&url).send().await.unwrap();
103
104 if response.status() == 404 {
105 return Ok(None);
106 }
107
108 if !response.status().is_success() {
109 let error_msg = response.text().await.unwrap();
110 return Err(format!("api error: {}", error_msg).to_string());
111 }
112
113 let resource: Resource = response.json().await.unwrap();
114 Ok(Some(resource))
115 }
116
117 pub async fn get_account_module_vec(&self, address: &str) -> Result<Vec<Module>, String> {
119 let url = format!("{}/accounts/{}/modules", self.base_url, address);
120 let response = self.client.get(&url).send().await.unwrap();
121 if !response.status().is_success() {
122 let error_msg = response.text().await.unwrap();
123 return Err(format!("api error: {}", error_msg).to_string());
124 }
125 let modules: Vec<Module> = response.json().await.unwrap();
126 Ok(modules)
127 }
128
129 pub async fn get_account_module(
131 &self,
132 address: &str,
133 module_name: &str,
134 ) -> Result<Option<Module>, String> {
135 let url = format!(
136 "{}/accounts/{}/module/{}",
137 self.base_url, address, module_name
138 );
139 let response = self.client.get(&url).send().await.unwrap();
140 if response.status() == 404 {
141 return Ok(None);
142 }
143 if !response.status().is_success() {
144 let error_msg = response.text().await.unwrap();
145 return Err(format!("api error: {}", error_msg).to_string());
146 }
147 let module: Module = response.json().await.unwrap();
148 Ok(Some(module))
149 }
150
151 pub async fn submit_transaction(&self, txn_payload: &Value) -> Result<TransactionInfo, String> {
153 let url = format!("{}/transactions", self.base_url);
154 let response = self
155 .client
156 .post(&url)
157 .header("Content-Type", "application/json")
158 .json(txn_payload)
159 .send()
160 .await
161 .unwrap();
162 if !response.status().is_success() {
163 let error_msg = response.text().await.unwrap();
164 return Err(format!("transaction submit failed: {}", error_msg).to_string());
165 }
166 let transaction: TransactionInfo = response.json().await.unwrap();
167 Ok(transaction)
168 }
169
170 pub async fn get_transaction_info_by_hash(
172 &self,
173 tx_hash: &str,
174 ) -> Result<TransactionInfo, String> {
175 let url = format!("{}/transactions/by_hash/{}", self.base_url, tx_hash);
176 let response = self.client.get(&url).send().await.unwrap();
177 if !response.status().is_success() {
178 let error_msg = response.text().await.unwrap();
179 return Err(format!("api error: {}", error_msg).to_string());
180 }
181
182 let transaction: TransactionInfo = response
183 .json()
184 .await
185 .map_err(|e| format!("transaction parsing error: {:?}", e))?;
186
187 println!("交易信息:{:?}", transaction);
188
189 Ok(transaction)
190 }
191
192 pub async fn get_transaction_info_by_version(
194 &self,
195 version: u64,
196 ) -> Result<TransactionInfo, String> {
197 let url = format!("{}/transactions/by_version/{}", self.base_url, version);
198 let response = self.client.get(&url).send().await.unwrap();
199 if !response.status().is_success() {
200 let error_msg = response.text().await.unwrap();
201 return Err(format!("api error: {}", error_msg).to_string());
202 }
203 let transaction: TransactionInfo = response.json().await.unwrap();
204 Ok(transaction)
205 }
206
207 pub async fn get_account_transaction_vec(
209 &self,
210 address: &str,
211 limit: Option<u64>,
212 start: Option<u64>,
213 ) -> Result<Vec<TransactionInfo>, String> {
214 let limit = limit.unwrap_or(25);
215 let mut url = format!(
216 "{}/accounts/{}/transactions?limit={}",
217 self.base_url, address, limit
218 );
219 if let Some(start) = start {
220 url.push_str(&format!("&start={}", start));
221 }
222 let response = self.client.get(&url).send().await.unwrap();
223 if !response.status().is_success() {
224 let error_msg = response.text().await.unwrap();
225 return Err(format!("api error: {}", error_msg).to_string());
226 }
227 let transactions: Vec<TransactionInfo> = response.json().await.unwrap();
228 Ok(transactions)
229 }
230
231 pub async fn get_chain_info(&self) -> Result<ChainInfo, String> {
233 let url = format!("{}/", self.base_url);
234 let response = self.client.get(&url).send().await.unwrap();
235 if !response.status().is_success() {
236 let error_msg = response.text().await.unwrap();
237 return Err(format!("api error: {}", error_msg).to_string());
238 }
239 let ledger_info: ChainInfo = response.json().await.unwrap();
240 Ok(ledger_info)
241 }
242
243 pub async fn get_block_by_height(&self, height: u64) -> Result<Block, String> {
245 let url = format!("{}/blocks/by_height/{}", self.base_url, height);
246 let response = self.client.get(&url).send().await.unwrap();
247 if !response.status().is_success() {
248 let error_msg = response.text().await.unwrap();
249 return Err(format!("api error: {}", error_msg).to_string());
250 }
251 let block: Block = response.json().await.unwrap();
252 Ok(block)
253 }
254
255 pub async fn get_block_by_version(&self, version: u64) -> Result<Block, String> {
257 let url = format!("{}/blocks/by_version/{}", self.base_url, version);
258 let response = self.client.get(&url).send().await.unwrap();
259 if !response.status().is_success() {
260 let error_msg = response.text().await.unwrap();
261 return Err(format!("api error: {}", error_msg).to_string());
262 }
263 let block: Block = response.json().await.unwrap();
264 Ok(block)
265 }
266
267 pub async fn get_account_event_vec(
269 &self,
270 address: &str,
271 event_type: &str,
272 limit: Option<u64>,
273 start: Option<u64>,
274 ) -> Result<Vec<Event>, String> {
275 let limit = limit.unwrap_or(25);
276 let mut url = format!(
277 "{}/accounts/{}/events/{}?limit={}",
278 self.base_url, address, event_type, limit
279 );
280 if let Some(start) = start {
281 url.push_str(&format!("&start={}", start));
282 }
283 let response = self.client.get(&url).send().await.unwrap();
284 if !response.status().is_success() {
285 let error_msg = response.text().await.unwrap();
286 return Err(format!("api error: {}", error_msg).to_string());
287 }
288 let events: Vec<Event> = response.json().await.unwrap();
289 Ok(events)
290 }
291
292 pub async fn get_table_item(
294 &self,
295 table_handle: &str,
296 key_type: &str,
297 value_type: &str,
298 key: &Value,
299 ) -> Result<Value, String> {
300 let url = format!("{}/tables/{}/item", self.base_url, table_handle);
301 let request = TableRequest {
302 key_type: key_type.to_string(),
303 value_type: value_type.to_string(),
304 key: key.clone(),
305 };
306 let response = self
307 .client
308 .post(&url)
309 .header("Content-Type", "application/json")
310 .json(&request)
311 .send()
312 .await
313 .unwrap();
314 if !response.status().is_success() {
315 let error_msg = response.text().await.unwrap();
316 return Err(format!("api error: {}", error_msg).to_string());
317 }
318 let value: Value = response.json().await.unwrap();
319 Ok(value)
320 }
321
322 pub async fn view(&self, view_request: &ViewRequest) -> Result<Vec<Value>, String> {
324 let url = format!("{}/view", self.base_url);
325 let response = self
326 .client
327 .post(&url)
328 .header("Content-Type", "application/json")
329 .json(view_request)
330 .send()
331 .await
332 .unwrap();
333 if !response.status().is_success() {
334 let error_msg = response.text().await.unwrap();
335 return Err(format!("api error: {}", error_msg).to_string());
336 }
337 let result: Vec<Value> = response.json().await.unwrap();
338 Ok(result)
339 }
340
341 pub async fn estimate_gas_price(&self) -> Result<u64, String> {
343 let url = format!("{}/estimate_gas_price", self.base_url);
344 let response = self.client.get(&url).send().await.unwrap();
345 if !response.status().is_success() {
346 let error_msg = response.text().await.unwrap();
347 return Err(format!("api error: {}", error_msg).to_string());
348 }
349 let gas_estimation: GasEstimation = response.json().await.unwrap();
350 Ok(gas_estimation.gas_estimate * 2000)
351 }
352
353 pub async fn get_account_balance(&self, address: &str) -> Result<u64, String> {
355 let resources = self.get_account_resource_vec(address).await.unwrap();
356 for resource in resources {
357 if resource.r#type == "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>" {
358 if let Some(data) = resource.data.as_object() {
359 if let Some(coin) = data.get("coin") {
360 if let Some(value) = coin.get("value") {
361 return if let Some(balance) = value.as_str() {
362 Ok(balance.parse().unwrap_or(0))
363 } else if let Some(balance) = value.as_u64() {
364 Ok(balance)
365 } else {
366 Ok(0)
367 };
368 }
369 }
370 }
371 }
372 }
373 Ok(0)
374 }
375 pub async fn get_token_balance(&self, address: &str, token_type: &str) -> Result<u64, String> {
377 let resource_type = format!("0x1::coin::CoinStore<{}>", token_type);
378 if let Some(resource) = self
379 .get_account_resource(address, &resource_type)
380 .await
381 .unwrap()
382 {
383 if let Some(data) = resource.data.as_object() {
384 if let Some(coin) = data.get("coin") {
385 if let Some(value) = coin.get("value") {
386 return if let Some(balance) = value.as_str() {
387 Ok(balance.parse().unwrap_or(0))
388 } else if let Some(balance) = value.as_u64() {
389 Ok(balance)
390 } else {
391 Ok(0)
392 };
393 }
394 }
395 }
396 }
397 Ok(0)
398 }
399 pub async fn waiting_transaction(
401 &self,
402 txn_hash: &str,
403 timeout_secs: u64,
404 ) -> Result<TransactionInfo, String> {
405 let start = std::time::Instant::now();
406 let timeout = Duration::from_secs(timeout_secs);
407 while start.elapsed() < timeout {
408 match self.get_transaction_info_by_hash(txn_hash).await {
409 Ok(txn) => {
410 return Ok(txn);
412 }
413 Err(e) => {
414 tokio::time::sleep(Duration::from_millis(WAITING_TRANSACTION_DELAY_TIME)).await;
416 }
417 }
418 }
419 Err(format!(
420 "Transaction timeout tx:{:?}\ntime:{:?}",
421 txn_hash, timeout_secs
422 )
423 .to_string())
424 }
425 pub async fn is_transaction_successful(&self, txn_hash: &str) -> Result<bool, String> {
427 match self.get_transaction_info_by_hash(txn_hash).await {
428 Ok(t) => Ok(t.success),
429 Err(e) => Err(e),
430 }
431 }
432 pub async fn get_apt_balance_by_account(&self, address: &str) -> Result<f64, String> {
434 match self.get_account_balance(address).await {
435 Ok(balance) => Ok(balance as f64 / 100_000_000.0),
436 Err(e) => Err(e),
437 }
438 }
439 pub async fn get_account_sequence_number(&self, address: &str) -> Result<u64, String> {
441 match self.get_account_info(address).await {
442 Ok(info) => Ok(info.sequence_number.parse::<u64>().unwrap()),
443 Err(e) => Err(e),
444 }
445 }
446 pub async fn account_exists(&self, address: &str) -> Result<bool, String> {
448 match self.get_account_info(address).await {
449 Ok(_) => Ok(true),
450 Err(e) => {
451 if e.to_string().contains("Account not found") {
452 Ok(false)
453 } else {
454 Err(e)
455 }
456 }
457 }
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use std::sync::Arc;
465
466 #[tokio::test]
467 async fn test_get_specific_transaction() {
468 let client = Aptos::new(AptosType::Mainnet);
469 let known_tx_hash = "0xc4da6f117be28bdf63ee455dcb845fe2c4447c5b89a9fb20e3afa92d9b8f2f50";
470 let result = client.get_transaction_info_by_hash(known_tx_hash).await;
471 match result {
472 Ok(tx) => {
473 println!("✅Find Transaction: {:?}", tx);
474 println!("Hash: {}", tx.hash);
475 println!("Version: {}", tx.version);
476 }
477 Err(e) => {
478 println!("❌ error: {}", e);
479 }
480 }
481 }
482}