flashbots_sdk/
transaction.rs1use ethers::prelude::*;
2use ethers::types::transaction::eip2718::TypedTransaction;
3use evm_client::{EvmClient, EvmType};
4use std::sync::Arc;
5
6use crate::types::{FlashbotsError, FlashbotsResult};
7pub struct TransactionBuilder {
9 evm_client: Arc<EvmClient>,
10}
11
12impl TransactionBuilder {
14 pub async fn new(evm_type: EvmType) -> FlashbotsResult<Self> {
15 Ok(Self {
16 evm_client: Arc::new(
17 EvmClient::from_type(evm_type)
18 .await
19 .map_err(|e| FlashbotsError::Error(format!("{:?}", e)))?,
20 ),
21 })
22 }
23
24 pub async fn create_eth_transfer(
39 &self,
40 from: LocalWallet,
41 to: Address,
42 value: U256,
43 ) -> Result<TransactionRequest, Box<dyn std::error::Error>> {
44 let nonce = self
45 .evm_client
46 .provider
47 .get_transaction_count(from.address(), None)
48 .await?;
49 let gas_price = self.evm_client.provider.get_gas_price().await?;
50 Ok(TransactionRequest::new()
51 .from(from.address())
52 .to(to)
53 .value(value)
54 .nonce(nonce)
55 .gas_price(gas_price)
56 .gas(21000))
57 }
58
59 pub async fn create_contract_call(
61 &self,
62 from: Address,
63 to: Address,
64 data: Bytes,
65 value: Option<U256>,
66 ) -> Result<TransactionRequest, Box<dyn std::error::Error>> {
67 let nonce = self
68 .evm_client
69 .provider
70 .get_transaction_count(from, None)
71 .await?;
72 let gas_price = self.evm_client.provider.get_gas_price().await?;
73 let mut tx = TransactionRequest::new()
74 .from(from)
75 .to(to)
76 .data(data)
77 .nonce(nonce)
78 .gas_price(gas_price);
79 if let Some(val) = value {
80 tx = tx.value(val);
81 }
82 Ok(tx)
83 }
84
85 pub async fn sign_transaction(
98 &self,
99 wallet: LocalWallet,
100 tx: TransactionRequest,
101 ) -> Result<String, Box<dyn std::error::Error>> {
102 let chain_id = self.evm_client.provider.get_chainid().await?.as_u64();
103 let mut typed_tx: TypedTransaction = tx.into();
104 typed_tx.set_chain_id(chain_id);
105 let signature = wallet.sign_transaction(&typed_tx).await?;
106 let signed_tx = typed_tx.rlp_signed(&signature);
107 let signed_tx_hex = format!("0x{}", hex::encode(signed_tx));
108 Ok(signed_tx_hex)
109 }
110
111 pub async fn estimate_gas(
113 &self,
114 tx: &TransactionRequest,
115 ) -> Result<U256, Box<dyn std::error::Error>> {
116 let typed_tx: TypedTransaction = tx.clone().into();
117 let gas_estimate = self
118 .evm_client
119 .provider
120 .estimate_gas(&typed_tx, None)
121 .await?;
122 Ok(gas_estimate)
123 }
124
125 pub async fn get_current_gas_price(&self) -> Result<U256, Box<dyn std::error::Error>> {
127 let gas_price = self.evm_client.provider.get_gas_price().await?;
128 Ok(gas_price)
129 }
130
131 pub async fn get_nonce(&self, address: Address) -> Result<U256, Box<dyn std::error::Error>> {
133 let nonce = self
134 .evm_client
135 .provider
136 .get_transaction_count(address, None)
137 .await?;
138 Ok(nonce)
139 }
140
141 pub async fn create_and_sign_eth_transfer(
143 &self,
144 wallet: LocalWallet,
145 to: Address,
146 value: U256,
147 ) -> Result<String, Box<dyn std::error::Error>> {
148 let tx_request = self.create_eth_transfer(wallet.clone(), to, value).await?;
149 self.sign_transaction(wallet, tx_request).await
150 }
151
152 pub async fn create_and_sign_contract_call(
154 &self,
155 wallet: LocalWallet,
156 to: Address,
157 data: Bytes,
158 value: Option<U256>,
159 ) -> Result<String, Box<dyn std::error::Error>> {
160 let tx_request = self
161 .create_contract_call(wallet.address(), to, data, value)
162 .await?;
163 self.sign_transaction(wallet, tx_request).await
164 }
165
166 pub async fn create_eip1559_transaction(
177 &self,
178 from: LocalWallet,
179 to: Address,
180 value: U256,
181 ) -> Result<Eip1559TransactionRequest, Box<dyn std::error::Error>> {
182 let nonce = self
183 .evm_client
184 .provider
185 .get_transaction_count(from.address(), None)
186 .await?;
187 let block = self
188 .evm_client
189 .provider
190 .get_block(BlockNumber::Latest)
191 .await?;
192 let base_fee = block
193 .and_then(|b| b.base_fee_per_gas)
194 .unwrap_or_else(|| U256::from(1000000000)); let max_priority_fee_per_gas = U256::from(1500000000); Ok(Eip1559TransactionRequest::new()
197 .from(from.address())
198 .to(to)
199 .value(value)
200 .nonce(nonce)
201 .max_fee_per_gas(base_fee + max_priority_fee_per_gas)
202 .max_priority_fee_per_gas(max_priority_fee_per_gas)
203 .gas(21000))
204 }
205
206 pub async fn sign_eip1559_transaction(
208 &self,
209 wallet: LocalWallet,
210 tx: Eip1559TransactionRequest,
211 ) -> Result<String, Box<dyn std::error::Error>> {
212 let chain_id = self.evm_client.provider.get_chainid().await?.as_u64();
213 let mut typed_tx: TypedTransaction = tx.into();
214 typed_tx.set_chain_id(chain_id);
215 let signature = wallet.sign_transaction(&typed_tx).await?;
216 let signed_tx = typed_tx.rlp_signed(&signature);
217 let signed_tx_hex = format!("0x{}", hex::encode(signed_tx));
218 Ok(signed_tx_hex)
219 }
220
221 pub async fn sign_raw_transaction(
223 &self,
224 wallet: LocalWallet,
225 tx_data: Bytes,
226 ) -> Result<String, Box<dyn std::error::Error>> {
227 let chain_id = self.evm_client.provider.get_chainid().await?.as_u64();
228 let tx_request = TransactionRequest::new().data(tx_data);
229 let mut typed_tx: TypedTransaction = tx_request.into();
230 typed_tx.set_chain_id(chain_id);
231 let signature = wallet.sign_transaction(&typed_tx).await?;
232 let signed_tx = typed_tx.rlp_signed(&signature);
233 let signed_tx_hex = format!("0x{}", hex::encode(signed_tx));
234 Ok(signed_tx_hex)
235 }
236}
237
238pub struct BatchTransactionBuilder {
240 evm_client: Arc<EvmClient>,
241 transactions: Vec<TransactionRequest>,
242}
243
244impl BatchTransactionBuilder {
245 pub async fn new(evm_type: EvmType) -> FlashbotsResult<Self> {
247 Ok(Self {
248 evm_client: Arc::new(
249 EvmClient::from_type(evm_type)
250 .await
251 .map_err(|e| FlashbotsError::Error(format!("{:?}", e)))?,
252 ),
253 transactions: Vec::new(),
254 })
255 }
256 pub fn add_transaction(mut self, tx: TransactionRequest) -> Self {
258 self.transactions.push(tx);
259 self
260 }
261
262 pub async fn estimate_all_gas(&self) -> Result<Vec<U256>, Box<dyn std::error::Error>> {
264 let mut estimates = Vec::new();
265 for tx in &self.transactions {
266 let typed_tx: TypedTransaction = tx.clone().into();
267 let estimate = self
268 .evm_client
269 .provider
270 .estimate_gas(&typed_tx, None)
271 .await?;
272 estimates.push(estimate);
273 }
274 Ok(estimates)
275 }
276
277 pub async fn sign_all(&self, wallet: LocalWallet) -> FlashbotsResult<Vec<String>> {
290 let mut signed_txs = Vec::new();
291 let tx_builder = TransactionBuilder {
293 evm_client: Arc::clone(&self.evm_client),
294 };
295 for tx in &self.transactions {
296 let signed_tx = tx_builder
297 .sign_transaction(wallet.clone(), tx.clone())
298 .await
299 .map_err(|e| FlashbotsError::Error(format!("{:?}", e)))?;
300 signed_txs.push(signed_tx);
301 }
302 Ok(signed_txs)
303 }
304}