Skip to content

Add EIP-7702 SetCode transactions (type 0x04, closes #66)#93

Merged
koko1123 merged 2 commits into
mainfrom
feat/eip7702
Jun 10, 2026
Merged

Add EIP-7702 SetCode transactions (type 0x04, closes #66)#93
koko1123 merged 2 commits into
mainfrom
feat/eip7702

Conversation

@koko1123

@koko1123 koko1123 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

What

EIP-7702 (Pectra, live on mainnet) lets an EOA delegate to contract code via a signed authorization. alloy and viem both ship it -- this closes the parity gap.

  • transaction.Authorization { chain_id, address, nonce, y_parity, r, s } and Eip7702Transaction (1559 base + access list + authorization_list, non-nullable to), wired into the Transaction union and the serialize/hash dispatch at type byte 0x04.
  • signer.signAuthorization / signer.hashAuthorization implementing the EIP's signing hash keccak256(0x05 || rlp([chain_id, address, nonce])) (MAGIC = 0x05). y_parity is the 0/1 recovery id.
  • rpc_transaction.RpcAuthorization + optional RpcTransaction.authorization_list, parsed best-effort.

Payload (type 0x04)

0x04 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, authorization_list{, y_parity, r, s}]); each auth item [chain_id, address, nonce, y_parity, r, s].

Correctness (security-critical, independently cross-checked)

EIP-7702 signing authorizes code on an EOA, so it must be exact. No official signing test vector exists in the EIP or execution-spec-tests. Correctness is established by:

  • sign-then-recover round trips: secp256k1.recoverAddress(y_parity, r, s) over the auth hash returns the authority's address.
  • RLP-shape assertions, which I independently reproduced: rlp([1, addr, 5]) == d70194<addr>05, and secp256k1.sign returns v as the 0/1 R.y parity (not 27/28), so y_parity = sig.v is correct.

Verification

11 new tests (auth hash + shape, sign/recover for two accounts incl. chain_id=0, type-0x04 serialization, empty + 1-item lists, RPC parse). 811/811 pass, 0 skip on 0.16.0 and 0.17-dev. transactions.mdx updated with a signAuthorization example.

Closes #66

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for EIP-7702 (SetCode) transactions with full signing, serialization, and RPC handling of authorization lists.
    • Enabled authorization signing and verification for EIP-7702 transactions.
  • Documentation

    • Updated transaction docs with EIP-7702 specs and usage examples.
  • Tests

    • Added unit tests covering authorization hashing/signing, transaction serialization, RPC parsing, and resource freeing.

EIP-7702 (Pectra) lets an EOA delegate to contract code via a signed
authorization. alloy and viem ship it; this adds parity.

- transaction.Authorization { chain_id, address, nonce, y_parity, r, s }
- transaction.Eip7702Transaction (1559 base + access_list +
  authorization_list, non-nullable ), wired into the Transaction
  union and serializeForSigning/hashForSigning/serializeSigned at
  type byte 0x04
- signer.signAuthorization / signer.hashAuthorization with the EIP's
  signing hash keccak256(0x05 || rlp([chain_id, address, nonce])).
  y_parity is the 0/1 recovery id (verified: secp256k1.sign returns
  R.y parity, not 27/28)
- rpc_transaction.RpcAuthorization + optional
  RpcTransaction.authorization_list, parsed best-effort in
  parseSingleTransaction

Signing payload (type 0x04):
  0x04 || rlp([chain_id, nonce, max_priority_fee_per_gas,
    max_fee_per_gas, gas_limit, to, value, data, access_list,
    authorization_list{, y_parity, r, s}])
each auth item: [chain_id, address, nonce, y_parity, r, s].

No official EIP-7702 signing vector exists; correctness is established
by sign-then-recover round trips (recovered signer == authority) plus
RLP-shape assertions independently cross-checked (rlp([1,addr,5]) ==
d70194<addr>05). 11 new tests; 811/811 pass on 0.16.0 and 0.17-dev.
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
eth-zig Ready Ready Preview, Comment Jun 10, 2026 8:10pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae71224a-2434-4d64-b8a9-fd01380af1d9

