Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
17 changes: 17 additions & 0 deletions crates/anvil-polkadot/src/api_server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ impl ApiServer {
EthRequest::SetStorageAt(address, key, value) => {
self.set_storage_at(address, key, value).to_rpc_result()
}
EthRequest::SetImmutableStorageAt(address, data) => {
self.set_immutable_storage_at(address, data).to_rpc_result()
}
EthRequest::SetChainId(chain_id) => self.set_chain_id(chain_id).to_rpc_result(),
// --- Revert ---
EthRequest::EvmSnapshot(()) => self.snapshot().await.to_rpc_result(),
Expand Down Expand Up @@ -1327,6 +1330,20 @@ impl ApiServer {
Ok(())
}

fn set_immutable_storage_at(
&self,
address: Address,
data: alloy_primitives::Bytes,
) -> Result<()> {
node_info!("anvil_setImmutableStorageAt");

let latest_block = self.latest_block();

self.backend.inject_immutable_data(latest_block, address, data.to_vec());

Ok(())
}

// ----- Wallet RPCs
async fn sign(&self, address: Address, content: impl AsRef<[u8]>) -> Result<String> {
Ok(alloy_primitives::hex::encode_prefixed(self.wallet.sign(address, content.as_ref())?))
Expand Down
15 changes: 15 additions & 0 deletions crates/anvil-polkadot/src/substrate_node/service/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ impl BackendWithOverlay {
overrides.set_code_info(at, code_hash, code_info);
}

pub fn inject_immutable_data(&self, at: Hash, address: Address, data: Vec<u8>) {
let mut overrides = self.overrides.lock();
overrides.set_immutable_data(at, address, data);
}

pub fn inject_child_storage(
&self,
at: Hash,
Expand Down Expand Up @@ -353,6 +358,16 @@ impl StorageOverrides {
self.add(latest_block, changeset);
}

fn set_immutable_data(&mut self, latest_block: Hash, address: Address, data: Vec<u8>) {
let mut changeset = BlockOverrides::default();
changeset.top.insert(
well_known_keys::immutable_data_of(H160::from_slice(address.as_slice())),
Some(data.encode()),
);

self.add(latest_block, changeset);
}

fn set_child_storage(
&mut self,
latest_block: Hash,
Expand Down
9 changes: 9 additions & 0 deletions crates/anvil-polkadot/src/substrate_node/service/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,13 @@ pub mod well_known_keys {

key
}

pub fn immutable_data_of(address: H160) -> Vec<u8> {
let mut key = Vec::new();
key.extend_from_slice(&twox_128("Revive".as_bytes()));
key.extend_from_slice(&twox_128("ImmutableDataOf".as_bytes()));
key.extend_from_slice(&address.encode());

key
}
}
74 changes: 74 additions & 0 deletions crates/anvil-polkadot/test-data/ImmutableStorage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"abi": [
{
"inputs": [
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
},
{
"internalType": "address",
"name": "_addr",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "getImmutableAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getImmutableValue",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "immutableAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "immutableValue",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bin": "60c060405234801561000f575f5ffd5b5060405161038b38038061038b83398181016040528101906100319190610105565b81608081815250508073ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250505050610143565b5f5ffd5b5f819050919050565b61008a81610078565b8114610094575f5ffd5b50565b5f815190506100a581610081565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100d4826100ab565b9050919050565b6100e4816100ca565b81146100ee575f5ffd5b50565b5f815190506100ff816100db565b92915050565b5f5f6040838503121561011b5761011a610074565b5b5f61012885828601610097565b9250506020610139858286016100f1565b9150509250929050565b60805160a05161021b6101705f395f818160f0015261013a01525f818160c90152610116015261021b5ff3fe608060405234801561000f575f5ffd5b506004361061004a575f3560e01c8063304e1cb21461004e578063477198e21461006c57806382f155161461008a5780638d726ac7146100a8575b5f5ffd5b6100566100c6565b6040516100639190610174565b60405180910390f35b6100746100ed565b60405161008191906101cc565b60405180910390f35b610092610114565b60405161009f9190610174565b60405180910390f35b6100b0610138565b6040516100bd91906101cc565b60405180910390f35b5f7f0000000000000000000000000000000000000000000000000000000000000000905090565b5f7f0000000000000000000000000000000000000000000000000000000000000000905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b5f819050919050565b61016e8161015c565b82525050565b5f6020820190506101875f830184610165565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6101b68261018d565b9050919050565b6101c6816101ac565b82525050565b5f6020820190506101df5f8301846101bd565b9291505056fea264697066735822122040ab77b65e5147c48aee1cbca580ba83cdadcec862bf5c18d744a06874682ae064736f6c634300081e0033",
"bin-runtime": "608060405234801561000f575f5ffd5b506004361061004a575f3560e01c8063304e1cb21461004e578063477198e21461006c57806382f155161461008a5780638d726ac7146100a8575b5f5ffd5b6100566100c6565b6040516100639190610174565b60405180910390f35b6100746100ed565b60405161008191906101cc565b60405180910390f35b610092610114565b60405161009f9190610174565b60405180910390f35b6100b0610138565b6040516100bd91906101cc565b60405180910390f35b5f7f0000000000000000000000000000000000000000000000000000000000000000905090565b5f7f0000000000000000000000000000000000000000000000000000000000000000905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b5f819050919050565b61016e8161015c565b82525050565b5f6020820190506101875f830184610165565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6101b68261018d565b9050919050565b6101c6816101ac565b82525050565b5f6020820190506101df5f8301846101bd565b9291505056fea264697066735822122040ab77b65e5147c48aee1cbca580ba83cdadcec862bf5c18d744a06874682ae064736f6c634300081e0033"
}
19 changes: 19 additions & 0 deletions crates/anvil-polkadot/test-data/ImmutableStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pragma solidity ^0.8.0;

contract ImmutableStorage {
uint256 public immutable immutableValue;
address public immutable immutableAddress;

constructor(uint256 _value, address _addr) {
immutableValue = _value;
immutableAddress = _addr;
}

function getImmutableValue() public view returns (uint256) {
return immutableValue;
}

function getImmutableAddress() public view returns (address) {
return immutableAddress;
}
}
6 changes: 6 additions & 0 deletions crates/anvil-polkadot/tests/it/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ sol!(
SimpleStorageCaller,
"test-data/SimpleStorageCaller.json"
);

sol!(
#[derive(Debug)]
ImmutableStorage,
"test-data/ImmutableStorage.json"
);
64 changes: 63 additions & 1 deletion crates/anvil-polkadot/tests/it/state_injector.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
abi::SimpleStorage,
abi::{ImmutableStorage, SimpleStorage},
utils::{ContractCode, TestNode, get_contract_code, unwrap_response},
};
use alloy_eips::BlockId;
Expand Down Expand Up @@ -776,3 +776,65 @@ async fn test_set_storage() {
assert_eq!(stored_value, 511);
}
}

#[tokio::test(flavor = "multi_thread")]
async fn test_set_immutable_storage() {
use alloy_sol_types::SolValue;

let anvil_node_config = AnvilNodeConfig::test_config();
let substrate_node_config = SubstrateNodeConfig::new(&anvil_node_config);
let mut node = TestNode::new(anvil_node_config, substrate_node_config).await.unwrap();

let alith = Account::from(subxt_signer::eth::dev::alith());
let alith_addr = Address::from(ReviveAddress::new(alith.address()));
let initial_value = U256::from(12345);
let initial_address =
Address::from(ReviveAddress::new(Account::from(subxt_signer::eth::dev::baltathar()).address()));

// Deploy contract with constructor args: (uint256 _value, address _addr)
let ContractCode { init: bytecode, .. } = get_contract_code("ImmutableStorage");
let deployment_bytecode =
[bytecode.as_slice(), (initial_value, initial_address).abi_encode().as_slice()].concat();
let tx_hash = node.deploy_contract(&deployment_bytecode, alith.address()).await;
unwrap_response::<()>(node.eth_rpc(EthRequest::Mine(None, None)).await.unwrap()).unwrap();
let contract_address = Address::from(ReviveAddress::new(
node.get_transaction_receipt(tx_hash).await.contract_address.unwrap(),
));

// Read and verify initial immutable value
let call_tx = TransactionRequest::default()
.from(alith_addr)
.to(contract_address)
.input(TransactionInput::both(ImmutableStorage::getImmutableValueCall {}.abi_encode().into()));
let result = unwrap_response::<Bytes>(
node.eth_rpc(EthRequest::EthCall(call_tx.into(), None, None, None)).await.unwrap(),
)
.unwrap();
assert_eq!(ImmutableStorage::getImmutableValueCall::abi_decode_returns(&result.0).unwrap(), initial_value);

// Read and verify initial immutable address
let call_tx = TransactionRequest::default()
.from(alith_addr)
.to(contract_address)
.input(TransactionInput::both(ImmutableStorage::getImmutableAddressCall {}.abi_encode().into()));
let result = unwrap_response::<Bytes>(
node.eth_rpc(EthRequest::EthCall(call_tx.into(), None, None, None)).await.unwrap(),
)
.unwrap();
assert_eq!(ImmutableStorage::getImmutableAddressCall::abi_decode_returns(&result.0).unwrap(), initial_address);

// Test anvil_setImmutableStorageAt with new values
let new_value = U256::from(99999);
let new_address =
Address::from(ReviveAddress::new(Account::from(subxt_signer::eth::dev::charleth()).address()));

unwrap_response::<()>(
node.eth_rpc(EthRequest::SetImmutableStorageAt(
contract_address,
Bytes::from((new_value, new_address).abi_encode_packed()),
))
.await
.unwrap(),
)
.unwrap();
}
8 changes: 8 additions & 0 deletions crates/anvil/core/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,14 @@ pub enum EthRequest {
B256,
),

/// Sets the immutable data associated with a contract address
#[serde(rename = "anvil_setImmutableStorageAt")]
SetImmutableStorageAt(
Address,
/// immutable data
Bytes,
),

/// Sets the coinbase address
#[serde(rename = "anvil_setCoinbase", alias = "hardhat_setCoinbase", with = "sequence")]
SetCoinbase(Address),
Expand Down
5 changes: 5 additions & 0 deletions crates/anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@ impl EthApi {
EthRequest::SetStorageAt(addr, slot, val) => {
self.anvil_set_storage_at(addr, slot, val).await.to_rpc_result()
}
EthRequest::SetImmutableStorageAt(_, _) => {
return ResponseResult::Error(RpcError::invalid_params(
"anvil_setImmutableStorageAt is not supported on EVM anvil",
));
}
EthRequest::SetCoinbase(addr) => self.anvil_set_coinbase(addr).await.to_rpc_result(),
EthRequest::EthCoinbase(()) => self.author().to_rpc_result(),
EthRequest::SetChainId(id) => self.anvil_set_chain_id(id).await.to_rpc_result(),
Expand Down
Loading