feat: wasm evm (via alloy-rs/evm2) - #366
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
alloy-rs/evm2)alloy-rs/evm2)
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2aaa049a7d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
d5e9e16 to
7dd24e4
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 46c30834de
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b94bd230c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2eab42ff2b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
alloy-rs/evm2)alloy-rs/evm2)
Binds a pinned alloy-rs/evm2 revision to WebAssembly through a versioned ABI, with a host database bridge, generated artifact, and internal TypeScript loader.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 492ce019d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Bytecode::new_raw_checked(Vec::from(&self.code[..length]).into()) | ||
| .map_err(|_| HostError::Bytecode(B256::ZERO)) |
There was a problem hiding this comment.
Fall back to legacy for malformed delegation prefixes
When stored account code begins with the EIP-7702 prefix but is not an exact 23-byte designator—for example 0xef01—new_raw_checked rejects it, and the shipped WASM adapter reports a database failure. Such bytes can exist in genesis or pre-EIP-3541 state and are ordinary legacy bytecode, so evm2 should execute them and return an invalid-opcode TxResult; retain autodetection for valid designators but fall back to legacy bytecode when the checked parse fails.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 66d08d2c29
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return attempt(evm, () => | ||
| apply(evm, { | ||
| ...evm['~config'], | ||
| block: merge(evm['~config'].block, block), |
There was a problem hiding this comment.
Snapshot queued block updates before async execution
When this runs on an async EVM, the closure can be delayed behind an in-flight database read, so merge reads the caller's block object later rather than at setBlock call time. If the caller reuses or mutates that options object before the queue drains, the EVM applies a different block environment than the one submitted, unlike the synchronous path and evm2's value-based setter; snapshot the block fields before entering attempt (and apply the same pattern to setBlockAndExecutionConfig).
Useful? React with 👍 / 👎.
| evm: Evm<asynchronous>, | ||
| options: setExecutionConfig.Options, | ||
| ): Awaitable<asynchronous, void> { | ||
| const specId = options.specId ?? evm['~config'].specId |
There was a problem hiding this comment.
Default spec ID after the async queue drains
For async EVMs, this default is computed before the operation is serialized, so a queued setExecutionConfig({ version }) can capture the old spec while an earlier queued setter is still changing it. For example, issuing setExecutionConfig({ specId: 'cancun' }) and then setExecutionConfig({ version: ... }) in the same tick leaves the engine back on the original spec instead of keeping Cancun, even though omitting specId is documented as unchanged; move the ?? evm['~config'].specId fallback into the attempt callback while still snapshotting the explicit option value.
Useful? React with 👍 / 👎.
| evm: Evm<asynchronous>, | ||
| options: setExecutionConfig.Options, | ||
| ): Awaitable<asynchronous, void> { | ||
| const specId = options.specId ?? evm['~config'].specId |
There was a problem hiding this comment.
Resolve omitted spec IDs at execution time
For async EVMs, this default is computed before the operation is serialized, so a queued setExecutionConfig({ version }) can capture the old spec while an earlier queued setter is still changing it. For example, issuing setExecutionConfig({ specId: 'cancun' }) and then setExecutionConfig({ version: ... }) in the same tick leaves the engine back on the original spec instead of keeping Cancun, even though omitting specId is documented as unchanged; move the ?? evm['~config'].specId fallback into the attempt callback while still snapshotting the explicit option value.
Useful? React with 👍 / 👎.
| (entry) => | ||
| entry.codeHash.toLowerCase() === current.codeHash.toLowerCase(), | ||
| )?.code | ||
| return { ...current, ...(code ? { code } : {}) } |
There was a problem hiding this comment.
Copy pending bytecode before exposing it
When a detached pending state contains bytecode, this returns the stored Uint8Array by reference; StateChange.visit routes the same stored array to sinks. If an inspector, persistence layer, or tee branch mutates or zeroizes that buffer, the PendingState is corrupted and later accountInfo/visit calls observe code different from the detached evm2 state, so copy bytecode bytes at the boundary before returning or routing them.
Useful? React with 👍 / 👎.
| const source = evm['~driver'] | ||
| if (!source) return run() as never | ||
| // Queued: awaiting a source yields control, and the engine is exclusive. | ||
| return source.serialize(() => driver.until(source, run)) as never |
There was a problem hiding this comment.
Reject async operations submitted while borrowed
On async EVMs this schedules the engine call in a later microtask, so a caller can submit Evm.callTx, setBlock, or another operation while an ExecutedTx is still outstanding and then commit/discard that handle before the queue reaches this closure; the queued operation then succeeds against post-resolution state instead of being rejected like the synchronous path and evm2's exclusive-borrow lifecycle. Check the borrow synchronously when the operation is submitted, or serialize handle resolutions through the same queue, so calls made while the handle holds the EVM cannot be reordered past its resolution.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
* feat(evm): record executions with an inspector * fix(evm): bound and reset traces correctly * test(evm): assert setters are awaitable on asynchronous evms
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a3fa593d49
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| limit: options.limit ?? 1_048_576, | ||
| memory: options.memory ?? false, | ||
| stack: options.stack ?? false, | ||
| steps: options.steps ?? false, |
There was a problem hiding this comment.
Snapshot inspector options before queueing
On async EVMs this closure is scheduled through serialize, so these options.* reads can happen after the caller has already mutated or reused the options object. In that case Evm.setInspector(evm, options) may install a different trace configuration than the one submitted (for example toggling steps or changing limit before the microtask/queued read drains); copy the four option fields before entering attempt.
Useful? React with 👍 / 👎.
| const pointer = exports.ox_alloc(request.length) | ||
| if (pointer === 0) throw new ReentrancyError() |
There was a problem hiding this comment.
Mark allocation traps as fatal
When a large-but-allowed request makes ox_alloc trap (for example because the runtime cannot grow linear memory enough), the adapter's RUNNING flag can remain set, but this call is outside the trap handler that records trapped. The first failure therefore escapes as the raw WebAssembly error and later calls can be reported as ReentrancyError instead of the documented dead-instance TrapError; wrap ox_alloc in the same fatal-trap path used for ox_call.
Useful? React with 👍 / 👎.
* feat(evm): record executions with an inspector * fix(evm): bound and reset traces correctly * test(evm): assert setters are awaitable on asynchronous evms * feat(evm): wrap block access lists * fix(evm): report uncovered reads from the operations that raise them
* feat(evm): wrap block execution * refactor(evm): identify the block accumulator by token * fix(evm): carry bytecode with applied state, and only changes
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c9af6a427b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /// | ||
| /// EIP-3860 caps deployed code at 24576 bytes; this leaves room for the initcode | ||
| /// limit above it rather than tracking the active specification here. | ||
| const MAX_CODE: usize = 49152; |
There was a problem hiding this comment.
Raise the BAL code ceiling to the Amsterdam limit
When setBal receives a code change between 49,153 and 65,536 bytes, reader.bytes(MAX_CODE) rejects it even though Amsterdam permits 65,536-byte deployed code (and this adapter already uses MAX_CODE_SIZE_AMSTERDAM for account code). A BAL built from an execution using the higher configured limit can therefore be emitted successfully but cannot be attached again; size this bound for the largest supported deployed-code limit rather than the initcode limit.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
| export function startBlockState<asynchronous extends boolean>( | ||
| evm: Evm<asynchronous>, | ||
| ): Awaitable<asynchronous, BlockState.Token> { | ||
| return attempt(evm, () => evm['~engine'].startBlockState()) |
There was a problem hiding this comment.
Bind block-state tokens to their originating EVM
When two EVM instances have each started the same generation (commonly token 1n), passing the token from one instance to takeBlockState or ExecutedTx.commitTo on the other silently matches the other instance's local accumulator. This can drain or write the wrong block state instead of rejecting the cross-instance token; carry the owning engine identity with the token and validate it before dispatch.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
| ...(options.original ? { original: options.original } : {}), | ||
| } | ||
| return from({ | ||
| ...changes, |
There was a problem hiding this comment.
Update change records when editing pending state
After insertAccount returns, the grouped accounts array contains the replacement but the spread preserves the original records; StateChange.visit reads only records, while commitSource reads the grouped arrays. A caller that edits detached state and streams it for persistence or auditing therefore observes the old account even though committing that same state applies the new one. Update the record stream alongside the grouped view, and apply the same fix to insertStorage.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
| * | ||
| * // Transaction `i` records at index `i + 1`. | ||
| * Evm.setBalIndex(evm, 1n) | ||
| * Evm.transact(evm, transaction) |
There was a problem hiding this comment.
Resolve the BAL example transaction before taking the list
When this public example is followed, Evm.transact leaves an ExecutedTx outstanding and the EVM remains exclusively borrowed, so the immediately following Evm.takeBal(evm) throws BorrowedError instead of returning the built list. Commit the returned handle with ExecutedTx.commit before taking the BAL so the documented flow is executable and actually records the transaction.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4fd10d145d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| record::STORAGE_WIPE => { | ||
| let _address = reader.address()?; |
There was a problem hiding this comment.
Preserve self-destruct wipes when applying detached state
When a detached transaction self-destructs an account—either before Cancun or after creating the account in the same transaction under EIP-6780—this branch consumes and discards its storage-wipe record. Consequently, Evm.commitSource(evm, ExecutedTx.detach(executed).pendingState) does not reproduce a normal evm2 commit: storage changes can remain in the accepted overlay instead of being cleared, producing incorrect state for later execution. Preserve the wipe and associated lifecycle information through the reconstructed state source rather than dropping it.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3001387feb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return attempt(evm, () => | ||
| evm['~engine'].setBal({ bal, fallback: options.fallback ?? false }), |
There was a problem hiding this comment.
Snapshot the BAL before queuing it
When evm uses Database.fromAsync, attempt schedules this closure through the promise queue, so setBal returns before bal and options.fallback are read. If the caller reuses or mutates either value—even later in the same tick, or while an earlier database read drains—the engine can attach different coverage and state from what was submitted, unlike the synchronous path and evm2's value-based setter; copy or encode both inputs before entering attempt.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
| type ErrorType = | ||
| | AbiError | ||
| | BorrowedError | ||
| | EncodeError | ||
| | ReentrancyError |
There was a problem hiding this comment.
Include oversized BAL failures in the error union
When a caller attaches a BAL whose encoded request exceeds the adapter's 64 MiB limit, engine.setBal reaches bindings.call and throws RequestTooLargeError, but the public setBal.ErrorType omits that error even though callTx.ErrorType and transact.ErrorType declare the same boundary failure. Consumers using this exported union for exhaustive error handling therefore miss an error that valid setBal inputs can produce; include RequestTooLargeError in the union.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3228787c36
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| version?: Version | undefined | ||
| } | ||
|
|
||
| type ErrorType = EncodeError | VersionError | Errors.GlobalErrorType |
There was a problem hiding this comment.
Include ABI failures in create's error union
When a version override fits the TypeScript wire type but not wasm32's usize—for example maxCodeSize: 2n ** 32n—encoding succeeds, read_usize returns an ABI failure, and create throws Evm.AbiError. Because this exported union omits AbiError, consumers cannot exhaustively type error handling for an input the public API accepts; include it in create.ErrorType.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
| address: options.address, | ||
| caller: options.caller ?? System.address, | ||
| data: Bytes.fromHex(options.data ?? '0x'), |
There was a problem hiding this comment.
Snapshot system-call options before queueing
When this is called on an asynchronous EVM, attempt schedules the closure through the promise queue, so mutating or reusing options immediately after Evm.systemCall returns can change the target, caller, or calldata that eventually executes. This can apply state for a different system call than the submitted one, unlike the synchronous path and evm2's value-based SystemTx; copy and encode these fields before entering attempt.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
* fix(evm): read operation inputs when they are submitted * fix(evm): build envelopes from fields the caller can actually supply * fix(evm): keep caller-held state and sink guards intact * fix(evm): accept the state and code a caller can hold * fix(evm): remove the read ceiling, hold the borrow, ship license texts * fix(evm): copy what a queued operation holds and keep its failures async * fix(evm): merge queued blocks and resolve licenses for every source * test(evm): group cases by the export they exercise * fix(evm): classify bytecode alike and keep edits in evm2 order * fix(evm): snapshot the state commitSource applies
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2b2237e7b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let decoded = Bytecode::new_raw_checked(bytes.to_vec().into()) | ||
| .map_err(|_| abi::Error::Bytecode)?; |
There was a problem hiding this comment.
Fall back to legacy when rebuilding pending bytecode
When commitSource reapplies detached state containing pre-EIP-3541 code that begins with 0xef01 but is not a 23-byte delegation designator, this checked parser returns an AbiError, even though the database path now correctly classifies the same bytes as legacy and evm2 can hold them. Consequently, detaching and reapplying an otherwise valid legacy execution cannot reproduce a normal commit; use the same checked-then-legacy fallback here.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 57e7e08b9b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| op::START_BLOCK_STATE => match reader.finish() { | ||
| Ok(()) => start_block_state(), | ||
| Err(error) => abi_failure(error), | ||
| }, | ||
| op::TAKE_BLOCK_STATE => match read_token(&mut reader) { | ||
| Ok(token) => take_block_state(token), | ||
| Err(error) => abi_failure(error), |
There was a problem hiding this comment.
Enforce the EVM borrow before block-state operations
On a synchronous EVM with an outstanding ExecutedTx, these dispatch arms bypass engine() and access the global block accumulator directly. Consequently, startBlockState can silently discard the active accumulator and takeBlockState can drain it while the transaction still exclusively borrows the EVM, whereas the async submission path rejects both with BorrowedError; apply the same borrow check used by other non-resolution operations before mutating the accumulator.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
| op::START_BLOCK_STATE => match reader.finish() { | ||
| Ok(()) => start_block_state(), | ||
| Err(error) => abi_failure(error), | ||
| }, | ||
| op::TAKE_BLOCK_STATE => match read_token(&mut reader) { | ||
| Ok(token) => take_block_state(token), | ||
| Err(error) => abi_failure(error), |
There was a problem hiding this comment.
Enforce the EVM borrow before block-state operations
On a synchronous EVM with an outstanding ExecutedTx, these dispatch arms bypass engine() and access the global block accumulator directly. Consequently, startBlockState can silently discard the active accumulator and takeBlockState can drain it while the transaction still exclusively borrows the EVM, whereas the async submission path rejects both with BorrowedError; apply the same borrow check used by other non-resolution operations.
AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.
Adds a TS EVM interpreter via
alloy-rs/evm2.