pallet_revive_eth_rpc/
example.rs
1use crate::{EthRpcClient, ReceiptInfo};
19use anyhow::Context;
20use pallet_revive::evm::*;
21use std::sync::Arc;
22
23pub struct TransactionBuilder<Client: EthRpcClient + Sync + Send> {
25 client: Arc<Client>,
26 signer: Account,
27 value: U256,
28 input: Bytes,
29 to: Option<H160>,
30 mutate: Box<dyn FnOnce(&mut TransactionLegacyUnsigned)>,
31}
32
33#[derive(Debug)]
34pub struct SubmittedTransaction<Client: EthRpcClient + Sync + Send> {
35 tx: GenericTransaction,
36 hash: H256,
37 client: Arc<Client>,
38}
39
40impl<Client: EthRpcClient + Sync + Send> SubmittedTransaction<Client> {
41 pub fn hash(&self) -> H256 {
43 self.hash
44 }
45
46 pub fn gas(&self) -> U256 {
48 self.tx.gas.unwrap()
49 }
50
51 pub async fn wait_for_receipt(&self) -> anyhow::Result<ReceiptInfo> {
53 let hash = self.hash();
54 for _ in 0..30 {
55 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
56 let receipt = self.client.get_transaction_receipt(hash).await?;
57 if let Some(receipt) = receipt {
58 if receipt.is_success() {
59 assert!(
60 self.gas() > receipt.gas_used,
61 "Gas used should be less than gas estimated."
62 );
63 return Ok(receipt)
64 } else {
65 anyhow::bail!("Transaction failed")
66 }
67 }
68 }
69
70 anyhow::bail!("Timeout, failed to get receipt")
71 }
72}
73
74impl<Client: EthRpcClient + Send + Sync> TransactionBuilder<Client> {
75 pub fn new(client: &Arc<Client>) -> Self {
76 Self {
77 client: Arc::clone(client),
78 signer: Account::default(),
79 value: U256::zero(),
80 input: Bytes::default(),
81 to: None,
82 mutate: Box::new(|_| {}),
83 }
84 }
85 pub fn signer(mut self, signer: Account) -> Self {
87 self.signer = signer;
88 self
89 }
90
91 pub fn value(mut self, value: U256) -> Self {
93 self.value = value;
94 self
95 }
96
97 pub fn input(mut self, input: Vec<u8>) -> Self {
99 self.input = Bytes(input);
100 self
101 }
102
103 pub fn to(mut self, to: H160) -> Self {
105 self.to = Some(to);
106 self
107 }
108
109 pub fn mutate(mut self, mutate: impl FnOnce(&mut TransactionLegacyUnsigned) + 'static) -> Self {
111 self.mutate = Box::new(mutate);
112 self
113 }
114
115 pub async fn eth_call(self) -> anyhow::Result<Vec<u8>> {
117 let TransactionBuilder { client, signer, value, input, to, .. } = self;
118
119 let from = signer.address();
120 let result = client
121 .call(
122 GenericTransaction {
123 from: Some(from),
124 input: input.into(),
125 value: Some(value),
126 to,
127 ..Default::default()
128 },
129 None,
130 )
131 .await
132 .with_context(|| "eth_call failed")?;
133 Ok(result.0)
134 }
135
136 pub async fn send(self) -> anyhow::Result<SubmittedTransaction<Client>> {
138 let TransactionBuilder { client, signer, value, input, to, mutate } = self;
139
140 let from = signer.address();
141 let chain_id = Some(client.chain_id().await?);
142 let gas_price = client.gas_price().await?;
143 let nonce = client
144 .get_transaction_count(from, BlockTag::Latest.into())
145 .await
146 .with_context(|| "Failed to fetch account nonce")?;
147
148 let gas = client
149 .estimate_gas(
150 GenericTransaction {
151 from: Some(from),
152 input: input.clone().into(),
153 value: Some(value),
154 gas_price: Some(gas_price),
155 to,
156 ..Default::default()
157 },
158 None,
159 )
160 .await
161 .with_context(|| "Failed to fetch gas estimate")?;
162
163 println!("Gas estimate: {gas:?}");
164 let mut unsigned_tx = TransactionLegacyUnsigned {
165 gas,
166 nonce,
167 to,
168 value,
169 input,
170 gas_price,
171 chain_id,
172 ..Default::default()
173 };
174
175 mutate(&mut unsigned_tx);
176
177 let signed_tx = signer.sign_transaction(unsigned_tx.into());
178 let bytes = signed_tx.signed_payload();
179
180 let hash = client
181 .send_raw_transaction(bytes.into())
182 .await
183 .with_context(|| "transaction failed")?;
184
185 Ok(SubmittedTransaction {
186 tx: GenericTransaction::from_signed(signed_tx, gas_price, Some(from)),
187 hash,
188 client,
189 })
190 }
191}
192
193#[test]
194fn test_dummy_payload_has_correct_len() {
195 let signer = Account::from(subxt_signer::eth::dev::ethan());
196 let unsigned_tx: TransactionUnsigned =
197 TransactionLegacyUnsigned { input: vec![42u8; 100].into(), ..Default::default() }.into();
198
199 let signed_tx = signer.sign_transaction(unsigned_tx.clone());
200 let signed_payload = signed_tx.signed_payload();
201 let unsigned_tx = signed_tx.unsigned();
202
203 let dummy_payload = unsigned_tx.dummy_signed_payload();
204 assert_eq!(dummy_payload.len(), signed_payload.len());
205}