1use crate::{
2 Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, DatabaseExt, Result, Vm::*,
3 json::json_value_to_token,
4};
5use alloy_dyn_abi::DynSolValue;
6use alloy_evm::EvmEnv;
7use alloy_network::AnyNetwork;
8use alloy_primitives::{Address, B256, U256, map::AddressHashMap};
9use alloy_provider::{Provider, RootProvider};
10use alloy_rpc_types::Filter;
11use alloy_sol_types::SolValue;
12use foundry_common::provider::ProviderBuilder;
13use foundry_evm_core::{
14 FoundryContextExt,
15 backend::{ContextUpdateFor, ForkAccountField, JournaledState, LocalForkId},
16 evm::{BlockEnvFor, EvmFactoryFor, FoundryContextFor, FoundryEvmNetwork, SpecFor, TxEnvFor},
17 fork::CreateFork,
18};
19use revm::context::ContextTr;
20
21impl Cheatcode for activeForkCall {
22 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
23 let Self {} = self;
24 ccx.active_fork_id().map(|id| id.abi_encode()).ok_or_else(|| fmt_err!("no active fork"))
25 }
26}
27
28impl Cheatcode for createFork_0Call {
29 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
30 let Self { urlOrAlias } = self;
31 create_fork(ccx, urlOrAlias, None)
32 }
33}
34
35impl Cheatcode for createFork_1Call {
36 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
37 let Self { urlOrAlias, blockNumber } = self;
38 create_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to()))
39 }
40}
41
42impl Cheatcode for createFork_2Call {
43 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
44 let Self { urlOrAlias, txHash } = self;
45 create_fork_at_transaction(ccx, urlOrAlias, txHash)
46 }
47}
48
49impl Cheatcode for createSelectFork_0Call {
50 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
51 let Self { urlOrAlias } = self;
52 create_select_fork(ccx, urlOrAlias, None)
53 }
54}
55
56impl Cheatcode for createSelectFork_1Call {
57 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
58 let Self { urlOrAlias, blockNumber } = self;
59 create_select_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to()))
60 }
61}
62
63impl Cheatcode for createSelectFork_2Call {
64 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
65 let Self { urlOrAlias, txHash } = self;
66 create_select_fork_at_transaction(ccx, urlOrAlias, txHash)
67 }
68}
69
70impl Cheatcode for rollFork_0Call {
71 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
72 let Self { blockNumber } = self;
73 persist_caller(ccx);
74 let result = fork_env_op::<FEN, _>(ccx.ecx, |db, evm_env, tx_env, inner| {
75 db.roll_fork(None, (*blockNumber).to(), evm_env, tx_env, inner)
76 .map(|context| ((), context))
77 })?;
78 record_fork_roll(ccx, None);
79 Ok(result)
80 }
81}
82
83impl Cheatcode for rollFork_1Call {
84 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
85 let Self { txHash } = self;
86 persist_caller(ccx);
87 let result = fork_env_op::<FEN, _>(ccx.ecx, |db, evm_env, tx_env, inner| {
88 db.roll_fork_to_transaction(None, *txHash, evm_env, tx_env, inner)
89 .map(|context| ((), context))
90 })?;
91 record_fork_roll(ccx, None);
92 Ok(result)
93 }
94}
95
96impl Cheatcode for rollFork_2Call {
97 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
98 let Self { forkId, blockNumber } = self;
99 persist_caller(ccx);
100 let result = fork_env_op::<FEN, _>(ccx.ecx, |db, evm_env, tx_env, inner| {
101 db.roll_fork(Some(*forkId), (*blockNumber).to(), evm_env, tx_env, inner)
102 .map(|context| ((), context))
103 })?;
104 record_fork_roll(ccx, Some(*forkId));
105 Ok(result)
106 }
107}
108
109impl Cheatcode for rollFork_3Call {
110 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
111 let Self { forkId, txHash } = self;
112 persist_caller(ccx);
113 let result = fork_env_op::<FEN, _>(ccx.ecx, |db, evm_env, tx_env, inner| {
114 db.roll_fork_to_transaction(Some(*forkId), *txHash, evm_env, tx_env, inner)
115 .map(|context| ((), context))
116 })?;
117 record_fork_roll(ccx, Some(*forkId));
118 Ok(result)
119 }
120}
121
122impl Cheatcode for selectForkCall {
123 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
124 let Self { forkId } = self;
125 persist_caller(ccx);
126 check_broadcast(ccx.state)?;
127 let source_fork_id = ccx.active_fork_id();
128 let initial = ccx.state.created_account_bindings(None);
129 let propagated = persistent_created_accounts(ccx);
130 let result = fork_env_op::<FEN, _>(ccx.ecx, |db, evm_env, tx_env, inner| {
131 db.select_fork(*forkId, evm_env, tx_env, inner).map(|context| ((), context))
132 })?;
133 record_fork_switch(ccx, source_fork_id, initial, propagated);
134 Ok(result)
135 }
136}
137
138impl Cheatcode for transact_0Call {
139 fn apply_full<FEN: FoundryEvmNetwork>(
140 &self,
141 ccx: &mut CheatsCtxt<'_, '_, FEN>,
142 executor: &mut dyn CheatcodesExecutor<FEN>,
143 ) -> Result {
144 let Self { txHash } = *self;
145 transact(ccx, executor, txHash, None)
146 }
147}
148
149impl Cheatcode for transact_1Call {
150 fn apply_full<FEN: FoundryEvmNetwork>(
151 &self,
152 ccx: &mut CheatsCtxt<'_, '_, FEN>,
153 executor: &mut dyn CheatcodesExecutor<FEN>,
154 ) -> Result {
155 let Self { forkId, txHash } = *self;
156 transact(ccx, executor, txHash, Some(forkId))
157 }
158}
159
160impl Cheatcode for allowCheatcodesCall {
161 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
162 let Self { account } = self;
163 ccx.ecx.db_mut().allow_cheatcode_access(*account);
164 Ok(Default::default())
165 }
166}
167
168impl Cheatcode for makePersistent_0Call {
169 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
170 let Self { account } = self;
171 ccx.ecx.db_mut().add_persistent_account(*account);
172 Ok(Default::default())
173 }
174}
175
176impl Cheatcode for makePersistent_1Call {
177 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
178 let Self { account0, account1 } = self;
179 ccx.ecx.db_mut().add_persistent_account(*account0);
180 ccx.ecx.db_mut().add_persistent_account(*account1);
181 Ok(Default::default())
182 }
183}
184
185impl Cheatcode for makePersistent_2Call {
186 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
187 let Self { account0, account1, account2 } = self;
188 ccx.ecx.db_mut().add_persistent_account(*account0);
189 ccx.ecx.db_mut().add_persistent_account(*account1);
190 ccx.ecx.db_mut().add_persistent_account(*account2);
191 Ok(Default::default())
192 }
193}
194
195impl Cheatcode for makePersistent_3Call {
196 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
197 let Self { accounts } = self;
198 for account in accounts {
199 ccx.ecx.db_mut().add_persistent_account(*account);
200 }
201 Ok(Default::default())
202 }
203}
204
205impl Cheatcode for revokePersistent_0Call {
206 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
207 let Self { account } = self;
208 ccx.ecx.db_mut().remove_persistent_account(account);
209 Ok(Default::default())
210 }
211}
212
213impl Cheatcode for revokePersistent_1Call {
214 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
215 let Self { accounts } = self;
216 for account in accounts {
217 ccx.ecx.db_mut().remove_persistent_account(account);
218 }
219 Ok(Default::default())
220 }
221}
222
223impl Cheatcode for isPersistentCall {
224 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
225 let Self { account } = self;
226 Ok(ccx.is_persistent(account).abi_encode())
227 }
228}
229
230impl Cheatcode for rpc_0Call {
231 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
232 let Self { method, params } = self;
233 let url = ccx.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?;
234 let result = rpc_call(&url, method, params)?;
235 refresh_active_fork_state(ccx, method, params)?;
236 Ok(result)
237 }
238}
239
240impl Cheatcode for rpc_1Call {
241 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
242 let Self { urlOrAlias, method, params } = self;
243 let url = state.config.rpc_endpoint(urlOrAlias)?.url()?;
244 rpc_call(&url, method, params)
245 }
246}
247
248impl Cheatcode for rpcJson_0Call {
249 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
250 let Self { method, params } = self;
251 let url = ccx.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?;
252 let result = rpc_json_call(&url, method, params)?;
253 refresh_active_fork_state(ccx, method, params)?;
254 Ok(result)
255 }
256}
257
258impl Cheatcode for rpcJson_1Call {
259 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
260 let Self { urlOrAlias, method, params } = self;
261 let url = state.config.rpc_endpoint(urlOrAlias)?.url()?;
262 rpc_json_call(&url, method, params)
263 }
264}
265
266impl Cheatcode for eth_getLogsCall {
267 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
268 let Self { fromBlock, toBlock, target, topics } = self;
269 let (Ok(from_block), Ok(to_block)) = (u64::try_from(fromBlock), u64::try_from(toBlock))
270 else {
271 bail!("blocks in block range must be less than 2^64")
272 };
273
274 if topics.len() > 4 {
275 bail!("topics array must contain at most 4 elements")
276 }
277
278 let provider = active_fork_provider(ccx)?;
279 let mut filter = Filter::new().address(*target).from_block(from_block).to_block(to_block);
280 for (i, &topic) in topics.iter().enumerate() {
281 filter.topics[i] = topic.into();
282 }
283
284 let logs = foundry_common::block_on(provider.get_logs(&filter))
285 .map_err(|e| fmt_err!("failed to get logs: {e}"))?;
286
287 let eth_logs = logs
288 .into_iter()
289 .map(|log| EthGetLogs {
290 emitter: log.address(),
291 topics: log.topics().to_vec(),
292 data: log.inner.data.data,
293 blockHash: log.block_hash.unwrap_or_default(),
294 blockNumber: log.block_number.unwrap_or_default(),
295 transactionHash: log.transaction_hash.unwrap_or_default(),
296 transactionIndex: log.transaction_index.unwrap_or_default(),
297 logIndex: U256::from(log.log_index.unwrap_or_default()),
298 removed: log.removed,
299 })
300 .collect::<Vec<_>>();
301
302 Ok(eth_logs.abi_encode())
303 }
304}
305
306impl Cheatcode for eth_getProofCall {
307 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
308 let Self { target, slots, blockNumber } = self;
309 let block_number = u64::try_from(blockNumber)
310 .map_err(|_| fmt_err!("block number must be less than 2^64"))?;
311
312 let provider = active_fork_provider(ccx)?;
313 let proof = foundry_common::block_on(async move {
314 provider.get_proof(*target, slots.clone()).number(block_number).await
315 })
316 .map_err(|e| fmt_err!("failed to get proof: {e}"))?;
317
318 let storage_proof = proof
319 .storage_proof
320 .into_iter()
321 .map(|p| EthStorageProof { key: p.key.as_b256(), value: p.value, proof: p.proof })
322 .collect();
323
324 Ok(EthGetProof {
325 account: proof.address,
326 balance: proof.balance,
327 codeHash: proof.code_hash,
328 nonce: proof.nonce,
329 storageHash: proof.storage_hash,
330 accountProof: proof.account_proof,
331 storageProof: storage_proof,
332 }
333 .abi_encode())
334 }
335}
336
337impl Cheatcode for getRawBlockHeaderCall {
338 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
339 let Self { blockNumber } = self;
340 let provider = active_fork_provider(ccx)?;
341 let block_number = u64::try_from(blockNumber)
342 .map_err(|_| fmt_err!("block number must be less than 2^64"))?;
343 let block =
344 foundry_common::block_on(async move { provider.get_block(block_number.into()).await })
345 .map_err(|e| fmt_err!("failed to get block: {e}"))?
346 .ok_or_else(|| fmt_err!("block {block_number} not found"))?;
347
348 let header: alloy_consensus::Header = block
349 .into_inner()
350 .header
351 .inner
352 .try_into_header()
353 .map_err(|e| fmt_err!("failed to convert to header: {e}"))?;
354 Ok(alloy_rlp::encode(&header).abi_encode())
355 }
356}
357
358fn create_select_fork<FEN: FoundryEvmNetwork>(
360 ccx: &mut CheatsCtxt<'_, '_, FEN>,
361 url_or_alias: &str,
362 block: Option<u64>,
363) -> Result {
364 check_broadcast(ccx.state)?;
365
366 let fork = create_fork_request(ccx, url_or_alias, block)?;
367 let source_fork_id = ccx.active_fork_id();
368 let initial = ccx.state.created_account_bindings(None);
369 let propagated = persistent_created_accounts(ccx);
370 let result = fork_env_op::<FEN, _>(ccx.ecx, |db, evm_env, tx_env, inner| {
371 db.create_select_fork(fork, evm_env, tx_env, inner)
372 })?;
373 record_fork_switch(ccx, source_fork_id, initial, propagated);
374 Ok(result)
375}
376
377fn create_fork<FEN: FoundryEvmNetwork>(
379 ccx: &mut CheatsCtxt<'_, '_, FEN>,
380 url_or_alias: &str,
381 block: Option<u64>,
382) -> Result {
383 let fork = create_fork_request(ccx, url_or_alias, block)?;
384 let id = ccx.ecx.db_mut().create_fork(fork)?;
385 Ok(id.abi_encode())
386}
387
388fn create_select_fork_at_transaction<FEN: FoundryEvmNetwork>(
390 ccx: &mut CheatsCtxt<'_, '_, FEN>,
391 url_or_alias: &str,
392 transaction: &B256,
393) -> Result {
394 check_broadcast(ccx.state)?;
395
396 let fork = create_fork_request(ccx, url_or_alias, None)?;
397 let source_fork_id = ccx.active_fork_id();
398 let initial = ccx.state.created_account_bindings(None);
399 let propagated = persistent_created_accounts(ccx);
400 let result = fork_env_op::<FEN, _>(ccx.ecx, |db, evm_env, tx_env, inner| {
401 db.create_select_fork_at_transaction(fork, evm_env, tx_env, inner, *transaction)
402 })?;
403 record_fork_switch(ccx, source_fork_id, initial, propagated);
404 Ok(result)
405}
406
407fn create_fork_at_transaction<FEN: FoundryEvmNetwork>(
409 ccx: &mut CheatsCtxt<'_, '_, FEN>,
410 url_or_alias: &str,
411 transaction: &B256,
412) -> Result {
413 let fork = create_fork_request(ccx, url_or_alias, None)?;
414 let id = ccx.ecx.db_mut().create_fork_at_transaction(fork, *transaction)?;
415 Ok(id.abi_encode())
416}
417
418fn create_fork_request<FEN: FoundryEvmNetwork>(
420 ccx: &mut CheatsCtxt<'_, '_, FEN>,
421 url_or_alias: &str,
422 block: Option<u64>,
423) -> Result<CreateFork> {
424 persist_caller(ccx);
425
426 let rpc_endpoint = ccx.state.config.rpc_endpoint(url_or_alias)?;
427 let url = rpc_endpoint.url()?;
428 let mut evm_opts = ccx.state.config.evm_opts.clone();
429 evm_opts.fork_block_number = block;
430 evm_opts.fork_block_number_is_inferred = false;
431 evm_opts.fork_retries = rpc_endpoint.config.retries;
432 evm_opts.fork_retry_backoff = rpc_endpoint.config.retry_backoff;
433 if let Some(Ok(auth)) = rpc_endpoint.auth {
434 let fork_headers = Some(vec![format!("Authorization: {auth}")]);
435 if evm_opts.fork_headers != fork_headers {
438 evm_opts.fork_endpoint = None;
439 }
440 evm_opts.fork_headers = fork_headers;
441 }
442 let fork = CreateFork {
443 enable_caching: !ccx.state.config.no_storage_caching
444 && ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url),
445 url,
446 evm_opts,
447 };
448 Ok(fork)
449}
450
451#[cfg(feature = "monad")]
453fn apply_context_update<FEN: FoundryEvmNetwork>(
454 ecx: &mut FoundryContextFor<'_, FEN>,
455 context_update: foundry_evm_core::backend::ContextUpdate<foundry_evm_core::evm::ChainFor<FEN>>,
456) {
457 match context_update {
458 foundry_evm_core::backend::ContextUpdate::Unchanged => {}
459 foundry_evm_core::backend::ContextUpdate::Replace(chain_context) => {
460 *ecx.chain_mut() = chain_context;
461 foundry_evm_core::evm::refresh_chain_journal(ecx);
462 }
463 foundry_evm_core::backend::ContextUpdate::Rebase => {
464 foundry_evm_core::evm::refresh_chain_journal(ecx);
465 }
466 }
467}
468
469fn fork_env_op<FEN: FoundryEvmNetwork, T: SolValue>(
473 ecx: &mut FoundryContextFor<'_, FEN>,
474 f: impl FnOnce(
475 &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
476 &mut EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
477 &mut TxEnvFor<FEN>,
478 &mut JournaledState,
479 ) -> eyre::Result<(T, ContextUpdateFor<EvmFactoryFor<FEN>>)>,
480) -> Result {
481 let mut evm_env = ecx.evm_clone();
482 let mut tx_env = ecx.tx_clone();
483 let (db, inner) = ecx.db_journal_inner_mut();
484 let (result, context_update) = f(db, &mut evm_env, &mut tx_env, inner)?;
485 ecx.set_evm(evm_env);
486 ecx.set_tx(tx_env);
487 #[cfg(not(feature = "monad"))]
488 let _ = context_update;
489 #[cfg(feature = "monad")]
490 apply_context_update::<FEN>(ecx, context_update);
491 Ok(result.abi_encode())
492}
493
494fn persistent_created_accounts<FEN: FoundryEvmNetwork>(
495 ccx: &CheatsCtxt<'_, '_, FEN>,
496) -> Vec<(Address, usize)> {
497 let fork_id = ccx.active_fork_id();
498 ccx.state
499 .created_account_bindings(fork_id)
500 .into_iter()
501 .filter(|(address, _)| ccx.is_persistent(address))
502 .collect()
503}
504
505fn record_fork_switch<FEN: FoundryEvmNetwork>(
506 ccx: &mut CheatsCtxt<'_, '_, FEN>,
507 source_fork_id: Option<LocalForkId>,
508 initial: AddressHashMap<usize>,
509 propagated: Vec<(Address, usize)>,
510) {
511 let target_fork_id = ccx.active_fork_id();
512 ccx.state.fork_block_number_override = ccx.active_fork_block_number();
513 if source_fork_id != target_fork_id {
514 ccx.state.commit_created_account_changes(source_fork_id);
515 }
516 ccx.state.record_initial_created_accounts(target_fork_id, initial);
517 ccx.state.record_propagated_accounts(target_fork_id, propagated);
518}
519
520fn record_fork_roll<FEN: FoundryEvmNetwork>(
521 ccx: &mut CheatsCtxt<'_, '_, FEN>,
522 target_fork_id: Option<LocalForkId>,
523) {
524 let active_fork_id = ccx.active_fork_id();
525 let rolled_fork_id = target_fork_id.or(active_fork_id);
526 ccx.state.env_overrides.clear_implicit_basefee(rolled_fork_id);
527 if target_fork_id.is_none() || target_fork_id == active_fork_id {
528 ccx.state.fork_block_number_override = ccx.active_fork_block_number();
529 ccx.state.commit_created_account_changes(active_fork_id);
530 }
531}
532
533fn check_broadcast<FEN: FoundryEvmNetwork>(state: &Cheatcodes<FEN>) -> Result<()> {
534 if state.broadcast.is_none() {
535 Ok(())
536 } else {
537 Err(fmt_err!("cannot select forks during a broadcast"))
538 }
539}
540
541fn transact<FEN: FoundryEvmNetwork>(
542 ccx: &mut CheatsCtxt<'_, '_, FEN>,
543 executor: &mut dyn CheatcodesExecutor<FEN>,
544 transaction: B256,
545 fork_id: Option<U256>,
546) -> Result {
547 let context_update = executor.transact_on_db(ccx.state, ccx.ecx, fork_id, transaction)?;
548 #[cfg(not(feature = "monad"))]
549 let _ = context_update;
550 #[cfg(feature = "monad")]
551 apply_context_update::<FEN>(ccx.ecx, context_update);
552 Ok(Default::default())
553}
554
555fn persist_caller<FEN: FoundryEvmNetwork>(ccx: &mut CheatsCtxt<'_, '_, FEN>) {
560 ccx.ecx.db_mut().add_persistent_account(ccx.caller);
561}
562
563fn active_fork_provider<FEN: FoundryEvmNetwork>(
566 ccx: &CheatsCtxt<'_, '_, FEN>,
567) -> Result<RootProvider<AnyNetwork>> {
568 let fork = ccx.active_fork_options().ok_or_else(|| fmt_err!("no active fork URL found"))?;
569 Ok(fork.evm_opts.fork_provider_with_url(&fork.url)?)
570}
571
572fn rpc_call(url: &str, method: &str, params: &str) -> Result {
574 let result = rpc_result(url, method, params)?;
575 let result_as_tokens = convert_to_bytes(
576 &json_value_to_token(&result, None)
577 .map_err(|err| fmt_err!("failed to parse result: {err}"))?,
578 );
579
580 let payload = match &result_as_tokens {
581 DynSolValue::Bytes(b) => b.clone(),
582 _ => result_as_tokens.abi_encode(),
583 };
584 Ok(DynSolValue::Bytes(payload).abi_encode())
585}
586
587fn rpc_json_call(url: &str, method: &str, params: &str) -> Result {
589 let result = rpc_result(url, method, params)?;
590 Ok(serde_json::to_string(&result)?.abi_encode())
591}
592
593fn refresh_active_fork_state<FEN: FoundryEvmNetwork>(
597 ccx: &mut CheatsCtxt<'_, '_, FEN>,
598 method: &str,
599 params: &str,
600) -> Result<()> {
601 let account_field = match method {
602 "anvil_setBalance"
603 | "hardhat_setBalance"
604 | "tenderly_setBalance"
605 | "anvil_addBalance"
606 | "hardhat_addBalance"
607 | "tenderly_addBalance" => Some(ForkAccountField::Balance),
608 "anvil_setNonce" | "hardhat_setNonce" | "evm_setAccountNonce" => {
609 Some(ForkAccountField::Nonce)
610 }
611 "anvil_setCode" | "hardhat_setCode" => Some(ForkAccountField::Code),
612 "anvil_setStorageAt" | "hardhat_setStorageAt" => None,
613 _ => return Ok(()),
614 };
615
616 let Ok(params) = serde_json::from_str::<serde_json::Value>(params) else { return Ok(()) };
617 let Some(address) =
618 params.get(0).and_then(|v| v.as_str()).and_then(|s| s.parse::<Address>().ok())
619 else {
620 return Ok(());
621 };
622
623 let (db, journaled_state) = ccx.ecx.db_journal_inner_mut();
624 if let Some(field) = account_field {
625 db.refresh_fork_account(address, field, journaled_state)?;
626 } else {
627 let Some(slot) =
628 params.get(1).and_then(|v| v.as_str()).and_then(|s| s.parse::<U256>().ok())
629 else {
630 return Ok(());
631 };
632 db.refresh_fork_storage(address, slot, journaled_state)?;
633 }
634 Ok(())
635}
636
637fn rpc_result(url: &str, method: &str, params: &str) -> Result<serde_json::Value> {
638 let provider = ProviderBuilder::<AnyNetwork>::new(url).build()?;
639 let params_json: serde_json::Value = serde_json::from_str(params)?;
640 foundry_common::block_on(provider.raw_request(method.to_string().into(), params_json))
641 .map_err(|err| fmt_err!("{method:?}: {err}"))
642}
643
644fn convert_to_bytes(token: &DynSolValue) -> DynSolValue {
646 match token {
647 DynSolValue::FixedBytes(bytes, size) => {
650 DynSolValue::Bytes(bytes.as_slice()[..*size].to_vec())
651 }
652 DynSolValue::Address(addr) => DynSolValue::Bytes(addr.to_vec()),
653 val => val.clone(),
654 }
655}