HomeEIPs
EIPsERC-5189
ERC-5189

Account Abstraction via Endorsed Operations

An account abstraction proposal that avoids protocol changes while maintaining compatibility with existing smart contract wallets.
DraftStandards Track: ERC
Created: 2022-06-29
Agustín Aguilar (@agusx1211), Philippe Castonguay (@phabc), Michael Standen (@ScreamingHawk)
DiscussionsOriginal linkEdit
1 min read

The ERC-5189 proposal suggests a way to implement account abstraction without making changes to the consensus layer and while maintaining compatibility with existing smart contract wallets. Instead of defining a strict structure for meta-transactions, the proposal introduces the concept of endorser contracts. These smart contract instances are responsible for determining the quality of submitted meta-transactions, thus helping bundlers decide whether to keep a meta-transaction in the mempool or not. Developers who want to make their smart contract wallet compatible with this proposal must create and deploy an instance of an endorser, which must be seeded with a small amount of ETH to incentivize good behavior. The proposal aims to achieve the primary goal of account abstraction, which is to allow users to use smart contract wallets containing arbitrary verification and execution logic instead of externally owned accounts (EOAs) as their primary account. It also aims to support existing smart contract wallet implementations, work with all activity happening over a public mempool, and avoid trust assumptions between bundlers, developers, and wallets. The proposal is designed to accommodate almost all possible variations of smart contract wallets and to maintain a healthy mempool without risking its participants from getting flooded with invalid or malicious payloads.

Video
Anyone may contribute to propose contents.
Go propose
Original

Abstract

This EIP proposes a form of account abstraction (AA) that ensures compatibility with existing smart contract wallets and provides flexibility for alternative designs while avoiding introducing changes to the consensus layer. Instead of defining a strict structure for AA transactions, this proposal introduces the figure of endorser contracts. These smart contract instances are tasked with determining the quality of the submitted AA transactions, thus safely helping bundlers determine if a transaction should be kept in the mempool or not. Developers that intend to make their smart contract wallet compatible with this EIP must create and deploy an instance of an endorser or use an existing one compatible with their wallet.

Motivation

This account abstraction proposal aims to implement a generalized system for executing AA transactions while maintaining the following goals:

  • Achieve the primary goal of account abstraction: allow users to use smart contract wallets containing arbitrary verification and execution logic instead of EOAs as their primary account.
  • Decentralization:
    • Allow any bundler to participate in the process of including AA transactions.
    • Work with all activity happening over a public mempool without having to concentrate transactions on centralized relayers.
    • Define structures that help maintain a healthy mempool without risking its participants from getting flooded with invalid or malicious payloads.
    • Avoid trust assumptions between bundlers, developers, and wallets.
  • Support existing smart contract wallet implementations: Work with all the smart contract wallets already deployed and active while avoiding forcing each wallet instance to be manually upgraded.
  • Provide an unrestrictive framework: Smart contract wallets are very different in design, limitations, and capabilities from one another; the proposal is designed to accommodate almost all possible variations.
  • No overhead: Smart contract wallets already have a cost overhead compared to EOA alternatives, the proposal does not worsen the current situation.
  • Support other use cases:
    • Privacy-preserving applications.
    • Atomic multi-operations (similar to EIP-3074).
    • Payment of transaction fees using ERC-20 tokens.
    • Scheduled execution of smart contracts without any user input.
    • Applications that require a generalistic relayer.

Specification

To avoid Ethereum consensus changes, we do not attempt to create new transaction types for account-abstracted transactions. Instead, AA transactions are packed up in a struct called Operation, operations are structs composed by the following fields:

FieldTypeDescription
entrypointaddresscontract address that must be called with callData to execute the operation.
callDatabytesdata that must be passed to the entrypoint call to execute the operation.
gasLimituint64minimum gasLimit that must be passed when executing the operation.
feeTokenaddresscontract address of the ERC-20 token used to repay the bundler. (address(0) for the native token).
endorseraddressaddress of the endorser contract that should be used to validate the operation.
endorserCallDatabytesadditional data that must be passed to the endorser when calling isOperationReady().
endorserGasLimituint64amount of gas that should be passed to the endorser when validating the operation.
maxFeePerGasuint256max amount of basefee that the operation execution is expected to pay. (Similar to EIP-1559 max_fee_per_gas).
priorityFeePerGasuint256fixed amount of fees that the operation execution is expected to pay to the bundler. (Similar to EIP-1559 max_priority_fee_per_gas).

