EIP-8202 - Scheme-Agile Transactions

Created 2026-03-22
Status Draft
Category Core
Type Standards Track
Authors
Requires

Abstract

This EIP introduces a new EIP-2718 typed transaction (TransactionType = 0x05) called SchemedTransaction.

A SchemedTransaction has one common EIP-1559-style fee header and one common execution payload (to, value, data). Optional features are attached as flat typed components inside the payload rather than by minting a new top-level transaction family for every combination.

The transaction has two typed attachment lists:

Scheme agility is expressed as an authorization capability, not a separate transaction family. This EIP initially defines one required top-level authorization role, SENDER, and two sender signature schemes:

This EIP also defines two initial extensions:

The extension model is open to future additions. For example, an EIP-8141-style frame extension could attach nested execution frames to the same transaction envelope without requiring a new top-level transaction type.

The sender address is derived deterministically from the public key recovered or verified from the SENDER authorization. For secp256k1, address derivation is exactly the same as legacy Ethereum. For ephemeral secp256k1, the address is derived from a Merkle root over the full key sequence, providing a stable identity across key rotations.

This design keeps transaction composition flat: one execution payload, many orthogonal capabilities, and no recursive transaction structure or frame execution model.

Motivation

Stop proliferating top-level transaction families

Ethereum currently adds new features such as blobs and set-code authorizations by minting fresh EIP-2718 transaction types whose payloads are each mostly EIP-1559-shaped with a few appended fields.

That approach does not compose well. A user who wants passkey signing, blobs, and set-code authorization should not require yet another top-level transaction family. The cleaner model is:

Under EIP-2718, the payload of a typed transaction is already just an opaque byte string. This EIP uses that flexibility to define one outer type whose inner parts compose cleanly.

Native quantum-safe support

Quantum computers will eventually break ECDSA over secp256k1. Ethereum needs a user-facing transaction format that lets EOAs migrate to quantum-safe signing without wrapping execution in a new abstraction.

The ephemeral secp256k1 scheme achieves quantum safety by making each key pair single-use, eliminating long-term public key exposure. This requires no new cryptographic primitives beyond Keccak-256 preimage resistance. Future signature schemes (P256/passkeys, Falcon-512, or other post-quantum algorithms) can be added as new scheme_id values without changing the transaction envelope.

Flat composition, not recursive composition

This EIP is intentionally not a frame transaction system. It does not introduce recursive transactions, multiple execution payloads, new execution modes, or new opcodes.

The model is:

This keeps validation phase-based and close to today's transaction processing, while still allowing new protocol features and new signature schemes to be added without minting a new outer transaction type for every combination.

Specification

Constants

Name Value Description
SCHEMED_TX_TYPE 0x05 EIP-2718 transaction type byte
ROLE_SENDER 0x00 Top-level sender authorization
ROLE_PAYER 0x01 Reserved for future use
SCHEME_SECP256K1 0x00 secp256k1 ECDSA
SCHEME_EPHEMERAL_K1 0x01 Ephemeral secp256k1 with Merkle-committed key rotation
EXT_BLOB 0x01 Blob extension
EXT_SET_CODE 0x02 Set-code / delegation extension
SET_CODE_AUTH_MAGIC 0x05 Domain byte for set-code authorization messages
PER_AUTH_BASE_COST 12500 Per-authorization processing base cost
PER_EMPTY_ACCOUNT_COST 25000 Per-authorization intrinsic charge
EPHEMERAL_TREE_DEPTH 20 Merkle tree depth for ephemeral key commitments
EPHEMERAL_PROOF_LEN 640 Merkle proof length in bytes (20 × 32)

The role_id, scheme_id, and extension_id spaces each range from 0x00 to 0xFF. New values MUST be specified by a distinct EIP.

SET_CODE_AUTH_MAGIC, PER_AUTH_BASE_COST, and PER_EMPTY_ACCOUNT_COST intentionally match the corresponding EIP-7702 constants.

Transaction Envelope

