Skip to main content

evm_client/
lib.rs

1use std::{fmt, sync::Arc};
2
3use ethers::{
4    providers::{Http, Middleware, Provider},
5    signers::LocalWallet,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum EvmType {
10    ETHEREUM_MAINNET,
11    ARB_MAINNET,
12    BSC_MAINNET,
13    BASE_MAINNET,
14    HYPEREVM_MAINNET,
15    PLASMA_MAINNET,
16    POLYGON_MAINNET,
17    OPTIMISM_MAINNET,
18    ZKSYNC_MAINNET,
19    STARKNET_MAINNET,
20    AVALANCHE_MAINNET,
21    FANTOM_MAINNET,
22    RONIN_MAINNET,
23    SKALE_MAINNET,
24    IMMUTABLE_MAINNET,
25}
26
27impl EvmType {
28    pub fn name(&self) -> &'static str {
29        match self {
30            EvmType::ETHEREUM_MAINNET => "Ethereum Mainnet",
31            EvmType::ARB_MAINNET => "Arbitrum One",
32            EvmType::BSC_MAINNET => "BNB Smart Chain",
33            EvmType::BASE_MAINNET => "Base Mainnet",
34            EvmType::HYPEREVM_MAINNET => "HyperEVM Mainnet",
35            EvmType::PLASMA_MAINNET => "Plasma Mainnet",
36            EvmType::POLYGON_MAINNET => "Polygon Mainnet",
37            EvmType::OPTIMISM_MAINNET => "Optimism Mainnet",
38            EvmType::ZKSYNC_MAINNET => "zkSync Era Mainnet",
39            EvmType::STARKNET_MAINNET => "StarkNet Mainnet",
40            EvmType::AVALANCHE_MAINNET => "Avalanche C-Chain",
41            EvmType::FANTOM_MAINNET => "Fantom Opera",
42            EvmType::RONIN_MAINNET => "Ronin Mainnet",
43            EvmType::SKALE_MAINNET => "SKALE Mainnet",
44            EvmType::IMMUTABLE_MAINNET => "Immutable zkEVM",
45        }
46    }
47
48    pub fn chain_id(&self) -> u64 {
49        match self {
50            EvmType::ETHEREUM_MAINNET => 1,
51            EvmType::ARB_MAINNET => 42161,
52            EvmType::BSC_MAINNET => 56,
53            EvmType::BASE_MAINNET => 8453,
54            EvmType::HYPEREVM_MAINNET => 123420111,
55            EvmType::PLASMA_MAINNET => 123420555,
56            EvmType::POLYGON_MAINNET => 137,
57            EvmType::OPTIMISM_MAINNET => 10,
58            EvmType::ZKSYNC_MAINNET => 324,
59            EvmType::STARKNET_MAINNET => 23448594291968334,
60            EvmType::AVALANCHE_MAINNET => 43114,
61            EvmType::FANTOM_MAINNET => 250,
62            EvmType::RONIN_MAINNET => 2020,
63            EvmType::SKALE_MAINNET => 1273227453,
64            EvmType::IMMUTABLE_MAINNET => 13371,
65        }
66    }
67
68    pub fn rpc(&self) -> Vec<&str> {
69        match self {
70            EvmType::ETHEREUM_MAINNET => vec![
71                "https://reth-ethereum.ithaca.xyz/rpc",
72                "https://cloudflare-eth.com",
73                "https://rpc.ankr.com/eth",
74                "https://eth.llamarpc.com",
75                "https://1rpc.io/eth",
76                "https://rpc.flashbots.net",
77                "https://ethereum.publicnode.com",
78                "https://eth-rpc.gateway.pokt.network",
79                "https://api.mycryptoapi.com/eth",
80                "https://nodes.mewapi.io/rpc/eth",
81                "https://mainnet-nethermind.blockscout.com",
82                "https://eth-pokt.nodies.app",
83                "https://eth.rpc.blxrbdn.com",
84                "https://virginia.rpc.blxrbdn.com",
85                "https://uk.rpc.blxrbdn.com",
86                "https://singapore.rpc.blxrbdn.com",
87                "https://eth-mainnet.public.blastapi.io",
88                "https://api.securerpc.com/v1",
89                "https://eth-mainnet.gateway.pokt.network/v1/5f3453978e354ab992c4da79",
90                "https://eth-mainnet.rpcfast.com?api_key=xbhWBI1Wkguk8SNMu1bvvLurPGLXmgwYeC4S6g2H7WdwFigZSmPWVZRxrskEQwIf",
91            ],
92            EvmType::ARB_MAINNET => vec![
93                "https://arb1.arbitrum.io/rpc",
94                "https://arbitrum-one.publicnode.com",
95                "https://1rpc.io/arb",
96                "https://arbitrum.rpc.subquery.network/public",
97                "https://arbitrum.blockpi.network/v1/rpc/public",
98                "https://arbitrum.meowrpc.com",
99                "https://arbitrum.api.onfinality.io/public",
100                "https://endpoints.omniatech.io/v1/arbitrum/one/public",
101                "https://arbitrum.drpc.org",
102                "https://arb-pokt.nodies.app",
103                "https://arbitrum-rpc.publicnode.com",
104                "https://rpc.arb1.arbitrum.gateway.fm",
105                "https://arbitrum-one-rpc.publicnode.com",
106                "https://arb-mainnet.g.alchemy.com/v2/demo",
107                "https://arbitrum-mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
108                "https://arbitrum.getblock.io/api/mainnet",
109            ],
110            EvmType::BSC_MAINNET => vec![
111                "https://bsc-dataseed.binance.org",
112                "https://bsc-dataseed1.binance.org",
113                "https://bsc-dataseed2.binance.org",
114                "https://bsc-dataseed3.binance.org",
115                "https://bsc-dataseed4.binance.org",
116                "https://bsc-dataseed1.defibit.io",
117                "https://bsc-dataseed2.defibit.io",
118                "https://bsc-dataseed3.defibit.io",
119                "https://bsc-dataseed1.ninicoin.io",
120                "https://bsc-dataseed2.ninicoin.io",
121                "https://bsc-dataseed3.ninicoin.io",
122                "https://bsc-rpc.publicnode.com",
123                "https://bsc.publicnode.com",
124                "https://1rpc.io/bnb",
125                "https://bsc.meowrpc.com",
126                "https://bsc.blockpi.network/v1/rpc/public",
127                "https://bsc-mainnet.nodereal.io/v1/64a9df0874fb4a93b9d0a3849de012d3",
128                "https://bsc-mainnet.rpcfast.com?api_key=xbhWBI1Wkguk8SNMu1bvvLurPGLXmgwYeC4S6g2H7WdwFigZSmPWVZRxrskEQwIf",
129                "https://binance.llamarpc.com",
130                "https://bsc-dataseed.binance.gg",
131            ],
132            EvmType::BASE_MAINNET => vec![
133                "https://mainnet.base.org",
134                "https://base-rpc.publicnode.com",
135                "https://base.gateway.tenderly.co",
136                "https://1rpc.io/base",
137                "https://base-pokt.nodies.app",
138                "https://base.meowrpc.com",
139                "https://base.api.onfinality.io/public",
140                "https://base.blockpi.network/v1/rpc/public",
141                "https://developer-access-mainnet.base.org",
142                "https://base.llamarpc.com",
143                "https://base-mainnet.public.blastapi.io",
144                "https://base.drpc.org",
145                "https://base-mainnet.core.chainstack.com",
146                "https://base-mainnet.gateway.pokt.network/v1/5f3453978e354ab992c4da79",
147                "https://base.getblock.io/api/mainnet",
148            ],
149            EvmType::HYPEREVM_MAINNET => vec![
150                "https://hybrid-mainnet.evm.nodes.onflow.org",
151                "https://mainnet.evm.nodes.onflow.org",
152                "https://evm-rpc.mainnet.hyperchain.xyz",
153                "https://hyperchain.alt.technology",
154                "https://rpc.hybrid.mainnet.hyperchain.xyz",
155                "https://hyper-evm.rpc.subquery.network/public",
156                "https://hyper-evm.publicnode.com",
157                "https://hyper-evm.blockpi.network/v1/rpc/public",
158                "https://hyper-evm.drpc.org",
159                "https://flow-hybrid-mainnet.g.alchemy.com/v2/demo",
160            ],
161            EvmType::PLASMA_MAINNET => vec![
162                "https://rpc.plasma.evm.onyx.org",
163                "https://plasma-rpc.daoswap.cc",
164                "https://plasma.daoswap.cc",
165                "https://plasma-evm.rpc.subquery.network/public",
166                "https://plasma-evm.blockpi.network/v1/rpc/public",
167                "https://plasma-evm.publicnode.com",
168                "https://rpc.plasma.quarkchain.io",
169                "https://plasma-mainnet.rpc.thirdweb.com",
170                "https://plasma.drpc.org",
171                "https://plasma-rpc.nodeinfra.com",
172                "https://plasma-mainnet.core.chainstack.com",
173            ],
174            EvmType::POLYGON_MAINNET => vec![
175                "https://polygon-rpc.com",
176                "https://polygon-bor.publicnode.com",
177                "https://rpc.ankr.com/polygon",
178                "https://1rpc.io/matic",
179                "https://polygon.blockpi.network/v1/rpc/public",
180                "https://polygon.meowrpc.com",
181                "https://rpc-mainnet.matic.network",
182                "https://rpc-mainnet.maticvigil.com",
183                "https://rpc-mainnet.matic.quiknode.pro",
184                "https://matic-mainnet-full-rpc.bwarelabs.com",
185                "https://matic-mainnet-archive-rpc.bwarelabs.com",
186                "https://polygon-mainnet.public.blastapi.io",
187                "https://polygon.drpc.org",
188                "https://polygon-pokt.nodies.app",
189                "https://polygon-mainnet.g.alchemy.com/v2/demo",
190                "https://polygon-mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
191                "https://polygon.api.onfinality.io/public",
192            ],
193            EvmType::OPTIMISM_MAINNET => vec![
194                "https://optimism-rpc.publicnode.com",
195                "https://rpc.ankr.com/optimism",
196                "https://1rpc.io/op",
197                "https://optimism.blockpi.network/v1/rpc/public",
198                "https://mainnet.optimism.io",
199                "https://optimism.meowrpc.com",
200                "https://opt-mainnet.g.alchemy.com/v2/demo",
201                "https://optimism-mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
202                "https://optimism.api.onfinality.io/public",
203                "https://optimism.drpc.org",
204                "https://optimism-pokt.nodies.app",
205                "https://optimism-mainnet.public.blastapi.io",
206                "https://endpoints.omniatech.io/v1/op/mainnet/public",
207                "https://optimism.getblock.io/api/mainnet",
208            ],
209            EvmType::ZKSYNC_MAINNET => vec![
210                "https://mainnet.era.zksync.io",
211                "https://zksync-rpc.com",
212                "https://1rpc.io/zksync",
213                "https://zksync.drpc.org",
214                "https://zksync.meowrpc.com",
215                "https://zksync-era.blockpi.network/v1/rpc/public",
216                "https://zksync-mainnet.public.blastapi.io",
217                "https://zksync.rpc.subquery.network/public",
218                "https://zksync-pokt.nodies.app",
219                "https://zksync.getblock.io/api/mainnet",
220                "https://zksync-era.core.chainstack.com",
221                "https://zksync-era.rpc.thirdweb.com",
222                "https://zksync-era-mainnet.public.immudb.io",
223            ],
224            EvmType::STARKNET_MAINNET => vec![
225                "https://starknet-mainnet.public.blastapi.io",
226                "https://free-rpc.nethermind.io/mainnet",
227                "https://rpc.starknet.lava.build",
228                "https://starknet-mainnet.public.chainstack.com",
229                "https://starknet-mainnet.g.alchemy.com/v2/demo",
230                "https://starknet-mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
231                "https://starknet.api.onfinality.io/public",
232                "https://starknet.drpc.org",
233                "https://starknet-mainnet.core.chainstack.com",
234                "https://starknet.rpc.subquery.network/public",
235                "https://starknet-mainnet.public.immudb.io",
236            ],
237            EvmType::AVALANCHE_MAINNET => vec![
238                "https://avalanche-c-chain-rpc.publicnode.com",
239                "https://rpc.ankr.com/avalanche",
240                "https://1rpc.io/avax",
241                "https://avax.meowrpc.com",
242                "https://api.avax.network/ext/bc/C/rpc",
243                "https://avalanche-mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
244                "https://avax-pokt.nodies.app",
245                "https://avalanche.drpc.org",
246                "https://avalanche-mainnet.public.blastapi.io",
247                "https://avalanche.api.onfinality.io/public",
248                "https://avalanche-c-chain-rpc.gateway.pokt.network",
249                "https://avax.getblock.io/api/mainnet",
250                "https://avalanche-mainnet.core.chainstack.com",
251                "https://avalanche.rpc.subquery.network/public",
252                "https://avalanche-mainnet.gateway.pokt.network/v1/5f3453978e354ab992c4da79",
253            ],
254            EvmType::FANTOM_MAINNET => vec![
255                "https://fantom-rpc.publicnode.com",
256                "https://rpc.ankr.com/fantom",
257                "https://1rpc.io/ftm",
258                "https://fantom.blockpi.network/v1/rpc/public",
259                "https://rpc.fantom.network",
260                "https://fantom.publicnode.com",
261                "https://fantom-mainnet.gateway.pokt.network/v1/5f3453978e354ab992c4da79",
262                "https://fantom.drpc.org",
263                "https://fantom-mainnet.public.blastapi.io",
264                "https://fantom.api.onfinality.io/public",
265                "https://fantom.getblock.io/api/mainnet",
266                "https://fantom-mainnet.core.chainstack.com",
267                "https://fantom.rpc.subquery.network/public",
268                "https://fantom-mainnet.g.alchemy.com/v2/demo",
269            ],
270            EvmType::RONIN_MAINNET => vec![
271                "https://rpc.roninchain.com",
272                "https://api.roninchain.com/rpc",
273                "https://ronin-rpc.nodeinfra.com",
274                "https://ronin.drpc.org",
275                "https://ronin-mainnet.public.blastapi.io",
276                "https://ronin.api.onfinality.io/public",
277                "https://ronin.blockpi.network/v1/rpc/public",
278                "https://ronin-pokt.nodies.app",
279                "https://ronin.getblock.io/api/mainnet",
280                "https://ronin-mainnet.core.chainstack.com",
281                "https://ronin.rpc.subquery.network/public",
282            ],
283            EvmType::SKALE_MAINNET => vec![
284                "https://mainnet.skalenodes.com/v1/whispering-turais",
285                "https://rpc.skale.space",
286                "https://skale-rpc.nodeinfra.com",
287                "https://skale.drpc.org",
288                "https://skale-mainnet.public.blastapi.io",
289                "https://skale.api.onfinality.io/public",
290                "https://skale.blockpi.network/v1/rpc/public",
291                "https://skale-pokt.nodies.app",
292                "https://skale.getblock.io/api/mainnet",
293                "https://skale-mainnet.core.chainstack.com",
294                "https://skale.rpc.subquery.network/public",
295            ],
296            EvmType::IMMUTABLE_MAINNET => vec![
297                "https://rpc.immutable.com",
298                "https://immutable-zkevm.drpc.org",
299                "https://rpc.immutable.com/node-api",
300                "https://immutable-mainnet.core.chainstack.com",
301                "https://immutable-mainnet.public.blastapi.io",
302                "https://immutable.api.onfinality.io/public",
303                "https://immutable.blockpi.network/v1/rpc/public",
304                "https://immutable-pokt.nodies.app",
305                "https://immutable.getblock.io/api/mainnet",
306                "https://immutable.rpc.subquery.network/public",
307                "https://immutable-mainnet.g.alchemy.com/v2/demo",
308            ],
309        }
310    }
311}
312
313#[derive(Debug)]
314pub enum EvmClientError {
315    RpcError(String),
316}
317
318impl fmt::Display for EvmClientError {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        match self {
321            EvmClientError::RpcError(msg) => write!(f, "RPC error: {}", msg),
322        }
323    }
324}
325
326impl std::error::Error for EvmClientError {}
327
328/// EVM Client for interacting with various EVM chains
329#[derive(Clone)]
330pub struct EvmClient {
331    /// The ethers-rs provider for blockchain interactions
332    pub provider: Arc<Provider<Http>>,
333    /// The type of EVM chain this client is connected to
334    pub evm_type: Option<EvmType>,
335    /// Optional wallet for transaction signing
336    pub wallet: Option<LocalWallet>,
337}
338
339impl EvmClient {
340    /// Create a new EVM client from chain type
341    ///
342    /// # Params
343    ///
344    /// evm_type - The type of EVM chain to connect to
345    ///
346    pub async fn from_type(evm_type: EvmType) -> Result<Self, EvmClientError> {
347        for (_index, url) in evm_type.rpc().iter().enumerate() {
348            match Provider::<Http>::try_from(url.clone()) {
349                Ok(p) => {
350                    return Ok(Self {
351                        provider: Arc::new(p),
352                        evm_type: Some(evm_type),
353                        wallet: None,
354                    });
355                }
356                Err(_) => continue,
357            }
358        }
359        return Err(EvmClientError::RpcError(format!(
360            "All RPC links are invalid"
361        )));
362    }
363
364    /// Create a new EVM client from a specific RPC URL
365    ///
366    /// # Params
367    ///
368    /// rpc_url - The RPC endpoint URL to connect to
369    ///
370    pub async fn from_rpc(rpc_url: &str) -> Result<Self, EvmClientError> {
371        match Provider::<Http>::try_from(rpc_url) {
372            Ok(p) => {
373                return Ok(Self {
374                    provider: Arc::new(p),
375                    evm_type: None,
376                    wallet: None,
377                });
378            }
379            Err(e) => Err(EvmClientError::RpcError(format!("RPC Error: {:?}", e))),
380        }
381    }
382
383    /// Create a new EVM client from chain type and private key
384    ///
385    /// # Params
386    ///
387    /// evm_type - The type of EVM chain to connect to
388    /// private_key - The private key string for the wallet
389    ///
390    pub async fn from_wallet(evm_type: EvmType, private_key: &str) -> Result<Self, EvmClientError> {
391        let mut client = Self::from_type(evm_type).await?;
392        let wallet: LocalWallet = private_key
393            .parse()
394            .map_err(|e| EvmClientError::RpcError(format!("Failed to parse private key: {}", e)))?;
395        client.wallet = Some(wallet);
396        Ok(client)
397    }
398
399    /// Create a new EVM client from RPC URL and private key
400    ///
401    /// # Params
402    ///
403    /// rpc_url - The RPC endpoint URL to connect to
404    /// private_key - The private key string for the wallet
405    ///
406    pub async fn from_rpc_and_wallet(
407        rpc_url: &str,
408        private_key: &str,
409    ) -> Result<Self, EvmClientError> {
410        let mut client = Self::from_rpc(rpc_url).await?;
411        let wallet: LocalWallet = private_key
412            .parse()
413            .map_err(|e| EvmClientError::RpcError(format!("Failed to parse private key: {}", e)))?;
414        client.wallet = Some(wallet);
415        Ok(client)
416    }
417
418    /// heartbeat check
419    pub async fn health(&self) -> Result<(), EvmClientError> {
420        self.provider
421            .get_chainid()
422            .await
423            .map_err(|e| EvmClientError::RpcError(format!("Health check failed: {}", e)))?;
424        Ok(())
425    }
426
427    // get block interval time, unit: second.
428    pub fn get_block_interval_time(&self) -> Option<u64> {
429        match self.evm_type {
430            Some(EvmType::ETHEREUM_MAINNET) => Some(12),
431            Some(EvmType::ARB_MAINNET) => Some(1),
432            Some(EvmType::BSC_MAINNET) => Some(3),
433            Some(EvmType::BASE_MAINNET) => Some(2),
434            Some(EvmType::HYPEREVM_MAINNET) => Some(2),
435            Some(EvmType::PLASMA_MAINNET) => Some(2),
436            Some(EvmType::POLYGON_MAINNET) => Some(2),
437            Some(EvmType::OPTIMISM_MAINNET) => Some(2),
438            Some(EvmType::ZKSYNC_MAINNET) => Some(2),
439            Some(EvmType::STARKNET_MAINNET) => Some(10),
440            Some(EvmType::AVALANCHE_MAINNET) => Some(2),
441            Some(EvmType::FANTOM_MAINNET) => Some(1),
442            Some(EvmType::RONIN_MAINNET) => Some(3),
443            Some(EvmType::SKALE_MAINNET) => Some(5),
444            Some(EvmType::IMMUTABLE_MAINNET) => Some(2),
445            None => None,
446        }
447    }
448}