---
eip: 5189
title: Account Abstraction via Endorsed Operations
description: An account abstraction proposal that avoids protocol changes while maintaining compatibility with existing smart contract wallets.
author: Agustín Aguilar (@agusx1211), Philippe Castonguay (@phabc), Michael Standen (@ScreamingHawk)
discussions-to: https://ethereum-magicians.org/t/erc-account-abstraction-via-endorsed-operations/9799
status: Draft
type: Standards Track
category: ERC
created: 2022-06-29
---

## Abstract

This ERC 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 ERC 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](./eip-3074.md)).
  * Payment of transaction fees using tokens. (E.g. [ERC-20](./eip-20.md), [ERC-777](./eip-777.md), etc.)
  * 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:

| Field                      | Type    | Description                                                                                                                                                 |
| -------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| entrypoint                 | address | Contract address that must be called with `callData` to execute the `operation`.                                                                            |
| callData                   | bytes   | Data that must be passed to the `entrypoint` call to execute the `operation`.                                                                               |
| fixedGas                   | uint64  | Amount of gas that the operation will pay for, regardless execution costs, and independent from `gasLimit`.                                                 |
| gasLimit                   | uint64  | Minimum gasLimit that must be passed when executing the `operation`.                                                                                        |
| feeToken                   | address | Contract address of the token used to repay the bundler. _(`address(0)` for the native token)_.                                                             |
| endorser                   | address | Address of the endorser contract that should be used to validate the `operation`.                                                                           |
| endorserCallData           | bytes   | Additional data that must be passed to the `endorser` when calling `isOperationReady()`.                                                                    |
| endorserGasLimit           | uint64  | Amount of gas that should be passed to the endorser when validating the `operation`.                                                                        |
| maxFeePerGas               | uint256 | Max amount of basefee that the `operation` execution is expected to pay. _(Similar to [EIP-1559](./eip-1559.md) `max_fee_per_gas`)_.                        |
| priorityFeePerGas          | uint256 | Fixed amount of fees that the `operation` execution is expected to pay to the bundler. _(Similar to [EIP-1559](./eip-1559.md) `max_priority_fee_per_gas`)_. |
| feeScalingFactor           | uint256 | Scaling factor to convert the computed fee into the `feeToken` unit.                                                                                        |
| feeNormalizationFactor     | uint256 | Normalization factor to convert the computed fee into the `feeToken` unit.                                                                                  |
| hasUntrustedContext        | bool    | If `true`, the operation _may_ have untrusted code paths. These should be treated differently by the bundler (see untrusted environment).                   |
| chainId                    | uint256 | Chain ID of the network where the `operation` is intended to be executed.                                                                                   |

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:

```solidity
interface Endorser {
  struct Operation {
    address entrypoint;
    bytes callData;
    uint256 fixedGas;
    uint256 gasLimit;
    address endorser;
    bytes endorserCallData;
    uint256 endorserGasLimit;
    uint256 maxFeePerGas;
    uint256 priorityFeePerGas;
    address feeToken;
    uint256 feeScalingFactor;
    uint256 feeNormalizationFactor;
    bool hasUntrustedContext;
  }

  struct GlobalDependency {
    bool baseFee;
    bool blobBaseFee;
    bool chainId;
    bool coinBase;
    bool difficulty;
    bool gasLimit;
    bool number;
    bool timestamp;
    bool txOrigin;
    bool txGasPrice;
    uint256 maxBlockNumber;
    uint256 maxBlockTimestamp;
  }

  struct Constraint {
    bytes32 slot;
    bytes32 minValue;
    bytes32 maxValue;
  }

  struct Dependency {
    address addr;
    bool balance;
    bool code;
    bool nonce;
    bool allSlots;
    bytes32[] slots;
    Constraint[] constraints;
  }

  struct Replacement {
    address oldAddr;
    address newAddr;
    SlotReplacement[] slots;
  }

  struct SlotReplacement {
    bytes32 slot;
    bytes32 value;
  }

  function simulationSettings(
    Operation calldata _operation
  ) external view returns (
    Replacement[] memory replacements
  );

  function isOperationReady(
    Operation calldata _operation
  ) external returns (
    bool readiness,
    GlobalDependency memory globalDependency,
    Dependency[] memory dependencies
  );
}
```

