Skip to content

Commit 872e2f3

Browse files
authored
fix(anvil): reset from fork to another fork (#8768)
fix(anvil): reset from fork to another fork
1 parent d8f6631 commit 872e2f3

File tree

3 files changed

+72
-27
lines changed

3 files changed

+72
-27
lines changed

crates/anvil/src/config.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,6 @@ impl NodeConfig {
10751075
let provider = Arc::new(
10761076
ProviderBuilder::new(&eth_rpc_url)
10771077
.timeout(self.fork_request_timeout)
1078-
// .timeout_retry(self.fork_request_retries)
10791078
.initial_backoff(self.fork_retry_backoff.as_millis() as u64)
10801079
.compute_units_per_second(self.compute_units_per_second)
10811080
.max_retry(self.fork_request_retries)

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::{
3030
storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome},
3131
},
3232
revm::{db::DatabaseRef, primitives::AccountInfo},
33-
NodeConfig, PrecompileFactory,
33+
ForkChoice, NodeConfig, PrecompileFactory,
3434
};
3535
use alloy_consensus::{Account, Header, Receipt, ReceiptWithBloom};
3636
use alloy_eips::eip4844::MAX_BLOBS_PER_BLOCK;
@@ -427,37 +427,51 @@ impl Backend {
427427
.ok_or(BlockchainError::BlockNotFound)?;
428428
// update all settings related to the forked block
429429
{
430-
let mut env = self.env.write();
431-
env.cfg.chain_id = fork.chain_id();
432-
433-
env.block = BlockEnv {
434-
number: U256::from(fork_block_number),
435-
timestamp: U256::from(fork_block.header.timestamp),
436-
gas_limit: U256::from(fork_block.header.gas_limit),
437-
difficulty: fork_block.header.difficulty,
438-
prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()),
439-
// Keep previous `coinbase` and `basefee` value
440-
coinbase: env.block.coinbase,
441-
basefee: env.block.basefee,
442-
..env.block.clone()
443-
};
430+
if let Some(fork_url) = forking.json_rpc_url {
431+
// Set the fork block number
432+
let mut node_config = self.node_config.write().await;
433+
node_config.fork_choice = Some(ForkChoice::Block(fork_block_number));
444434

445-
self.time.reset(env.block.timestamp.to::<u64>());
435+
let mut env = self.env.read().clone();
436+
let (forked_db, client_fork_config) =
437+
node_config.setup_fork_db_config(fork_url, &mut env, &self.fees).await;
446438

447-
// this is the base fee of the current block, but we need the base fee of
448-
// the next block
449-
let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
450-
fork_block.header.gas_used,
451-
fork_block.header.gas_limit,
452-
fork_block.header.base_fee_per_gas.unwrap_or_default(),
453-
);
439+
*self.db.write().await = Box::new(forked_db);
440+
let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db));
441+
*self.fork.write() = Some(fork);
442+
*self.env.write() = env;
443+
} else {
444+
let mut env = self.env.write();
445+
env.cfg.chain_id = fork.chain_id();
446+
env.block = BlockEnv {
447+
number: U256::from(fork_block_number),
448+
timestamp: U256::from(fork_block.header.timestamp),
449+
gas_limit: U256::from(fork_block.header.gas_limit),
450+
difficulty: fork_block.header.difficulty,
451+
prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()),
452+
// Keep previous `coinbase` and `basefee` value
453+
coinbase: env.block.coinbase,
454+
basefee: env.block.basefee,
455+
..env.block.clone()
456+
};
454457

455-
self.fees.set_base_fee(next_block_base_fee);
458+
// this is the base fee of the current block, but we need the base fee of
459+
// the next block
460+
let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
461+
fork_block.header.gas_used,
462+
fork_block.header.gas_limit,
463+
fork_block.header.base_fee_per_gas.unwrap_or_default(),
464+
);
465+
466+
self.fees.set_base_fee(next_block_base_fee);
467+
}
468+
469+
// reset the time to the timestamp of the forked block
470+
self.time.reset(fork_block.header.timestamp);
456471

457472
// also reset the total difficulty
458473
self.blockchain.storage.write().total_difficulty = fork.total_difficulty();
459474
}
460-
461475
// reset storage
462476
*self.blockchain.storage.write() = BlockchainStorage::forked(
463477
fork.block_number(),

crates/anvil/tests/it/fork.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
abi::{Greeter, ERC721},
55
utils::{http_provider, http_provider_with_signer},
66
};
7+
use alloy_chains::NamedChain;
78
use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder};
89
use alloy_primitives::{address, b256, bytes, Address, Bytes, TxHash, TxKind, U256};
910
use alloy_provider::Provider;
@@ -17,7 +18,7 @@ use alloy_signer_local::PrivateKeySigner;
1718
use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle};
1819
use foundry_common::provider::get_http_provider;
1920
use foundry_config::Config;
20-
use foundry_test_utils::rpc::{self, next_http_rpc_endpoint};
21+
use foundry_test_utils::rpc::{self, next_http_rpc_endpoint, next_rpc_endpoint};
2122
use futures::StreamExt;
2223
use std::{sync::Arc, thread::sleep, time::Duration};
2324

@@ -476,6 +477,37 @@ async fn can_reset_properly() {
476477
assert!(fork_tx_provider.get_transaction_by_hash(tx.transaction_hash).await.unwrap().is_none())
477478
}
478479

480+
// Ref: <https://github.com/foundry-rs/foundry/issues/8684>
481+
#[tokio::test(flavor = "multi_thread")]
482+
async fn can_reset_fork_to_new_fork() {
483+
let eth_rpc_url = next_rpc_endpoint(NamedChain::Mainnet);
484+
let (api, handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(eth_rpc_url))).await;
485+
let provider = handle.http_provider();
486+
487+
let op = address!("C0d3c0d3c0D3c0D3C0d3C0D3C0D3c0d3c0d30007"); // L2CrossDomainMessenger - Dead on mainnet.
488+
489+
let tx = TransactionRequest::default().with_to(op).with_input("0x54fd4d50");
490+
491+
let tx = WithOtherFields::new(tx);
492+
493+
let mainnet_call_output = provider.call(&tx).await.unwrap();
494+
495+
assert_eq!(mainnet_call_output, Bytes::new()); // 0x
496+
497+
let optimism = next_rpc_endpoint(NamedChain::Optimism);
498+
499+
api.anvil_reset(Some(Forking {
500+
json_rpc_url: Some(optimism.to_string()),
501+
block_number: Some(124659890),
502+
}))
503+
.await
504+
.unwrap();
505+
506+
let code = provider.get_code_at(op).await.unwrap();
507+
508+
assert_ne!(code, Bytes::new());
509+
}
510+
479511
#[tokio::test(flavor = "multi_thread")]
480512
async fn test_fork_timestamp() {
481513
let start = std::time::Instant::now();

0 commit comments

Comments
 (0)