📥 Commits

Reviewing files that changed from the base of the PR and between 4819fe9 and ea5471e.

📒 Files selected for processing (1)
  • src/provider.zig

📝 Walkthrough

Walkthrough

Adds EIP-7702 (type 0x04) SetCode transaction support: authorization hashing/signing (keccak256(0x05||rlp([chain_id,address,nonce]))), new Authorization/Eip7702Transaction types, RPC authorizationList parsing, type-0x04 serialization dispatch, tests, and docs.

Changes

EIP-7702 SetCode Transaction Support

Layer / File(s) Summary
Authorization signing and types
src/transaction.zig, src/signer.zig
Authorization tuple and Eip7702Transaction struct are introduced. Signer.signAuthorization computes authorization hash via hashAuthorization (keccak256 of 0x05 magic byte prepended to RLP-encoded [chain_id, address, nonce]), signs the digest, and returns Authorization with chain_id, address, nonce, y_parity, r, s. Tests verify hash computation and round-trip signature recovery.
RPC authorization structure
src/rpc_transaction.zig
RpcAuthorization struct represents authorization tuples from RPC responses. RpcTransaction gains optional heap-owned authorization_list field. freeRpcTransaction updated to free the authorization list allocation when present, with test coverage for leak prevention.
RPC authorization list parsing
src/provider.zig
parseAuthorizationList decodes authorizationList from transaction JSON with best-effort handling (absent/non-array returns null), tolerant y_parity/v aliasing, and heap allocation. parseSingleTransaction integrates parsing with error cleanup. Tests verify parsing behavior for populated and absent authorization lists.
Transaction serialization dispatch and RLP encoding
src/transaction.zig
Transaction tagged union extended with .eip7702 variant. serializeForSigning and serializeSigned dispatch EIP-7702 transactions to typed serialization with type byte 0x04. RLP encoder updated to conditionally encode authorization list after access list with new helpers for per-item and outer-list length calculation and direct buffer writing. Length calculator updated for accurate buffer sizing. Tests verify type prefix, RLP structure, encoding of empty/non-empty lists, length differences, byte-identical shared prefix with EIP-1559 for common fields, and signed serialization correctness.
Documentation and changelog
docs/content/docs/transactions.mdx, CHANGELOG.md
Supported transaction types list updated to include EIP-7702. New EIP-7702 documentation section added with complete Zig example: creating authorizations via signer.signAuthorization, constructing .eip7702 transaction, calling transaction.serializeSigned, and broadcasting via provider.sendRawTransaction. Changelog documents new types, signing/hashing/serialization behavior, and RPC parsing support.

Sequence Diagrams

sequenceDiagram
  participant App as Application
  participant Signer
  participant Hash as hashAuthorization
  participant ECDSA
  participant TxBuilder as Transaction Builder
  participant Provider

  App->>Signer: signAuthorization(chain_id, delegate, nonce)
  Signer->>Hash: compute authorization digest
  Hash->>Hash: RLP encode [chain_id, address, nonce]\nprepend 0x05 magic byte
  Hash-->>Signer: [32]u8 digest
  Signer->>ECDSA: sign digest
  ECDSA-->>Signer: (r, s, y_parity)
  Signer-->>App: Authorization

  App->>TxBuilder: Eip7702Transaction{to, authorization_list, ...}
  TxBuilder->>TxBuilder: serializeSigned()
  TxBuilder->>TxBuilder: prefix 0x04\nRLP encode with auth_list\nappend signature fields
  TxBuilder-->>App: signed bytes

  App->>Provider: sendRawTransaction(bytes)
  Provider-->>App: transaction hash
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • StrobeLabs/eth.zig#53: Overlaps parseSingleTransaction JSON parsing changes and earlier transaction parsing work.

Poem

