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
21 changes: 16 additions & 5 deletions packages/typewriter-evm/src/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ struct ExecuteOk {
#[serde(skip_serializing_if = "Option::is_none")]
journal_id: Option<u64>,
gas_used: u64,
gas_limit: u64,
// Only simulate estimates a gas limit; execute leaves it unset.
#[serde(skip_serializing_if = "Option::is_none")]
gas_limit: Option<u64>,
output: String,
access_list: Vec<AccessListEntry>,
slot_writes: Vec<SlotWriteEntry>,
Expand Down Expand Up @@ -386,7 +388,7 @@ impl EvmHarness {

fn execute(&mut self, params: &ExecuteParams) -> Result<ExecuteOk, String> {
self.ensure_initialized()?;
let mut result = match self.run_two_pass(params, None) {
let mut result = match self.run_two_pass(params, None, false) {
Ok(result) => result,
Err(error) => {
let _ = self.evm.finalize();
Expand Down Expand Up @@ -429,7 +431,7 @@ impl EvmHarness {
}

let cached_access_list = self.merge_journal_access_lists(&params.journal_ids);
let result = self.run_two_pass(&params.as_execute_params(), cached_access_list);
let result = self.run_two_pass(&params.as_execute_params(), cached_access_list, true);

// Drop pass 2's journal-state writes so they never reach the DB.
let _ = self.evm.finalize();
Expand Down Expand Up @@ -517,6 +519,11 @@ impl EvmHarness {
// those addresses/slots pre-warmed so the metered gas matches what the
// chain will charge for a tx broadcast with the discovered access list.
//
// With `estimate_gas`, a successful pass 2 is followed by a gas-limit
// search and a final pass at that limit. Only simulate needs this: it
// builds the transaction that goes onchain. Execute only accepts the
// mutation into local state, so it skips the search (about 15 extra runs).
//
// After pass 1, finalize drains the journal state and we discard it so
// pass 2 starts from the same DB state. The alternative —
// checkpoint_revert — doesn't work because transact_one calls commit_tx
Expand All @@ -525,6 +532,7 @@ impl EvmHarness {
&mut self,
params: &ExecuteParams,
cached_access_list: Option<AccessList>,
estimate_gas: bool,
) -> Result<ExecuteOk, String> {
let from = parse_address(&params.from)?;
let to = parse_address(&params.to)?;
Expand Down Expand Up @@ -590,6 +598,7 @@ impl EvmHarness {
.map_err(|e| format!("transact (pass 2): {e:?}"))?;

let gas_limit = match &result {
ExecutionResult::Success { .. } if !estimate_gas => None,
ExecutionResult::Success { .. } => {
self.evm.0.inspector.clear();
let _ = self.evm.finalize();
Expand All @@ -603,9 +612,11 @@ impl EvmHarness {
.evm
.inspect_one_tx(build_tx(tx_access_list.clone(), gas_limit))
.map_err(|e| format!("transact (final pass): {e:?}"))?;
gas_limit
Some(gas_limit)
}
ExecutionResult::Revert { .. } | ExecutionResult::Halt { .. } => {
estimate_gas.then(|| result.tx_gas_used())
}
ExecutionResult::Revert { .. } | ExecutionResult::Halt { .. } => result.tx_gas_used(),
};

let (success, gas_used, output, revert_data) = match &result {
Expand Down
2 changes: 2 additions & 0 deletions packages/typewriter-evm/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ test("execute against counter succeeds and returns a journal id", async () => {
expect(exec.success).toBe(true);
expect(exec.journal_id).toBe(1);
expect(exec.revert_data).toBeUndefined();
// Only simulate estimates a gas limit.
expect(exec).not.toHaveProperty("gas_limit");
expect(exec.slot_writes).toEqual([
{
address: COUNTER_ADDR,
Expand Down
6 changes: 4 additions & 2 deletions packages/typewriter-evm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
Request,
Response,
SimulateParams,
SimulateResult,
} from "./types";

export type {
Expand All @@ -24,6 +25,7 @@ export type {
ReadStorageParams,
ReadStorageResult,
SimulateParams,
SimulateResult,
Spec,
} from "./types";

Expand Down Expand Up @@ -76,7 +78,7 @@ export type EVM = {
) => Effect.Effect<ExecuteResult, EvmError>;
readonly simulate: (
params: SimulateParams,
) => Effect.Effect<ExecuteResult, EvmError>;
) => Effect.Effect<SimulateResult, EvmError>;
readonly readStorage: (
params: ReadStorageParams,
) => Effect.Effect<ReadStorageResult, EvmError>;
Expand Down Expand Up @@ -175,7 +177,7 @@ export const createEVM = (): Effect.Effect<EVM, never, Scope.Scope> =>
params,
})),
simulate: (params) =>
call<ExecuteResult>("simulate", (id) => ({
call<SimulateResult>("simulate", (id) => ({
method: "simulate",
id,
params,
Expand Down
7 changes: 6 additions & 1 deletion packages/typewriter-evm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export type ExecuteResult = {
success: boolean;
journal_id?: number;
gas_used: number;
gas_limit: number;
output: Hex.Hex;
access_list: { address: Address.Address; storageKeys: Hex.Hex[] }[];
slot_writes: {
Expand All @@ -57,6 +56,12 @@ export type ExecuteResult = {
revert_data?: Hex.Hex;
};

// Simulate also estimates the smallest gas limit that lets the call succeed.
// Execute skips that search because accepting a mutation does not need it.
export type SimulateResult = ExecuteResult & {
gas_limit: number;
};

export type ReadStorageParams = {
address: Address.Address;
slots: Hex.Hex[];
Expand Down
Loading