1use aptos_network_tool::address::address_to_bytes;
2use futures::future::join_all;
3use serde_json::{Value, json};
5use std::{collections::HashMap, sync::Arc, time::Duration};
6
7use crate::{
8 Aptos,
9 trade::Trade,
10 types::{
11 ContractCall, ContractReadResult, ContractWriteResult, EntryFunctionPayload, Event,
12 ViewRequest,
13 },
14 wallet::Wallet,
15};
16
17pub const COIN_STORE: &str = "0x1::coin::CoinStore";
19pub const APTOS_COIN: &str = "0x1::aptos_coin::AptosCoin";
20pub const COIN: &str = "0x1::coin";
21pub const TRANSFER: &str = "transfer";
23pub const BALANCE: &str = "balance";
24pub const REGISTER: &str = "register";
25pub const MINT: &str = "mint";
26pub const BURN: &str = "burn";
27
28pub struct Contract {}
29impl Contract {
30 pub async fn read(
32 client: Arc<Aptos>,
33 contract_call: &ContractCall,
34 ) -> Result<ContractReadResult, String> {
35 let function = format!(
36 "{}::{}::{}",
37 contract_call.module_address, contract_call.module_name, contract_call.function_name
38 );
39 let view_request = ViewRequest {
40 function,
41 type_arguments: contract_call.type_arguments.clone(),
42 arguments: contract_call.arguments.clone(),
43 };
44 match client.view(&view_request).await {
45 Ok(result) => Ok(ContractReadResult {
46 success: true,
47 data: Value::Array(result),
48 error: None,
49 }),
50 Err(e) => Ok(ContractReadResult {
51 success: false,
52 data: Value::Null,
53 error: Some(e.to_string()),
54 }),
55 }
56 }
57
58 pub async fn write(
60 client: Arc<Aptos>,
61 wallet: Arc<Wallet>,
62 contract_call: ContractCall,
63 ) -> Result<ContractWriteResult, String> {
64 let function_str = format!(
65 "{}::{}::{}",
66 contract_call.module_address, contract_call.module_name, contract_call.function_name
67 );
68 let function_vec = function_str.as_bytes().to_vec();
69 let mut type_args: Vec<Vec<u8>> = Vec::new();
70 contract_call
71 .type_arguments
72 .iter()
73 .for_each(|s| type_args.push(s.as_bytes().to_vec()));
74 let mut args: Vec<Vec<u8>> = Vec::new();
75 contract_call
76 .arguments
77 .iter()
78 .for_each(|s| args.push(s.as_str().unwrap().to_string().as_bytes().to_vec()));
79 let payload = EntryFunctionPayload {
80 module_address: address_to_bytes(&contract_call.module_address)
81 .unwrap()
82 .to_vec(),
83 module_name: address_to_bytes(&contract_call.module_name)
84 .unwrap()
85 .to_vec(),
86 function_name: function_vec,
87 type_arguments: type_args,
88 arguments: args,
89 };
90 let raw_txn = Trade::create_call_contract_tx(
91 Arc::clone(&client),
92 Arc::clone(&wallet),
93 None,
94 30,
95 2000,
96 100,
97 payload,
98 )
99 .await;
100 let signature = wallet.sign(&serde_json::to_vec(&raw_txn).unwrap()).unwrap();
102 let signed_txn = json!({
103 "transaction": raw_txn,
104 "signature": {
105 "type": "ed25519_signature",
106 "public_key": wallet.public_key_hex()?,
107 "signature": hex::encode(signature)
108 }
109 });
110 match client.submit_transaction(&signed_txn).await {
111 Ok(transaction) => {
112 if let Ok(confirmed_txn) = client.waiting_transaction(&transaction.hash, 30).await {
114 Ok(ContractWriteResult {
115 success: confirmed_txn.success,
116 transaction_hash: confirmed_txn.hash,
117 gas_used: confirmed_txn.max_gas_amount.unwrap(),
118 events: confirmed_txn
119 .events
120 .into_iter()
121 .map(|e| {
122 json!({
123 "type": e.r#type,
124 "data": e.data,
125 "sequence_number": e.sequence_number
126 })
127 })
128 .collect(),
129 error: if confirmed_txn.success {
130 None
131 } else {
132 Some(confirmed_txn.vm_status)
133 },
134 })
135 } else {
136 Ok(ContractWriteResult {
137 success: false,
138 transaction_hash: transaction.hash,
139 gas_used: "0".to_string(),
140 events: Vec::new(),
141 error: Some("Transaction confirmation timeout".to_string()),
142 })
143 }
144 }
145 Err(e) => Ok(ContractWriteResult {
146 success: false,
147 transaction_hash: String::new(),
148 gas_used: "0".to_string(),
149 events: Vec::new(),
150 error: Some(e.to_string()),
151 }),
152 }
153 }
154
155 pub async fn batch_read(
157 client: Arc<Aptos>,
158 calls: Vec<ContractCall>,
159 ) -> Result<Vec<ContractReadResult>, String> {
160 let mut results = Vec::new();
161 for call in calls {
162 results.push(Contract::read(Arc::clone(&client), &call).await.unwrap());
163 }
164 Ok(results)
165 }
166
167 pub async fn listen_events(
169 client: Arc<Aptos>,
170 address: &str,
171 event_type: &str,
172 callback: impl Fn(Result<Value, String>),
173 interval_secs: u64,
174 ) -> Result<(), ()> {
175 let mut last_sequence_number: Option<u64> = None;
176 loop {
177 match client
178 .get_account_event_vec(address, event_type, Some(100), None)
179 .await
180 {
181 Ok(events) => {
182 for event in events {
183 if let Ok(current_seq) = event.sequence_number.parse::<u64>() {
184 if let Some(last_seq) = last_sequence_number {
185 if current_seq > last_seq {
186 callback(Ok(event.data.clone()));
187 }
188 } else {
189 callback(Ok(event.data.clone()));
190 }
191 last_sequence_number = Some(current_seq);
193 }
194 }
195 }
196 Err(e) => callback(Err(format!("no event exists: {:?}", e).to_string())),
197 }
198 tokio::time::sleep(Duration::from_secs(interval_secs)).await;
199 }
200 }
201
202 pub async fn listen_events_all_info(
204 client: Arc<Aptos>,
205 address: &str,
206 event_type: &str,
207 callback: impl Fn(Result<Event, String>),
208 interval_secs: u64,
209 ) -> Result<(), ()> {
210 let mut last_sequence_number: Option<u64> = None;
211 loop {
212 match client
213 .get_account_event_vec(address, event_type, Some(100), None)
214 .await
215 {
216 Ok(events) => {
217 for event in events {
218 if let Ok(current_seq) = event.sequence_number.parse::<u64>() {
219 if let Some(last_seq) = last_sequence_number {
220 if current_seq > last_seq {
221 callback(Ok(event.clone()));
222 }
223 } else {
224 callback(Ok(event.clone()));
225 }
226 last_sequence_number = Some(current_seq);
227 }
228 }
229 }
230 Err(e) => callback(Err(format!("no event exists: {:?}", e).to_string())),
231 }
232 tokio::time::sleep(Duration::from_secs(interval_secs)).await;
233 }
234 }
235
236 pub async fn get_contract_resource(
238 client: Arc<Aptos>,
239 address: &str,
240 resource_type: &str,
241 ) -> Result<Option<Value>, String> {
242 match client.get_account_resource(address, resource_type).await {
243 Ok(resource) => match resource {
244 Some(r) => Ok(Some(r.data)),
245 None => Err(format!("get contract resource error: resource is none").to_string()),
246 },
247 Err(e) => Err(format!("get contract resource error: {:?}", e).to_string()),
248 }
249 }
250
251 pub async fn get_contract_state_snapshot(
253 client: Arc<Aptos>,
254 address: &str,
255 resource_types: Vec<&str>,
256 ) -> Result<HashMap<String, Option<Value>>, String> {
257 let mut snapshot = HashMap::new();
258 for resource_type in resource_types {
259 match Self::get_contract_resource(Arc::clone(&client), address, resource_type).await {
260 Ok(Some(data)) => {
261 snapshot.insert(resource_type.to_string(), Some(data));
262 }
263 Ok(None) => {
264 snapshot.insert(resource_type.to_string(), None);
265 }
266 Err(e) => {
267 snapshot.insert(resource_type.to_string(), None);
268 eprintln!("Error fetching resource {}: {}", resource_type, e);
269 }
270 }
271 }
272 Ok(snapshot)
273 }
274
275 pub fn validate_contract_call(contract_call: &ContractCall) -> Result<(), String> {
277 if contract_call.module_address.is_empty() {
278 return Err("Module address cannot be empty".to_string());
279 }
280 if contract_call.module_name.is_empty() {
281 return Err("Module name cannot be empty".to_string());
282 }
283 if contract_call.function_name.is_empty() {
284 return Err("Function name cannot be empty".to_string());
285 }
286 if !contract_call.module_address.starts_with("0x") {
288 return Err("Module address must start with 0x".to_string());
289 }
290 Ok(())
291 }
292
293 pub async fn estimate_gas_cost(
295 client: Arc<Aptos>,
296 wallet: Arc<Wallet>,
297 contract_call: &ContractCall,
298 ) -> Result<u64, String> {
299 let simulation_result = Self::simulate_call_contract(client, wallet, contract_call).await?;
301 simulation_result
302 .get("gas_used")
303 .and_then(|g| g.as_str())
304 .and_then(|g| g.parse().ok())
305 .ok_or_else(|| "Failed to estimate gas cost".to_string())
306 }
307
308 pub async fn retry_failed_call(
310 client: Arc<Aptos>,
311 wallet: Arc<Wallet>,
312 contract_call: ContractCall,
313 max_retries: u32,
314 retry_delay_secs: u64,
315 ) -> Result<ContractWriteResult, String> {
316 let mut retries = 0;
317 while retries < max_retries {
318 match Self::write(
319 Arc::clone(&client),
320 Arc::clone(&wallet),
321 contract_call.clone(),
322 )
323 .await
324 {
325 Ok(result) if result.success => return Ok(result),
326 Ok(result) => {
327 eprintln!("Call failed on attempt {}: {:?}", retries + 1, result.error);
328 }
329 Err(e) => {
330 eprintln!("Error on attempt {}: {}", retries + 1, e);
331 }
332 }
333 retries += 1;
334 if retries < max_retries {
335 tokio::time::sleep(Duration::from_secs(retry_delay_secs)).await;
336 }
337 }
338 Err(format!("Failed after {} retries", max_retries))
339 }
340
341 pub async fn batch_get_resources(
343 client: Arc<Aptos>,
344 address: &str,
345 resource_types: Vec<&str>,
346 ) -> Result<HashMap<String, Option<Value>>, String> {
347 let mut tasks = Vec::new();
348 for resource_type in resource_types {
349 let client_clone = Arc::clone(&client);
350 let address = address.to_string();
351 let resource_type = resource_type.to_string();
352 tasks.push(async move {
353 match client_clone
354 .get_account_resource(&address, &resource_type)
355 .await
356 {
357 Ok(Some(resource)) => (resource_type, Some(resource.data)),
358 Ok(None) => (resource_type, None),
359 Err(_) => (resource_type, None),
360 }
361 });
362 }
363 let results = join_all(tasks).await;
364 let mut resource_map = HashMap::new();
365 for (resource_type, data) in results {
366 resource_map.insert(resource_type, data);
367 }
368 Ok(resource_map)
369 }
370
371 pub async fn batch_write(
373 client: Arc<Aptos>,
374 wallet: Arc<Wallet>,
375 calls: Vec<ContractCall>,
376 ) -> Result<Vec<Value>, String> {
377 let mut results = Vec::new();
378 for call in calls {
379 match Self::write(Arc::clone(&client), Arc::clone(&wallet), call).await {
380 Ok(result) => results.push(json!(result)),
381 Err(e) => results.push(json!({
382 "success": false,
383 "error": e
384 })),
385 }
386 }
387 Ok(results)
388 }
389
390 pub async fn simulate_call_contract(
392 client: Arc<Aptos>,
393 wallet: Arc<Wallet>,
394 contract_call: &ContractCall,
395 ) -> Result<Value, String> {
396 let function = format!(
397 "{}::{}::{}",
398 contract_call.module_address, contract_call.module_name, contract_call.function_name
399 );
400 todo!();
401 let payload = json!({
402 "function": function,
403 "type_arguments": contract_call.type_arguments,
404 "arguments": contract_call.arguments,
405 "sender": wallet.address().map_err(|e| e.to_string())?,
406 });
407
408 todo!();
410 Ok(json!({
411 "gas_used": "1000",
412 "success": true,
413 "vm_status": "Executed successfully"
414 }))
415 }
416
417 pub async fn get_contract_abi(
419 client: Arc<Aptos>,
420 module_address: &str,
421 module_name: &str,
422 ) -> Result<Option<Value>, String> {
423 Ok(None)
424 }
425
426 pub async fn is_contract_deployed(
428 client: Arc<Aptos>,
429 module_address: &str,
430 module_name: &str,
431 ) -> Result<bool, String> {
432 match Self::get_contract_abi(client, module_address, module_name).await {
433 Ok(Some(_)) => Ok(true),
434 Ok(None) => Ok(false),
435 Err(_) => Ok(false),
436 }
437 }
438
439 pub async fn get_contract_events(
441 client: Arc<Aptos>,
442 address: &str,
443 event_handle: &str,
444 limit: Option<u64>,
445 start: Option<u64>,
446 ) -> Result<Vec<Value>, String> {
447 let events = client
448 .get_account_event_vec(address, event_handle, limit, start)
449 .await?;
450 let value_events: Vec<Value> = events
451 .into_iter()
452 .map(|event| {
453 json!({
454 "type": event.r#type,
455 "data": event.data,
456 "sequence_number": event.sequence_number,
457 "guid": event.guid
458 })
459 })
460 .collect();
461 Ok(value_events)
462 }
463
464 pub fn parse_complex_type_arguments(type_args: Vec<&str>) -> Vec<String> {
466 type_args.into_iter().map(|s| s.to_string()).collect()
467 }
468
469 pub fn build_complex_arguments(args: Vec<&str>) -> Vec<Value> {
471 args.into_iter()
472 .map(|s| Value::String(s.to_string()))
473 .collect()
474 }
475
476 pub fn analyze_contract_result(result: &Value) -> HashMap<String, String> {
478 let mut analysis = HashMap::new();
479 if let Some(success) = result.get("success").and_then(|s| s.as_bool()) {
480 analysis.insert(
481 "status".to_string(),
482 if success {
483 "success".to_string()
484 } else {
485 "failed".to_string()
486 },
487 );
488 }
489 if let Some(gas_used) = result.get("gas_used").and_then(|g| g.as_str()) {
490 analysis.insert("gas_used".to_string(), gas_used.to_string());
491 }
492 if let Some(error) = result.get("error").and_then(|e| e.as_str()) {
493 analysis.insert("error".to_string(), error.to_string());
494 }
495 analysis
496 }
497
498 pub async fn deploy_contract(
500 client: Arc<Aptos>,
501 wallet: Arc<Wallet>,
502 module_bytes: Vec<u8>,
503 metadata: Option<Value>,
504 ) -> Result<Value, String> {
505 let contract_call = ContractCall {
507 module_address: wallet.address().map_err(|e| e.to_string())?,
508 module_name: "".to_string(), function_name: "deploy".to_string(),
510 type_arguments: vec![],
511 arguments: vec![json!(hex::encode(module_bytes))],
512 };
513 Self::write(client, wallet, contract_call)
514 .await
515 .map(|result| json!(result))
516 }
517
518 pub async fn upgrade_contract(
520 client: Arc<Aptos>,
521 wallet: Arc<Wallet>,
522 module_name: &str,
523 new_module_bytes: Vec<u8>,
524 ) -> Result<Value, String> {
525 let contract_call = ContractCall {
526 module_address: wallet.address().map_err(|e| e.to_string())?,
527 module_name: module_name.to_string(),
528 function_name: "upgrade".to_string(),
529 type_arguments: vec![],
530 arguments: vec![json!(hex::encode(new_module_bytes))],
531 };
532 Self::write(client, wallet, contract_call)
533 .await
534 .map(|result| json!(result))
535 }
536}
537
538pub struct ContractUtils;
540
541impl ContractUtils {
542 pub fn create_standard_call(
544 module_address: &str,
545 module_name: &str,
546 function_name: &str,
547 type_arguments: Vec<String>,
548 arguments: Vec<Value>,
549 ) -> ContractCall {
550 ContractCall {
551 module_address: module_address.to_string(),
552 module_name: module_name.to_string(),
553 function_name: function_name.to_string(),
554 type_arguments,
555 arguments,
556 }
557 }
558
559 pub fn parse_event_data(event_data: Value, event_schema: &[&str]) -> HashMap<String, Value> {
561 let mut parsed = HashMap::new();
562
563 if let Value::Object(map) = event_data {
564 for (key, value) in map {
565 if event_schema.contains(&key.as_str()) {
566 parsed.insert(key, value);
567 }
568 }
569 }
570 parsed
571 }
572
573 pub fn calculate_call_signature(contract_call: &ContractCall, nonce: &str) -> String {
575 use sha3::{Digest, Sha3_256};
576 let mut hasher = Sha3_256::new();
577 hasher.update(contract_call.module_address.as_bytes());
578 hasher.update(contract_call.module_name.as_bytes());
579 hasher.update(contract_call.function_name.as_bytes());
580 hasher.update(nonce.as_bytes());
581 format!("0x{}", hex::encode(hasher.finalize()))
582 }
583}