1use std::{
2 borrow::Cow,
3 collections::BTreeMap,
4 io::Read,
5 sync::{Arc, Mutex},
6};
7
8use alloy::{
9 network::{Ethereum, EthereumWallet, Network, TransactionBuilder},
10 primitives::{Address, BlockNumber, Bytes, B256, U128, U256, U64},
11 providers::{
12 fillers::{
13 BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller,
14 WalletFiller,
15 },
16 EthCall, EthCallMany, Identity, PendingTransactionBuilder, Provider, ProviderBuilder,
17 ProviderCall, RootProvider, RpcWithBlock, SendableTx,
18 },
19 rpc::{
20 client::NoParams,
21 types::{
22 erc4337::TransactionConditional,
23 simulate::{SimulatePayload, SimulatedBlock},
24 AccessListResult, Bundle, EthCallResponse, SyncStatus, TransactionRequest,
25 },
26 },
27 signers::{
28 k256::{elliptic_curve::SecretKey, Secp256k1},
29 local::PrivateKeySigner,
30 },
31 transports::TransportResult,
32};
33use alloy_node_bindings::{Anvil, AnvilInstance};
34use reqwest::Url;
35use serde_json::{json, value::RawValue};
36
37pub struct Evm {
39 rpc_url: Url,
40 anvil: AnvilInstance,
41 users: std::sync::Mutex<BTreeMap<Address, EvmUser>>,
43}
44
45impl Evm {
46 pub fn new() -> Self {
48 Evm::default()
49 }
50
51 pub fn rpc_url(&self) -> Url {
53 self.rpc_url.clone()
54 }
55
56 pub fn chain_id(&self) -> u64 {
58 self.anvil.chain_id()
59 }
60
61 pub fn test_user_count(&self) -> usize {
63 self.anvil.addresses().len()
64 }
65
66 pub fn key(&self, index: usize) -> SecretKey<Secp256k1> {
71 self.anvil.keys()[index].clone()
72 }
73
74 pub fn test_user(&self, index: usize) -> EvmUser {
76 if index >= self.test_user_count() {
77 panic!(
78 "Reached maximum number of test users: {}",
79 self.test_user_count()
80 );
81 }
82 self.user_from(
83 self.anvil.addresses()[index],
84 self.anvil.keys()[index].clone(),
85 )
86 }
87
88 pub fn user_from(&self, address: Address, key: SecretKey<Secp256k1>) -> EvmUser {
90 let mut users = self.users.lock().unwrap();
91 if let Some(user) = users.get(&address) {
92 return user.clone();
93 }
94 let signer: PrivateKeySigner = key.clone().into();
95 let provider = ProviderBuilder::new()
96 .wallet(EthereumWallet::from(signer))
97 .connect_http(self.rpc_url.clone());
98 let user = EvmUser {
99 address,
100 key,
101 provider: Arc::new(provider),
102 };
103 users.insert(user.address, user.clone());
104 user
105 }
106
107 pub fn default_user(&self) -> EvmUser {
109 self.test_user(0)
110 }
111
112 pub async fn transfer(&self, user: &EvmUser, to: Address, amount: U256) {
114 let tx = TransactionRequest::default().with_to(to).with_value(amount);
115 user.provider
116 .send_transaction(tx)
117 .await
118 .unwrap()
119 .get_receipt()
120 .await
121 .unwrap();
122 }
123
124 pub async fn get_balance(&self, addr: Address) -> U256 {
126 self.default_user()
127 .provider
128 .get_balance(addr)
129 .await
130 .unwrap()
131 }
132
133 pub async fn mine_block(&self) {
135 let response: serde_json::Value = self
136 .default_user()
137 .provider
138 .client()
139 .request("evm_mine", json!({}))
140 .await
141 .unwrap();
142 assert_eq!(response, "0x0");
143 }
144}
145
146impl Default for Evm {
147 fn default() -> Self {
149 let mut anvil = Anvil::new().keep_stdout().try_spawn().unwrap();
150 let anvil_stdout = anvil.child_mut().stdout.take();
151
152 tokio::spawn(async {
153 let mut buf = [0_u8; 4096];
154 let mut mv = anvil_stdout;
155 loop {
156 tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
157 match mv.as_mut().unwrap().read(&mut buf) {
158 Ok(len) => {
159 eprintln!(
160 "{}",
161 String::from_utf8(buf[0..len].to_vec()).unwrap_or_default()
162 );
163 }
164 Err(_) => return,
165 }
166 }
167 });
168
169 let anvil_url: Url = anvil.endpoint().parse().unwrap();
170 Self {
171 rpc_url: anvil_url,
172 anvil,
173 users: Mutex::new(BTreeMap::new()),
174 }
175 }
176}
177
178pub type EvmProvider = FillProvider<
180 JoinFill<
181 JoinFill<
182 Identity,
183 JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>,
184 >,
185 WalletFiller<EthereumWallet>,
186 >,
187 RootProvider,
188 Ethereum,
189>;
190
191#[derive(Clone)]
193pub struct EvmUser {
194 pub address: Address,
196 pub key: SecretKey<Secp256k1>,
198 pub provider: Arc<EvmProvider>,
200}
201
202#[async_trait::async_trait]
203impl Provider<Ethereum> for EvmUser {
204 fn root(&self) -> &RootProvider {
205 self.provider.root()
206 }
207
208 fn get_accounts(&self) -> ProviderCall<NoParams, Vec<Address>> {
209 self.provider.get_accounts()
210 }
211
212 fn get_blob_base_fee(&self) -> ProviderCall<NoParams, U128, u128> {
213 self.provider.get_blob_base_fee()
214 }
215
216 fn get_block_number(&self) -> ProviderCall<NoParams, U64, BlockNumber> {
217 self.provider.get_block_number()
218 }
219
220 fn call<'req>(
221 &self,
222 tx: <Ethereum as Network>::TransactionRequest,
223 ) -> EthCall<Ethereum, Bytes> {
224 self.provider.call(tx)
225 }
226
227 fn call_many<'req>(
228 &self,
229 bundles: &'req [Bundle],
230 ) -> EthCallMany<'req, Ethereum, Vec<Vec<EthCallResponse>>> {
231 self.provider.call_many(bundles)
232 }
233
234 fn simulate<'req>(
235 &self,
236 payload: &'req SimulatePayload,
237 ) -> RpcWithBlock<
238 &'req SimulatePayload,
239 Vec<SimulatedBlock<<Ethereum as Network>::BlockResponse>>,
240 > {
241 self.provider.simulate(payload)
242 }
243
244 fn get_chain_id(&self) -> ProviderCall<NoParams, U64, u64> {
245 self.provider.get_chain_id()
246 }
247
248 fn create_access_list<'a>(
249 &self,
250 request: &'a <Ethereum as Network>::TransactionRequest,
251 ) -> RpcWithBlock<&'a <Ethereum as Network>::TransactionRequest, AccessListResult> {
252 self.provider.create_access_list(request)
253 }
254
255 async fn send_raw_transaction(
256 &self,
257 encoded_tx: &[u8],
258 ) -> TransportResult<PendingTransactionBuilder<Ethereum>> {
259 self.provider.send_raw_transaction(encoded_tx).await
260 }
261
262 async fn send_raw_transaction_conditional(
263 &self,
264 encoded_tx: &[u8],
265 conditional: TransactionConditional,
266 ) -> TransportResult<PendingTransactionBuilder<Ethereum>> {
267 self.provider
268 .send_raw_transaction_conditional(encoded_tx, conditional)
269 .await
270 }
271
272 async fn send_transaction_internal(
273 &self,
274 tx: SendableTx<Ethereum>,
275 ) -> TransportResult<PendingTransactionBuilder<Ethereum>> {
276 self.provider.send_transaction_internal(tx).await
277 }
278
279 fn syncing(&self) -> ProviderCall<NoParams, SyncStatus> {
280 self.provider.syncing()
281 }
282
283 fn get_client_version(&self) -> ProviderCall<NoParams, String> {
284 self.provider.get_client_version()
285 }
286
287 fn get_sha3(&self, data: &[u8]) -> ProviderCall<(String,), B256> {
288 self.provider.get_sha3(data)
289 }
290
291 fn get_net_version(&self) -> ProviderCall<NoParams, U64, u64> {
292 self.provider.get_net_version()
293 }
294
295 async fn raw_request_dyn(
296 &self,
297 method: Cow<'static, str>,
298 params: &RawValue,
299 ) -> TransportResult<Box<RawValue>> {
300 self.provider.raw_request_dyn(method, params).await
301 }
302
303 fn transaction_request(&self) -> <Ethereum as Network>::TransactionRequest {
304 self.provider.transaction_request()
305 }
306}