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