Skip to content
Closed
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
18 changes: 18 additions & 0 deletions .changeset/evm2-block-access-lists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"ox": minor
---

Added EIP-7928 block access lists: attach one so reads are gated by what it covers, or build one from executed transactions.

```ts
import { Bal, Evm } from 'ox/evm'

// Validating: a read the list does not cover is refused, not served.
Evm.setBal(evm, bal)

// Building: transaction `i` records at index `i + 1`.
Evm.enableBalBuilder(evm)
Evm.setBalIndex(evm, 1n)
Evm.transact(evm, transaction)
const built = Evm.takeBal(evm)
```
5 changes: 5 additions & 0 deletions .changeset/evm2-block-execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ox": minor
---

Added block execution: gathering what a block's transactions change, applying caller-held state back to an EVM, and running protocol system calls.
5 changes: 5 additions & 0 deletions .changeset/evm2-call-execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ox": minor
---

Added `ox/evm`, an EVM backed by `alloy-rs/evm2` compiled to WebAssembly, with read-only transaction execution; every operation reads its inputs when it is submitted, so mutating them afterwards cannot change what runs.
5 changes: 5 additions & 0 deletions .changeset/evm2-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ox": minor
---

Added version overrides to `Evm.create`, plus `Evm.setBlock`, `Evm.setExecutionConfig`, and `Evm.setBlockAndExecutionConfig`.
15 changes: 15 additions & 0 deletions .changeset/evm2-inspection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"ox": minor
---

Added `Evm.setInspector` and `Evm.clearInspector`, which record what an execution did and report it as a trace on the result.

```ts
import { Evm, Inspector } from 'ox/evm'

Evm.setInspector(evm, {})

const result = Evm.callTx(evm, transaction)
const [root] = Inspector.tree(result.trace)
root.calls.length
```
5 changes: 5 additions & 0 deletions .changeset/evm2-transaction-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ox": minor
---

Added `Evm.transact`, which keeps a transaction's state changes pending so the caller commits, discards, or detaches them.
25 changes: 25 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,5 +198,30 @@ jobs:
path: .wasm-toolchain
key: wasi-sdk-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('wasm/toolchain.json') }}

- name: Install the pinned Rust toolchain
# `rustup show` inside the crate installs whatever
# `rust-toolchain.toml` pins, including its `wasm32-unknown-unknown`
# target.
working-directory: wasm/evm2
run: rustup show

- name: Restore Cargo cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/git
~/.cargo/registry
wasm/evm2/target
key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('wasm/evm2/Cargo.lock', 'wasm/evm2/rust-toolchain.toml') }}

- name: Verify committed artifacts are reproducible
# Covers the C targets and the evm2 adapter; each builder pins its own
# toolchain.
run: pnpm wasm:check

