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:
authorizations - ordered transaction authorizationsextensions - flat typed extensions, unique by extension_idScheme 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.
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.
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.
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.
| 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.
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 | 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:
to = null means contract creation.access_list in the base transaction type.authorizations is ordered and the order is signed.extensions is a flat typed list and MUST be unique by extension_id.extension_id is invalid.ROLE_SENDER authorization is required.ROLE_PAYER is reserved and MUST NOT appear.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.
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
])
)
Each registered signature scheme defines:
scheme_idverify_or_recover(message, witness) -> pubkeyThis EIP initially defines two scheme_id values.
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.
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 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.
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.
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
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.
| 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 |
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 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:
extensions MUST be unique by extension_idextension_id is invalidThis EIP defines two initial extensions.
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:
EXT_BLOB is present, to MUST NOT be nullblob_versioned_hashes MUST be non-emptyEXT_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:
(y_parity, r, s) is replaced by (scheme_id, witness), making authorizations scheme-agilescheme_id is included in the authorization message hash: keccak256(SET_CODE_AUTH_MAGIC || rlp([chain_id, delegate_address, nonce, scheme_id]))access_list intrinsic charge (the base envelope has no access_list)If EXT_SET_CODE is present, authorization_list MUST be non-empty.
Relative to EIP-7702, the gas accounting delta is only envelope-level:
25000 per authorization exactly as EIP-7702 does12500 for non-empty authority exactly as EIP-7702 doesSchemedTransaction has no base access_listThe 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:
EXT_BLOB reuses the blob fee lane and blob accounting of EIP-4844EXT_SET_CODE adds 25000 * len(authorization_list) intrinsic gas and refunds 12500 for each successfully processed non-empty authorityThe 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.
A node validating a SchemedTransaction MUST perform the following checks in order:
Type check
First byte is 0x05.
RLP decode
Decode the payload according to the field list above.
Chain ID
chain_id matches the node's chain.
Authorization structure
authorizations contains exactly one ROLE_SENDER.
role_id is invalid.Unknown scheme_id is invalid.
Extension structure
extensions contains no duplicate extension_id.
extension_id is invalid.Each extension payload is statelessly well-formed.
Sender verification
Compute sender_signing_hash(tx).
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.Derive the sender address.
Nonce and balance
Sender account nonce matches tx.nonce.
Sender has sufficient balance for transaction execution and all extension-specific fee lanes.
Pre-execution extension processing
Apply stateless and fee-lane checks for EXT_BLOB.
Apply set-code authorizations for EXT_SET_CODE.
Intrinsic gas
Charge base intrinsic gas, calldata cost, sender scheme surcharge, and extension-specific intrinsic charges.
Execute
Execute the normal call/create payload using to, value, and data.
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.
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.
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:
scheme_idThey do not need to redefine the outer transaction envelope.
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.
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.
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.
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.
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.
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:
scheme_id is added to the signed set-code authorization payload(y_parity, r, s) is replaced by (scheme_id, witness)access_listEverything else intentionally stays aligned with EIP-7702.
SchemedTransaction using SCHEME_SECP256K1 produces the same sender address and the same execution semantics as a normal EIP-1559 transaction, but it uses a distinct typed envelope and signing hash.SchemedTransaction without changing address when they continue using secp256k1.msg.sender and tx.origin regardless of which authorization scheme was used.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 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.
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.
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).
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.
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.
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.
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.
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.
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 and related rights waived via CC0.