This EIP introduces a new EIP-2718 transaction type with EIP-1559 gas fields, typed capabilities (CALL and CREATE) that define the transaction’s operations, a separate signatures list for authentication, a fee_auth field that executes account code to sponsor gas, and three new opcodes — RETURNETH, SIG, and SIGHASH — for fee delegation and in-EVM signature introspection.
Motivation
Each Ethereum upgrade has introduced new transaction types for new capabilities: EIP-1559 for priority fees, EIP-4844 for blobs, and EIP-7702 for authorizations. Both EIP-4844 and EIP-7702 extend and reuse mostly the fields from EIP-1559, yet each required an entirely new transaction type. This leads to linear growth of transaction types with overlapping gas-payment semantics. This EIP proposes a single extensible transaction format where new features are added as typed capabilities without defining new transaction types.
Transaction sponsorship — where a third party pays gas on behalf of the sender — has been a long-sought feature. EIP-8141 proposes a solution using execution frames, new opcodes (APPROVE, TXPARAM), and per-frame gas budgets. This EIP achieves sponsorship with a simpler fee_auth field and three focused opcodes.
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.
Parameters
Parameter
Value
COMPOSABLE_TX_TYPE
0x05
CAP_CALL
0x01
CAP_CREATE
0x02
SIG_SECP256K1
0x01
SIG_ED25519
0x02
ROLE_SENDER
0x00
ROLE_PAYER
0x01
RETURNETH_OPCODE
0xf6
SIG_OPCODE
0xf7
SIGHASH_OPCODE
0xf8
RETURNETH_GAS
2
SIG_BASE_GAS
2
SIGHASH_GAS
2
PER_SIG_SECP256K1_GAS
3000
PER_SIG_ED25519_GAS
3000
Composable transaction
A new EIP-2718 transaction is introduced where the TransactionType is COMPOSABLE_TX_TYPE and the TransactionPayload is the RLP serialization of:
The fields chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, and gas_limit follow the same semantics as EIP-1559.
The fee_auth field is either empty (zero bytes) or a 20-byte address. When non-empty, it designates the account whose code is executed to provide ETH covering the transaction fee (see Fee Delegation via fee_auth).
The capabilities field is an RLP list of typed capabilities. Each capability is an RLP list whose first element is the capability type identifier. The transaction MUST contain at least one capability. See Capability types for the defined types. Future EIPs MAY define additional capability types for this transaction format.
The signatures field is an RLP list of typed signatures. Each signature authenticates the transaction using a per-role signing hash (see Signing Hash). Every signature type MUST be known and every signature MUST be cryptographically valid.
The EIP-2718ReceiptPayload for this transaction is rlp([status, cumulative_transaction_gas_used, logs_bloom, logs]).
Capability types
CALL (CAP_CALL = 0x01)
Executes a message call to an existing account.
Format: [cap_type, to, value, data]
Field
Description
cap_type
0x01
to
20-byte destination address
value
ETH value in wei to transfer
data
Calldata bytes
CREATE (CAP_CREATE = 0x02)
Creates a new contract.
Format: [cap_type, value, data]
Field
Description
cap_type
0x02
value
ETH value in wei to endow the new contract
data
Initialization code (initcode)
The contract address is derived as keccak256(rlp([sender_address, nonce]))[12:], where nonce is the sender’s nonce at the time the CREATE capability executes. The sender’s nonce is incremented by 1 for each CREATE capability.
Signatures
Signature schemes
SECP256K1 (SIG_SECP256K1 = 0x01):
Format: [signature_type, role, y_parity, r, s]
Field
Size
Description
signature_type
1 byte
0x01
role
1 byte
ROLE_SENDER or ROLE_PAYER
y_parity
1 byte
Recovery ID (0 or 1)
r
32 bytes
ECDSA r component
s
32 bytes
ECDSA s component
The s value MUST be less than or equal to secp256k1n/2, as specified in EIP-2. The signer address is recovered via ecrecover(sig_message, y_parity, r, s).
The signatures array is replaced with an empty list when computing base_hash. This allows all signers to sign independently and in any order. The signature_type and role are included in sig_message to prevent cross-scheme and cross-role signature confusion.
Fee delegation via fee_auth
When fee_auth is non-empty, the account at fee_auth sponsors the transaction fee. Before the main transaction executes, the protocol invokes fee_auth as a prelude call:
Context field
Value
ADDRESS
fee_auth
CALLER
sender (recovered from sender signature)
ORIGIN
sender
CALLVALUE
0
CALLDATA
empty
GAS
gas_limit minus intrinsic gas
The fee_auth code MUST use RETURNETH to credit ETH into the transaction-scoped fee escrow. The amount credited MUST be at least gas_limit * max_fee_per_gas (the maximum possible fee). The fee_auth code MAY use SIG and SIGHASH to introspect signatures for authorization.
If fee_auth execution reverts, or the fee escrow is insufficient after fee_auth returns, the transaction is invalid and MUST NOT be included in a block.
After the main transaction completes, the actual fee is settled:
actual_fee=gas_used*effective_gas_pricesurplus=fee_escrow-actual_fee# surplus is refunded to fee_auth address
When fee_auth is empty, gas is charged directly from the sender’s balance, following standard EIP-1559 behavior.
State changes made during fee_auth execution persist regardless of whether the main transaction reverts. This allows sponsors to maintain their own accounting (e.g., nonces, rate limits) independently.
New opcodes
RETURNETH (0xf6)
Returns ETH from the current execution context to the parent (calling) context.
Stack input
value — amount in wei
Stack output
(none)
Gas
RETURNETH_GAS (2)
Behavior:
Debits value wei from the balance of ADDRESS (the currently executing account).
Credits value wei to the parent calling context. If the parent context is a contract call, the ETH is credited to the caller’s account balance. If the parent context is the protocol-level fee_auth prelude, the ETH is credited to the transaction-scoped fee escrow.
If ADDRESS has insufficient balance, execution reverts.
RETURNETH is valid in any execution context (not restricted to fee_auth), including descendant calls. When used in nested calls within fee_auth, the ETH propagates upward: each RETURNETH credits the immediate parent, and the top-level fee_auth frame’s RETURNETH credits the fee escrow.
RETURNETH is invalid in a STATICCALL context and causes an exceptional halt.
If the enclosing call frame reverts, the RETURNETH credit is rolled back.
SIG (0xf7)
Loads a signature from the transaction’s signatures array into memory.
Stack input
index, mem_start
Stack output
sig_type
Gas
SIG_BASE_GAS (2) + memory expansion cost
Stack position
Value
top - 0
index — zero-based index into signatures
top - 1
mem_start — memory offset to write signature data
Behavior:
If index >= len(tx.signatures), execution reverts.
Pushes the signature_type identifier onto the stack.
Writes the signature body (excluding signature_type) to memory at mem_start:
SIG_SECP256K1: writes role ‖ y_parity ‖ r ‖ s (1 + 1 + 32 + 32 = 66 bytes).
Memory expansion cost is charged for the bytes written, following standard rules.
SIGHASH (0xf8)
Pushes the transaction base hash onto the stack.
Stack input
(none)
Stack output
hash — 32-byte base_hash as defined in Signing Hash
Gas
SIGHASH_GAS (2)
Behavior:
Pushes compute_base_hash(tx) onto the stack as a 256-bit value.
The fee_auth code can reconstruct sig_message from base_hash + signature data obtained via SIG, then verify signatures using the ecrecover precompile or future Ed25519 precompiles.
Execution order
The state transition for a Composable transaction proceeds as follows:
defprocess_composable_tx(tx,block):# 1. Static validation
validate_static_constraints(tx)# 2. Compute base hash
base_hash=compute_base_hash(tx)# 3. Validate all signatures and recover addresses
sender_address=Nonepayer_address=Noneforsigintx.signatures:sig_msg=compute_sig_message(tx,sig.signature_type,sig.role)addr=recover_address(sig,sig_msg)ifsig.role==ROLE_SENDER:sender_address=addrelifsig.role==ROLE_PAYER:payer_address=addr# 4. Nonce check and increment
assertstate[sender_address].nonce==tx.noncestate[sender_address].nonce+=1# 5. Intrinsic gas
intrinsic_gas=compute_intrinsic_gas(tx)asserttx.gas_limit>=intrinsic_gasgas_remaining=tx.gas_limit-intrinsic_gas# 6. Fee handling
fee_escrow=0max_tx_cost=tx.gas_limit*tx.max_fee_per_gasiftx.fee_auth:# Execute fee_auth prelude
fee_auth_result=evm_call(caller=sender_address,address=tx.fee_auth,value=0,data=b'',gas=gas_remaining,)gas_remaining-=fee_auth_result.gas_usedassertnotfee_auth_result.revertedassertfee_escrow>=max_tx_cost# accumulated via RETURNETH
else:# Standard: charge sender (or payer if present)
charge_account=payer_addressorsender_addressassertstate[charge_account].balance>=max_tx_coststate[charge_account].balance-=max_tx_costfee_escrow=max_tx_cost# 7. Execute capabilities sequentially
forcapintx.capabilities:ifcap.cap_type==CAP_CALL:result=evm_call(caller=sender_address,address=cap.to,value=cap.value,data=cap.data,gas=gas_remaining,)elifcap.cap_type==CAP_CREATE:result=evm_create(caller=sender_address,value=cap.value,initcode=cap.data,gas=gas_remaining,)gas_remaining=result.gas_remainingifresult.reverted:breakgas_used=tx.gas_limit-gas_remaining# 8. Settle fees
effective_gas_price=min(tx.max_fee_per_gas,tx.max_priority_fee_per_gas+block.base_fee_per_gas)actual_fee=gas_used*effective_gas_pricesurplus=fee_escrow-actual_feeblock.coinbase.balance+=gas_used*(effective_gas_price-block.base_fee_per_gas)# base fee portion is burned
# Refund surplus
iftx.fee_auth:state[tx.fee_auth].balance+=surpluselse:refund_account=payer_addressorsender_addressstate[refund_account].balance+=surplus
Gas handling
The intrinsic gas cost is:
defcompute_intrinsic_gas(tx):gas=21000# base transaction cost
forcapintx.capabilities:gas+=calldata_cost(cap.data)# 16 per non-zero byte, 4 per zero byte
ifcap.cap_type==CAP_CREATE:gas+=32000# contract creation cost
gas+=sum(PER_SIG_SECP256K1_GASifs.signature_type==SIG_SECP256K1elsePER_SIG_ED25519_GASforsintx.signatures)returngas
If fee_auth is non-empty, gas consumed by fee_auth execution is subtracted from gas_limit before capabilities execute.
Rationale
Linear growth of transaction types
Rather than defining a new transaction type per feature (blobs, authorizations, sponsorship), each potentially combined with each other, this EIP defines a single extensible format. New features are expressed as capabilities within the existing type.
Separating signatures from capabilities
Placing signatures in their own array cleanly separates extension data from authentication. The signatures array is blinded during hash computation, so all signers produce their signature independently. This eliminates the ordered commitment chain of prior designs and simplifies multi-party signing flows.
Domain-separated signing hash
The sig_message includes signature_type and role alongside the blinded transaction hash. This prevents a sender’s signature from being reused as a payer’s signature, even though both sign over the same base_hash. Without this, an attacker could reinterpret one role’s signature as another’s.
fee_auth for fee delegation
The fee_auth field provides programmable fee delegation. The sponsor’s code runs before the main transaction, uses RETURNETH to escrow the maximum fee, and can implement arbitrary authorization logic by introspecting signatures via SIG and SIGHASH. This is strictly more powerful than a static payer co-signature.
Upfront maximum cost escrow
Requiring fee_auth to escrow gas_limit * max_fee_per_gas before main execution avoids the chicken-and-egg problem of paying for execution with the proceeds of that execution. Surplus is refunded to fee_auth after settlement, mirroring EIP-1559 reserve-and-refund discipline.
fee_auth state persistence
State changes made during fee_auth execution persist even if the main transaction reverts. This allows sponsors to maintain internal accounting (nonces, rate limits, spend tracking) that must not be rolled back on user-transaction failure.
RETURNETH opcode
RETURNETH provides a safe, explicit mechanism for code to return ETH to the parent calling context. When used within the fee_auth prelude, ETH propagates up to the protocol-managed fee escrow. When used in regular calls, ETH is credited to the caller’s account balance. This general-purpose design avoids the need for SELFDESTRUCT or value-carrying calls back to a system address, and enables composable ETH flows beyond fee delegation.
SIG and SIGHASH opcodes
These opcodes enable in-EVM signature introspection. The fee_auth code loads signatures via SIG, obtains the base hash via SIGHASH, reconstructs sig_message, and verifies signatures using ecrecover. This allows arbitrary sponsor authorization without off-chain coordination beyond collecting signatures.
CALL and CREATE as capabilities
Rather than embedding to, value, and data as top-level transaction fields, these are expressed as typed capabilities. This makes the transaction envelope a pure fee-paying and authentication container, while the actual operations — message calls and contract creation — are composable payload items. A transaction may contain multiple capabilities, enabling batched execution within a single transaction. Future capability types (e.g., blob data, authorization lists) extend the same list without modifying the envelope format.
Backwards Compatibility
This EIP introduces a new transaction type and does not modify the behavior of existing transaction types. No backward compatibility issues are expected.
Security Considerations
Signature domain separation
The sig_message includes both signature_type and role, preventing cross-role and cross-scheme confusion. A SECP256K1 sender signature cannot be replayed as an ED25519 payer signature or vice versa.
Payer replay protection
Every signature commits to the full transaction content including the sender’s nonce. Since the sender’s nonce is incremented on each transaction, signatures cannot be replayed across transactions.
fee_auth execution safety
The fee_auth execution is bounded by gas_limit and runs before the main transaction. If fee_auth reverts or credits insufficient ETH, the transaction is invalid and produces no state changes. The upfront escrow requirement (gas_limit * max_fee_per_gas) ensures the protocol never under-collects fees.
fee_auth griefing
A fee_auth contract could consume gas without crediting sufficient ETH, making the transaction invalid. Block builders can simulate fee_auth before inclusion to avoid wasting block space. Mempool nodes SHOULD simulate the fee_auth prelude before accepting the transaction.
RETURNETH restrictions
RETURNETH credits ETH to the immediate parent calling context — either the caller’s account balance or the protocol fee escrow at the top-level fee_auth frame. It cannot send ETH to arbitrary addresses. It is invalid in STATICCALL contexts. If the enclosing call reverts, the credit is rolled back, preventing double-spending.
Transaction propagation
Composable transactions with fee_auth introduce validation complexity similar to EIP-7702 and EIP-8141. Nodes SHOULD keep at most one pending Composable transaction per sender in the public mempool. fee_auth simulation during mempool validation SHOULD be bounded in gas and restricted in state access patterns to limit DoS surface.