A SchemedTransaction is serialized as:

0x05 || rlp([ chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, authorizations, extensions ])

This defines one canonical base transaction body:

base = [
    chain_id,
    nonce,
    max_priority_fee_per_gas,
    max_fee_per_gas,
    gas_limit,
    to,
    value,
    data
]

Field Definitions

Field Type Description
chain_id uint256 EIP-155 chain identifier
nonce uint64 Sender account nonce
max_priority_fee_per_gas uint256 EIP-1559 priority fee
max_fee_per_gas uint256 EIP-1559 max fee
gas_limit uint64 Gas limit
to Address \| null Recipient address, or null for contract creation
value uint256 Wei to transfer
data bytes Calldata / initcode
authorizations List[Authorization] Ordered transaction authorizations
extensions List[Extension] Typed extension list

Base rules:

Authorization and Extension Containers

An authorization is encoded as:

authorization = [role_id, scheme_id, witness]

An extension is encoded as:

extension = [extension_id, extension_payload]

witness and extension_payload are opaque bytes whose internal meaning is determined by scheme_id and extension_id respectively. In this EIP, each defined witness or extension payload is itself a canonical RLP encoding of the scheme-specific or extension-specific structure.

Signing Data

The sender signs the full base transaction and all extensions, with authorization witnesses elided from the current and later authorization positions.

For this EIP, valid transactions contain exactly one top-level authorization, so this reduces to signing the full transaction with the sender witness omitted.

def signing_hash(tx, auth_index):
    committed_auths = []
    for j, auth in enumerate(tx.authorizations):
        if j < auth_index:
            committed_auths.append(auth)
        else:
            committed_auths.append([auth.role_id, auth.scheme_id])

    return keccak256(
        SCHEMED_TX_TYPE || rlp([
            tx.chain_id,
            tx.nonce,
            tx.max_priority_fee_per_gas,
            tx.max_fee_per_gas,
            tx.gas_limit,
            tx.to,
            tx.value,
            tx.data,
            committed_auths,
            tx.extensions
        ])
    )

For the required sender authorization at index 0, this is equivalent to:

def sender_signing_hash(tx):
    return keccak256(
        SCHEMED_TX_TYPE || rlp([
            tx.chain_id,
            tx.nonce,
            tx.max_priority_fee_per_gas,
            tx.max_fee_per_gas,
            tx.gas_limit,
            tx.to,
            tx.value,
            tx.data,
            [[ROLE_SENDER, tx.authorizations[0].scheme_id]],
            tx.extensions
        ])
    )

Scheme Registry

Each registered signature scheme defines:

This EIP initially defines two scheme_id values.

Signature Schemes

secp256k1 (0x00)

Witness payload:

rlp([y_parity, r, s])
Field Size
y_parity 1 byte
r 32 bytes
s 32 bytes

s MUST satisfy s <= secp256k1n / 2 per EIP-2.

Public key recovery and address derivation:

pubkey = ecrecover(sender_signing_hash(tx), y_parity, r, s)  # 64-byte uncompressed x || y
sender = keccak256(pubkey)[12:]

This intentionally preserves legacy Ethereum address derivation for secp256k1 keys.

Scheme gas surcharge: 0.

Ephemeral secp256k1 (0x01)

This scheme achieves quantum safety by making each secp256k1 key pair single-use. At account creation, the user derives a deterministic sequence of key pairs (e.g. via BIP 44) and commits to all future public keys by constructing a Merkle tree over their hashes. The Merkle root is embedded in the sender address. Each transaction provides a standard ECDSA signature plus a Merkle proof that the signing key is the leaf at position nonce in the committed tree.

The long-lived secret is the user's seed (e.g. a BIP-39 mnemonic), which never touches the chain. Each ephemeral private key sk_i is derived on-the-fly, used exactly once for the transaction at nonce i, and is never valid again because the protocol binds the Merkle leaf index to the account nonce.

Account Setup

Account setup is performed entirely off-chain:

