1use crate::{
4 api::{Eth, Namespace},
5 error,
6 transports::ic_http_client::CallOptions,
7 types::{Bytes, TransactionReceipt, TransactionRequest, H256, U64},
8 Transport,
9};
10use futures::{Future, StreamExt};
11use std::time::Duration;
12
13pub trait ConfirmationCheck {
15 type Check: Future<Output = error::Result<Option<U64>>>;
17
18 fn check(&self) -> Self::Check;
20}
21
22impl<F, T> ConfirmationCheck for F
23where
24 F: Fn() -> T,
25 T: Future<Output = error::Result<Option<U64>>>,
26{
27 type Check = T;
28
29 fn check(&self) -> Self::Check {
30 (*self)()
31 }
32}
33
34async fn transaction_receipt_block_number_check<T: Transport>(
69 eth: &Eth<T>,
70 hash: H256,
71 options: CallOptions,
72) -> error::Result<Option<U64>> {
73 let receipt = eth.transaction_receipt(hash, options).await?;
74 Ok(receipt.and_then(|receipt| receipt.block_number))
75}
76
77async fn send_transaction_with_confirmation_<T: Transport>(
78 hash: H256,
79 transport: T,
80 poll_interval: Duration,
81 confirmations: usize,
82 options: CallOptions,
83) -> error::Result<TransactionReceipt> {
84 let eth = Eth::new(transport.clone());
85 let receipt = eth
102 .transaction_receipt(hash, options)
103 .await?
104 .expect("receipt can't be null after wait for confirmations; qed");
105 Ok(receipt)
106}
107
108pub async fn send_transaction_with_confirmation<T>(
110 transport: T,
111 tx: TransactionRequest,
112 poll_interval: Duration,
113 confirmations: usize,
114 options: CallOptions,
115) -> error::Result<TransactionReceipt>
116where
117 T: Transport,
118{
119 let hash = Eth::new(&transport).send_transaction(tx, options.clone()).await?;
120 send_transaction_with_confirmation_(hash, transport, poll_interval, confirmations, options).await
121}
122
123pub async fn send_raw_transaction_with_confirmation<T>(
125 transport: T,
126 tx: Bytes,
127 poll_interval: Duration,
128 confirmations: usize,
129 options: CallOptions,
130) -> error::Result<TransactionReceipt>
131where
132 T: Transport,
133{
134 let hash = Eth::new(&transport).send_raw_transaction(tx, options.clone()).await?;
135 send_transaction_with_confirmation_(hash, transport, poll_interval, confirmations, options).await
136}
137
138pub async fn get_transaction_receipt<T: Transport>(
139 transport: T,
140 hash: H256,
141 options: CallOptions,
142) -> error::Result<Option<TransactionReceipt>> {
143 Eth::new(&transport).transaction_receipt(hash, options).await
144}
145
146#[cfg(test)]
147mod tests {
148 use super::send_transaction_with_confirmation;
149 use crate::{
150 rpc::Value,
151 transports::{ic_http_client::CallOptions, test::TestTransport},
152 types::{Address, TransactionReceipt, TransactionRequest, H256, U64},
153 };
154 use serde_json::json;
155 use std::time::Duration;
156
157 #[test]
158 fn test_send_transaction_with_confirmation() {
159 let mut transport = TestTransport::default();
160 let confirmations = 3;
161 let transaction_request = TransactionRequest {
162 from: Address::from_low_u64_be(0x123),
163 to: Some(Address::from_low_u64_be(0x123)),
164 gas: None,
165 gas_price: Some(1.into()),
166 value: Some(1.into()),
167 data: None,
168 nonce: None,
169 condition: None,
170 transaction_type: None,
171 access_list: None,
172 max_fee_per_gas: None,
173 max_priority_fee_per_gas: None,
174 };
175
176 let transaction_receipt = TransactionReceipt {
177 transaction_hash: H256::zero(),
178 transaction_index: U64::zero(),
179 block_hash: Some(H256::zero()),
180 block_number: Some(2.into()),
181 from: Address::from_low_u64_be(0x123),
182 to: Some(Address::from_low_u64_be(0x123)),
183 cumulative_gas_used: 0.into(),
184 gas_used: Some(0.into()),
185 contract_address: None,
186 logs: vec![],
187 status: Some(1.into()),
188 root: Some(H256::zero()),
189 logs_bloom: Default::default(),
190 transaction_type: None,
191 effective_gas_price: Default::default(),
192 };
193
194 let poll_interval = Duration::from_secs(0);
195 transport.add_response(Value::String(
196 r#"0x0000000000000000000000000000000000000000000000000000000000000111"#.into(),
197 ));
198 transport.add_response(Value::String("0x123".into()));
199 transport.add_response(Value::Array(vec![
200 Value::String(r#"0x0000000000000000000000000000000000000000000000000000000000000456"#.into()),
201 Value::String(r#"0x0000000000000000000000000000000000000000000000000000000000000457"#.into()),
202 ]));
203 transport.add_response(Value::Array(vec![Value::String(
204 r#"0x0000000000000000000000000000000000000000000000000000000000000458"#.into(),
205 )]));
206 transport.add_response(Value::Array(vec![Value::String(
207 r#"0x0000000000000000000000000000000000000000000000000000000000000459"#.into(),
208 )]));
209 transport.add_response(Value::Null);
210 transport.add_response(Value::Array(vec![
211 Value::String(r#"0x0000000000000000000000000000000000000000000000000000000000000460"#.into()),
212 Value::String(r#"0x0000000000000000000000000000000000000000000000000000000000000461"#.into()),
213 ]));
214 transport.add_response(Value::Null);
215 transport.add_response(json!(transaction_receipt));
216 transport.add_response(Value::String("0x6".into()));
217 transport.add_response(json!(transaction_receipt));
218 transport.add_response(Value::Bool(true));
219
220 let confirmation = {
221 let future = send_transaction_with_confirmation(
222 &transport,
223 transaction_request,
224 poll_interval,
225 confirmations,
226 CallOptions::default(),
227 );
228 futures::executor::block_on(future)
229 };
230
231 transport.assert_request("eth_sendTransaction", &[r#"{"from":"0x0000000000000000000000000000000000000123","gasPrice":"0x1","to":"0x0000000000000000000000000000000000000123","value":"0x1"}"#.into()]);
232 transport.assert_request(
238 "eth_getTransactionReceipt",
239 &[r#""0x0000000000000000000000000000000000000000000000000000000000000111""#.into()],
240 );
241 transport.assert_no_more_requests();
256 }
258}