From d57a8e23dfe30353f791b8f1c973ccd2fb481ebf Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:25:00 +0100 Subject: [PATCH 1/6] EIP-8141: add canonical paymaster reference bytecode, code hash, and measured gas Insert the assembled 355-byte runtime bytecode and its per-fork keccak256 code hash into the canonical paymaster section, replace the ~2,150 gas estimate with the measured validation-path cost, and add the reference artifacts asset file. --- EIPS/eip-8141.md | 14 +++++++++++--- assets/eip-8141/canonical-paymaster.md | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 assets/eip-8141/canonical-paymaster.md diff --git a/EIPS/eip-8141.md b/EIPS/eip-8141.md index 2bef269f9f3c48..42a3c3ea9a3559 100644 --- a/EIPS/eip-8141.md +++ b/EIPS/eip-8141.md @@ -856,7 +856,7 @@ We address this conflict in two ways: ##### Canonical paymaster -The canonical paymaster is not a singleton deployment. Many instances may be deployed - the expected model is one instance per sponsor. For public mempool purposes, a paymaster instance is considered canonical if and only if its runtime code hash equals the canonical paymaster code hash of the active fork. The runtime code and its hash will be listed in this section; a new canonical version is introduced by pinning a new code hash, and versions unpinned at a fork boundary demote to non-canonical. +The canonical paymaster is not a singleton deployment. Many instances may be deployed - the expected model is one instance per sponsor. For public mempool purposes, a paymaster instance is considered canonical if and only if its runtime code hash equals the canonical paymaster code hash of the active fork. The runtime code and its hash are listed in this section; a new canonical version is introduced by pinning a new code hash, and versions unpinned at a fork boundary demote to non-canonical. **Storage layout.** The layout is normative per canonical version. Nodes read these slots directly for admission and revalidation; no EVM execution is required to compute solvency. @@ -888,7 +888,7 @@ At most one pending action exists at a time: a withdrawal (slot 1 non-zero) or a An operation is authorized by `signer` when the caller is `signer`, or - inside a frame transaction - when the entry at signature index 1 is protocol-verified, names `signer`, and has empty `msg`, exactly as in the payment path. The caller check keeps the plain-transaction path for secp256k1 signers; the signature check makes administration available to signers of every protocol-verified scheme, whose derived addresses cannot originate calls. The signature covers the canonical transaction hash, so an administrative authorization binds the entire transaction and cannot be replayed. Finalizing a withdrawal sends the amount to `signer` and reverts if the balance is insufficient (the sponsor cancels and re-initiates a smaller amount) or if `signer` cannot receive the transfer; a sponsor using a contract signer must ensure it accepts the withdrawal. Finalizing a rotation writes slot 3 into slot 0. Both clear the pending state before acting. -**Runtime behavior** (normative; the assembled bytecode and its code hash will be added to this section): +**Runtime behavior** (normative): ```python if CALLVALUE > 0: @@ -931,7 +931,15 @@ if op == 0x04: # finalize REVERT ``` -**Gas.** The validation path costs approximately 2,150 gas (three `SIGPARAM` reads, one cold `SLOAD`, dispatch, and `APPROVE`). A canonical `pay` frame `gas_limit` of 15,000 is sufficient with wide margin. +The assembled runtime bytecode (355 bytes) is: + +```text +0x3461002e57366100355760016001b41561005a575f6001b45f54141561005a5760026001b461005a5760015f5faa5b3661005a57005b5f3560f81c8060011461005e57806002146100a557806003146100ec57600414610123575b5f5ffd5b50335f54146100875760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b60025461005a57600135801561005a57600155426201518001600255005b50335f54146100ce5760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b60025461005a57600135801561005a57600355426201518001600255005b50335f54146101155760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b5f6001555f6002555f600355005b600254801561005a57421061005a576001548015610153575f6001555f6002555f5f5f5f845f545af11561005a57005b506003545f555f6003555f60025500 +``` + +Its per-fork `keccak256` code hash is `0xda42f0d11838c4c0c3129b8b8e93e9718127ad6b315e517e1088125707c4d45c`. The bytecode and hash are reproducible from the reference assembler and are also listed in `assets/eip-8141/canonical-paymaster.md`. + +**Gas.** The validation path costs a single cold `SLOAD` of slot 0 plus dispatch, three `SIGPARAM` reads, and `APPROVE` — roughly 3,100 gas at the state-access prices of this fork (dominated by the cold `SLOAD`; the reference implementation measures 3,110). A canonical `pay` frame's `gas_limit` of 15,000 is sufficient with wide margin, and nodes MAY reject canonical `pay` frames with a `gas_limit` above that bound as non-canonical usage. **Tracked dependencies.** For revalidation purposes, the canonical paymaster's tracked state is its balance, its code, and storage slots 0 through 3. A finalized rotation changes slot 0 and therefore invalidates pending transactions authorized by the previous signer. diff --git a/assets/eip-8141/canonical-paymaster.md b/assets/eip-8141/canonical-paymaster.md new file mode 100644 index 00000000000000..c892c42f3426b6 --- /dev/null +++ b/assets/eip-8141/canonical-paymaster.md @@ -0,0 +1,15 @@ +# Canonical paymaster reference artifacts + +Assembled runtime bytecode (355 bytes): + +```text +0x3461002e57366100355760016001b41561005a575f6001b45f54141561005a5760026001b461005a5760015f5faa5b3661005a57005b5f3560f81c8060011461005e57806002146100a557806003146100ec57600414610123575b5f5ffd5b50335f54146100875760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b60025461005a57600135801561005a57600155426201518001600255005b50335f54146100ce5760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b60025461005a57600135801561005a57600355426201518001600255005b50335f54146101155760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b5f6001555f6002555f600355005b600254801561005a57421061005a576001548015610153575f6001555f6002555f5f5f5f845f545af11561005a57005b506003545f555f6003555f60025500 +``` + +Per-fork `keccak256` code hash: + +```text +0xda42f0d11838c4c0c3129b8b8e93e9718127ad6b315e517e1088125707c4d45c +``` + +Both values are reproducible byte-for-byte from the reference assembler (two-pass label resolution, self-contained keccak-256). From 7c44423550f30e88a5956b9ae397319b023dfd4e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:33:47 +0100 Subject: [PATCH 2/6] EIP-8141: add annotated assembly for the canonical paymaster reference Present the reference contract as annotated EVM assembly (mnemonics, labels, per-block comments) alongside the compiled bytecode and code hash, matching the house style of the system-contract EIPs. The listing assembles byte-for-byte to the pinned runtime bytecode and hash. --- EIPS/eip-8141.md | 2 +- assets/eip-8141/canonical-paymaster.md | 297 ++++++++++++++++++++++++- 2 files changed, 295 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-8141.md b/EIPS/eip-8141.md index 42a3c3ea9a3559..1ffa95ada6cc0f 100644 --- a/EIPS/eip-8141.md +++ b/EIPS/eip-8141.md @@ -937,7 +937,7 @@ The assembled runtime bytecode (355 bytes) is: 0x3461002e57366100355760016001b41561005a575f6001b45f54141561005a5760026001b461005a5760015f5faa5b3661005a57005b5f3560f81c8060011461005e57806002146100a557806003146100ec57600414610123575b5f5ffd5b50335f54146100875760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b60025461005a57600135801561005a57600155426201518001600255005b50335f54146100ce5760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b60025461005a57600135801561005a57600355426201518001600255005b50335f54146101155760016001b41561005a575f6001b45f54141561005a5760026001b461005a575b5f6001555f6002555f600355005b600254801561005a57421061005a576001548015610153575f6001555f6002555f5f5f5f845f545af11561005a57005b506003545f555f6003555f60025500 ``` -Its per-fork `keccak256` code hash is `0xda42f0d11838c4c0c3129b8b8e93e9718127ad6b315e517e1088125707c4d45c`. The bytecode and hash are reproducible from the reference assembler and are also listed in `assets/eip-8141/canonical-paymaster.md`. +Its per-fork `keccak256` code hash is `0xda42f0d11838c4c0c3129b8b8e93e9718127ad6b315e517e1088125707c4d45c`. An annotated assembly listing that assembles byte-for-byte to this bytecode, together with the bytecode and hash, is given in [`assets/eip-8141/canonical-paymaster.md`](../assets/eip-8141/canonical-paymaster.md); all three are reproducible from the reference assembler. **Gas.** The validation path costs a single cold `SLOAD` of slot 0 plus dispatch, three `SIGPARAM` reads, and `APPROVE` — roughly 3,100 gas at the state-access prices of this fork (dominated by the cold `SLOAD`; the reference implementation measures 3,110). A canonical `pay` frame's `gas_limit` of 15,000 is sufficient with wide margin, and nodes MAY reject canonical `pay` frames with a `gas_limit` above that bound as non-canonical usage. diff --git a/assets/eip-8141/canonical-paymaster.md b/assets/eip-8141/canonical-paymaster.md index c892c42f3426b6..85f325dd00ac36 100644 --- a/assets/eip-8141/canonical-paymaster.md +++ b/assets/eip-8141/canonical-paymaster.md @@ -1,15 +1,306 @@ # Canonical paymaster reference artifacts -Assembled runtime bytecode (355 bytes): +## Annotated assembly + +Storage layout: `slot0 = signer`, `slot1 = pending_withdrawal_amount`, +`slot2 = pending_unlock_time`, `slot3 = pending_new_signer`. `DELAY = 86400`. +Jump targets are assembled as fixed-width `push2