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}, trade::Transaction, types::*
18};
19use reqwest::Client;
20use serde_json::Value;
21use std::time::Duration;
22
23const WAITING_TRANSACTION_DELAY_TIME: u64 = 500;
25
26#[derive(Debug, Clone)]
28pub enum AptosClientType {
29 Mainnet,
30 Testnet,
31 Devnet,
32}
33
34#[derive(Debug, Clone)]
35pub struct AptosClient {
36 client: Client,
37 base_url: String,
38}
39
40impl AptosClient {
41 pub fn new(network: AptosClientType) -> Self {
42 let base_url = match network {
43 AptosClientType::Mainnet => APTOS_MAINNET_URL.to_string(),
44 AptosClientType::Testnet => APTOS_TESTNET_URL.to_string(),
45 AptosClientType::Devnet => APTOS_DEVNET_URL.to_string(),
46 };
47 AptosClient {
48 client: Client::new(),
49 base_url,
50 }
51 }
52
53 pub async fn get_chain_height(&self) -> Result<u64, String> {
55 let chain_info = self.get_chain_info().await?;
56 Ok(chain_info.ledger_version.parse::<u64>().unwrap_or(0))
57 }
58
59 pub async fn get_account_info(&self, address: &str) -> Result<AccountInfo, String> {
61 let url: String = format!("{}/accounts/{}", self.base_url, address);
62 let response = self.client.get(&url).send().await.unwrap();
63 if !response.status().is_success() {
64 let error_msg = response.text().await.unwrap();
65 return Err(format!("api error: {}", error_msg).to_string());
66 }
67
68 let account_info: AccountInfo = response.json().await.unwrap();
69 Ok(account_info)
70 }
71
72 pub async fn get_account_resource_vec(&self, address: &str) -> Result<Vec<Resource>, String> {
74 let url = format!("{}/accounts/{}/resources", self.base_url, address);
75 let response = self.client.get(&url).send().await.unwrap();
76 if !response.status().is_success() {
77 let error_msg = response.text().await.unwrap();
78 return Err(format!("api error: {}", error_msg).to_string());
79 }
80 let resources: Vec<Resource> = response.json().await.unwrap();
81 Ok(resources)
82 }
83
84 pub async fn get_account_resource(
86 &self,
87 address: &str,
88 resource_type: &str,
89 ) -> Result<Option<Resource>, String> {
90 let url = format!(
91 "{}/accounts/{}/resource/{}",
92 self.base_url, address, resource_type
93 );
94 let response = self.client.get(&url).send().await.unwrap();
95
96 if response.status() == 404 {
97 return Ok(None);
98 }
99
100 if !response.status().is_success() {
101 let error_msg = response.text().await.unwrap();
102 return Err(format!("api error: {}", error_msg).to_string());
103 }
104
105 let resource: Resource = response.json().await.unwrap();
106 Ok(Some(resource))
107 }
108
109 pub async fn get_account_module_vec(&self, address: &str) -> Result<Vec<Module>, String> {
111 let url = format!("{}/accounts/{}/modules", self.base_url, address);
112 let response = self.client.get(&url).send().await.unwrap();
113 if !response.status().is_success() {
114 let error_msg = response.text().await.unwrap();
115 return Err(format!("api error: {}", error_msg).to_string());
116 }
117 let modules: Vec<Module> = response.json().await.unwrap();
118 Ok(modules)
119 }
120
121 pub async fn get_account_module(
123 &self,
124 address: &str,
125 module_name: &str,
126 ) -> Result<Option<Module>, String> {
127 let url = format!(
128 "{}/accounts/{}/module/{}",
129 self.base_url, address, module_name
130 );
131 let response = self.client.get(&url).send().await.unwrap();
132 if response.status() == 404 {
133 return Ok(None);
134 }
135 if !response.status().is_success() {
136 let error_msg = response.text().await.unwrap();
137 return Err(format!("api error: {}", error_msg).to_string());
138 }
139 let module: Module = response.json().await.unwrap();
140 Ok(Some(module))
141 }
142
143 pub async fn submit_transaction(&self, txn_payload: &Value) -> Result<Transaction, String> {
145 let url = format!("{}/transactions", self.base_url);
146 let response = self
147 .client
148 .post(&url)
149 .header("Content-Type", "application/json")
150 .json(txn_payload)
151 .send()
152 .await
153 .unwrap();
154 if !response.status().is_success() {
155 let error_msg = response.text().await.unwrap();
156 return Err(format!("transaction submit failed: {}", error_msg).to_string());
157 }
158 let transaction: Transaction = response.json().await.unwrap();
159 Ok(transaction)
160 }
161
162 pub async fn get_transaction_info(&self, txn_hash: &str) -> Result<Transaction, String> {
164 let url = format!("{}/transactions/{}", self.base_url, txn_hash);
165 let response = self.client.get(&url).send().await.unwrap();
166 if !response.status().is_success() {
167 let error_msg = response.text().await.unwrap();
168 return Err(format!("api error: {}", error_msg).to_string());
169 }
170 let transaction: Transaction = response.json().await.unwrap();
171 Ok(transaction)
172 }
173
174 pub async fn get_transaction_by_version(&self, version: u64) -> Result<Transaction, String> {
176 let url = format!("{}/transactions/by_version/{}", self.base_url, version);
177 let response = self.client.get(&url).send().await.unwrap();
178 if !response.status().is_success() {
179 let error_msg = response.text().await.unwrap();
180 return Err(format!("api error: {}", error_msg).to_string());
181 }
182 let transaction: Transaction = response.json().await.unwrap();
183 Ok(transaction)
184 }
185
186 pub async fn get_account_transaction_vec(
188 &self,
189 address: &str,
190 limit: Option<u64>,
191 start: Option<u64>,
192 ) -> Result<Vec<Transaction>, String> {
193 let limit = limit.unwrap_or(25);
194 let mut url = format!(
195 "{}/accounts/{}/transactions?limit={}",
196 self.base_url, address, limit
197 );
198 if let Some(start) = start {
199 url.push_str(&format!("&start={}", start));
200 }
201 let response = self.client.get(&url).send().await.unwrap();
202 if !response.status().is_success() {
203 let error_msg = response.text().await.unwrap();
204 return Err(format!("api error: {}", error_msg).to_string());
205 }
206 let transactions: Vec<Transaction> = response.json().await.unwrap();
207 Ok(transactions)
208 }
209
210 pub async fn get_chain_info(&self) -> Result<ChainInfo, String> {
212 let url = format!("{}/", self.base_url);
213 let response = self.client.get(&url).send().await.unwrap();
214 if !response.status().is_success() {
215 let error_msg = response.text().await.unwrap();
216 return Err(format!("api error: {}", error_msg).to_string());
217 }
218 let ledger_info: ChainInfo = response.json().await.unwrap();
219 Ok(ledger_info)
220 }
221
222 pub async fn get_block_by_height(&self, height: u64) -> Result<Block, String> {
224 let url = format!("{}/blocks/by_height/{}", self.base_url, height);
225 let response = self.client.get(&url).send().await.unwrap();
226 if !response.status().is_success() {
227 let error_msg = response.text().await.unwrap();
228 return Err(format!("api error: {}", error_msg).to_string());
229 }
230 let block: Block = response.json().await.unwrap();
231 Ok(block)
232 }
233
234 pub async fn get_block_by_version(&self, version: u64) -> Result<Block, String> {
236 let url = format!("{}/blocks/by_version/{}", self.base_url, version);
237 let response = self.client.get(&url).send().await.unwrap();
238 if !response.status().is_success() {
239 let error_msg = response.text().await.unwrap();
240 return Err(format!("api error: {}", error_msg).to_string());
241 }
242 let block: Block = response.json().await.unwrap();
243 Ok(block)
244 }
245
246 pub async fn get_account_event_vec(
248 &self,
249 address: &str,
250 event_type: &str,
251 limit: Option<u64>,
252 start: Option<u64>,
253 ) -> Result<Vec<Event>, String> {
254 let limit = limit.unwrap_or(25);
255 let mut url = format!(
256 "{}/accounts/{}/events/{}?limit={}",
257 self.base_url, address, event_type, limit
258 );
259 if let Some(start) = start {
260 url.push_str(&format!("&start={}", start));
261 }
262 let response = self.client.get(&url).send().await.unwrap();
263 if !response.status().is_success() {
264 let error_msg = response.text().await.unwrap();
265 return Err(format!("api error: {}", error_msg).to_string());
266 }
267 let events: Vec<Event> = response.json().await.unwrap();
268 Ok(events)
269 }
270
271 pub async fn get_table_item(
273 &self,
274 table_handle: &str,
275 key_type: &str,
276 value_type: &str,
277 key: &Value,
278 ) -> Result<Value, String> {
279 let url = format!("{}/tables/{}/item", self.base_url, table_handle);
280 let request = TableRequest {
281 key_type: key_type.to_string(),
282 value_type: value_type.to_string(),
283 key: key.clone(),
284 };
285 let response = self
286 .client
287 .post(&url)
288 .header("Content-Type", "application/json")
289 .json(&request)
290 .send()
291 .await
292 .unwrap();
293 if !response.status().is_success() {
294 let error_msg = response.text().await.unwrap();
295 return Err(format!("api error: {}", error_msg).to_string());
296 }
297 let value: Value = response.json().await.unwrap();
298 Ok(value)
299 }
300
301 pub async fn view(&self, view_request: &ViewRequest) -> Result<Vec<Value>, String> {
303 let url = format!("{}/view", self.base_url);
304 let response = self
305 .client
306 .post(&url)
307 .header("Content-Type", "application/json")
308 .json(view_request)
309 .send()
310 .await
311 .unwrap();
312 if !response.status().is_success() {
313 let error_msg = response.text().await.unwrap();
314 return Err(format!("api error: {}", error_msg).to_string());
315 }
316 let result: Vec<Value> = response.json().await.unwrap();
317 Ok(result)
318 }
319
320 pub async fn estimate_gas_price(&self) -> Result<u64, String> {
322 let url = format!("{}/estimate_gas_price", self.base_url);
323 let response = self.client.get(&url).send().await.unwrap();
324 if !response.status().is_success() {
325 let error_msg = response.text().await.unwrap();
326 return Err(format!("api error: {}", error_msg).to_string());
327 }
328 let gas_estimation: GasEstimation = response.json().await.unwrap();
329 Ok(gas_estimation.gas_estimate * 2000)
330 }
331
332 pub async fn get_account_balance(&self, address: &str) -> Result<u64, String> {
334 let resources = self.get_account_resource_vec(address).await.unwrap();
335 for resource in resources {
336 if resource.r#type == "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>" {
337 if let Some(data) = resource.data.as_object() {
338 if let Some(coin) = data.get("coin") {
339 if let Some(value) = coin.get("value") {
340 return if let Some(balance) = value.as_str() {
341 Ok(balance.parse().unwrap_or(0))
342 } else if let Some(balance) = value.as_u64() {
343 Ok(balance)
344 } else {
345 Ok(0)
346 };
347 }
348 }
349 }
350 }
351 }
352 Ok(0)
353 }
354 pub async fn get_token_balance(&self, address: &str, token_type: &str) -> Result<u64, String> {
356 let resource_type = format!("0x1::coin::CoinStore<{}>", token_type);
357 if let Some(resource) = self
358 .get_account_resource(address, &resource_type)
359 .await
360 .unwrap()
361 {
362 if let Some(data) = resource.data.as_object() {
363 if let Some(coin) = data.get("coin") {
364 if let Some(value) = coin.get("value") {
365 return if let Some(balance) = value.as_str() {
366 Ok(balance.parse().unwrap_or(0))
367 } else if let Some(balance) = value.as_u64() {
368 Ok(balance)
369 } else {
370 Ok(0)
371 };
372 }
373 }
374 }
375 }
376 Ok(0)
377 }
378 pub async fn waiting_transaction(
380 &self,
381 txn_hash: &str,
382 timeout_secs: u64,
383 ) -> Result<Transaction, String> {
384 let start = std::time::Instant::now();
385 let timeout = Duration::from_secs(timeout_secs);
386 while start.elapsed() < timeout {
387 match self.get_transaction_info(txn_hash).await {
388 Ok(txn) => {
389 return Ok(txn);
391 }
392 Err(e) => {
393 tokio::time::sleep(Duration::from_millis(WAITING_TRANSACTION_DELAY_TIME)).await;
395 }
396 }
397 }
398 Err(format!(
399 "Transaction timeout tx:{:?}\ntime:{:?}",
400 txn_hash, timeout_secs
401 )
402 .to_string())
403 }
404 pub async fn is_transaction_successful(&self, txn_hash: &str) -> Result<bool, String> {
406 match self.get_transaction_info(txn_hash).await {
407 Ok(t) => Ok(t.success),
408 Err(e) => Err(e),
409 }
410 }
411 pub async fn get_apt_balance_by_account(&self, address: &str) -> Result<f64, String> {
413 match self.get_account_balance(address).await {
414 Ok(balance) => Ok(balance as f64 / 100_000_000.0),
415 Err(e) => Err(e),
416 }
417 }
418 pub async fn get_account_sequence_number(&self, address: &str) -> Result<u64, String> {
420 match self.get_account_info(address).await {
421 Ok(info) => Ok(info.sequence_number.parse::<u64>().unwrap()),
422 Err(e) => Err(e),
423 }
424 }
425 pub async fn account_exists(&self, address: &str) -> Result<bool, String> {
427 match self.get_account_info(address).await {
428 Ok(_) => Ok(true),
429 Err(e) => {
430 if e.to_string().contains("Account not found") {
431 Ok(false)
432 } else {
433 Err(e)
434 }
435 }
436 }
437 }
438}