Protocol
Mempool design
8130 transactions work in the existing mempool. Optional hardening can raise builder performance and give accounts higher in-flight rate.
Motivation
8130 transactions work with the existing mempool: admit them with the checklist below, and builders can include them like any other typed transaction. Hardening is optional. When you want it, precise invalidation and two-surface account limits can raise builder performance (drop dead txs before they waste packing work) and give accounts a higher in-flight rate than today’s blunt per-sender caps.
For a standard EIP-1559 transaction, after validation only nonce consumption or sender balance under the max gas cost typically invalidate it. EIP-7702 widens the surface (delegated code can move balance mid-block); today that is often mitigated bluntly with max_inflight_delegated_slots = 1.
EIP-8130 adds more surfaces, but they are known at first sight:
- Payer balance: not necessarily the sender’s.
- Nonce: 2D
(nonce_key, nonce_sequence). - Sender actor_config: one storage slot.
- Payer actor_config: one slot when sponsored.
- Config-change txs: sender account-state / lock slot, plus up to ~3 actor slots touched.
- Expiry: transaction and/or actor key expiry.
Protocol admission checklist
- Parse & structure: Validate
sender_auth. At most one create (0x00, first) and one delegation (0x02). SHOULD cap config-change entries. - Resolve sender:
senderif set; else ecrecover from sender_auth. - Effective owner state: Create path: CREATE2 derivation, empty code, initial actors. Else read Account Configuration.
- Account changes: Reject config/delegation if locked; simulate apply in order.
- Validate sender_auth: SENDER or POLICY scope; delegation also needs admin EOA-actor authority.
- Resolve payer: Self-pay with SELF_PAYER, or sponsored payer_auth with SPONSOR_PAYER.
- Nonce, balance, expiry: 2D or nonce-free rules (below).
- Account limits: Two surfaces (signature + payment); see hardening § limits. Legacy “pending count” is the coarse ancestor of this.
Nonce rules in the pool
| Concept | Meaning |
|---|---|
| Standard / parallel | nonce_key ≠ NONCE_KEY_MAX → nonce_sequence must match current(sender, nonce_key). Parallel keys = independent lanes. |
| Nonce-free | nonce_key == NONCE_KEY_MAX → skip nonce; sequence MUST be 0; expiry MUST be non-zero; SHOULD reject far-future expiry (short window). |
| Dedupe / replace | Standard & 2D: keyed on (sender, nonce_key, nonce_sequence). Nonce-free: keyed on the fee-invariant replay_id. The full transaction hash MUST NOT be used. |
Hardening: WatchSet
At validation (when relevant state is already read), each transaction records a bounded WatchSet of invalidation keys (≤ ~7 for 8130):
| Concept | Meaning |
|---|---|
| Balance(addr) | Payer balance (or sender for self-pay / 1559 / 7702). |
| ProtocolNonce(addr) | EOA protocol nonce, used when nonce_key == 0. |
| Slot { address, slot } | Actor-config, account-state/lock, or 2D nonce-manager channel slot. |
| ExpiryBucket(u64) | Coarse wall-clock bucket for nonce-free and other expiring txs. |
WatchSet by transaction kind
| Concept | Meaning |
|---|---|
| 1559 / 7702 | Balance(sender), ProtocolNonce(sender). |
| 8130 (no config change) | Balance(payer), nonce key¹, Slot(sender actor cfg), Slot(payer actor cfg), ExpiryBucket². |
| 8130 (with config change) | Above + Slot(sender account-state/lock) + up to 3 Slot(actor). |
¹ Nonce key: concrete Slot on the 2D nonce manager for channelized txs; ProtocolNonce(sender) when nonce_key == 0; omitted for nonce-free (NONCE_KEY_MAX), which relies on expiry. ² Present when the tx or an actor key carries expiry; always for nonce-free.
Three invalidation semantics
- Exact-match: nonce, actor-config, account-state/lock. Slot value changed → drop unconditionally.
- Threshold / aggregate: Balance. Re-evaluate against a running reservation (PayerBook), do not auto-drop on every balance touch.
- Time: ExpiryBucket. Fired by wall-clock, proactively one block ahead: when a bucket is due, drop every watcher.
Deep dive: Invalidation & revalidation.
Balance invalidation & high-throughput payers
Balance is the hard case when one payer sponsors many independent senders/channels: affordability is a set property, not a single nonce walk.
PayerBook
balance: cached from the touched-key stream.reserved: Σmax_costof inflight sponsored txs.by_priority: tip-ordered map for eviction (low tip first).
max_cost = gas_limit × max_fee_per_gas + l1_data_fee + operator_fee, attributed to the payer.
- Admission (balance-bounded payers): accept while
reserved + tx.max_cost ≤ balance. - Balance decrease: if
new_balance < reserved, evict from the low-tip end until reserved fits. Keep the most valuable affordable subset. - Balance increase: nothing to drop; optionally promote parked txs.
Self-spend nets out: when a payer’s own sponsored txs mine, reserved decrements in lockstep, so no extra eviction. A payer at full tilt within budget causes zero invalidation churn.
Account limits (two surfaces)
Every tx names a sender X and payer Y (equal for 1559/7702; possibly distinct for 8130). Bound the two invalidation surfaces with independent per-account counts; admit only if both pass:
| Concept | Meaning |
|---|---|
| Signature count S[a] | Blast radius of one owner-config change to a, charged when a authenticates (sender always; payer also on sponsored), default cap 4. Locked → exempt (config immutable). |
| Payment count P[a] | Blast radius of one balance change to a, charged when a pays, default cap 4. Locked + trusted bytecode → balance book (Σ max_cost ≤ balance) instead of a count. |
Charging rule (sender X, payer Y)
S[X] += 1unless X locked.S[Y] += 1unless Y locked, skipped when Y == X (self-pay = one signature).- Y payment: if Y trusted → reserve in PayerBook; else
P[Y] += 1.
Hard reject at the cap; fee-bump via nonce replacement. Rejection labels for metrics: sender, payer, payment, payer_balance.
Self-pay (X == Y == A)
| Concept | Meaning |
|---|---|
| Unlocked, untrusted | S+1 and P+1 (cap 4 each) → ≤ 4 txs; per-tx affordability. |
| Locked, untrusted | S exempt; P+1 (cap 4) → ≤ 4 txs on payment count. |
| Locked, trusted | S exempt; P → book → unlimited count, Σ max_cost ≤ balance. |
Sponsored (X ≠ Y)
| Concept | Meaning |
|---|---|
| Sender X | S[X]+1 unless locked (then exempt), no payment charge. |
| Payer Y unlocked | S[Y]+1 and P[Y]+1 (both cap 4). |
| Payer Y locked untrusted | S exempt; P+1 (cap 4). |
| Payer Y locked trusted | S exempt; payment → balance book. |
Guarantees
- Config-change blast to A ≤ S[A] (default 4) across sends, self-pays, and sponsorships. Locked ⇒ blast is zero.
- Balance-change blast to A ≤ P[A] (default 4) when untrusted; trusted locked payers bounded by Σ max_cost ≤ balance.
Why trusted bytecode makes the balance bound sound
Trusted bytecode guarantees ETH only leaves through gas the account sponsors while locked; it cannot send ETH-moving txs, then reserved = Σ max_cost is a true upper bound on the only drain, so “admit while reserved ≤ balance” is provably safe. The PayerBook is the whole story (no separate protocol-pool slice). Balance-bounded accounting applies only to locked + trusted accounts; everyone else uses payment count + per-tx affordability.
Lock / bytecode cache
Lock lives in the packed account-state slot: LOCKED, UNLOCK_INITIATED, and lock_union (either unlock_delay or unlocks_at). Nodes MAY grant a higher pending-tx tier when the account is locked, has no pending unlock, and unlock_delay meets a threshold (e.g. ≥ 6 hours). Cache lock state and trusted-bytecode classification per account; coherence rides the same touched-key stream because the account-state slot is already a WatchSet key. Locked accounts are signature-exempt for config blast precisely because that slot is not changing until unlock completes.
Authenticator policy
Prefer canonical-only admission on production chains. The canonical authenticator set gives known gas bounds and no external state deps, so each transaction's work is bounded and its WatchSet is complete. Non-canonical authenticators are not admitted on the 8130 path at all (they still run inside ordinary EVM execution); extending the allowlist beyond the canonical set reintroduces unbounded validation work and fights the WatchSet model. See Authenticators & locks.