seed = generate_bip39_mnemonic()

leaves = []
for i in range(2**EPHEMERAL_TREE_DEPTH):
    sk_i = derive_bip44(seed, path=f"m/44'/60'/0'/0/{i}")
    pk_i = secp256k1_pubkey(sk_i)        # 64 bytes, uncompressed x || y
    leaves.append(keccak256(pk_i))

merkle_root = build_merkle_tree(leaves)   # binary Keccak-256 Merkle tree

# The address is derived from the root — no on-chain state required
address = keccak256(SCHEME_EPHEMERAL_K1 || merkle_root)[12:]

No on-chain registration, storage writes, or contract deployment is required. The Merkle root is carried in every transaction witness and validated against the sender address.

Witness Payload
rlp([y_parity, r, s, merkle_root, merkle_proof])
Field Size
y_parity 1 byte
r 32 bytes
s 32 bytes
merkle_root 32 bytes
merkle_proof EPHEMERAL_TREE_DEPTH * 32 = 640 bytes

s MUST satisfy s <= secp256k1n / 2 per EIP-2.

merkle_proof is an ordered list of EPHEMERAL_TREE_DEPTH sibling hashes for the leaf-to-root path.

Signing
def sign_ephemeral(tx, seed, merkle_tree):
    i = tx.nonce
    sk_i = derive_bip44(seed, path=f"m/44'/60'/0'/0/{i}")

    # Compute signing hash — identical to all other schemes
    msg = sender_signing_hash(tx)

    # Standard secp256k1 ECDSA
    y_parity, r, s = ecdsa_sign(sk_i, msg)

    # Merkle proof that pk_i is the leaf at position i
    merkle_root = merkle_tree.root
    merkle_proof = merkle_tree.prove(i)   # EPHEMERAL_TREE_DEPTH × 32 bytes

    # Construct the witness
    witness = rlp([y_parity, r, s, merkle_root, merkle_proof])

    # Build the authorization
    tx.authorizations = [[ROLE_SENDER, SCHEME_EPHEMERAL_K1, witness]]

    return tx
Verification and Address Derivation
def verify_ephemeral_k1(tx):
    auth = tx.authorizations[0]
    assert auth.role_id == ROLE_SENDER
    assert auth.scheme_id == SCHEME_EPHEMERAL_K1

    y_parity, r, s, merkle_root, merkle_proof = rlp_decode(auth.witness)

    # 1. Address binding — the Merkle root must match the sender address
    derived_address = keccak256(SCHEME_EPHEMERAL_K1 || merkle_root)[12:]
    assert derived_address == tx.sender

    # 2. Recover public key from ECDSA signature
    msg = sender_signing_hash(tx)
    pubkey = ecrecover(msg, y_parity, r, s)

    # 3. Merkle membership — pubkey hash must be the leaf at position nonce
    leaf = keccak256(pubkey)
    assert verify_merkle_proof(merkle_root, merkle_proof, leaf, index=tx.nonce)

    # 4. Standard s-value check (EIP-2)
    assert s <= secp256k1n // 2

    return derived_address

The Merkle proof verification uses a standard binary Keccak-256 Merkle tree. verify_merkle_proof checks that hashing the leaf with the provided sibling path from position index produces merkle_root.

Key Material Summary
Secret Location On-chain exposure
BIP 39 seed User's device only Never
sk_i (ephemeral private key) Derived on-the-fly for nonce i Never (public key exposed for exactly one transaction)
merkle_root Encoded in the address, carried in witness Fully public by design
merkle_tree User's device (or recomputable from seed) Per-transaction proof is public
Why This Is Quantum-Safe

After transaction at nonce i, the attacker observes pk_i (recoverable from the ECDSA signature). A quantum computer running Shor's algorithm can derive sk_i from pk_i. However, sk_i is now useless: the next valid transaction requires nonce i+1, which requires a Merkle proof for leaf i+1. To forge that transaction, the attacker needs sk_{i+1}, which requires pk_{i+1}, which requires inverting keccak256(pk_{i+1}) from the Merkle leaf — a preimage attack on Keccak-256. Grover's algorithm provides a quadratic speedup on brute-force preimage search but Keccak-256 retains 128-bit security against Grover's, which is computationally infeasible.