- name: Test the evm2 adapter and its native oracle
# `cargo test` covers the ABI, the failure encodings, and the fixtures
# native evm2 owns. The WASM half of that comparison runs in the `core`
# Vitest project.
working-directory: wasm/evm2
run: cargo test --locked
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.map
.agents/
.bench
.claude/
.DS_Store
Expand All @@ -18,6 +19,7 @@ ox.api.json
erc4337.api.json
node.api.json
tempo.api.json
evm.api.json
wasm.api.json
lookup.json
config-generated.ts
Expand All @@ -29,6 +31,7 @@ site/src/pages/node
site/src/pages/tempo/*
!site/src/pages/tempo/guides/
!site/src/pages/tempo/guides/**
site/src/pages/evm
site/src/pages/wasm
site/src/pages/webauthn
site/src/pages/zod
Expand All @@ -40,6 +43,10 @@ tsdoc-metadata.json
.wasm-toolchain
wasm/.build

# Rust adapter build output. `wasm/evm2/Cargo.lock` is committed: the shipped
# artifact must rebuild byte-for-byte, which pinning evm2 alone does not give.
wasm/evm2/target

# Rust bench build output (`pnpm bench:engines`). `Cargo.lock` is committed: the
# dependency ranges would otherwise re-resolve per machine, and a benchmark
# whose point is comparison over time cannot have its baseline move underneath
Expand Down
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
## API Conventions

- **Stateless module APIs** -- public APIs are module namespaces full of functions and types. Do not introduce stateful classes for normal library behavior.
- **`ox/evm` follows evm2** -- every Ox-owned EVM API, name, lifecycle, state boundary, result, error, and extension point must map directly to the pinned `alloy-rs/evm2` model. Adapt only syntax and runtime mechanics required by TypeScript or WebAssembly; record each unavoidable difference and get explicit approval before implementation.
- **Public entrypoint docs** -- when adding a public module or export, update `src/index.ts` with the module export and TSDoc block.
- **Package exports are generated** -- run `pnpm exports:update` only when intentionally adding, removing, or renaming public subpath exports.
- **Keep public APIs lean** -- avoid exposing options for values the library can derive from existing inputs.
Expand Down Expand Up @@ -123,6 +124,7 @@

- **Conventional commits** -- use `feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `chore:` prefixes. Scope is optional (e.g. `feat(abi): add tuple formatter`).
- **Preserve dirty work** -- do not revert, clean, or overwrite existing local changes unless explicitly asked.
- **Never force push** -- do not force push a branch, with or without `--force-with-lease`, and do not amend a commit that is already pushed. Add a follow-up commit instead. A pushed branch is shared history: force pushing discards review context and breaks anything built on it.

## Learned Workspace Facts

Expand All @@ -132,6 +134,11 @@
- **Generated site pages** -- API/reference pages under `site/src/pages/api`, `site/src/pages/ercs`, `site/src/pages/node`, `site/src/pages/tempo`, `site/src/pages/webauthn`, `site/src/pages/wasm`, and `site/src/pages/glossary` are generated.
- **Contracts submodule** -- `contracts/lib/forge-std` is a submodule path. Treat submodule status changes as user work unless the task is specifically about contracts setup.
- **WASM artifacts are generated** -- C lives in `wasm/src`, targets in `wasm/targets.ts`, and the committed base64 modules (`src/wasm/internal/*.wasm.ts`, `src/tempo/internal/mine.wasm.ts`) are written by `pnpm wasm:build`. Never hand-edit them. After changing any C or target config, run `pnpm wasm:build` and commit the result; `pnpm wasm:check` fails CI otherwise. The toolchain is pinned in `wasm/toolchain.json` because compiled bytes depend on the exact compiler version. See `wasm/README.md`.
- **Keep the evm2 adapter thin** -- `wasm/evm2` may own Ox's Rust-to-WASM ABI, host bridge, serialization, and runtime glue, but never EVM behavior that belongs to evm2. Resolve evm2 as an exact-revision Cargo dependency from the external cache; never add its source as a checkout, submodule, subtree, or vendored directory.
- **The evm2 artifact is generated** -- `src/wasm/internal/evm2.wasm.ts`, `wasm/evm2/NOTICE.md`, and the two `wasm/evm2/LICENSE-*` files are written by `pnpm wasm:build`. Never hand-edit them; `pnpm wasm:check` fails CI otherwise. `--target=evm2` selects this builder alone, which compiles inside the Docker image pinned in `wasm/toolchain.json` because `rustc` output is not byte-identical across host platforms. `OX_EVM2_NATIVE=1` builds on the host for local iteration; `pnpm wasm:check` refuses it. See `wasm/README.md`.
- **Record every evm2 divergence** -- a difference between an Ox EVM API and evm2's needs a recorded technical constraint and explicit approval before implementation, not after. Casing, namespace functions in place of Rust methods, and async WASM compilation are adaptations; anything else is a divergence.
- **ABI v1 has two halves** -- `wasm/evm2/src/abi.rs` and `src/evm/internal/codec.ts` encode the same layouts and must change together. evm2's Rust API never crosses that boundary.
- **The evm2 adapter pins stable Rust deliberately** -- evm2's build script selects a tail-call interpreter backend on nightly, which needs unstable features WebAssembly cannot use. Stable resolves to `single_return`, the backend evm2 intends for wasm. The adapter is also `no_std`: the `std` feature reaches `getrandom 0.2`, which refuses to compile for `wasm32-unknown-unknown`.
- **Trusted setup artifacts are generated** -- `src/trusted-setups/internal/setups/mainnet.json` is canonical. Refresh it from an explicit `c-kzg-4844` release with `pnpm trusted-setups:update <tag>`; `pnpm trusted-setups:check` verifies its recorded hash and packed TypeScript artifact. See `src/trusted-setups/README.md`.
- **KZG WASM disables LTO** -- full LTO makes valid c-kzg trusted-setup initialization trap. Preserve `-fno-lto` and the KZG conformance tests.
- **Secrets are local** -- `.env` is local. Do not print, rewrite, or commit secrets.
Expand Down
83 changes: 80 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"deps:ci": "pnpx actions-up",
"dev": "pnpm exports:update && zile dev",
"docs:dev": "pnpm --filter site dev",
"docs:extract": "pnpm api-extractor run -c scripts/docgen/api-extractor.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.erc4337.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.node.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.tempo.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.wasm.json --local && node --import tsx scripts/docgen/build.ts",
"docs:extract": "pnpm api-extractor run -c scripts/docgen/api-extractor.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.erc4337.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.evm.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.node.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.tempo.json --local && pnpm api-extractor run -c scripts/docgen/api-extractor.wasm.json --local && node --import tsx scripts/docgen/build.ts",
"docs:gen": "pnpm build && pnpm docs:extract",
"docs:build": "pnpm docs:gen && pnpm --filter site build",
"exports:update": "node --import tsx ./scripts/exports:update.ts",
Expand Down Expand Up @@ -121,7 +121,8 @@
{
"name": "import * from 'ox'",
"path": "./dist/index.js",
"import": "*"
"import": "*",
"limit": "200 kB"
},
{
"name": "import { Hex } from 'ox'; console.log(Hex.assert)",
Expand All @@ -131,7 +132,14 @@
{
"name": "import * from 'ox/wasm'",
"path": "./dist/wasm/index.js",
"import": "*"
"import": "*",
"limit": "225 kB"
},
{
"name": "import * from 'ox/evm'",
"path": "./dist/evm/index.js",
"import": "*",
"limit": "320 kB"
},
{
"name": "import { Hash } from 'ox/wasm'",
Expand Down Expand Up @@ -201,6 +209,10 @@
"CHANGELOG.md",
"dist",
"src",
"wasm/evm2/LICENSE-APACHE",
"wasm/evm2/LICENSE-MIT",
"wasm/evm2/NOTICE.md",
"wasm/evm2/THIRD-PARTY-LICENSES.md",
"wasm/vendor/c-kzg-4844/LICENSE",
"wasm/vendor/c-kzg-4844/blst/LICENSE",
"wasm/vendor/mldsa-native/LICENSE"
Expand Down Expand Up @@ -701,6 +713,71 @@
"types": "./dist/erc8021/Attribution.d.ts",
"default": "./dist/erc8021/Attribution.js"
},
"./evm": {
"src": "./src/evm/index.ts",
"types": "./dist/evm/index.d.ts",
"default": "./dist/evm/index.js"
},
"./evm/Bal": {
"src": "./src/evm/Bal.ts",
"types": "./dist/evm/Bal.d.ts",
"default": "./dist/evm/Bal.js"
},
"./evm/BlockState": {
"src": "./src/evm/BlockState.ts",
"types": "./dist/evm/BlockState.d.ts",
"default": "./dist/evm/BlockState.js"
},
"./evm/Database": {
"src": "./src/evm/Database.ts",
"types": "./dist/evm/Database.d.ts",
"default": "./dist/evm/Database.js"
},
"./evm/Ethereum": {
"src": "./src/evm/Ethereum.ts",
"types": "./dist/evm/Ethereum.d.ts",
"default": "./dist/evm/Ethereum.js"
},
"./evm/Evm": {
"src": "./src/evm/Evm.ts",
"types": "./dist/evm/Evm.d.ts",
"default": "./dist/evm/Evm.js"
},
"./evm/ExecutedTx": {
"src": "./src/evm/ExecutedTx.ts",
"types": "./dist/evm/ExecutedTx.d.ts",
"default": "./dist/evm/ExecutedTx.js"
},
"./evm/Inspector": {
"src": "./src/evm/Inspector.ts",
"types": "./dist/evm/Inspector.d.ts",
"default": "./dist/evm/Inspector.js"
},
"./evm/PendingState": {
"src": "./src/evm/PendingState.ts",
"types": "./dist/evm/PendingState.d.ts",
"default": "./dist/evm/PendingState.js"
},
"./evm/SpecId": {
"src": "./src/evm/SpecId.ts",
"types": "./dist/evm/SpecId.d.ts",
"default": "./dist/evm/SpecId.js"
},
"./evm/StateChange": {
"src": "./src/evm/StateChange.ts",
"types": "./dist/evm/StateChange.d.ts",
"default": "./dist/evm/StateChange.js"
},
"./evm/System": {
"src": "./src/evm/System.ts",
"types": "./dist/evm/System.d.ts",
"default": "./dist/evm/System.js"
},
"./evm/TxResult": {
"src": "./src/evm/TxResult.ts",
"types": "./dist/evm/TxResult.d.ts",
"default": "./dist/evm/TxResult.js"
},
"./node": {
"src": "./src/node/index.ts",
"types": "./dist/node/index.d.ts",
Expand Down
35 changes: 35 additions & 0 deletions scripts/docgen/api-extractor.evm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "../../dist/evm/index.d.ts",
"bundledPackages": [],
"newlineKind": "lf",
"enumMemberOrder": "preserve",
"compiler": {
"tsconfigFilePath": "<projectFolder>/tsconfig.json",
"overrideTsconfig": {},
"skipLibCheck": true
},
"apiReport": {
"enabled": false
},
"docModel": {
"enabled": true,
"apiJsonFilePath": "<projectFolder>/scripts/docgen/evm.api.json",
"includeForgottenExports": true,
"projectFolderUrl": "https://github.com/wevm/ox/blob/main"
},
"dtsRollup": {
"enabled": false
},
"messages": {
"extractorMessageReporting": {
"default": { "logLevel": "warning" },
"ae-different-release-tags": { "logLevel": "none" },
"ae-incompatible-release-tags": { "logLevel": "none" },
"ae-internal-mixed-release-tag": { "logLevel": "none" },
"ae-missing-release-tag": { "logLevel": "none" },
"ae-unresolved-link": { "logLevel": "none" },
"ae-forgotten-export": { "logLevel": "none" }
}
}
}
2 changes: 1 addition & 1 deletion scripts/docgen/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ console.log('Generating API docs.')
/// Clean previously generated pages
////////////////////////////////////////////////////////////

for (const dir of ['api', 'ercs', 'node', 'wasm', 'webauthn', 'zod']) {
for (const dir of ['api', 'ercs', 'evm', 'node', 'wasm', 'webauthn', 'zod']) {
fs.removeSync(`./site/src/pages/${dir}`)
}
// Preserve hand-authored Tempo guides while replacing generated pages.
Expand Down
Loading
Loading