These Operation objects can be sent to a dedicated operations mempool. A specialized class of actors called bundlers (either block producers running special-purpose code, or just users that can relay transactions to block producers) listen for operations on the mempool and execute these transactions.

Transactions are executed by calling the entrypoint with the provided callData. The entrypoint can be any contract, but most commonly it will be the wallet contract itself. Alternatively it can be an intermediary utility that deploys the wallet and then performs the transaction.

Endorser functionality

Mempool participants need to be able to able to filter "good operations" (operations that pay the bundler the defined fee) from "bad operations" (operations that either miss payment or revert altogether).

This categorization is facilitated by the endorser; the endorser must be a deployed smart contract that implements the following interface:

interface Endorser { struct BlockDependency { uint256 maxNumber; uint256 maxTimestamp; } struct Dependency { address addr; bool balance; bool code; bool nonce; bool allSlots; bytes32[] slots; } function isOperationReady( address _entrypoint, bytes calldata _data, bytes calldata _endorserCallData, uint256 _gasLimit, uint256 _maxFeePerGas, uint256 _maxPriorityFeePerGas ) external view returns ( bool readiness, BlockDependency memory blockDependency, Dependency[] memory dependencies ); }

Endorsers SHOULD be registered in the EndorserRegistry with a minimum amount of burned ETH (Mempool operators are free to accept operations from endorsers without any burned ETH, but they would increase their risk exposing themselves to denial of service attacks).

When the isOperationReady method is called, the endorser must return this information:

  • readiness: when returningtrue, it means the transaction WILL be executed correctly and the bundler WILL be paid the offered gas fees (even if the underlying intent of the operation fails).
  • blockDependency: maximum values for block values; once the block reaches these values, the readiness result MUST be re-evaluated.
  • dependencies: a comprehensive list of addresses and storage slots that must be monitored; any state change in these dependencies MUST trigger a re-evaluation of the operation's readiness.

The information provided by the endorser helps the mempool operator maintain a pool of "good" AA transactions that behave correctly; it DOES NOT guarantee that such transactions will be able to be executed correctly. Bundlers must always simulate the result of the execution before including a transaction in a block. They can alternatively monitor the dependencies for changes induced by other transactions in the mempool.

For efficiency, additional information CAN be provided to the endorser with _endorserCallData. If used, the endorser MUST validate that the provided _endorserCallData is valid and relevant to the other values provided.

Block Dependencies

FieldTypeDescription
maxNumberuint256The maximum value of block.number that readiness applies to.
maxTimestampuint256The maximum value of block.timestamp that readiness applies to.

The endorser can use the maxNumber and maxTimestamp fields to limit the validity of the readiness result. This is useful for operations that are only valid for a certain period of time.

Note that all values are inclusive. If the endorser determines the validity of the operation is indefinite, the maxNumber and maxTimestamp fields should be set to type(uint256).max.

Dependencies

FieldTypeDescription
addraddressContract address of the dependencies entry. (Only one entry per address should be allowed).
balancebooltrue if the balance of addr should be considered a dependency of the operation.
codebooltrue if the code of addr should be considered a dependency of the operation.
noncebooltrue if the nonce of addr should be considered a dependency of the operation.
allSlotsbooltrue if all storage slots of addr should be considered a dependency of the operation .
slotsbytes32[]List of all storage slots of addr that should be considered dependencies of operation.

The endorser does not need to include all accessed storage slots on the dependencies list, it only needs to include storage slots that after a change may also result in a change of readiness.

Note that allSlots and slots are mutually exclusive, if allSlots is set to true, then slots MUST be an empty array. The endorser should prefer to use slots over allSlots whenever possible.

E.g. A wallet may pay fees using funds stored as WETH. During isValidOperation(), the endorser contract may call the balanceOf method of the WETH contract to determine if the wallet has enough WETH balance. Even though the ETH balance of the WETH contract and the code of the WETH contract are being accessed, the endorser only cares about the user's WETH balance for this operation and hence does not include these as dependencies.