The security assumption is therefore: Keccak-256 preimage resistance, not the hardness of the discrete logarithm problem.

Scheme gas surcharge: 5,000 gas.

Extensions

Extensions attach optional typed protocol features to the transaction without minting a new outer transaction type.

Each extension is encoded as:

extension = [extension_id, extension_payload]

Rules:

This EIP defines two initial extensions.

Blob Extension (EXT_BLOB = 0x01)

Extension payload:

rlp([max_fee_per_blob_gas, blob_versioned_hashes])
Field Type Description
max_fee_per_blob_gas uint256 Per-blob gas cap
blob_versioned_hashes List[bytes32] Versioned hashes for referenced blobs

This extension carries the transaction-local blob fields of EIP-4844 under a typed extension instead of requiring a dedicated top-level blob transaction family. EIP-4844 defines these as max_fee_per_blob_gas and blob_versioned_hashes, and blob transactions there also forbid to = nil; this extension preserves those transaction-local semantics. The sidecar format, blob fee lane, and block-level blob accounting remain those of EIP-4844.

Additional rules:

Set-Code Extension (EXT_SET_CODE = 0x02)

Extension payload:

rlp([authorization_list])

Where each authorization item is:

set_code_authorization = [chain_id, delegate_address, nonce, scheme_id, witness]

This extension carries EIP-7702-style pre-execution set-code authorizations. All processing rules, gas constants, and refund behavior follow EIP-7702. The differences from EIP-7702 are:

If EXT_SET_CODE is present, authorization_list MUST be non-empty.

Set-Code Gas Accounting

Relative to EIP-7702, the gas accounting delta is only envelope-level:

The set-code authorization gas constants and refund behavior are otherwise identical to EIP-7702.

The intrinsic gas for a SchemedTransaction follows the decomposed model of EIP-2780 and the calldata floor of EIP-7976:

intrinsic_gas =
    TX_BASE_COST
    + calldata_gas(data)
    + sender_scheme_gas_surcharge(sender_authorization)
    + extension_intrinsic_gas(extensions)

Where TX_BASE_COST is the decomposed base cost from EIP-2780 (replacing the legacy flat 21,000) and calldata_gas follows the uniform per-byte floor of EIP-7976.

The sender scheme surcharge replaces the ECRECOVER component of the EIP-2780 base cost. For secp256k1, the cost is equivalent. For other schemes, the surcharge reflects the verification cost and witness size:

Scheme Surcharge
SCHEME_SECP256K1 0
SCHEME_EPHEMERAL_K1 5,000

Extension-specific charging rules are:

The overhead of a non-secp256k1 sender scheme relative to a standard ECDSA transaction is:

scheme_overhead = calldata_cost(scheme_witness) - calldata_cost(ecdsa_witness) + scheme_surcharge

Where ecdsa_witness is the 65-byte secp256k1 (y_parity, r, s) witness and calldata_cost follows the EIP-7976 uniform floor (64 gas/byte). For ephemeral secp256k1 (737-byte witness including Merkle root and proof), the calldata overhead is (737 - 65) * 64 = 43,008 gas plus the 5,000 gas surcharge; verification is a standard ecrecover plus EPHEMERAL_TREE_DEPTH Keccak-256 hashes for the Merkle proof.

Transaction Validation

