Skip to content

Commit 9dbf916

Browse files
authored
feat(anvil): bypass sidecar requirement when impersonating (#10224)
1 parent 8937c09 commit 9dbf916

File tree

5 files changed

+104
-16
lines changed

5 files changed

+104
-16
lines changed

crates/anvil/core/src/eth/transaction/mod.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn transaction_request_to_typed(
8383
access_list.as_ref(),
8484
max_fee_per_blob_gas,
8585
blob_versioned_hashes.as_ref(),
86-
sidecar,
86+
sidecar.as_ref(),
8787
to,
8888
) {
8989
// legacy transaction
@@ -132,7 +132,7 @@ pub fn transaction_request_to_typed(
132132
}))
133133
}
134134
// EIP4844
135-
(Some(3), None, _, _, _, _, Some(_), Some(sidecar), to) => {
135+
(Some(3), None, _, _, _, _, Some(_), _, to) => {
136136
let tx = TxEip4844 {
137137
nonce: nonce.unwrap_or_default(),
138138
max_fee_per_gas: max_fee_per_gas.unwrap_or_default(),
@@ -149,9 +149,14 @@ pub fn transaction_request_to_typed(
149149
access_list: access_list.unwrap_or_default(),
150150
blob_versioned_hashes: blob_versioned_hashes.unwrap_or_default(),
151151
};
152-
Some(TypedTransactionRequest::EIP4844(TxEip4844Variant::TxEip4844WithSidecar(
153-
TxEip4844WithSidecar::from_tx_and_sidecar(tx, sidecar),
154-
)))
152+
153+
if let Some(sidecar) = sidecar {
154+
Some(TypedTransactionRequest::EIP4844(TxEip4844Variant::TxEip4844WithSidecar(
155+
TxEip4844WithSidecar::from_tx_and_sidecar(tx, sidecar),
156+
)))
157+
} else {
158+
Some(TypedTransactionRequest::EIP4844(TxEip4844Variant::TxEip4844(tx)))
159+
}
155160
}
156161
_ => None,
157162
}

crates/anvil/src/eth/api.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2884,6 +2884,7 @@ impl EthApi {
28842884
let gas_price = request.gas_price;
28852885

28862886
let gas_limit = request.gas.unwrap_or_else(|| self.backend.gas_limit());
2887+
let from = request.from;
28872888

28882889
let request = match transaction_request_to_typed(request) {
28892890
Some(TypedTransactionRequest::Legacy(mut m)) => {
@@ -2931,10 +2932,26 @@ impl EthApi {
29312932
}
29322933
TxEip4844Variant::TxEip4844WithSidecar(m)
29332934
}
2934-
// It is not valid to receive a TxEip4844 without a sidecar, therefore
2935-
// we must reject it.
2936-
TxEip4844Variant::TxEip4844(_) => {
2937-
return Err(BlockchainError::FailedToDecodeTransaction)
2935+
TxEip4844Variant::TxEip4844(mut tx) => {
2936+
if !self.backend.skip_blob_validation(from) {
2937+
return Err(BlockchainError::FailedToDecodeTransaction)
2938+
}
2939+
2940+
// Allows 4844 with no sidecar when impersonation is active.
2941+
tx.nonce = nonce;
2942+
tx.chain_id = chain_id;
2943+
tx.gas_limit = gas_limit;
2944+
if max_fee_per_gas.is_none() {
2945+
tx.max_fee_per_gas = self.gas_price();
2946+
}
2947+
if max_fee_per_blob_gas.is_none() {
2948+
tx.max_fee_per_blob_gas = self
2949+
.excess_blob_gas_and_price()
2950+
.unwrap_or_default()
2951+
.map_or(0, |g| g.blob_gasprice)
2952+
}
2953+
2954+
TxEip4844Variant::TxEip4844(tx)
29382955
}
29392956
})
29402957
}

crates/anvil/src/eth/backend/cheats.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,18 @@ impl CheatsManager {
4040

4141
/// Returns true if the `addr` is currently impersonated
4242
pub fn is_impersonated(&self, addr: Address) -> bool {
43-
if self.state.read().auto_impersonate_accounts {
43+
if self.auto_impersonate_accounts() {
4444
true
4545
} else {
4646
self.state.read().impersonated_accounts.contains(&addr)
4747
}
4848
}
4949

50+
/// Returns true is auto impersonation is enabled
51+
pub fn auto_impersonate_accounts(&self) -> bool {
52+
self.state.read().auto_impersonate_accounts
53+
}
54+
5055
/// Sets the auto impersonation flag which if set to true will make the `is_impersonated`
5156
/// function always return true
5257
pub fn set_auto_impersonate_account(&self, enabled: bool) {

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,13 @@ impl Backend {
623623
&self.cheats
624624
}
625625

626+
/// Whether to skip blob validation
627+
pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
628+
self.cheats().auto_impersonate_accounts() ||
629+
impersonator
630+
.is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
631+
}
632+
626633
/// Returns the `FeeManager` that manages fee/pricings
627634
pub fn fees(&self) -> &FeeManager {
628635
&self.fees
@@ -2825,9 +2832,11 @@ impl TransactionValidator for Backend {
28252832
return Err(InvalidTransactionError::TooManyBlobs(blob_count))
28262833
}
28272834

2828-
// Check for any blob validation errors
2829-
if let Err(err) = tx.validate(env.cfg.kzg_settings.get()) {
2830-
return Err(InvalidTransactionError::BlobTransactionValidationError(err))
2835+
// Check for any blob validation errors if not impersonating.
2836+
if !self.skip_blob_validation(Some(*pending.sender())) {
2837+
if let Err(err) = tx.validate(env.cfg.kzg_settings.get()) {
2838+
return Err(InvalidTransactionError::BlobTransactionValidationError(err))
2839+
}
28312840
}
28322841
}
28332842

crates/anvil/tests/it/eip4844.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use crate::utils::{http_provider, http_provider_with_signer};
22
use alloy_consensus::{SidecarBuilder, SimpleCoder, Transaction};
3-
use alloy_eips::eip4844::{BLOB_TX_MIN_BLOB_GASPRICE, DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK};
4-
use alloy_network::{EthereumWallet, TransactionBuilder, TransactionBuilder4844};
5-
use alloy_primitives::U256;
3+
use alloy_eips::{
4+
eip4844::{BLOB_TX_MIN_BLOB_GASPRICE, DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK},
5+
Typed2718,
6+
};
7+
use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder, TransactionBuilder4844};
8+
use alloy_primitives::{b256, Address, U256};
69
use alloy_provider::Provider;
710
use alloy_rpc_types::{BlockId, TransactionRequest};
811
use alloy_serde::WithOtherFields;
@@ -293,3 +296,52 @@ async fn can_correctly_estimate_blob_gas_with_recommended_fillers_with_signer()
293296
DATA_GAS_PER_BLOB
294297
);
295298
}
299+
300+
// <https://github.com/foundry-rs/foundry/issues/9924>
301+
#[tokio::test]
302+
async fn can_bypass_sidecar_requirement() {
303+
tracing_subscriber::fmt::init();
304+
let node_config = NodeConfig::test()
305+
.with_hardfork(Some(EthereumHardfork::Cancun.into()))
306+
.with_auto_impersonate(true);
307+
let (api, handle) = spawn(node_config).await;
308+
let provider = http_provider(&handle.http_endpoint());
309+
310+
let eip1559_est = provider.estimate_eip1559_fees().await.unwrap();
311+
let gas_price = provider.get_gas_price().await.unwrap();
312+
313+
let from = Address::random();
314+
let to = Address::random();
315+
316+
api.anvil_set_balance(from, U256::from(60262144030131080_u128)).await.unwrap();
317+
318+
let tx = TransactionRequest {
319+
from: Some(from),
320+
to: Some(alloy_primitives::TxKind::Call(to)),
321+
nonce: Some(0),
322+
value: Some(U256::from(0)),
323+
max_fee_per_blob_gas: Some(gas_price + 1),
324+
max_fee_per_gas: Some(eip1559_est.max_fee_per_gas),
325+
max_priority_fee_per_gas: Some(eip1559_est.max_priority_fee_per_gas),
326+
blob_versioned_hashes: Some(vec![b256!(
327+
"0x01d5446006b21888d0267829344ab8624fdf1b425445a8ae1ca831bf1b8fbcd4"
328+
)]),
329+
sidecar: None,
330+
transaction_type: Some(3),
331+
..Default::default()
332+
};
333+
334+
let receipt = provider
335+
.send_transaction(WithOtherFields::new(tx))
336+
.await
337+
.unwrap()
338+
.get_receipt()
339+
.await
340+
.unwrap();
341+
342+
assert!(receipt.status());
343+
344+
let tx = provider.get_transaction_by_hash(receipt.transaction_hash).await.unwrap().unwrap();
345+
346+
assert_eq!(tx.inner.ty(), 3);
347+
}

0 commit comments

Comments
 (0)