Misbehavior detection

The endorser contracts may behave maliciously or erratically in the following ways:

  • (1) It may consider an operation ready, but when the operation is executed it transfers less than the agreed-upon fees to the bundler.
  • (2) It may consider an operation ready, but when the operation is executed the top-level call fails.
  • (3) It may change the status from ready to not-ready while none of the dependencies register any change.

The bundler must always discard and re-evaluate the readiness status after a change on any of the dependencies of the operation, meaning that only operations considered ready are candidates for constructing the next block.

If, when simulating the final inclusion of the operation, the bundler discovers that it does not result in correct payment (either because the transaction fails, or transferred amount is below the defined fee), then it should proceed to ban the endorser for one of the following reasons:

  1. The endorser returns isOperationReady() == true even though the operation is not healthy to be included in a block.
  2. The operation changed readiness status from true to false while all dependencies remained unchanged.

After an endorser is banned, the mempool operator should drop all operations related to such endorser.

Notice: The mempool operator could call one last time isOperationReady to determine if the endorser should be banned because (1) or (2), but this step is not strictly necessary since both scenarios lead to the endoser being banned.

Bundler behavior upon receiving an operation

Bundlers can add their own rules for how to ensure the successful relaying of AA transactions and for getting paid for relaying these transactions. However, we propose here a baseline specification that should be sufficient.

When a bundler receives an operation, it SHOULD perform these sanity checks:

  • The endorserGasLimit is sufficiently low (<= MAX_ENDORSER_GAS).
  • The endorser (i) is registered and has enough burn (>= MIN_ENDORSER_BURN), and (ii) it has not been internally flagged as banned.
  • The gasLimit is at least the cost of a CALL with a non-zero value.
  • The feeToken is address(0) or a known ERC-20 token that the bundler is willing to accept.
  • The maxFeePerGas and priorityPerGas are above a configurable minimum value the bundler is willing to accept.
  • If another operation exists in the mempool with the exact same dependency set AND the same endorser address, the maxFeePerGas and priorityFeePerGas of the newly received operation MUST be 12% higher than the one on the mempool to replace it. (Similar with how EOA with same nonce work)

If the operation passes these checks, then the bundler MUST call isOperationReady() on the endorser. If the endorser considers the operation ready, then the client MUST add the operation to the mempool. Otherwise, the operation MUST be dropped.

The endorser result SHOULD be invalidated and its readiness SHOULD be re-evaluated if any of the values of the provided dependencies change. If the operation readiness changes to false, the operation MUST be discarded.

Before including the operation in a block, a last simulation MUST be performed, this time without calling the endorser, but by constructing the block and probing the result. All transactions in the block listed before the operation must be simulated and the endorser must be queried again there for readiness in-case some dependencies changed.

If the operation fails during simulation, the endorser must be banned because (i) it returned a bad readiness state or (ii) it changed the operation readiness independently from the dependencies.

Additional events that must invalidate the readiness are:

  • A transaction or operation modifies the same storage slots (as the dependencies) is queued before the given operation.

Optional rules

Mempool clients could implement additional rules to further protect against maliciously constructed transactions.

  • Limit the size of accepted dependencies to MAX_OPERATION_DEPENDENCIES, dropping operations that cross the boundary.
  • Limit the number of times an operation may trigger a re-evaluation to MAX_OPERATION_REEVALS, dropping operations that cross the boundary.
  • Limit the number of operations in the mempool that depend on the same dependency slots.

If these rules are widely adopted, wallet developers should keep usage of dependencies to the lowest possible levels.

Evaluation

To evaluate an operation, the bundler must call the isOperationReady() method, with a gasLimit above or equal to endorserGasLimit.

If the call fails, or the endorser returns readiness == false, then the operation must be dropped from the mempool.

If the call succeeds and returns readiness == true, then the operation can be kept in the mempool and used when constructing a block.

After operation inclusion

There is no limit in-place that defines that an operation can only be executed once.

The bundler MUST NOT drop an operation after successfully including such operation in a block, the operation must remain in the mempool and a last isOperationReady() call must be performed.

