You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Update EIP-8282: Sync specification with sys-asm@83f9801
- Define INHIBITOR = 2**256 - 1 constant (renamed from EXCESS_INHIBITOR
to match the source-level macro in sys-asm@83f9801)
- Define storage layout: slot 0 stored_excess, slot 1 count,
slot 2 queue_head, slot 3 queue_tail, slot 4+ queued records
- Specify the ordered system-call transition:
1. dequeue (construct request_data, advance head; full drain resets
both head and tail, dequeued slots not cleared)
2. non-empty calldata sets stored_excess to INHIBITOR
3. empty calldata clears INHIBITOR to zero if set, otherwise
updates stored_excess to max(0, stored_excess + count - target)
4. reset count to zero
5. return request_data
- Specify that the inhibition check gates all non-system calls before
dispatch (write path and fee getter)
- Define effective_excess = stored_excess + max(0, count - target)
- Specify the deposit LOG0 emits the 184-byte input verbatim
(amount big-endian in both queue storage and log)
- Add Contract upgrade subsection explaining the rationale for the
reversible inhibition: a future predeploy upgrade can deploy a
new contract, continue draining the old one through empty-calldata
system calls, and reject new requests on the old contract by sending
non-empty calldata (setting INHIBITOR)
- Add EIP-7997 to requires (the CREATE2 factory used for deployment)
- Update reference links to sys-asm@83f9801
- Open question ethereum#2 (pre-fork count accumulation): noted as confirmed
intentional per Felix (2026-08-12) — he confirmed the inhibition
and drain-while-inhibited behaviors, which together imply the
multi-block count accumulation is also intended
Copy file name to clipboardExpand all lines: EIPS/eip-8282.md
+38-10Lines changed: 38 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ status: Review
8
8
type: Standards Track
9
9
category: Core
10
10
created: 2026-05-22
11
-
requires: 1559, 7685, 7732
11
+
requires: 1559, 7685, 7732, 7997
12
12
---
13
13
14
14
## Abstract
@@ -46,6 +46,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S
46
46
|`TARGET_EXIT_REQUESTS_PER_BLOCK`|`2`| Per-block request count above which the fee rises for the exit contract |
47
47
|`MIN_REQUEST_FEE`|`1`| Minimum request fee, in wei |
48
48
|`REQUEST_FEE_UPDATE_FRACTION`|`17`| Controls the fee's rate of change |
49
+
|`INHIBITOR`|`2**256 - 1`| Sentinel value written to `stored_excess` to inhibit non-system calls. Matches the source-level macro in `src/builder_deposits/main.eas:31` and `src/builder_exits/main.eas:32`. |
49
50
|`BUILDER_MIN_DEPOSIT`|`1000000000000000000`| Minimum credited stake for a deposit, in wei (1 ETH — the [EIP-7732](./eip-7732.md) builder minimum) |
50
51
|`BUILDER_DEPOSIT_CONTRACT_RUNTIME_CODE`|*see [Reference Implementation](#reference-implementation)*| Runtime bytecode of the builder deposit contract |
51
52
|`BUILDER_EXIT_CONTRACT_RUNTIME_CODE`|*see [Reference Implementation](#reference-implementation)*| Runtime bytecode of the builder exit contract |
@@ -58,35 +59,62 @@ Both contracts are deployed by a `CREATE2` factory ([EIP-7997](./eip-7997.md)).
58
59
59
60
The contracts MUST be deployed before the fork that activates this EIP. If there is no code at either address once the EIP is active, every block from activation onward MUST be invalid.
60
61
62
+
The deposit contract's constructor leaves all storage at zero. Its `stored_excess` and `count` start at zero, so the write path is active from deployment and pre-fork deposits are accepted. The exit contract's constructor initializes its `stored_excess` to `INHIBITOR`, which inhibits non-system calls until a system call with empty calldata clears it to zero.
63
+
61
64
### Request queue and system call
62
65
63
-
Both predeploys follow the [EIP-7002](./eip-7002.md) and [EIP-7251](./eip-7251.md) contract design, with minor tweaks, and reuse their storage layout. There is no Solidity-compatible ABI. Each contract dispatches on the caller and on `calldatasize` alone. Calls that match none of the cases below MUST revert.
66
+
Both predeploys follow the [EIP-7002](./eip-7002.md) and [EIP-7251](./eip-7251.md) contract design, with minor tweaks, and reuse their storage layout. There is no Solidity-compatible ABI. Each contract dispatches on the caller and on `calldatasize` alone.
67
+
68
+
The contracts use the following storage layout:
69
+
70
+
- slot `0` stores `stored_excess`;
71
+
- slot `1` stores `count`;
72
+
- slot `2` stores `queue_head`;
73
+
- slot `3` stores `queue_tail`;
74
+
- queued records start at slot `4`.
75
+
76
+
Before dispatching any call from an address other than `SYSTEM_ADDRESS`, the contract MUST revert if `stored_excess == INHIBITOR`. Calls that match none of the cases below MUST also revert.
64
77
65
78
#### Write path
66
79
67
-
A call from any address other than `SYSTEM_ADDRESS`, with calldata of exactly the contract's input size, submits a request. The contract MUST validate the request and the value sent (see below), append one record to its queue, increment the per-block count, and emit the accepted record as an anonymous log.
80
+
A call from any address other than `SYSTEM_ADDRESS`, with calldata of exactly the contract's input size, submits a request. The contract MUST validate the request and the value sent (see below), append one record to its queue, increment `count`, and emit the accepted record as an anonymous log.
81
+
82
+
`count` is the number of successful submissions since the last system call. It is stored in slot `1` and reset to zero by the system call.
68
83
69
84
#### Fee getter
70
85
71
-
A call from any address other than `SYSTEM_ADDRESS`, with empty calldata, returns the current fee without modifying state. The contract MUST revert if any value is attached.
86
+
A call from any address other than `SYSTEM_ADDRESS`, with empty calldata, returns the current fee without modifying state. The contract MUST revert if any value is attached. The fee is returned as a 32-byte big-endian unsigned integer.
72
87
73
88
#### System call
74
89
75
-
At the end of each block, the contract is called by `SYSTEM_ADDRESS`. If any calldata is included, the queue is permanently disabled via an inhibitor. If no calldata is included, it MUST dequeue up to its per-block maximum of records (`MAX_DEPOSIT_REQUESTS_PER_BLOCK` or `MAX_EXIT_REQUESTS_PER_BLOCK`, oldest first), return their concatenation as its `request_data`, and reset the per-block count. Records beyond the cap remain queued for subsequent blocks.
90
+
At the end of each block, the contract is called by `SYSTEM_ADDRESS`. The system call performs the following transition unconditionally and in order:
91
+
92
+
1. Dequeue up to the per-block maximum of records (`MAX_DEPOSIT_REQUESTS_PER_BLOCK` or `MAX_EXIT_REQUESTS_PER_BLOCK`, oldest first) and construct `request_data` from their concatenation. If the dequeue empties the queue, set both `queue_head` and `queue_tail` to zero. Otherwise, advance `queue_head` past the dequeued records. The contract does not clear the dequeued record slots.
93
+
2. If calldata is non-empty, set `stored_excess` to `INHIBITOR`. This inhibits non-system calls.
94
+
3. If calldata is empty and `stored_excess` equals `INHIBITOR`, set `stored_excess` to zero, re-enabling non-system calls. If calldata is empty and `stored_excess` does not equal `INHIBITOR`, set `stored_excess` to `max(0, stored_excess + count - target)` where `target` is `TARGET_DEPOSIT_REQUESTS_PER_BLOCK` or `TARGET_EXIT_REQUESTS_PER_BLOCK` as applicable.
95
+
4. Reset `count` to zero.
96
+
5. Return `request_data`.
97
+
98
+
Records beyond the cap remain queued for subsequent blocks. A system call with non-empty calldata always sets the inhibitor, regardless of whether records were dequeued. If `stored_excess` remains equal to `INHIBITOR`, the next system call with empty calldata clears it to zero.
76
99
77
100
The execution layer prepends the contract's request-type byte and includes `request_type ++ request_data` in the block requests list, committed via the `requests_hash` ([EIP-7685](./eip-7685.md)). The system call follows the same rules as in [EIP-7002](./eip-7002.md). It runs with a dedicated gas limit of `30_000_000` that does not count against the block gas limit. If either contract's system call fails, the block MUST be invalid.
78
101
102
+
### Contract upgrade
103
+
104
+
The reversible inhibition mechanism exists to support a future contract upgrade without losing queued requests. To upgrade the predeploy, a new contract is deployed, the old contract continues to drain its queue through subsequent system calls with empty calldata, and the system invocation is changed to call the new contract with non-empty calldata. The non-empty calldata sets `stored_excess` to `INHIBITOR` on the old contract, rejecting all new requests, while the new contract accepts new requests. For the duration of one fork, the old contract is drained to empty and the new contract receives new deposits.
105
+
79
106
### Request fee
80
107
81
108
Each request carries a fee, computed as in [EIP-7002](./eip-7002.md):
where `fake_exponential` is the [EIP-1559](./eip-1559.md)-style integer approximation of `MIN_REQUEST_FEE * e**(excess / REQUEST_FEE_UPDATE_FRACTION)`. The fee rises super-linearly while blocks contain more than the target number of requests and decays back to `MIN_REQUEST_FEE` otherwise. It is charged on top of any staked value and is left locked in the contract.
115
+
where `TARGET_REQUESTS_PER_BLOCK` is `TARGET_DEPOSIT_REQUESTS_PER_BLOCK` or `TARGET_EXIT_REQUESTS_PER_BLOCK` as applicable, `count` is read from slot `1` before incrementing it for the current request, and `fake_exponential` is the [EIP-1559](./eip-1559.md)-style integer approximation of `MIN_REQUEST_FEE * e**(effective_excess / REQUEST_FEE_UPDATE_FRACTION)`. The fee rises super-linearly while blocks contain more than the target number of requests and decays back to `MIN_REQUEST_FEE` otherwise. It is charged on top of any staked value and is left locked in the contract.
88
116
89
-
Both contracts are modified to apply fee increases for each write path, rather than at end-of-block, as in [EIP-7002](./eip-7002.md).
117
+
Unlike [EIP-7002](./eip-7002.md), both contracts include the current `count` when they compute the fee for a non-system call. This allows the fee to increase within a block. The end-of-block system call still updates `stored_excess`as specified above.
90
118
91
119
### Deposit requests
92
120
@@ -101,7 +129,7 @@ A deposit request is submitted by calling `BUILDER_DEPOSIT_CONTRACT_ADDRESS` wit
101
129
102
130
A deposit request serves both a builder's first deposit and subsequent top-ups. The contract MUST reject the request unless `amount * 1 gwei >= BUILDER_MIN_DEPOSIT` and `msg.value >= amount * 1 gwei + fee`. Any value beyond `amount * 1 gwei + fee` is retained by the contract and not credited to the builder.
103
131
104
-
On success the contract queues the 184 input bytes. The dequeued record is the input verbatim with `amount` converted to little-endian, as in [EIP-7002](./eip-7002.md). The contract does not verify the `signature`. It is carried in the record and verified by the consensus layer. Submitters SHOULD verify the proof-of-possession off-chain before broadcasting a first deposit.
132
+
On success, the contract queues the exact 184-byte input and emits the same bytes as an anonymous log. The amount is therefore big-endian in both queue storage and the log. When the record is dequeued, the system output converts the amount to little-endian, as in [EIP-7002](./eip-7002.md). The contract does not verify the `signature`. It is carried in the record and verified by the consensus layer. Submitters SHOULD verify the proof-of-possession off-chain before broadcasting a first deposit.
105
133
106
134
### Exit requests
107
135
@@ -175,7 +203,7 @@ This EIP is additive at the execution layer. It introduces new contracts at prev
175
203
176
204
## Reference Implementation
177
205
178
-
See [`src/builder_deposits`](https://github.com/ethereum/sys-asm/tree/3bba94c696bc46beeecef05db6c2df0f18b4b239/src/builder_deposits) and [`src/builder_deposits`](https://github.com/ethereum/sys-asm/tree/3bba94c696bc46beeecef05db6c2df0f18b4b239/src/builder_exits).
206
+
See [`src/builder_deposits`](https://github.com/ethereum/sys-asm/tree/83f9801245ff56878a450b5625801101b9a225a1/src/builder_deposits) and [`src/builder_exits`](https://github.com/ethereum/sys-asm/tree/83f9801245ff56878a450b5625801101b9a225a1/src/builder_exits).
0 commit comments