Skip to content

Commit b759aae

Browse files
Th0rgalTh0rgal
authored andcommitted
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
1 parent ca8dfe9 commit b759aae

1 file changed

Lines changed: 38 additions & 10 deletions

File tree

EIPS/eip-8282.md

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ status: Review
88
type: Standards Track
99
category: Core
1010
created: 2026-05-22
11-
requires: 1559, 7685, 7732
11+
requires: 1559, 7685, 7732, 7997
1212
---
1313

1414
## Abstract
@@ -46,6 +46,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S
4646
| `TARGET_EXIT_REQUESTS_PER_BLOCK` | `2` | Per-block request count above which the fee rises for the exit contract |
4747
| `MIN_REQUEST_FEE` | `1` | Minimum request fee, in wei |
4848
| `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`. |
4950
| `BUILDER_MIN_DEPOSIT` | `1000000000000000000` | Minimum credited stake for a deposit, in wei (1 ETH — the [EIP-7732](./eip-7732.md) builder minimum) |
5051
| `BUILDER_DEPOSIT_CONTRACT_RUNTIME_CODE` | *see [Reference Implementation](#reference-implementation)* | Runtime bytecode of the builder deposit contract |
5152
| `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)).
5859

5960
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.
6061

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+
6164
### Request queue and system call
6265

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.
6477

6578
#### Write path
6679

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.
6883

6984
#### Fee getter
7085

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.
7287

7388
#### System call
7489

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.
7699

77100
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.
78101

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+
79106
### Request fee
80107

81108
Each request carries a fee, computed as in [EIP-7002](./eip-7002.md):
82109

83110
```
84-
fee = fake_exponential(MIN_REQUEST_FEE, excess, REQUEST_FEE_UPDATE_FRACTION)
111+
effective_excess = stored_excess + max(0, count - TARGET_REQUESTS_PER_BLOCK)
112+
fee = fake_exponential(MIN_REQUEST_FEE, effective_excess, REQUEST_FEE_UPDATE_FRACTION)
85113
```
86114

87-
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.
88116

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.
90118

91119
### Deposit requests
92120

@@ -101,7 +129,7 @@ A deposit request is submitted by calling `BUILDER_DEPOSIT_CONTRACT_ADDRESS` wit
101129

102130
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.
103131

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.
105133

106134
### Exit requests
107135

@@ -175,7 +203,7 @@ This EIP is additive at the execution layer. It introduces new contracts at prev
175203

176204
## Reference Implementation
177205

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).
179207

180208
## Security Considerations
181209

0 commit comments

Comments
 (0)