If the endorser still returns readiness == true (after inclusion) then the operation SHOULD be treated as any other healthy operation, and thus it COULD be kept in the mempool.

Endorser registry

The endorser registry serves as a place to register the burn of each endorser, anyone can increase the burn of any endorser by calling the addBurn() function.

All burn is effectively locked forever; slashing can't be reliably proved on-chain without protocol alterations, so it remains a virtual event on which mempool operators will ignore the deposited ETH.

Implementation

(EXAMPLE)

// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.15; contract EndorserRegistry { event Burned( address indexed _endorser, address indexed _sender, uint256 _new, uint256 _total ); mapping(address => uint256) public burn; function addBurn(address _endorser) external payable returns (uint256) { uint256 total = burn[_endorser] + msg.value; burn[_endorser] = total; emit Burned(_endorser, msg.sender, msg.value, total); return total; } }

Rationale

Griefing protection

The main challenge with a purely smart contract wallet-based account abstraction system is DoS safety: how can a bundler that includes an operation make sure it will be paid without executing the entire operation?

Bundlers could execute the entire operation to determine if it is healthy or not, but this operation may be expensive and complex for the following reasons:

  • The bundler does not have a way to simulate the transaction with a reduced amount of gas; it has to use the whole gasLimit, exposing itself to a higher level of griefing.
  • The bundler does not have a way to know if a change to the state will affect the operation or not, and thus it has to re-evaluate the operation after every single change.
  • The bundler does not have a way to know if a change to the state will invalidate a large portion of the mempool.

In this proposal, we add the endorser as a tool for the bundlers to validate arbitrary operations in a controlled manner, without the bundler having to know any of the inner workings of such operation.

In effect, we move the responsibility from the wallet to the wallet developer; the developer must code, deploy and burn ETH for the endorser; this is a nearly ideal scenario because developers know how their wallet operations work, and thus they can build tools to evaluate these operations efficiently.

Additionally, the specification is kept as simple as possible as enforcing a highly structured behavior and schema for smart contract wallet transactions may stagnate the adoption of more innovative types of wallets and the adoption of a shared standard among them.

Burned ETH

Anyone can deploy a endorser contract and wallet clients are the one providing which endorser contract should be used for the given transaction. Instead of having each bundler rely on an off-chain registry that they need to maintain, the endorser registry can be called to see if the requested endorser contract is present and how much ETH was burned for it. Bundlers can then decide a minimum treshshold for how much ETH burnt is required for an endorser contract to be accepted. Bundlers are also free to support endorsers contract that are not part of the registry or are part of it but have no ETH burned associated.

Minimum overhead

Since the validation of an AA transactions is done off-chain by the bundler rather than at execution time, there is no additional gas fee overhead for executing transactions. The bundler bears the risk rather than all users having to pay for that security.

Differences with alternative proposals

  1. This proposal does not require monitoring for forbidden opcodes or storage access boundaries. Wallets have complete freedom to use any EVM capabilities during validation and execution.
  2. This proposal does not specify any replay protection logic since all existing smart contract wallets already have their own, and designs can vary among them. Nonces can be communicated to the bundler using a dependency.
  3. This proposal does not specify a pre-deployment logic because it can be handled directly by the entrypoint.
  4. This proposal does not require wallets to accept execution transactions from a trusted entrypoint contract, reducing overhead and allowing existing wallets to be compatible with the proposal.
  5. This proposal does not distinguish between execution and signature payloads, this distinction remains implementation-specific.

Backwards Compatibility

This EIP does not change he consensus layer, nor does impose changes on existing smart contract wallets, so there are no backwards compatibility issues.

Security Considerations

This EIP does not make changes to on-chain interactions. Endorsers are explicitly for off-chain validations.

Bundlers are responsible for managing their own security and for ensuring that they are paid for the transactions they include in blocks.

Copyright and related rights waived via CC0.

Further reading
Anyone may contribute to propose contents.
Go propose
Adopted by projects
Anyone may contribute to propose contents.
Go propose

Not miss a beat of EIPs' update?

Subscribe EIPs Fun to receive the latest updates of EIPs Good for Buidlers to follow up.

View all
Serve EIP builders, scale Ethereum.
Resources
GitHub
Supported by