A node validating a SchemedTransaction MUST perform the following checks in order:

  1. Type check

  2. First byte is 0x05.

  3. RLP decode

  4. Decode the payload according to the field list above.

  5. Chain ID

  6. chain_id matches the node's chain.

  7. Authorization structure

  8. authorizations contains exactly one ROLE_SENDER.

  9. Unknown role_id is invalid.
  10. Unknown scheme_id is invalid.

  11. Extension structure

  12. extensions contains no duplicate extension_id.

  13. Unknown extension_id is invalid.
  14. Each extension payload is statelessly well-formed.

  15. Sender verification

  16. Compute sender_signing_hash(tx).

  17. Verify or recover the sender public key using the sender authorization scheme.
  18. For SCHEME_EPHEMERAL_K1: verify that the Merkle root in the witness matches the sender address, recover the public key, and verify the Merkle proof at leaf index tx.nonce.
  19. Derive the sender address.

  20. Nonce and balance

  21. Sender account nonce matches tx.nonce.

  22. Sender has sufficient balance for transaction execution and all extension-specific fee lanes.

  23. Pre-execution extension processing

  24. Apply stateless and fee-lane checks for EXT_BLOB.

  25. Apply set-code authorizations for EXT_SET_CODE.

  26. Intrinsic gas

  27. Charge base intrinsic gas, calldata cost, sender scheme surcharge, and extension-specific intrinsic charges.

  28. Execute

  29. Execute the normal call/create payload using to, value, and data.

Receipt

The EIP-2718 ReceiptPayload for this transaction is:

rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])

Identical to EIP-1559, EIP-4844, and EIP-7702.

Rationale

Why a single outer transaction type?

A single new transaction type is still needed because the transaction body itself changes. The point of this EIP is not to avoid adding one new outer type - it is to avoid adding a new outer type for every future combination of features.

Why make scheme agility an authorization capability?

Signature scheme choice determines how the sender proves control of the transaction. That is an authorization concern, not a payload family. Defining scheme agility as an authorization capability means future signature schemes only register:

They do not need to redefine the outer transaction envelope.

Why flat extensions instead of nested transactions?

Composing whole existing payloads inside a new transaction preserves the inheritance tree this EIP is trying to remove. The cleaner model is:

This yields:

No new outer type is needed for any of those combinations.

Why no access list in the base type?

Transaction-level access lists are being deprecated. Any protocol benefit they provided has been superseded by block-level access lists (EIP-7928), and EIP-7981 increases their cost to reflect their diminished utility. This EIP omits access_list from the base transaction entirely rather than carrying forward a deprecated field. Similarly, a frame extension as proposed in EIP-8141 could be added as a new extension_id to support nested execution frames within the same transaction envelope.

Why preserve the legacy secp256k1 address?

For SCHEME_SECP256K1, this EIP intentionally preserves Ethereum's existing keccak256(pubkey)[12:] address derivation. That avoids creating a second address for the same secp256k1 key and lets current EOAs use the new transaction format without on-chain migration.

Why hash scheme_id into non-secp256k1 addresses?

For non-secp256k1 schemes, keccak256(scheme_id || pubkey)[12:] or keccak256(scheme_id || merkle_root)[12:] provides address domain separation from secp256k1 and from each other. Future schemes (P256/passkeys, Falcon-512, etc.) added via new scheme_id values will inherit this separation automatically.

Why include an ephemeral secp256k1 scheme?

The ephemeral secp256k1 scheme provides an immediately deployable quantum-safe alternative that reuses existing ECDSA infrastructure, with no new cryptographic primitives.

