--- rip: 7712 title: Enable RIP-7560 transactions using a two-dimensional nonce description: An RIP-7560 transaction modification that allows Smart Contract Accounts to define their own transaction sequencing author: Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn) discussions-to: https://ethereum-magicians.org/t/rip-7712-multi-dimensional-256-bit-nonce-for-rip-7560-account-abstraction-transactions/20094 status: Draft type: Standards Track category: Core created: 2023-09-01 requires: 7560 --- ## Abstract An RIP-7560 transaction modification that replaces the existing nonce-based sequencing mechanism of Ethereum with an on-chain pre-deployed contract in order to provide Smart Contract Accounts with the flexibility they need to support some advanced use cases. ## Motivation The traditional nonce-based system provides guarantees of transaction ordering and replay protection, but does so at the expanse of configurability. Accounts are not able to express their intentions like "allow these three transactions to be mined in whatever order" or "these two sets of transactions must have separate sequential nonces". However, with Smart Contract Accounts this creates a bottleneck for some use-cases. For example, Smart Contract Accounts designed to be operated by multiple participants simultaneously, will require these participants to coordinate their transactions to avoid invalidating each other. Another example when this can also be a limitation is a case where there are separate execution flows. A configuration change may require multiple participants to co-sign a transaction but a regular operation does not. With sequential nonces, all operations will have to be halted until the configuration change is executed. Having a more agile ordering and replay protection system will significantly improve the user experience with RIP-7560. Additionally, this change is required for the Native Account Abstraction to achieve feature parity and compatibility with ERC-4337, as all ERC-4337 Smart Contract Accounts already use the solution described below. ## Specification ### Constants | Name | Value | |--------------------------|-------| | AA_BASE_GAS_COST | 15000 | | AA_BASE_GAS_COST_RIP7712 | 10000 | | AA_NONCE_MANAGER | tbd | ### Non-sequential nonce support We propose an introduction of the `nonceKey` parameter to provide a second dimension for the RIP-7560 transaction's `nonce` parameter. The two-dimensional nonce value of the transaction is represented as `uint256 nonceKey || uint256 nonceSequence` value. The contract account nonce is then defined as a mapping `address account => uint256 nonceKey => uint256 nonceSequence`. This approach guarantees unique transaction nonce and hash but removes the requirement of nonces being sequential numbers. This two-dimensional nonce mechanism is exposed to the EVM in a `NonceManager` pre-deployed contract located at the `AA_NONCE_MANAGER` address. The two-dimensional nonce is [validated and incremented](#nonce-validation-frame) on-chain as part of a `AA_TX_TYPE` transaction validation before the rest of the validation code. ### Nonce validation We propose to introduce a new validation frame to the RIP-7560 transaction validation flow. This validation frame performs an on-chain nonce validation and runs before all other frames in a transaction. If nonceKey is zero (non-existent), the nonce field is compared to the system nonce, and then the system nonce is incremented. The transaction's base cost is `A_BASE_GAS_COST`. If `nonceKey` is non-zero, then the `NonceManager` is called to [validate and increment](#nonce-validation-frame) the nonce for this key, and the base cost is `AA_BASE_GAS_COST_RIP7712`, as the `NonceManager` frame is paid separately. In this case, the old system `nonce` is not checked and not incremented. ### Deployment transaction During the account deployment transaction, the `nonceKey` MUST be zero. The old system `nonce` parameter is not incremented before the transaction: the `CREATE` or `CREATE2` opcode used by the deployer will cause the nonce to be incremented to 1 according to [EIP-161](https://eips.ethereum.org/EIPS/eip-161). ### The legacy `nonce` account parameter use The legacy 64-bit `nonce` account parameter is used when the `nonceKey` parameter of the `AA_TX_TYPE` transaction is set to 0. The legacy `nonce` account parameter is also use for transactions initiated by EOAs, for the `CREATE` opcode and for EIP-7702 authorizations. ### Account deployment transaction does not increment the legacy `nonce` The first `AA_TX_TYPE` transaction that contains a `deployer` and `deployerData` fields to create the `sender` account does not increment the `sender`'s legacy `nonce` account parameter even in case the `key` parameter is set to 0. #### Nonce validation frame Before the first RIP-7560 transaction type defined validation frame is applied during the validation phase, the `NonceManager` is invoked with the following data: ``` sender{20 bytes} nonceKey{32 bytes} nonceSequence{32 bytes} ``` The gas costs of this validation frame are counted towards the total for a transaction, and is deducted from the account's `validationGasLimit`. #### Nonce query frame In order to query its current `nonceSequence` for a given `nonceKey`, the user performs a view call to the `NonceManager` contract with the following data: ``` sender{20 bytes} nonceKey{32 bytes} ``` The returned value of this call is the current `nonceSequence` that can be used in the [nonce validation frame](#nonce-validation-frame). #### NonceManager Pseudocode ``` if evm.caller == AA_ENTRY_POINT: validate_increment() else: get() def get(): if len(evm.calldata) != 52: evm.revert() // address sender, 256 bit nonce key address = to_uint160_be(evm.calldata[0:20]) key = to_uint256_be(evm.calldata[20:52]) nonce = storage.get(keccak(address, key)) evm.return(nonce) def validate_increment(): address = to_uint256_be(evm.calldata[0:20]) key = to_uint256_be(evm.calldata[20:52]) nonce = to_uint256_be(evm.calldata[52:84]) current_nonce = storage.get(keccak(address, key)) if (nonce != current_nonce): evm.revert() storage.set(keccak(address, key), current_nonce + 1) ``` #### NonceManager Bytecode and deployment TODO. ## Rationale ### Creating a pre-deployed contract instead of modifying an account data structure While there is no technical benefit to either option, allowing EVM code to control the nonce seems to be a smaller change to the Ethereum protocol and is more aligned with the vision of Account Abstraction. ### Account deployment transaction does not increment the legacy `nonce` The [EIP-684](https://eips.ethereum.org/EIPS/eip-684) explicitly prevents deployment of contracts for accounts with non-zero nonce value. This rule collides with the normal flow of incrementing the `sender` nonce before execution of a transaction starts. However, [EIP-161](https://eips.ethereum.org/EIPS/eip-161) guarantees that the `nonce` of a newly created account is set to `1` on creation. This guarantees that the `sender` deployment transaction can pass the EIP-684 rule check and still increment the nonce. Note that revert in the creation frame makes the `AA_TX_TYPE` transaction invalid and `sender`'s nonce cannot be incremented without successfully deploying its code. ## Backwards Compatibility As actual `nonce` value was never observable inside the EVM, there should be no major complications caused by the migration to a different nonce mechanism. Applications should not assume that `eth_getTransactionCount` method returns the total transactions for an account ### EIP-7702 authorization revocation with a nonce bump In EIP-7702 the EOA is able to revoke an "authorization tuple" it has previously signed as long as it has not been included on-chain. This can be done by incrementing the EOAs `nonce`, which in turn can only be done by sending a new transaction. With RIP-7712, however, there exists a new way of sending a transaction without affecting the legacy `nonce` field. Such a transaction will not invalidate the previously signed "authorization tuple" either. Users who are unaware of this change may not expect an "authorization tuple" to remain valid after sending a different transaction and should be aware of the existence of RIP-7712 and multidimensional nonces. ## Security Considerations Smart Contract Accounts that need to enforce the sequence of transaction execution must apply appropriate restrictions on the `nonceKey` value. In order to require the incremental sequential `nonce` behaviour on-chain, the contracts may choose to `require(nonceKey == 0)`. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).