Skip to content

fix(anvil): recomputing next-base-fee after reloading state #10488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,26 @@ impl Backend {
}
}

if let Some(block) = state.blocks.last() {
let header = &block.header;
let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
header.gas_used as u128,
header.gas_limit as u128,
header.base_fee_per_gas.unwrap_or_default(),
);
let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
header.excess_blob_gas.map(|g| g as u128).unwrap_or_default(),
header.blob_gas_used.map(|g| g as u128).unwrap_or_default(),
);

// update next base fee
self.fees.set_base_fee(next_block_base_fee);
self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
next_block_excess_blob_gas,
false,
));
}

if !self.db.write().await.load_state(state.clone())? {
return Err(RpcError::invalid_params(
"Loading state not supported with the current configuration",
Expand Down
32 changes: 32 additions & 0 deletions crates/anvil/tests/it/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,35 @@ async fn test_fork_load_state_with_greater_state_block() {

assert_eq!(new_block_number, block_number);
}

// <https://github.com/foundry-rs/foundry/issues/10488>
#[tokio::test(flavor = "multi_thread")]
async fn computes_next_base_fee_after_loading_state() {
let tmp = tempfile::tempdir().unwrap();
let state_file = tmp.path().join("state.json");

let (api, handle) = spawn(NodeConfig::test()).await;

let bob = address!("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
let alice = address!("0x9276449EaC5b4f7Bc17cFC6700f7BeeB86F9bCd0");

let provider = handle.http_provider();

let value = Unit::ETHER.wei().saturating_mul(U256::from(1)); // 1 ether
let tx = TransactionRequest::default().with_to(alice).with_value(value).with_from(bob);
let tx = WithOtherFields::new(tx);

let base_fee_empty_chain = api.backend.fees().base_fee();
provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();

let base_fee_after_one_tx = api.backend.fees().base_fee();
// the test is meaningless if this does not hold
assert!(base_fee_empty_chain != base_fee_after_one_tx);

let ser_state = api.serialized_state(true).await.unwrap();
foundry_common::fs::write_json_file(&state_file, &ser_state).unwrap();

let (api, _handle) = spawn(NodeConfig::test().with_init_state_path(state_file)).await;
let base_fee_after_reload = api.backend.fees().base_fee();
assert_eq!(base_fee_after_reload, base_fee_after_one_tx);
}
Loading