avalanche_types/wallet/evm/
mod.rs1pub mod eip1559;
2
3use std::{ops::Div, sync::Arc, time::Duration};
4
5use crate::{
6 errors::{Error, Result},
7 jsonrpc::client::evm as jsonrpc_client_evm,
8 key, wallet,
9};
10use ethers::{
11 prelude::{
12 gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice},
13 NonceManagerMiddleware, SignerMiddleware,
14 },
15 utils::Units::Gwei,
16};
17use ethers_providers::{Http, HttpRateLimitRetryPolicy, Provider, RetryClient};
18use lazy_static::lazy_static;
19use primitive_types::U256;
20use reqwest::ClientBuilder;
21use url::Url;
22
23#[derive(Clone, Debug)]
24pub struct Evm<T, S>
25where
26 T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
27 S: ethers_signers::Signer + Clone,
28 S::Error: 'static,
29{
30 pub inner: wallet::Wallet<T>,
31
32 pub chain_rpc_url: String,
33 pub provider: Arc<Provider<RetryClient<Http>>>,
34
35 pub eth_signer: S,
36
37 #[allow(clippy::type_complexity)]
44 pub middleware: Arc<
45 NonceManagerMiddleware<
46 SignerMiddleware<GasEscalatorMiddleware<Arc<Provider<RetryClient<Http>>>>, S>,
47 >,
48 >,
49
50 pub chain_id: U256,
51}
52
53impl<T, S> Evm<T, S>
54where
55 T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
56 S: ethers_signers::Signer + Clone,
57 S::Error: 'static,
58{
59 pub async fn balance(&self) -> Result<U256> {
61 let cur_balance =
62 jsonrpc_client_evm::get_balance(&self.chain_rpc_url, self.inner.h160_address).await?;
63 Ok(cur_balance)
64 }
65}
66
67impl<T> wallet::Wallet<T>
68where
69 T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
70{
71 pub fn evm<S>(&self, eth_signer: &S, chain_rpc_url: &str, chain_id: U256) -> Result<Evm<T, S>>
75 where
76 S: ethers_signers::Signer + Clone,
77 S::Error: 'static,
78 {
79 let provider = new_provider(
81 chain_rpc_url,
82 Duration::from_secs(15),
83 Duration::from_secs(30),
84 5,
85 Duration::from_secs(3),
86 )?;
87 let provider_arc = Arc::new(provider);
88
89 let nonce_middleware = new_middleware(Arc::clone(&provider_arc), eth_signer, chain_id)?;
90 let middleware = Arc::new(nonce_middleware);
91
92 Ok(Evm::<T, S> {
93 inner: self.clone(),
94
95 chain_rpc_url: chain_rpc_url.to_string(),
96 provider: provider_arc,
97
98 eth_signer: eth_signer.clone(),
99
100 middleware,
101
102 chain_id,
103 })
104 }
105}
106
107pub fn new_provider(
110 chain_rpc_url: &str,
111 connect_timeout: Duration,
112 request_timeout: Duration,
113 max_retries: u32,
114 backoff_timeout: Duration,
115) -> Result<Provider<RetryClient<Http>>> {
116 let u = Url::parse(chain_rpc_url).map_err(|e| Error::Other {
117 message: format!("failed to parse chain RPC URL {}", e),
118 retryable: false,
119 })?;
120
121 let http_cli = ClientBuilder::new()
122 .user_agent(env!("CARGO_PKG_NAME"))
123 .connect_timeout(connect_timeout)
124 .connection_verbose(true)
125 .timeout(request_timeout)
126 .danger_accept_invalid_certs(true) .build()
128 .map_err(|e| {
129 Error::Other {
131 message: format!("failed reqwest::ClientBuilder.build '{}'", e),
132 retryable: false,
133 }
134 })?;
135
136 let retry_client = RetryClient::new(
138 Http::new_with_client(u, http_cli),
139 Box::new(HttpRateLimitRetryPolicy),
140 max_retries,
141 backoff_timeout.as_millis() as u64,
142 );
143
144 let provider = Provider::new(retry_client).interval(Duration::from_millis(2000u64));
145 Ok(provider)
146}
147
148#[allow(clippy::type_complexity)]
150pub fn new_middleware<S>(
151 provider: Arc<Provider<RetryClient<Http>>>,
152 eth_signer: &S,
153 chain_id: U256,
154) -> Result<
155 NonceManagerMiddleware<
156 SignerMiddleware<GasEscalatorMiddleware<Arc<Provider<RetryClient<Http>>>>, S>,
157 >,
158>
159where
160 S: ethers_signers::Signer + Clone,
161 S::Error: 'static,
162{
163 let escalator = GeometricGasPrice::new(5.0, 10u64, None::<u64>);
165
166 let gas_escalator_middleware =
169 GasEscalatorMiddleware::new(Arc::clone(&provider), escalator, Frequency::PerBlock);
170
171 let signer_middleware = SignerMiddleware::new(
172 gas_escalator_middleware,
173 eth_signer.clone().with_chain_id(chain_id.as_u64()),
174 );
175
176 let nonce_middleware = NonceManagerMiddleware::new(signer_middleware, eth_signer.address());
177 Ok(nonce_middleware)
178}
179
180lazy_static! {
181 pub static ref GWEI: U256 = U256::from(10).checked_pow(Gwei.as_num().into()).unwrap();
182}
183
184pub fn wei_to_gwei(wei: impl Into<U256>) -> U256 {
186 let wei: U256 = wei.into();
187 if wei.is_zero() {
188 U256::zero()
189 } else {
190 wei.div(*GWEI)
191 }
192}