Endorsers SHOULD be registered in the `EndorserRegistry` with an amount of burned ETH.
The amount of ETH to be burned is not specified in this proposal as mempool operators are free to set their own minimum thresholds.
Mempool operators MAY accept operations from endorsers without any burned ETH, but they would increase their risk exposing themselves to denial of service attacks.
Mempool operators MAY publish the minimum amount of burned ETH required for each endorser.

To check for operation status, the caller must first call `simulationSettings` to retrieve a list of on chain alterations.
Then call when the `isOperationReady` method is called, the endorser must return this information:

* **readiness:** when returning `true`, it means the transaction MUST be executed correctly and the bundler MUST be paid the offered gas fees (even if the underlying intent of the operation fails).
* **globalDependency:** a list of possible dependencies that don't belong to a given address, defines if the execution of the transaction MAY be invalidated by a change on one of these global variables. `maxBlockNumber` and `maxBlockTimestamp` are used as global constraints.
* **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; but it only provides a soft guarantee that the transaction will be executed correctly. Bundlers must always simulate the result of the execution before including a transaction in a block.

If the result of a simulation fails and the endorser still returns `readiness == true` with the same dependencies, then the endorser can not be trusted and it MUST be banned by the mempool operator.

The dependency list serves as a shortcut for the bundler to know which operations are fully independent from each other. This shortcut is useful for (a) clearing the mempool from operations that are no longer valid, and (b) for bundlers to know which operations can be included in the same block.

For efficiency, additional information MAY 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.

While the endorser is deployed on chain, calls to the endorser MUST NOT be submitted on chain. The bundler MUST read the results of `simulationSettings`, perform chain alterations and simulate the execution off chain.

### Global Dependencies

| Field             | Type    | Description                                                           |
| ----------------- | ------- | --------------------------------------------------------------------- |
| baseFee           | bool    | `true` if the `block.basefee` should be considered a dependency.      |
| blobBaseFee       | bool    | `true` if the `block.blockbasefee` should be considered a dependency. |
| chainId           | bool    | `true` if the `block.chainid` should be considered a dependency.      |
| coinbase          | bool    | `true` if the `block.coinbase` should be considered a dependency.     |
| difficulty        | bool    | `true` if the `block.difficulty` should be considered a dependency.   |
| gasLimit          | bool    | `true` if the `block.gaslimit` should be considered a dependency.     |
| number            | bool    | `true` if the `block.number` should be considered a dependency.       |
| timestamp         | bool    | `true` if the `block.timestamp` should be considered a dependency.    |
| txOrigin          | bool    | `true` if the `tx.origin` should be considered a dependency.          |
| txGasPrice        | bool    | `true` if the `tx.gasprice` should be considered a dependency.        |
| maxBlockNumber    | uint256 | The maximum value of `block.number` that `readiness` applies to.      |
| maxBlockTimestamp | uint256 | The maximum value of `block.timestamp` that `readiness` applies to.   |

The `endorser` MUST use the `maxBlockNumber` and `maxBlockTimestamp` 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 `maxBlockNumber` and `maxBlockTimestamp` fields MUST be set to `type(uint256).max`.

### Dependencies