A rabbit hops through type-safe chains,
I prepend magic 0x05 for gains,
RLP lists and keccak hum,
Signatures pop, authorizations come,
Type 0x04 sets code—hooray! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main feature: adding EIP-7702 SetCode transactions (type 0x04). It is concise, clear, and directly reflects the primary objective stated in the linked issue #66.
Linked Issues check ✅ Passed The PR fully addresses all requirements from issue #66: authorization_list structure with signing hash, Signer.signAuthorization, type-0x04 serialization, RPC authorizationList parsing, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly related to EIP-7702 implementation: transaction types, signer helpers, RPC support, and documentation. No unrelated modifications detected outside the stated scope of issue #66.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/eip7702

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/provider.zig (1)

886-914: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce the type-0x04 RPC invariants here.

This path currently attaches authorization_list to any transaction that happens to carry authorizationList, and it still accepts type == 0x04 with to == null. That produces RpcTransaction states that contradict the new contracts in this PR.

Suggested fix
     const type_val: u8 = if (jsonGetString(obj, "type")) |t| try parseHexU8(t) else 0;
     const chain_id = try parseOptionalHexU64(jsonGetString(obj, "chainId"));

-    // EIP-7702 authorization list (best-effort: parse if the field is present
-    // and is an array). Present only on type-0x04 transactions.
-    const authorization_list = try parseAuthorizationList(allocator, obj);
+    if (type_val == 4 and to_addr == null) return error.InvalidResponse;
+
+    // EIP-7702 authorization list (best-effort: parse if the field is present
+    // and is an array). Present only on type-0x04 transactions.
+    const authorization_list = if (type_val == 4)
+        try parseAuthorizationList(allocator, obj)
+    else
+        null;
     errdefer if (authorization_list) |list| allocator.free(list);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/provider.zig` around lines 886 - 914, When constructing RpcTransaction
enforce the type-0x04 invariants: only attach authorization_list when type_val
== 0x04 (call parseAuthorizationList conditionally) and reject/return an error
if type_val == 0x04 and to_addr is null; otherwise ensure authorization_list is
null for non-0x04 types. Locate parseAuthorizationList, type_val,
authorization_list, to_addr and the RpcTransaction return and modify the
parsing/validation so authorization_list is produced only for type 0x04 and a
missing to_addr for type 0x04 triggers a failure instead of producing an invalid
RpcTransaction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/provider.zig`:
- Around line 942-945: The parsed yParity value (yp_str -> y_parity via
parseHexU8) must be validated to only allow 0 or 1 per EIP-7702; after obtaining
y_parity in the RpcAuthorization parsing path, add a check that returns
error.InvalidResponse if y_parity is not 0 and not 1 (reject values like 0x1b),
so RpcAuthorization entries only accept recovery IDs 0 or 1.

---

Outside diff comments:
In `@src/provider.zig`:
- Around line 886-914: When constructing RpcTransaction enforce the type-0x04
invariants: only attach authorization_list when type_val == 0x04 (call
parseAuthorizationList conditionally) and reject/return an error if type_val ==
0x04 and to_addr is null; otherwise ensure authorization_list is null for
non-0x04 types. Locate parseAuthorizationList, type_val, authorization_list,
to_addr and the RpcTransaction return and modify the parsing/validation so
authorization_list is produced only for type 0x04 and a missing to_addr for type
0x04 triggers a failure instead of producing an invalid RpcTransaction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21e07595-4279-421d-85a7-74b44c972c4f

📥 Commits

Reviewing files that changed from the base of the PR and between f76feb2 and 4819fe9.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • docs/content/docs/transactions.mdx
  • src/provider.zig
  • src/rpc_transaction.zig
  • src/signer.zig
  • src/transaction.zig

Comment thread src/provider.zig
…ng RPC

A malformed authorizationList entry with yParity outside {0,1} is now
rejected as error.InvalidResponse instead of silently accepted.
@koko1123
koko1123 merged commit 7127b0d into main Jun 10, 2026
14 checks passed
@koko1123
koko1123 deleted the feat/eip7702 branch June 10, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EIP-7702 SetCode transaction support (type 0x04)

1 participant