Skip to content
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
2 changes: 1 addition & 1 deletion crates/cheatcodes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ alloy-signer-local = { workspace = true, features = [
"keystore",
] }
parking_lot.workspace = true
alloy-consensus = { workspace = true, features = ["k256"] }
alloy-consensus = { workspace = true, features = ["k256", "kzg"] }
alloy-network.workspace = true
alloy-rlp.workspace = true
alloy-chains.workspace = true
Expand Down
20 changes: 20 additions & 0 deletions crates/cheatcodes/assets/cheatcodes.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/cheatcodes/spec/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2236,6 +2236,10 @@ interface Vm {
#[cheatcode(group = Scripting)]
function signAndAttachDelegation(address implementation, uint256 privateKey, uint64 nonce) external returns (SignedDelegation memory signedDelegation);

/// Attach an EIP-4844 blob to the next call
#[cheatcode(group = Scripting)]
function attachBlob(bytes calldata blob) external;

/// Returns addresses of available unlocked wallets in the script environment.
#[cheatcode(group = Scripting)]
function getWallets() external returns (address[] memory wallets);
Expand Down
34 changes: 30 additions & 4 deletions crates/cheatcodes/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use crate::{
CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
Vm::{self, AccountAccess},
};
use alloy_consensus::BlobTransactionSidecar;
use alloy_network::TransactionBuilder4844;
use alloy_primitives::{
hex,
map::{AddressHashMap, HashMap, HashSet},
Expand Down Expand Up @@ -393,6 +395,9 @@ pub struct Cheatcodes {
/// transaction construction.
pub active_delegation: Option<SignedAuthorization>,

/// The active EIP-4844 blob that will be attached to the next call.
pub active_blob_sidecar: Option<BlobTransactionSidecar>,

/// The gas price.
///
/// Used in the cheatcode handler to overwrite the gas price separately from the gas price
Expand Down Expand Up @@ -526,6 +531,7 @@ impl Cheatcodes {
config,
block: Default::default(),
active_delegation: Default::default(),
active_blob_sidecar: Default::default(),
gas_price: Default::default(),
pranks: Default::default(),
expected_revert: Default::default(),
Expand Down Expand Up @@ -1127,10 +1133,30 @@ where {
..Default::default()
};

if let Some(auth_list) = self.active_delegation.take() {
tx_req.authorization_list = Some(vec![auth_list]);
} else {
tx_req.authorization_list = None;
match (self.active_delegation.take(), self.active_blob_sidecar.take()) {
(Some(_), Some(_)) => {
let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
return Some(CallOutcome {
result: InterpreterResult {
result: InstructionResult::Revert,
output: Error::encode(msg),
gas,
},
memory_offset: call.return_memory_offset.clone(),
});
}
(Some(auth_list), None) => {
tx_req.authorization_list = Some(vec![auth_list]);
tx_req.sidecar = None;
}
(None, Some(blob_sidecar)) => {
tx_req.set_blob_sidecar(blob_sidecar);
tx_req.authorization_list = None;
}
(None, None) => {
tx_req.sidecar = None;
tx_req.authorization_list = None;
}
}

self.broadcastable_transactions.push_back(BroadcastableTransaction {
Expand Down
18 changes: 17 additions & 1 deletion crates/cheatcodes/src/script.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! Implementations of [`Scripting`](spec::Group::Scripting) cheatcodes.

use crate::{Cheatcode, CheatsCtxt, Result, Vm::*};
use alloy_consensus::{SidecarBuilder, SimpleCoder};
use alloy_primitives::{Address, Uint, B256, U256};
use alloy_rpc_types::Authorization;
use alloy_signer::SignerSync;
use alloy_signer_local::PrivateKeySigner;
use alloy_sol_types::SolValue;
use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner};
use parking_lot::Mutex;
use revm::primitives::{Bytecode, SignedAuthorization};
use revm::primitives::{Bytecode, SignedAuthorization, SpecId};
use std::sync::Arc;

impl Cheatcode for broadcast_0Call {
Expand Down Expand Up @@ -133,6 +134,21 @@ fn write_delegation(ccx: &mut CheatsCtxt, auth: SignedAuthorization) -> Result<(
Ok(())
}

impl Cheatcode for attachBlobCall {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { blob } = self;
ensure!(
ccx.ecx.spec_id() >= SpecId::CANCUN,
"`attachBlob` is not supported before the Cancun hard fork; \
see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844"
);
let sidecar: SidecarBuilder<SimpleCoder> = SidecarBuilder::from_slice(blob);
let sidecar = sidecar.build().map_err(|e| format!("{e}"))?;
ccx.state.active_blob_sidecar = Some(sidecar);
Ok(Default::default())
}
}

impl Cheatcode for startBroadcast_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self {} = self;
Expand Down
1 change: 1 addition & 0 deletions testdata/cheats/Vm.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions testdata/default/cheats/AttachBlob.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.25;

import "ds-test/test.sol";
import "cheats/Vm.sol";

contract Counter {
uint256 public counter;

function increment() public {
counter++;
}
}

contract AttachBlobTest is DSTest {
Vm constant vm = Vm(HEVM_ADDRESS);
uint256 bobPk = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;
address bob = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8;

Counter public counter;

function setUp() public {
counter = new Counter();
}

function testAttachBlob() public {
bytes memory blob = abi.encode("Blob the Builder");
vm.attachBlob(blob);

vm.broadcast(bobPk);
counter.increment();
}

function testAttachBlobWithCreateTx() public {
bytes memory blob = abi.encode("Blob the Builder");
vm.attachBlob(blob);

vm.broadcast(bobPk);
new Counter();

// blob is attached with this tx instead
vm.broadcast(bobPk);
counter.increment();
}

/// forge-config: default.allow_internal_expect_revert = true
function testRevertAttachBlobWithDelegation() public {
bytes memory blob = abi.encode("Blob the Builder");
vm.attachBlob(blob);
vm.signAndAttachDelegation(address(0), bobPk);

vm.broadcast(bobPk);
vm.expectRevert("both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible");
counter.increment();
}
}
Loading