evm_sdk/lib.rs
1pub mod block;
2/// This module is the EVM network abstraction layer.
3pub mod contract;
4pub mod erc;
5pub mod global;
6pub mod mempool;
7pub mod safe;
8pub mod tool;
9pub mod trade;
10pub mod types;
11pub mod onchain;
12
13use std::sync::Arc;
14
15use crate::block::BlockService;
16use crate::mempool::MempoolListener;
17use crate::mempool::MempoolService;
18use crate::trade::Trade;
19use crate::trade::TradeEventListener;
20use crate::types::EvmError;
21use ethers::providers::Middleware;
22use ethers::providers::StreamExt;
23use ethers::types::Block;
24use ethers::types::BlockNumber;
25use ethers::{
26 signers::Signer,
27 types::{Address, H256, TransactionRequest, U256},
28};
29use evm_client::EvmClient;
30use evm_client::EvmType;
31
32use ethers::types::Transaction;
33
34/// EVM Client for interacting with various EVM chains
35#[derive(Clone)]
36pub struct Evm {
37 pub client: EvmClient,
38}
39
40impl Evm {
41 /// Get block by number (height) - returns block with transaction hashes
42 pub async fn get_block_by_number(
43 &self,
44 block_number: BlockNumber,
45 ) -> Result<Option<Block<H256>>, EvmError> {
46 self.client
47 .provider
48 .get_block(block_number)
49 .await
50 .map_err(|e| EvmError::RpcError(format!("Failed to get block: {}", e)))
51 }
52
53 /// Get block by hash - returns block with transaction hashes
54 pub async fn get_block_by_hash(
55 &self,
56 block_hash: H256,
57 ) -> Result<Option<Block<H256>>, EvmError> {
58 self.client
59 .provider
60 .get_block(block_hash)
61 .await
62 .map_err(|e| EvmError::RpcError(format!("Failed to get block: {}", e)))
63 }
64
65 /// Get block with full transaction details
66 pub async fn get_block_with_txs(
67 &self,
68 block_number: BlockNumber,
69 ) -> Result<Option<Block<Transaction>>, EvmError> {
70 self.client
71 .provider
72 .get_block_with_txs(block_number)
73 .await
74 .map_err(|e| EvmError::RpcError(format!("Failed to get block with txs: {}", e)))
75 }
76
77 /// Get block with full transaction details by hash
78 pub async fn get_block_with_txs_by_hash(
79 &self,
80 block_hash: H256,
81 ) -> Result<Option<Block<Transaction>>, EvmError> {
82 let block = self.get_block_by_hash(block_hash).await?;
83 if let Some(ref block_info) = block {
84 if let Some(block_number) = block_info.number {
85 return self
86 .get_block_with_txs(BlockNumber::Number(block_number))
87 .await;
88 }
89 }
90 Ok(None)
91 }
92
93 /// Create a new EVM client without wallet
94 ///
95 /// # Example
96 /// ```
97 /// use evm_utils::Evm;
98 /// use evm_client::EvmType;
99 ///
100 /// async fn example() -> Result<(), Box<dyn std::error::Error>> {
101 /// let evm = Evm::new(EvmType::Ethereum).await?;
102 /// let chain_id = evm.get_chain_id().await?;
103 /// println!("Connected to chain ID: {}", chain_id);
104 /// Ok(())
105 /// }
106 /// ```
107 pub async fn new(evm_type: EvmType) -> Result<Self, EvmError> {
108 match EvmClient::from_type(evm_type).await {
109 Ok(client) => Ok(Self { client: client }),
110 Err(e) => Err(EvmError::RpcError(format!("Rpc Error:{:?}", e))),
111 }
112 }
113
114 /// Create a new EVM client with wallet
115 ///
116 /// # Example
117 /// ```
118 /// use evm_utils::Evm;
119 /// use evm_client::EvmType;
120 ///
121 /// async fn example() -> Result<(), Box<dyn std::error::Error>> {
122 /// let private_key = "your_private_key_here";
123 /// let evm = Evm::with_wallet(EvmType::Ethereum, private_key).await?;
124 /// let balance = evm.get_wallet_balance().await?;
125 /// println!("Wallet balance: {}", balance);
126 /// Ok(())
127 /// }
128 /// ```
129 pub async fn with_wallet(evm_type: EvmType, private_key: &str) -> Result<Self, EvmError> {
130 match EvmClient::from_wallet(evm_type, private_key).await {
131 Ok(client) => Ok(Self { client: client }),
132 Err(e) => Err(EvmError::RpcError(format!("Rpc Error:{:?}", e))),
133 }
134 }
135
136 /// Get chain ID
137 ///
138 /// # Example
139 /// ```
140 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
141 /// let chain_id = evm.get_chain_id().await?;
142 /// println!("Chain ID: {}", chain_id);
143 /// Ok(())
144 /// }
145 /// ```
146 pub async fn get_chain_id(&self) -> Result<u64, EvmError> {
147 self.client
148 .provider
149 .get_chainid()
150 .await
151 .map_err(|e| EvmError::RpcError(format!("Failed to get chain ID: {}", e)))
152 .map(|id| id.as_u64())
153 }
154
155 /// Get block number
156 ///
157 /// # Example
158 /// ```
159 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
160 /// let block_number = evm.get_block_number().await?;
161 /// println!("Current block number: {}", block_number);
162 /// Ok(())
163 /// }
164 /// ```
165 pub async fn get_block_number(&self) -> Result<u64, EvmError> {
166 self.client
167 .provider
168 .get_block_number()
169 .await
170 .map_err(|e| EvmError::RpcError(format!("Failed to get block number: {}", e)))
171 .map(|num| num.as_u64())
172 }
173
174 /// Get balance of an address
175 ///
176 /// # Example
177 /// ```
178 /// use ethers::types::Address;
179 ///
180 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
181 /// let address: Address = "0x742d35Cc6634C0532925a3b8D6B5d7a4C03a3a7d".parse()?;
182 /// let balance = evm.get_balance(address).await?;
183 /// println!("Balance: {}", balance);
184 /// Ok(())
185 /// }
186 /// ```
187 pub async fn get_balance(&self, address: Address) -> Result<U256, EvmError> {
188 self.client
189 .provider
190 .get_balance(address, None)
191 .await
192 .map_err(|e| EvmError::RpcError(format!("Failed to get balance: {}", e)))
193 }
194
195 /// Get transaction count (nonce) for an address
196 ///
197 /// # Example
198 /// ```
199 /// use ethers::types::Address;
200 ///
201 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
202 /// let address: Address = "0x742d35Cc6634C0532925a3b8D6B5d7a4C03a3a7d".parse()?;
203 /// let nonce = evm.get_transaction_count(address).await?;
204 /// println!("Nonce: {}", nonce);
205 /// Ok(())
206 /// }
207 /// ```
208 pub async fn get_transaction_count(&self, address: Address) -> Result<u64, EvmError> {
209 self.client
210 .provider
211 .get_transaction_count(address, None)
212 .await
213 .map_err(|e| EvmError::RpcError(format!("Failed to get transaction count: {}", e)))
214 .map(|nonce| nonce.as_u64())
215 }
216
217 /// Get gas price
218 ///
219 /// # Example
220 /// ```
221 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
222 /// let gas_price = evm.get_gas_price().await?;
223 /// println!("Gas price: {}", gas_price);
224 /// Ok(())
225 /// }
226 /// ```
227 pub async fn get_gas_price(&self) -> Result<U256, EvmError> {
228 self.client
229 .provider
230 .get_gas_price()
231 .await
232 .map_err(|e| EvmError::RpcError(format!("Failed to get gas price: {}", e)))
233 }
234
235 /// Send a raw transaction
236 ///
237 /// # Example
238 /// ```
239 /// use ethers::types::{TransactionRequest, Address, U256};
240 ///
241 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
242 /// let to_address: Address = "0x742d35Cc6634C0532925a3b8D6B5d7a4C03a3a7d".parse()?;
243 /// let tx = TransactionRequest::new()
244 /// .to(to_address)
245 /// .value(U256::from(1000000000000000u64));
246 ///
247 /// let tx_hash = evm.send_transaction(tx).await?;
248 /// println!("Transaction sent: {:?}", tx_hash);
249 /// Ok(())
250 /// }
251 /// ```
252 pub async fn send_transaction(&self, mut tx: TransactionRequest) -> Result<H256, EvmError> {
253 if self.client.wallet.is_none() {
254 return Err(EvmError::WalletError("No wallet configured".to_string()));
255 }
256 let wallet = self.client.wallet.as_ref().unwrap();
257 tx.from = Some(wallet.address());
258 let chain_id = self.get_chain_id().await?;
259 tx.chain_id = Some(chain_id.into());
260 if tx.nonce.is_none() {
261 let nonce = self.get_transaction_count(wallet.address()).await?;
262 tx.nonce = Some(nonce.into());
263 }
264 if tx.gas_price.is_none() {
265 let gas_price = self.get_gas_price().await?;
266 tx.gas_price = Some(gas_price);
267 }
268 let pending_tx = self
269 .client
270 .provider
271 .send_transaction(tx, None)
272 .await
273 .map_err(|e| {
274 EvmError::TransactionError(format!("Failed to send transaction: {}", e))
275 })?;
276 Ok(pending_tx.tx_hash())
277 }
278
279 /// Get transaction receipt
280 ///
281 /// # Example
282 /// ```
283 /// use ethers::types::H256;
284 ///
285 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
286 /// let tx_hash: H256 = "0x...".parse()?;
287 /// let receipt = evm.get_transaction_receipt(tx_hash).await?;
288 /// if let Some(receipt) = receipt {
289 /// println!("Transaction status: {}", receipt.status);
290 /// }
291 /// Ok(())
292 /// }
293 /// ```
294 pub async fn get_transaction_receipt(
295 &self,
296 tx_hash: H256,
297 ) -> Result<Option<ethers::types::TransactionReceipt>, EvmError> {
298 self.client
299 .provider
300 .get_transaction_receipt(tx_hash)
301 .await
302 .map_err(|e| EvmError::RpcError(format!("Failed to get transaction receipt: {}", e)))
303 }
304
305 /// Get logs by filter
306 ///
307 /// # Example
308 /// ```
309 /// use ethers::types::Filter;
310 /// use ethers::types::Address;
311 ///
312 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
313 /// let contract_address: Address = "0x...".parse()?;
314 /// let filter = Filter::new()
315 /// .address(contract_address)
316 /// .from_block(1000000)
317 /// .to_block(1000100);
318 ///
319 /// let logs = evm.get_logs(filter).await?;
320 /// println!("Found {} logs", logs.len());
321 /// Ok(())
322 /// }
323 /// ```
324 pub async fn get_logs(
325 &self,
326 filter: ethers::types::Filter,
327 ) -> Result<Vec<ethers::types::Log>, EvmError> {
328 self.client
329 .provider
330 .get_logs(&filter)
331 .await
332 .map_err(|e| EvmError::RpcError(format!("Failed to get logs: {}", e)))
333 }
334
335 /// Get native token balance for the wallet
336 ///
337 /// # Example
338 /// ```
339 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
340 /// let balance = evm.get_wallet_balance().await?;
341 /// println!("Wallet balance: {}", balance);
342 /// Ok(())
343 /// }
344 /// ```
345 pub async fn get_wallet_balance(&self) -> Result<U256, EvmError> {
346 if let Some(wallet) = &self.client.wallet {
347 let address = wallet.address();
348 self.get_balance(address).await
349 } else {
350 Err(EvmError::WalletError("No wallet configured".to_string()))
351 }
352 }
353
354 /// Get trade service for executing trades
355 ///
356 /// # Example
357 /// ```
358 /// use std::sync::Arc;
359 ///
360 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
361 /// let evm_arc = Arc::new(evm);
362 /// let trade_service = evm_arc.clone().get_trade_service();
363 /// Ok(())
364 /// }
365 /// ```
366 pub fn get_trade_service(self: Arc<Self>) -> Trade {
367 Trade::new(self.clone())
368 }
369
370 /// Get trade event listener for monitoring trade events
371 ///
372 /// # Example
373 /// ```
374 /// use std::sync::Arc;
375 ///
376 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
377 /// let evm_arc = Arc::new(evm);
378 /// let trade_listener = evm_arc.clone().get_trade_listener();
379 /// Ok(())
380 /// }
381 /// ```
382 pub fn get_trade_listener(self: Arc<Self>) -> TradeEventListener {
383 TradeEventListener::new(self.clone())
384 }
385
386 /// Get mempool service for mempool interactions
387 ///
388 /// # Example
389 /// ```
390 /// use std::sync::Arc;
391 ///
392 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
393 /// let evm_arc = Arc::new(evm);
394 /// let mempool_service = evm_arc.clone().get_mempool_service();
395 /// Ok(())
396 /// }
397 /// ```
398 pub fn get_mempool_service(self: Arc<Self>) -> MempoolService {
399 MempoolService::new(self.clone())
400 }
401
402 /// Get mempool listener for monitoring mempool activities
403 ///
404 /// # Example
405 /// ```
406 /// use std::sync::Arc;
407 ///
408 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
409 /// let evm_arc = Arc::new(evm);
410 /// let mempool_listener = evm_arc.clone().get_mempool_listener();
411 /// Ok(())
412 /// }
413 /// ```
414 pub fn get_mempool_listener(self: Arc<Self>) -> MempoolListener {
415 MempoolListener::new(self.clone())
416 }
417
418 /// Get block service for block-related operations
419 ///
420 /// # Example
421 /// ```
422 /// use std::sync::Arc;
423 ///
424 /// async fn example(evm: Evm) -> Result<(), Box<dyn std::error::Error>> {
425 /// let evm_arc = Arc::new(evm);
426 /// let block_service = evm_arc.clone().get_block_service();
427 ///
428 /// let latest_block = block_service.get_latest_block().await?;
429 /// if let Some(block) = latest_block {
430 /// println!("Latest block: #{}", block.number.unwrap_or_default());
431 /// }
432 /// Ok(())
433 /// }
434 /// ```
435 pub fn get_block_service(self: Arc<Self>) -> BlockService {
436 BlockService::new(self.clone())
437 }
438
439 /// Listen to the latest block (listen to newly generated blocks in real time)
440 ///
441 /// # Example
442 /// ```
443 /// let mut block_receiver = trade_service.listen_latest_blocks().await?;
444 ///
445 /// while let Some(block) = block_receiver.recv().await {
446 /// println!("New block: #{}", block.number.unwrap_or_default());
447 /// }
448 /// ```
449 pub async fn listen_latest_blocks(
450 &self,
451 ) -> Result<tokio::sync::broadcast::Receiver<ethers::types::Block<ethers::types::H256>>, EvmError>
452 {
453 use ethers::providers::Middleware;
454 use tokio::sync::broadcast;
455 let (sender, receiver) = broadcast::channel(1024);
456 let provider = self.client.provider.clone();
457 tokio::spawn(async move {
458 if let Ok(mut stream) = provider.watch_blocks().await {
459 while let Some(block_hash) = stream.next().await {
460 match provider.get_block(block_hash).await {
461 Ok(Some(block)) => {
462 if sender.send(block).is_err() {
463 break;
464 }
465 }
466 Ok(None) => {
467 log::warn!("Block not found for hash: {:?}", block_hash);
468 }
469 Err(e) => {
470 log::error!("Failed to get block: {:?}", e);
471 }
472 }
473 }
474 } else {
475 log::error!("Failed to create block watcher stream");
476 }
477 });
478 Ok(receiver)
479 }
480}