| Field       | Type         | Description                                                                                 |
| ----------- | ------------ | ------------------------------------------------------------------------------------------- |
| addr        | address      | Contract address of the dependencies entry. _(Only one entry per address is allowed)_.      |
| balance     | bool         | `true` if the balance of `addr` should be considered a dependency of the `operation`.       |
| code        | bool         | `true` if the code of `addr` should be considered a dependency of the `operation`.          |
| nonce       | bool         | `true` if the nonce of `addr` should be considered a dependency of the `operation`.         |
| allSlots    | bool         | `true` if all storage slots of `addr` should be considered a dependency of the `operation`. |
| slots       | bytes32[]    | List of all storage slots of `addr` that should be considered dependencies of `operation`.  |
| constraints | Constraint[] | List of storage slots of `addr` that have a range of specific values as dependencies.       |

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`, `constraints` and `slots` are mutually exclusive. If `allSlots` is set to `true`, then `constraints` and `slots` MUST be empty arrays.
If a slot is listed in `constraints`, it MUST NOT be listed in `slots`.
The `endorser` should prefer to use `constraints` over `slots`, and `slots` over `allSlots` whenever possible to limit reevaluation requirements of the bundler.

> E.g. A wallet may pay fees using funds stored as WETH. During `isOperationReady()`, 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.

#### Constraints

| Field    | Type    | Description                                                                 |
| -------- | ------- | --------------------------------------------------------------------------- |
| slot     | bytes32 | Storage slot of `addr` that has a range of specific values as dependencies. |
| minValue | bytes32 | Minimum value (inclusive) of `slot` that `readiness` applies to.            |
| maxValue | bytes32 | Maximum value (inclusive) of `slot` that `readiness` applies to.            |

The `endorser` can use the `minValue` and `maxValue` fields to limit the validity of the `readiness` result. This allows the endorser to fully validate an operation, even when this operation depends on storage values that are not directly accessible by the endorser.

Note that all values are **inclusive**. When an exact value is required, `minValue` and `maxValue` should be set to the same value.

### Simulation settings

The `simulationSettings` method returns a list of replacements that the bundler should apply to the operation before simulating the `isOperationReady`. Note that these replacements are only used for `isOperationReady` simulation and are not applied when simulating the operation itself.

| Field       | Type    | Description                                                                 |
| ----------- | ------- | --------------------------------------------------------------------------- |
| oldAddr     | address | The on chain address where contract code is currently located.              |
| newAddr     | address | The address the contract code should be located when performing simulation. |
| slots.slot  | bytes32 | The slot location to be changed.                                            |
| slots.value | bytes32 | The value of the slot to be set before performing simulation.               |

The `endorser` MAY use the `simulationSettings` method to provide a list of replacements that the bundler should apply to the network before simulating `isOperationReady`. This is useful for operations that must be called from specific contract addresses or that depend on specific storage values (e.g. [ERC-4337](./eip-4337.md)'s EntryPoint).

The `endorser` MAY provide it's own address for replacement. In this event, the bundler should update the `endorser` address used when calling `isOperationReady`.

### Misbehavior detection

It is possible for `endorser` contracts to behave maliciously or erratically in the following ways:

* (1) It considers an operation "ready", but when the operation is executed it transfers less than the agreed-upon fees to the bundler.
* (2) It considers an operation "ready", but when the operation is executed the top-level call fails.
* (3) It changes the readiness from `true` to `false` while none of the dependencies register any change.

The bundler MUST 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 MUST ban the `endorser`.

When an `endorser` is banned, the mempool operator MUST drop all `operations` related to the endorser.

### Untrusted environment

In some scenarios, the `endorser` may not be able to fully validate the `operation` but may be able to infer that a given code path *should* be safe. In these cases, the endorser can mark a section of the operation as `untrusted`. Any storage slots (balance, code, nonce, or specific slots) accessed in this untrusted context should be automatically considered as dependencies.

```sol
interface Endorser {
  event UntrustedStarted();
  event UntrustedEnded();
}
```

The endorser can use the `UntrustedStarted` and `UntrustedEnded` events to signal the start and end of an untrusted context. The bundler should listen to these events and extend the dependencies list accordingly.

Only the top-level `endorser` can signal an untrusted context; any other events with the same signature but emitted by a different contract should be ignored.

Untrusted contexts can be opened and closed multiple times and can be nested. If multiple events are emitted, the bundler MUST count the number of `UntrustedStarted` and `UntrustedEnded` events and only consider the untrusted context as ended when the number of `UntrustedEnded` events is equal to the number of `UntrustedStarted` events.

If `hasUntrustedContext` is set to `false`, the bundler should ignore any `UntrustedStarted` and `UntrustedEnded` events.

#### Automatic dependency graph construction

All code executed within the untrusted context must be monitored. If the code executes any of the following opcodes, the dependency graph must be extended accordingly.

| Opcode      | Dependency                              |
|-------------|-----------------------------------------|
| BALANCE     | `dependencies[addr].balance = true`     |
| ORIGIN      | `global.txOrigin = true`                |
| CODESIZE    | None                                    |
| CODECOPY    | None                                    |
| GASPRICE    | `global.txGasPrice = true`              |
| EXTCODESIZE | `dependencies[addr].code = true`        |
| EXTCODECOPY | `dependencies[addr].code = true`        |
| EXTCODEHASH | `dependencies[addr].code = true`        |
| COINBASE    | `global.coinbase = true`                |
| TIMESTAMP   | `global.timestamp = true`               |
| NUMBER      | `global.number = true`                  |
| DIFFICULTY  | `global.difficulty = true`              |
| PREVRANDAO  | `global.difficulty = true`              |
| CHAINID     | `global.chainId = true`                 |
| SELFBALANCE | `dependencies[self].balance = true`     |
| BASEFEE     | `global.baseFee = true`                 |
| SLOAD       | `dependencies[addr].slots[slot] = true` |
| CREATE      | `dependencies[addr].nonce = true`       |
| CREATE2     | `dependencies[contract].code = true`    |

Notice that untrusted contexts generate a lot of dependencies and may generate many false positives. This may lead to numerous re-evaluations and thus to the operation being dropped from the mempool. A bundler MAY choose to drop operations if the number of dependencies exceeds a certain threshold.

Block-level dependencies are specially sensitive as they will be shared with a large number of operations.

It is recommended to use untrusted contexts only when necessary, like when an `endorser` needs to validate a nested signature to a wallet that is not under its control.

### Fee payment

The `endorser` MUST guarantee that the operation will repay at least the spent gas to `tx.origin`.

The payment is always made in the `feeToken`, which can be any token standard (E.g. [ERC-20](./eip-20.md)). If `feeToken` is `address(0)`, then payment is made in the native currency. When `feeToken` is `address(0)`, `feeScalingFactor` and `feeNormalizationFactor` MUST be equal to `1`.

All units are expressed in the native token unit. The result of the fee calculation is then converted to the `feeToken` unit using the `feeScalingFactor` and `feeNormalizationFactor`.

The gas units consider a fixed amount of gas (`fixedGas`) and a variable amount of gas (`gasLimit`). Allowing fixed costs caters for gas overheads which may be outside the scope of the on chain execution, such as calldata fees. This also allows repayment to be reduced when execution is cheaper than expected (such as when an inner call fails without reverting the top-level transaction), while still repaying the bundler.

The expected gas repayment is calculated as follows:

```
gasUnits = op.fixedGas + Min(gasUsed, op.gasLimit)
feePerGas = Min(op.maxFeePerGas, block.baseFee + op.priorityFeePerGas)
expectedRepayment = (gasUnits * feePerGas * op.feeScalingFactor) / op.feeNormalizationFactor
```

While the `endorser` MUST guarantee the repayment of `expectedRepayment`, the actual repayment amount MAY exceed this fee. E.g. For ease of development, a bundler MAY choose to only endorse operations that repay the maximum values provided by the operation.

### Operation identification

Operations can be identified by their operation hash, which is calculated as a CIDv1 multihash of a `raw` file, containing the canonical JSON representation of the operation. This hash is never used on-chain, but it serves as a unique pointer to the operation that can be shared across systems.

The operation MAY be pinned on the IPFS network; this would allow other participants to retrieve the content of the operation after the operation has been removed from the mempool. This pinning is not mandatory, and it may be performed by the mempool operator or by the wallet itself if visibility of the operation is desired.

### 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 `fixedGas` is large enough to cover the cost associated with submitting the transaction (i.e. calldata gas costs).
* The `gasLimit` is at least the cost of a `CALL` with a non-zero value.
* The `feeToken` is `address(0)` or a known token address that the bundler is willing to accept.
* The `feeScalingFactor` and `feeNormalizationFactor` are `1` for a `feeToken` value of `address(0)` or values 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)

The bundler should then perform evaluation of the operation.

### Evaluation

To evaluate the `operation`, the bundler MUST call `simulationSettings()` on the `endorser` to obtain simulation setting values. The bundler MUST apply the settings and **simulate** a call to `isOperationReady()` on the `endorser`. If the endorser considers the operation ready, and the constraints are within bounds, 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 by constructing the block and probing the result. All transactions in the block listed **before** the operation must be simulated and then the `endorser` must be queried for readiness in-case some dependencies changed. Then constraints MUST be re-evaluated for correctness. Finally, the **operation** MUST be simulated.

If the **operation** fails during the final 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.

### Optional rules

Mempool clients MAY 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 and avoid shared dependency slots that are frequently updated.

### After operation inclusion

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

The bundler SHOULD NOT drop an `operation` after successfully including such operation in a block, the bundler MAY perform evaluation.

If the `endorser` still returns `readiness == true` (after inclusion) then the operation SHOULD be treated as any other healthy operation, and thus it MAY 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)

```solidity
// 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 ERC 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 ERC 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

Copyright and related rights waived via [CC0](../LICENSE.md).
