EIP-2733: Transaction package Source

AuthorMatt Garnett
TypeStandards Track
Requires 2718

Simple Summary

Creates a new transaction type which executes a package of one or more transactions, while passing status information to subsequent transactions.


After FORK_BLOCK, a new EIP-2718 transaction of type N is recognized. Transactions of type N will define a list of transactions, which must be executed serially by clients. Execution information (e.g. success, gas_used, etc.) will be propagated forward to the next transaction.


Meta-transaction relay contracts have historically been designed to catch reversions in their inner transactions by only passing a portion of the available gas to the subcall. This has been considered bad practice for a long time, but in the case of untrusted subcalls, like the ones relay contracts make, it is the only available solution. Transaction packages are an alternative that allow multiple transactions to be bundled into one package and executed atomically, similarly to how relay contracts operate. Transactions are able to pass their result to subsequent transactions. This allows for conditional workflows based on the outcome of previous transactions. Although this functionality is already possible as described above, workflows using transaction packages are more robust, because they are protected from future changes to the gas schedule.



N = TBD transaction type number
TOTAL_COST = INTRINSIC_COST + inner_txs.reduce(|itx, acc| acc += itx.value + tx.gas_price * itx.gas_limit)
TOTAL_GAS_LIMIT = inner_txs.reduce(|itx, acc| acc += itx.gas_limit)
TX_HASH = hash of transaction as defined below
SENDER = ecrecover(hash, v, r, s)
RESULT = result as defined below for the previous transaction, empty if its the first tx in a package


After FORK_BLOCK, a new EIP-2718 transaction type N will be interpreted as follows:

rlp([N, [v, r, s, chain_id, nonce, gas_price, [inner_tx_0, ..., inner_tx_n]])

where inner_tx_n is defined as:

[to, value, data, gas_limit]


The hash of transaction type N is defined to be the Keccak-256 hash of the rlp encoding of the entire transaction with v, r, and s values set to zero.


Subsequent transactions will be able to receive the result of the previous transaction via RETURNDATACOPY (0x3E) in first frame of exeuction, before making any subcalls. Each element, except the last, will be 0-padded left to 32 bytes.

Name Type Description
success bool Status of the previous transaction
gas_used uint256 Total gas used by the previous transaction
cum_gas_used uint256 Cumulative gas used by previous transactions
return_size uint256 The size of the return value
return_value bytes The return value of the previous transaction


  • (v, r, s) are a valid signature of the hash of the transaction
  • The nonce is one greater than recovered address’ current nonce
  • The recovered address has a balance of at least TOTAL_COST
  • The TOTAL_GAS_LIMIT is less than the current block’s gas_limit


Transaction packages should be executed as follows:

  1. Deduct TOTAL_COST from SENDER’s balance
  2. Execute the first inner transaction in the list
  3. Refund any unused gas
  4. Record all state changes, logs, and the receipt
  5. If there are no more transaction, stop
  6. Compute RESULT for the previously executed transaction
  7. Prepare RESULT to be available via return opcodes in the next transaction’s first frame
  8. Execute the next transaction
  9. Goto 3


Non-recursive inner transactions

For simplicity, inner transactions are fully defined within this EIP. However, there is value in supporting recursive transaction definitions. For example, suppose there is a transaction type which can become invalid after a certain block number. It would be beneficial to support those types of transactions within a package, but the complexity of this EIP would dramatically increase.

Appending result data to transaction input data

An alternative to using return opcodes to propagate RESULT would be to append the RESULT to the subsequent transaction’s data field. Unfortunately, in many cases contracts generated using Solidity will fail to resolve the intended function if additional data is present.

Backwards Compatibility

Contracts which rely on ORIGIN (0x32) == CALLER (0x33) && RETURNDATASIZE (0x3D) == 0x00 will now always fail in transaction packages, unless they are the first executed transaction. It’s unknown if any contracts conduct this check.

Test Cases




Security Considerations


Copyright and related rights waived via CC0.


Please cite this document as:

Matt Garnett, "EIP-2733: Transaction package [DRAFT]," Ethereum Improvement Proposals, no. 2733, June 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2733.