The security model shifts from "the signature scheme is quantum-resistant" to "each key is used exactly once, so recovering it is useless." This relies only on Keccak-256 preimage resistance (128-bit security against Grover's algorithm), not on any new cryptographic assumption.

The ephemeral scheme is stateless at the protocol level: the Merkle root is embedded in the sender address and carried in every witness. No new account fields, no storage writes, no contract deployment. The account nonce, already enforced by the protocol, serves as the Merkle leaf index. This makes it the lightest-weight quantum migration path available.

Users can start with SCHEME_EPHEMERAL_K1 for immediate quantum safety and later migrate to a future post-quantum scheme as those mature — potentially using EXT_SET_CODE to delegate their ephemeral account to a contract that bridges the transition.

Why spell out the set-code extension instead of inheriting EIP-7702 by reference?

Because set-code authorizations now participate in a scheme-agile transaction model, each authorization in the list carries its own (scheme_id, witness) pair. This means different authorizations within the same transaction can use different schemes. EIP-7702 hardcodes (y_parity, r, s) which limits set-code to secp256k1 only. The exact signed payload and exact gas accounting must therefore be explicit. This EIP spells out:

The only semantic changes are:

Everything else intentionally stays aligned with EIP-7702.

Backwards Compatibility

Security Considerations

Address collision across schemes

For secp256k1, this EIP deliberately preserves Ethereum's existing address derivation. For all other schemes, keccak256(scheme_id || pubkey)[12:] or keccak256(scheme_id || merkle_root)[12:] provides domain separation from secp256k1 and from each other. A collision would require a preimage attack on Keccak-256 across distinct inputs, which is computationally infeasible.

Unknown capability handling

Unknown scheme_id, role_id, or extension_id values are invalid. Clients MUST reject them rather than ignore them. Silent acceptance of unknown capabilities would create consensus and wallet-safety hazards.

Flat composition reduces validation complexity

This EIP allows multiple orthogonal capabilities, but only one execution payload. It intentionally forbids recursive transaction composition. That avoids drifting into frame-transaction validation complexity, where mempool validation may require richer execution-aware processing before inclusion.

Quantum migration timing

Any secp256k1 address whose public key has already been revealed on-chain remains vulnerable once ECDSA can be broken. This EIP enables proactive migration to SCHEME_EPHEMERAL_K1.

Users of SCHEME_EPHEMERAL_K1 are protected against retrospective key recovery: even after a quantum computer exists, previously used ephemeral keys are useless because the nonce has advanced. However, the mempool exposure window remains (see below).

Mempool exposure for ephemeral secp256k1

While a SCHEME_EPHEMERAL_K1 transaction is pending in the mempool, the current signing public key pk_i is visible in the witness. A quantum attacker who can run Shor's algorithm within a single block time could recover sk_i and submit a competing transaction at the same nonce. This requires breaking ECDSA within seconds, which is far beyond near-term quantum capabilities. Mitigations include private mempools and L2s with shorter inclusion latencies.

Ephemeral key exhaustion

A SCHEME_EPHEMERAL_K1 account with tree depth EPHEMERAL_TREE_DEPTH = 20 supports 2^20 (approximately 1,048,576) transactions. This is more than sufficient for any individual EOA. If exhaustion approaches, the user should migrate funds to a new ephemeral account (new seed, new Merkle root, new address) or transition to a post-quantum scheme.

Large witness DoS

Ephemeral secp256k1 witnesses are larger than standard ECDSA witnesses (737 bytes vs 65 bytes). The sender surcharge exists to avoid systematic under-pricing of bandwidth and storage costs. Extensions with large payloads must likewise carry their own appropriate pricing.

Set-code non-rollback

As with EIP-7702, processed set-code authorizations are not rolled back if the later execution phase reverts or otherwise fails. Wallets and users MUST treat set-code authorization as a state change that can persist even when the call payload does not succeed.

Downgrade attacks

A future attacker who can forge secp256k1 signatures could target addresses that continue to authorize transactions with SCHEME_SECP256K1. This is not introduced by this EIP. It is the pre-existing quantum threat. Users concerned about it SHOULD migrate to SCHEME_EPHEMERAL_K1.

Keccak-256 and post-quantum security margin

Address derivation, signing hashes, and ephemeral key Merkle trees all rely on Keccak-256. While Keccak is not broken by Shor's algorithm, Grover's algorithm reduces its effective security margin from 256 bits to 128 bits for preimage resistance. For the ephemeral scheme, this 128-bit margin is the primary security parameter — it is what prevents an attacker from recovering a future key from its Merkle leaf. This remains adequate for the foreseeable future, but a later fully post-quantum Ethereum may wish to revisit the hash choice for signing and address derivation. This EIP does not require such a change.

Copyright

Copyright and related rights waived via CC0.