Native accounts on rollups.
An account abstraction proposal that introduces consensus-layer protocol changes, instead of relying on higher-layer infrastructure.
Combining the EIP-2938 and ERC-4337 into a comprehensive Native Account Abstraction proposal.
We propose splitting the Ethereum transaction scope into multiple steps: validations, execution, and post-transaction logic. Transaction validity is determined by the result of the validation steps of a transaction.
We further separate transaction validation for the purposes of authorization and the gas fee payment, allowing contract B to pay gas for a transaction that will be executed from account contract A.
The benefits are in backward compatibility with the emerging ERC-4337 ecosystem while achieving the long-term goal of Native Account Abstraction.
ERC-4337 can do a lot as a purely voluntary ERC. However, any of the out-of-protocol ways of achieving Account Abstraction faces several drawbacks compared to native support. There are a few key areas where it is weaker than a truly in-protocol solution:
Extra gas overhead of ~42k for a basic UserOperation compared to ~21k for a basic transaction.
Less benefit from in-protocol censorship resistance techniques such as crLists, which target transactions
and would miss UserOperations.
Relying on a significantly smaller set of participating nodes and non-standard RPC methods like
eth_sendRawTransactionConditional.
Inability to use tx.origin or contracts that rely on it as it returns the meaningless address of a bundler.
EIP-2938 defines a very mature alternative approach to Account Abstraction. However, it does not translate well to the architecture of ERC-4337 that is being used in production without any protocol changes. Therefore, the implementation of EIP-2938 will not benefit as much from the production experience gained by using ERC-4337 and from maintaining backward compatibility with it.
There is also a possibility that at some point in the future, all EOAs on Ethereum will be replaced with pre-deployed smart contracts. This, however, is impossible without an addition of Native Account Abstraction to the protocol.
| Name | Value |
|---|---|
| FORK_BLOCK | TBD |
| AA_TX_TYPE | TBD |
| AA_ENTRY_POINT | address(7560) |
| AA_SENDER_CREATOR | address(ffff7560) |
| AA_BASE_GAS_COST | 15000 |
| VERSION | 1 |
| MAX_CONTEXT_SIZE | 65536 |
| MAX_REVERT_REASON_SIZE | 1024 |
A new EIP-2718 transaction with type AA_TX_TYPE is introduced. Transactions of this type are referred to as "AA transactions". Their payload should be interpreted as:
The base gas cost of this transaction is set to AA_BASE_GAS_COST instead of 21000 to reflect the lack of "intrinsic"
ECDSA signature verification.
The authorizationList parameter has the exact same behaviour as defined by the
EIP-7702.
The following table represents a full list of fields of an RIP-7560 transaction:
| Name | Type | Description |
|---|---|---|
| sender | DATA, 20 Bytes | Address of the Smart Contract Account making the transaction |
| deployer | DATA, 20 Bytes | Address of the Deployer - account factory contract (optional) |
| deployerData | DATA | Data that is provided to the Deployer contract (if deployer is set) |
| paymaster | DATA, 20 Bytes | Address of the Paymaster contract (optional) |
| paymasterData | DATA | Data that is provided to the Paymaster contract (if paymaster is set) |
| executionData | DATA | Data that is provided to the Account contract for execution |
| nonceKey | QUANTITY | A 192 bit nonce key. Use of nonceKey != 0 is defined in RIP-7712. |
| nonceSequence | QUANTITY | A 64 bit nonce sequence. Use of nonceKey != 0 is defined in RIP-7712. |
| builderFee | QUANTITY | Value passed from sender or paymaster to the coinbase |
| maxPriorityFeePerGas | QUANTITY | The maximum gas price to be included as a tip to the validator |
| maxFeePerGas | QUANTITY | The maximum fee per unit of gas |
| validationGasLimit | QUANTITY | Gas provided for the transaction account validation frame |
| paymasterValidationGasLimit | QUANTITY | Gas provided for the transaction paymaster validation frame (if paymaster is set) |
| paymasterPostOpGasLimit | QUANTITY | Gas provided for the transaction paymaster postOp frame (if paymaster is set) |
| callGasLimit | QUANTITY | Gas provided for the transaction RIP-7560 execution call frame |
| accessList | OBJECT | An EIP-2930 compatible Access List structure |
| authorizationsList | ARRAY | An EIP-7702 compatible list of contracts injected into EOAs |
| authorizationData | DATA | Data that will be used by the Account to verify transaction |
A transaction receipt object shape is modified to support the RIP-7560 transaction receipts.
| Name | Type | Description |
|---|---|---|
| sender | DATA, 20 Bytes | Address of the sender of this transaction |
| paymaster | DATA, 20 Bytes | Address of the Paymaster if it is paying for the transaction, null otherwise |
| deployer | DATA, 20 Bytes | Address of the Deployer if it is included in the transaction, null otherwise |
| senderCreationGasUsed | QUANTITY | The amount of gas actually used by the sender deployment frame, or zero if frame not executed |
| senderValidationGasUsed | QUANTITY | The amount of gas actually used by the sender validation frame |
| paymasterValidationGasUsed | QUANTITY | The amount of gas actually used by the paymaster validation frame, or zero if frame not executed |
| executionGasUsed | QUANTITY | The amount of gas actually used by the RIP-7560 execution call frame |
| postOpGasUsed | QUANTITY | The amount of gas actually used by the paymaster postOp frame, or zero if frame not executed |
| executionStatus | QUANTITY | 0 (success), 1 (execution reverted), 2 (postOp reverted), 3 (both execution and postOp reverted) status of the execution frame |
| validationLogs | ARRAY | Array of log objects, which this transaction'S VALIDATION FRAME generated. |
| transactionHash | DATA, 32 Bytes | Hash of the transaction. |
| transactionIndex | QUANTITY | Integer of the transactions index position in the block. |
| blockHash | DATA, 32 Bytes | Hash of the block where this transaction was in. |
| blockNumber | QUANTITY | Block number where this transaction was in. |
| cumulativeGasUsed | QUANTITY | The total amount of gas used when this transaction was executed in the block. |
| effectiveGasPrice | QUANTITY | The sum of the base fee and tip paid per unit of gas. |
| gasUsed | QUANTITY | The amount of gas used by this specific transaction alone. |
| logs | ARRAY | Array of log objects, which this transaction'S EXECUTION FRAME generated. |
| logsBloom | DATA, 256 Bytes | Bloom filter for light clients to quickly retrieve related logs. |
| type | QUANTITY | Integer of the transaction type |
The maximum gas cost of the AA_TX_TYPE transaction is defined as:
If paymaster is not specified, the maxPossibleGasCost is charged up-front, before any computation is done in any
execution frame, from the balance of the sender address.
If paymaster is specified, the gas cost is charged from its balance.
The transaction is invalid if the balance of the account that is being pre-charged,
whether it is a sender or a paymaster, is insufficient.
After the transaction finishes its execution, the address that was pre-charged may receive a gas refund.
The meanings of the maxPriorityFeePerGas and maxFeePerGas are unchanged from how they are defined in the
EIP-1559.
For all the existing transaction types, G_txdatazero (4 gas) and G_txdatanonzero (16 gas) per byte is
charged for the data parameter.
Transaction Type AA_TX_TYPE introduces the following dynamic length inputs: executionData, paymasterData,
deployerData, authorizationData. Each of these parameters' gas cost is counted towards transaction data cost.
This transaction data gas cost is referred to as calldataGasUsed and is subtracted from the validationGasLimit
before execution of the transaction.
The transaction is considered INVALID if validationGasLimit is smaller than calldataGasUsed.
As we need to account for an additional off-chain work that block builders have to perform to
include AA_TX_TYPE transactions in their blocks, as well as a potential L1 gas cost for builders
operating on L2 rollups, and given that this work does not correspond to the amount of gas spent on
validation and is not linked to the gas price, the sender may decide
to pay an extra builderFee as a "tip" to the block builder.
This value is denominated in wei and is passed from the sender, or the paymaster if it is specified,
to the coinbase of the current block as part of the gas pre-charge.
All existing transaction types only have an implicit validation phase where balance, nonce, and signature are checked,
and a single top-level execution frame with
tx.origin == msg.sender which is the address that is determined by a transaction ECDSA signature.
When processing a transaction of type AA_TX_TYPE, however, multiple execution frames will be created.
The full list of possible frames tries to replicate the ERC-4337 flow:
sender deployment frame (once per account)sender validation frame (required)paymaster validation frame (optional)sender execution frame (required)paymaster post-transaction frame (optional)All execution frames in the "Validation Phase" must be completed successfully without reverting, and
both sender and paymaster validation frames must include a call to a corresponding AA_ENTRY_POINT
approval callback functions
in order for the transaction to be considered valid for a given position in a block.
In all top-level frames, the global variables have the following meaning:
| Opcode Name | Solidity Equivalent | Value |
|---|---|---|
CALLER | msg.sender | The AA_ENTRY_POINT address. AA_SENDER_CREATOR for the "deployment frame". |
ORIGIN | tx.origin | The transaction sender address |
CALLDATA* | msg.data | The transaction data is set to inputs of the corresponding frame |
Note that some behaviours in the EVM depend on the transaction context. These behaviours are:
These features are not affected by the separation of the transaction into multiple frames. Meaning, for example, that a value set with TSTORE in one frame will remain available in the next one.
The deployer address is invoked with the deployerData as call data input from the AA_SENDER_CREATOR address.
The gas limit of this frame is set to validationGasLimit.
The amount of gas used by this frame is referred to as senderCreationGasUsed.
The sender deployment frame MUST result in the sender address becoming
initialized with contract code.
We define the following Solidity struct to represent the AA transaction on-chain:
We then define the following Solidity method and the sender of the transaction is invoked with the corresponding data:
The gas limit of this frame is set to validationGasLimit - senderCreationGasUsed - calldataGasUsed.
The transaction parameter is interpreted as an ABI encoding of TransactionTypeRIP7560.
The txHash parameter represents the hash of the AA_TX_TYPE transaction with empty authorizationData,
as defined in section
Calculation of Transaction Type AA_TX_TYPE hash.
The version parameter is added in order to maintain the Solidity method ID in case of changes to this struct
in future revisions of this EIP.
The amount of gas used by this frame is referred to as senderValidationGasUsed.
We then define the following Solidity methods as an AA_ENTRY_POINT approval callback function:
Calls to the AA_ENTRY_POINT approval callbacks have the following meaning:
acceptAccount - This callback is called by the account after it verified the transaction and agreed to pay for its execution.sigFailAccount - This callback is called by the account if the transaction is syntactically valid,
but the authorizationData is incorrect.The parameters passed to the callback functions have the following meaning:
The account MUST make exactly one call to the AA_ENTRY_POINT to be considered valid.
Any other outcome, such as the account not making any calls to the AA_ENTRY_POINT callbacks,
making any call other than acceptAccount,
making multiple calls to the AA_ENTRY_POINT,
or causing a reverted execution is considered to fail validation,
and the transaction is rejected and not included on-chain.
These callbacks may be called either by the account directly or inside a DELEGATECALL that runs in the context of the account.
If a callback is called in the context of any other contract during the validation frame, the transaction is rejected and not included on-chain.
The paymaster of the transaction, if specified, is invoked with the following data:
The gas limit of this frame is set to paymasterValidationGasLimit.
The amount of gas used by this frame is referred to as paymasterValidationGasUsed.
version parameter is VERSIONtransaction parameter is interpreted as an ABI encoding of TransactionTypeRIP7560.\txHash parameter represents the hash of the AA_TX_TYPE transaction with empty authorizationData,
as defined in section
Calculation of Transaction Type AA_TX_TYPE hash.We then define the following Solidity methods as an AA_ENTRY_POINT approval callback function:
Calls to the AA_ENTRY_POINT approval callbacks have the following meaning:
acceptPaymaster - This callback is called by the paymaster after it verified the transaction and agrees to pay for its execution.sigFailPaymaster - This callback is called by the paymaster if its paymasterData is expected to contain
some kind of signature, but it does not contain a valid one.The parameters passed to the callback functions have the following meaning:
validAfter, validUntil - same as defined in acceptAccountcontext - optional byte array provided by the paymaster contract, which is later passed to postPaymasterTransaction.
The length of this value MUST NOT exceed MAX_CONTEXT_SIZE bytes.The paymaster MUST make exactly one call to the AA_ENTRY_POINT to be considered valid.
Any other outcome, such as the paymaster not making any calls to the AA_ENTRY_POINT callbacks,
making any call other than acceptPaymaster,
making multiple calls to the AA_ENTRY_POINT,
or causing a reverted execution is considered to fail validation,
and the transaction is rejected and not included on-chain.
These callbacks may be called either by the paymaster directly or inside a DELEGATECALL that runs in the context of the paymaster.
If a callback is called by any other contract during the validation frame, the transaction is rejected and not included on-chain.
The sender address is invoked with executionData input.
The gas limit of this frame is set to callGasLimit.
Calculation of the calldataGasUsed value is defined in the
Gas fees charged for transaction input section.
The amount of gas used by this frame is referred to as gasUsedByExecution.
The validation frames do not revert even if the execution frame reverts.
The postPaymasterTransaction may still be called with a success: false flag.
After the sender execution frame is over the paymaster may need to perform some post-transaction logic,
for instance to perform some kind of cleanup or bookkeeping.
If the gas payment validation frame provided a non-zero context in the acceptPaymaster callback,
the paymaster is invoked again with the following inputs:
success indicates whether this transaction's execution frame completed without revert.actualGasCost parameter is the actual amount of gas spent by the paymaster for this transaction up to this point. Note that it does not include the gas cost of the postPaymasterTransaction function itself.The gas limit of this frame is set to paymasterPostOpGasLimit.
Revert in the postPaymasterTransaction frame reverts the transaction's execution frame as well.
The gas fees charged from the paymaster will include the validation frames and also gas cost of the reverted execution frame and postPaymasterTransaction frame.
The execution flow determined by an Account Abstraction Transaction is visualised by the following flow diagram:
Execution flow for the Native Account Abstraction Transactions
On the execution layer, the transaction validity conditions for a block are extended as follows:
In order to defend from DoS attack vectors, the block builders SHOULD consider the opcode banning and storage access rules described in ERC-7562.
Block validation takes roughly the same amount of work as without AA transactions.
In any case, validation must execute the entire block in order to verify the state change.
During this execution, it currently verifies signatures, nonces, and gas payment.
With Account Abstraction, it will also verify that all the validation frames were successful.
There is a slight increase in required memory mostly used to store the context value that is passed from
the paymaster validation frame to its post-transaction frame.
As long as all transaction validation steps execute successfully and provide correct values
to their AA_ENTRY_POINT callbacks, the block is considered valid.
Block builders who are willing to relax the rules applied to the validation frames MAY do so.
Zooming into a single transaction, the validation part of an AA transaction may include multiple execution frames:
Frames within a single Native Account Abstraction Transaction within a block
The Paymaster validation frame and the Sender validation frame each provide values for validUntil and validAfter.
These values allow the sender and paymaster contracts to specify
a time range for the blocks the transaction will be valid for.
Transaction cannot be included in a block outside of this time range. If included, such a block is considered invalid.
Passing validUntil = 0 and validAfter = 0 disables the check.
Note that the chainId and accessList parameters are included in the transaction hash
calculation but are not available on-chain as part of the TransactionTypeRIP7560 struct.
In order to calculate the transaction hash that will be used during the signing of the transaction and validation of
the transaction signature by the sender, the value of the authorizationData parameter is considered to be an empty
byte array.
The contracts that have a role in this Account Abstraction proposal, such as sender or paymaster,
MUST know which code to execute and understand the calldata provided to them in order to validate the transaction.
We argue that the most straightforward implementation is to rely on Solidity 4-byte method selectors as it is an established de-facto standard.
deployer from the AA_SENDER_CREATOR addressIt is important that the deployer is not invoked from the AA_ENTRY_POINT but from the AA_SENDER_CREATOR.
This is necessary to guarantee that AA_ENTRY_POINT may never initiate a call to a sender execution function
without first completing a successful validation.
Without this protection, as we do not control the deployerData field, it may be constructed to look like
a legitimate call from the AA_ENTRY_POINT to the sender.
AA_ENTRY_POINT approval callbacksThe successful validation frame execution in the context of a smart contract leads to a transaction gas payment. It is important to prevent any unsuspecting contract from being tricked into "accepting" an RIP-7560 transaction validation, which may lead to this contract being drained.
To do so we require specific call to the AA_ENTRY_POINT address to indicate RIP-7560 transaction acceptance.
sigFailAccount and sigFailPaymaster for authorizationData check failureThis callback is called by an account or a paymaster during their respective validation frames if they can verify
all relevant details of the transaction except for the transaction authorizationData.
This callback is used during gas estimation, and it helps wallet and apps to determine the gas used by the validation
process before generating any real signatures.
The transaction is expected to consume the same amount of gas when updated with a valid authorizationData.
This EIP preserves most of the design elements established by the ERC-4337. This allows the same client code and smart contracts to be used in both systems with minimal to no modifications, while providing significant UX improvements.
Existing contracts are not significantly affected by the change.
The assumption that tx.origin is guaranteed to be an EOA is no longer valid.
The assumption that tx.origin is the address that pays for the current transaction is no longer valid as well.
Any code that expects a single top-level execution frame for an Ethereum transaction will have to accommodate the new transaction type.
EIP-3607 introduces a ban on transactions from senders with deployed code. This limitation does not apply to AA_TX_TYPE transactions.
The ERC-4337 is not a protocol change and may remain operational in parallel to this EIP indefinitely.
Given the similarity to ERC-4337, the same block builders may easily support both ERC-4337 and AA_TX_TYPE transactions.
EntryPoint to an adapter contractThe team behind ERC-4337 will provide a reference implementation of a contract converting
the ABI of the paymaster and sender contracts. This adapter can be set as a trusted
EntryPoint address by the ERC-4337 contracts.
The sender contracts MAY support both ERC-4337 and AA_TX_TYPE transactions during a transition period,
as long as this EIP may be adopted by some chains and not by others.
This EIP creates a complex and sophisticated mechanism and aims to expand the usage of Smart Contract Accounts. All of it creates a lot of new risk vectors and attack surfaces.
The following is a non-exhaustive list of known security considerations regarding Native Account Abstraction.
This EIP adds a new way for a smart contract to have its balance charged simply by making a valid callback call
to the AA_ENTRY_POINT address from a
function with method ID that corresponds to validateTransaction, validatePaymasterTransaction.
This creates a new kind of risk for contracts that accidentally or maliciously contain such methods but are not public
about the fact that these contracts can be used as a sender or a paymaster in an AA_TX_TYPE transaction.
This concern is mitigated by requiring these contracts to call acceptAccount or acceptPaymaster callbacks
on the AA_ENTRY_POINT address, which makes contracts' interaction with the AA_ENTRY_POINT address explicit.
Code reviewers should be aware of this feature of RIP-7560 transactions.
Existing transaction types get included in a block even if reverted and provide a revert reason for debugging purposes. There is a very short list of things that can cause a transaction not to be included on-chain:
This is not the case for reverts that occur in the validation phase of an AA_TX_TYPE transaction.
In order to address this developers should track the validity of these transactions being signed and are encouraged
to rely on the validUntil time range parameter to guarantee a transaction that has not been included in the intended time
will not become valid again unexpectedly for the user who had sent it.
It is important to consider the ability of the block builder to fill the block with transactions in the allotted time.
In terms of block validity, all validation and RIP-7560 execution call frames may read and write any state when included in the block. This means that transactions are technically able to invalidate each other.
Block builders may face a risk of having multiple eligible transactions being invalidated by a single transaction once they include it in a block, causing the Denial of Service. Sequencer-like block builders do not have such a risk as long as they process incoming transactions in the order they are received.
As part of the mitigation, the AA transactions SHOULD be bound by storage access rules to avoid DoS on block builders. These rules are defined in ERC-7562.
The full mitigation may be achieved by separating the validation from into a separate context as described in the RIP-7711.
We recommend that Smart Contract Account developers make sure that users will be able to control their accounts
even in the event of AA_TX_TYPE transactions becoming unavailable for whatever reason.
This can be easily achieved by providing a public function that includes calls to both the validation and execution handlers of the Smart Contract Account.
Copyright and related rights waived via CC0.