use crate::{state::StateOverride, Block, BlockOverrides, Log, TransactionRequest};
use alloc::{string::String, vec::Vec};
use alloy_primitives::Bytes;
pub const MAX_SIMULATE_BLOCKS: u64 = 256;
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct SimBlock {
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub block_overrides: Option<BlockOverrides>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub state_overrides: Option<StateOverride>,
#[cfg_attr(feature = "serde", serde(default))]
pub calls: Vec<TransactionRequest>,
}
impl SimBlock {
pub fn with_state_overrides(mut self, overrides: StateOverride) -> Self {
self.state_overrides = Some(overrides);
self
}
pub fn with_block_overrides(mut self, overrides: BlockOverrides) -> Self {
self.block_overrides = Some(overrides);
self
}
pub fn call(mut self, call: TransactionRequest) -> Self {
self.calls.push(call);
self
}
pub fn extend_calls(mut self, calls: impl IntoIterator<Item = TransactionRequest>) -> Self {
self.calls.extend(calls);
self
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct SimulatedBlock<B = Block> {
#[cfg_attr(feature = "serde", serde(flatten))]
pub inner: B,
pub calls: Vec<SimCallResult>,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct SimCallResult {
pub return_data: Bytes,
#[cfg_attr(feature = "serde", serde(default))]
pub logs: Vec<Log>,
#[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
pub gas_used: u64,
#[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
pub status: bool,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub error: Option<SimulateError>,
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct SimulatePayload {
#[cfg_attr(feature = "serde", serde(default))]
pub block_state_calls: Vec<SimBlock>,
#[cfg_attr(feature = "serde", serde(default))]
pub trace_transfers: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub validation: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub return_full_transactions: bool,
}
impl SimulatePayload {
pub fn extend(mut self, block: SimBlock) -> Self {
self.block_state_calls.push(block);
self
}
pub fn extend_blocks(mut self, blocks: impl IntoIterator<Item = SimBlock>) -> Self {
self.block_state_calls.extend(blocks);
self
}
pub const fn with_trace_transfers(mut self) -> Self {
self.trace_transfers = true;
self
}
pub const fn with_validation(mut self) -> Self {
self.validation = true;
self
}
pub const fn with_full_transactions(mut self) -> Self {
self.return_full_transactions = true;
self
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct SimulateError {
pub code: i32,
pub message: String,
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::{Address, TxKind};
use serde_json::json;
use similar_asserts::assert_eq;
#[test]
#[cfg(feature = "serde")]
fn test_eth_simulate_v1_account_not_precompile() {
let request_json = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "eth_simulateV1",
"params": [{
"blockStateCalls": [
{
"blockOverrides": {},
"stateOverrides": {
"0xc000000000000000000000000000000000000000": {
"nonce": "0x5"
}
},
"calls": []
},
{
"blockOverrides": {},
"stateOverrides": {
"0xc000000000000000000000000000000000000000": {
"code": "0x600035600055"
}
},
"calls": [
{
"from": "0xc000000000000000000000000000000000000000",
"to": "0xc000000000000000000000000000000000000000",
"nonce": "0x0"
},
{
"from": "0xc100000000000000000000000000000000000000",
"to": "0xc100000000000000000000000000000000000000",
"nonce": "0x5"
}
]
}
],
"traceTransfers": false,
"validation": true,
"returnFullTransactions": false
}, "latest"]
});
let sim_opts: SimulatePayload =
serde_json::from_value(request_json["params"][0].clone()).unwrap();
let address_1: Address = "0xc000000000000000000000000000000000000000".parse().unwrap();
let address_2: Address = "0xc100000000000000000000000000000000000000".parse().unwrap();
assert!(sim_opts.validation);
assert_eq!(sim_opts.block_state_calls.len(), 2);
let block_state_call_1 = &sim_opts.block_state_calls[0];
assert!(block_state_call_1.state_overrides.as_ref().unwrap().contains_key(&address_1));
assert_eq!(
block_state_call_1
.state_overrides
.as_ref()
.unwrap()
.get(&address_1)
.unwrap()
.nonce
.unwrap(),
5
);
let block_state_call_2 = &sim_opts.block_state_calls[1];
assert!(block_state_call_2.state_overrides.as_ref().unwrap().contains_key(&address_1));
assert_eq!(block_state_call_2.calls.len(), 2);
assert_eq!(block_state_call_2.calls[0].from.unwrap(), address_1);
assert_eq!(block_state_call_2.calls[0].to.unwrap(), TxKind::Call(address_1));
assert_eq!(block_state_call_2.calls[0].nonce.unwrap(), 0);
assert_eq!(block_state_call_2.calls[1].from.unwrap(), address_2);
assert_eq!(block_state_call_2.calls[1].to.unwrap(), TxKind::Call(address_2));
assert_eq!(block_state_call_2.calls[1].nonce.unwrap(), 5);
}
}