EIP-7956 - Tx Ordering via Block-level Randomness

Created 2025-05-24
Status Draft
Category Core
Type Standards Track
Authors

Abstract

Proposers and builders can currently permute pending transactions arbitrarily, enabling reorder‑driven MEV. This EIP introduces a consensus rule that sorts all transactions inside a block by XOR‑ing each transaction hash with fresh slot randomness. The randomness is unknown until the slot starts, so the order is deterministic once known but unpredictable beforehand. The mechanism significantly reduces reorder‑based MEV; latency‑driven back‑running, censorship, and other classes of MEV remain and should be mitigated through complementary techniques (encrypted mempools, reputation, PBS marketplaces, etc.).

Motivation

Unrestricted ordering is the key enabler of sandwich and classic front‑running attacks. Deterministic ordering collapses these vectors to latency racing and information asymmetry. Clear candidate‑set and bundle semantics preserve fee markets while removing the need for trusted sequencers. Academic works shows deterministic ordering drives sandwich profits toward zero.

References

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Slot Randomness R

Consensus‑layer prerequisite — Companion EIP “EL‑VRF‑exposure” is needed to add the RANDAO's per‑slot VRF output to the execution layer.

<-- TODO -- add companion EIP “EL‑VRF‑exposure” -->

R = randao_mix_slot[0:16]     : bytes16

Execution payloads include randomness: bytes16 that MUST equal R; execution clients verify via EIP‑4788.

Builder Flow

  1. Candidate‑set selection – Builders MAY choose any subset of the mempool based on priority fees, side agreements, or policy. Transactions not chosen are ignored.
  2. Canonical sorting – Sort the chosen set by primary key H(tx) ⊕ R ascending, then secondary key H(tx) ascending, in case of collision on the primary key.
  3. Gas‑limit packing – Append items in order until adding the next would exceed the block gas limit.
  4. Bundles (optional cross‑address atomicity)

  5. Definition – A bundle is a user‑signed list of fully‑signed transactions. Each child_tx_rlp is the canonical signed RLP encoding, including signature fields (v, r, s). The bundle begins with a fee‑payment transaction that covers gas and builder tip for the entire bundle.

  6. Hashing / sort key – Treat the bundle as a virtual transaction with key H(concat(child_tx_rlps)), where:

  7. child_tx_rlps[i] MUST be the exact bytes that will later appear in the block body for that transaction, i.e. the canonical RLP of the fully‑signed transaction per EIP‑2718 / EIP‑155 rules (for typed transactions the leading type byte and length prefix are included).

  8. Implementations MUST NOT strip or normalise the signature fields (v,r,s); those 65 bytes are hashed as‑is so every participant derives an identical bundle key.
  9. The concatenation order is the author‑declared execution order of the child transactions.
  10. Gas accounting – Bundle gas is the sum of the gasLimit fields of all child transactions. Builders use that sum when evaluating step 3.
  11. Fit‑or‑skip rule – If the bundle (fee‑payment + children) would exceed the remaining gas limit, the bundle is skipped atomically.

  12. Fee dynamics – Priority fees influence membership in the candidate set (step 1) but never override the canonical order once a tx or bundle is selected.

Here is a pseudocode for the above flow:

//---------------------------------------------------------------------
// Inputs
//---------------------------------------------------------------------
// mempool          : All pending txs & user-signed bundles visible to the builder
// R                : 16-byte slot randomness supplied by consensus layer
// BLOCK_GAS_LIMIT  : Max gas allowed in the block

//---------------------------------------------------------------------
// Helper functions
//---------------------------------------------------------------------
function HASH(obj)           → bytes32       // Keccak-256 of the byte sequence
function PRIMARY_KEY(h, R)   → bytes32       // h XOR R (bit-wise)

function bundleGas(bundle):                      // sum of gasLimit of children
    total ← 0
    for childTx in bundle.children:
        total ← total + childTx.gasLimit
    return total

//---------------------------------------------------------------------
// 0. Candidate-set selection  (builder policy / side deals / fee filter)
//---------------------------------------------------------------------
candidates ← pickSubsetFrom(mempool)            //  ANY subset is allowed

