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