1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::collections::HashMap;
use serde::Deserialize;
use crate::{Client, EtherscanError, Response, Result};
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct ContractExecutionStatus {
is_error: String,
err_description: String,
}
#[derive(Deserialize)]
struct TransactionReceiptStatus {
status: String,
}
impl Client {
pub async fn check_contract_execution_status(&self, tx_hash: impl AsRef<str>) -> Result<()> {
let query = self.create_query(
"transaction",
"getstatus",
HashMap::from([("txhash", tx_hash.as_ref())]),
);
let response: Response<ContractExecutionStatus> = self.get_json(&query).await?;
if response.result.is_error == "0" {
Ok(())
} else {
Err(EtherscanError::ExecutionFailed(response.result.err_description))
}
}
pub async fn check_transaction_receipt_status(&self, tx_hash: impl AsRef<str>) -> Result<()> {
let query = self.create_query(
"transaction",
"gettxreceiptstatus",
HashMap::from([("txhash", tx_hash.as_ref())]),
);
let response: Response<TransactionReceiptStatus> = self.get_json(&query).await?;
match response.result.status.as_str() {
"0" => Err(EtherscanError::TransactionReceiptFailed),
"1" => Ok(()),
err => Err(EtherscanError::BadStatusCode(err.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use serial_test::serial;
use crate::{tests::run_at_least_duration, Chain};
use super::*;
#[tokio::test]
#[serial]
async fn check_contract_execution_status_success() {
run_at_least_duration(Duration::from_millis(250), async {
let client = Client::new_from_env(Chain::Mainnet).unwrap();
let status = client
.check_contract_execution_status(
"0x16197e2a0eacc44c1ebdfddcfcfcafb3538de557c759a66e0ba95263b23d9007",
)
.await;
assert!(status.is_ok());
})
.await
}
#[tokio::test]
#[serial]
async fn check_contract_execution_status_error() {
run_at_least_duration(Duration::from_millis(250), async {
let client = Client::new_from_env(Chain::Mainnet).unwrap();
let err = client
.check_contract_execution_status(
"0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a",
)
.await
.unwrap_err();
assert!(matches!(err, EtherscanError::ExecutionFailed(_)));
assert_eq!(err.to_string(), "contract execution call failed: Bad jump destination");
})
.await
}
#[tokio::test]
#[serial]
async fn check_transaction_receipt_status_success() {
run_at_least_duration(Duration::from_millis(250), async {
let client = Client::new_from_env(Chain::Mainnet).unwrap();
let success = client
.check_transaction_receipt_status(
"0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76",
)
.await;
assert!(success.is_ok());
})
.await
}
#[tokio::test]
#[serial]
async fn check_transaction_receipt_status_failed() {
run_at_least_duration(Duration::from_millis(250), async {
let client = Client::new_from_env(Chain::Mainnet).unwrap();
let err = client
.check_transaction_receipt_status(
"0x21a29a497cb5d4bf514c0cca8d9235844bd0215c8fab8607217546a892fd0758",
)
.await
.unwrap_err();
assert!(matches!(err, EtherscanError::TransactionReceiptFailed));
})
.await
}
}