//---------------------------------------------------------------------
// 1. Compute canonical keys & gas cost for every candidate
//---------------------------------------------------------------------
for item in candidates:
    if item.type == "single_tx":
        item.gasCost  ← item.gasLimit
        baseHash      ← HASH(item.RLP)          // canonical signed RLP (EIP-2718)
    else if item.type == "bundle":
        concatRlps    ← CONCAT(item.child_rlps) // fee-tx + children in author order
        item.gasCost  ← bundleGas(item)
        baseHash      ← HASH(concatRlps)

    item.primaryKey   ← PRIMARY_KEY(baseHash, R)
    item.secondaryKey ← baseHash

//---------------------------------------------------------------------
// 2. Canonical sorting  (primaryKey asc, then secondaryKey asc)
//---------------------------------------------------------------------
sort(candidates, by = (primaryKey ASC, secondaryKey ASC))

//---------------------------------------------------------------------
// 3. Gas-limit packing with fit-or-skip for bundles
//---------------------------------------------------------------------
blockList ← empty list
gasUsed   ← 0

for item in candidates:
    if gasUsed + item.gasCost > BLOCK_GAS_LIMIT:
        continue               // skip whole tx or bundle atomically
    append(blockList, item)
    gasUsed ← gasUsed + item.gasCost

//---------------------------------------------------------------------
// 4. Output — assemble block body & header field
//---------------------------------------------------------------------
block.randomness          ← R               // bytes16 in the payload header
block.txOrderingVersion   ← 1
block.body                ← flatten(blockList)   // bundles explode into children
return block

Consensus Rule

A block is invalid if the executed list deviates from the canonical order derived from its randomness and the included transactions/bundles. Verification is objective; fork‑choice remains unchanged.

Performance

Sorting ≤ 1 500 transactions remains O(n log n) (< 1 ms), on today's hardware.

Deployment

Added Parameters

Fork Parameters

Both parameters are constants in the fork config and may be tuned during test‑net experiments.

Activation Flow

  1. Consensus‑layer upgrade — Beacon‑chain fork at ORDERING_FORK_EPOCH activates EL‑VRF‑exposure (companion EIP) and begins populating the vrf_output_proposer field. Execution clients receiving the ExecutePayload after this epoch expect a non‑zero randomness field.

  2. Execution‑layer handshake — Builders and proposers include the new randomness and txOrderingVersion fields in Engine API engine_newPayloadV3 calls. Legacy nodes that have not upgraded will reject the payload, causing a natural chain split and economic incentive to upgrade.

  3. Transition window — For ORDERING_TRANSITION_EPOCHS after ORDERING_FORK_EPOCH, clients accept:

  4. Version 0 blocks — txOrderingVersion == 0; no randomness; legacy ordering.

  5. Version 1 blocks — txOrderingVersion == 1; valid randomness; canonical ordering enforced. During this period proposers are encouraged (but not forced) to adopt version 1 so that fee markets and MEV supply chains have time to adjust.

  6. Finalisation — At ORDERING_FORK_EPOCH + ORDERING_TRANSITION_EPOCHS the consensus rule changes: blocks MUST set txOrderingVersion == 1 and pass canonical‑order validation. A version‑0 block after this point is treated as invalid and will not be considered by fork‑choice.

Rationale

Why randomness‑driven ordering?

Why 128‑bit R rather than full 256 bits?

Why XOR as the mixing function?

Why allow builders to curate the candidate set first?

Why a secondary key H(tx) for tie‑breaking?

Why optional bundles instead of implicit nonce‑chain folding?

Why the "fit‑or‑skip" bundle rule?

Why the version flag + transition window?

Backwards Compatibility

Security Considerations

Randomness Bias & RANDAO Manipulation

Conclusion: Collusion attacks are economically unattractive; the mixed entropy from RANDAO and VRF provides strong unpredictability guarantees.

Hash Grinding

New signatures are required only when calldata changes, but attacks must begin after R is known (≤ 12 s). Propagation delays and inclusion fees sharply limit profitable grinding to high‑value trades.

Tie Collisions

Secondary key H(tx) guarantees total order; collision probability (2^{-256}) is negligible.

Bundle Gas Consistency

Explicit summation rule ensures every client computes identical gas usage for bundles, preventing divergent validation.

Residual MEV Vectors

Copyright

Copyright and related rights waived via CC0.