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#[derive(Clone)]
330pub struct EvmClient {
331 pub provider: Arc<Provider<Http>>,
333 pub evm_type: Option<EvmType>,
335 pub wallet: Option<LocalWallet>,
337}
338
339impl EvmClient {
340 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 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 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 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 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 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}