Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ERC-4337: Extract aggregation to a separate ERC #627

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
90 changes: 13 additions & 77 deletions ERCS/erc-4337.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ This proposal takes a different approach, avoiding any adjustments to the consen
* Privacy-preserving applications
* Atomic multi-operations (similar goal to [EIP-3074])
* Pay tx fees with [ERC-20](./eip-20.md) tokens, allow developers to pay fees for their users, and [EIP-3074]-like **sponsored transaction** use cases more generally
* Support aggregated signature (e.g. BLS)

## Specification

Expand All @@ -52,7 +51,6 @@ This proposal takes a different approach, avoiding any adjustments to the consen
other kind of PBS (proposer-builder separation)
* The `bundler` can also rely on an experimental `eth_sendRawTransactionConditional` RPC API if it is available.
* **Paymaster** - a helper contract that agrees to pay for the transaction, instead of the sender itself.
* **Aggregator** - a helper contract trusted by accounts to validate an aggregated signature. Bundlers/Clients whitelist the supported aggregators.

### UserOperation

Expand Down Expand Up @@ -102,17 +100,6 @@ The core interface of the entry point contract is as follows:

```solidity
function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary);

function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
);

struct UserOpsPerAggregator {
PackedUserOperation[] userOps;
IAggregator aggregator;
bytes signature;
}
```

### Account Contract Interface
Expand All @@ -132,18 +119,15 @@ The `userOpHash` is a hash over the userOp (except signature), entryPoint and ch
The account:

* MUST validate the caller is a trusted EntryPoint
* If the account does not support signature aggregation, it MUST validate that the signature is a valid signature of the `userOpHash`, and
* MUST validate that the signature is a valid signature of the `userOpHash`, and
SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch. Any other error MUST revert.
* MUST pay the entryPoint (caller) at least the "missingAccountFunds" (which might be zero, in case the current account's deposit is high enough)
* The account MAY pay more than this minimum, to cover future transactions (it can always issue `withdrawTo` to retrieve it)
* The return value MUST be packed of `authorizer`, `validUntil` and `validAfter` timestamps.
* authorizer - 0 for valid signature, 1 to mark signature failure. Otherwise, an address of an authorizer contract. This ERC defines a "signature aggregator" as an authorizer.
* authorizer - 0 for valid signature, 1 to mark signature failure. Otherwise, an address of an authorizer contract, as defined in [ERC-7766](./eip-7766.md).
* `validUntil` is 6-byte timestamp value, or zero for "infinite". The UserOp is valid only up to this time.
* `validAfter` is 6-byte timestamp. The UserOp is valid only after this time.

An account that works with aggregated signature, should return its signature aggregator address in the "sigAuthorizer" return value of validateUserOp.
It MAY ignore the signature field.

The account MAY implement the interface `IAccountExecute`

```solidity
Expand Down Expand Up @@ -222,11 +206,8 @@ this bundler is supposed to track the `key` and `sequence` pair of the UserOpera

### Required entry point contract functionality

There are 2 separate entry point methods: `handleOps` and `handleAggregatedOps`
The entry point method is `handleOps`, which handles an array of userOps

* `handleOps` handles userOps of accounts that don't require any signature aggregator.
* `handleAggregatedOps` can handle a batch that contains userOps of multiple aggregators (and also requests without any aggregator)
* `handleAggregatedOps` performs the same logic below as `handleOps`, but it must transfer the correct aggregator to each userOp, and also must call `validateSignatures` on each aggregator before doing all the per-account validation.
The entry point's `handleOps` function must perform the following steps (we first describe the simpler non-paymaster case). It must make two loops, the **verification loop** and the **execution loop**. In the verification loop, the `handleOps` call must perform the following steps for each `UserOperation`:

* **Create the account if it does not yet exist**, using the initcode provided in the `UserOperation`. If the account does not exist, _and_ the initcode is empty, or does not deploy a contract at the "sender" address, the call must fail.
Expand Down Expand Up @@ -322,33 +303,6 @@ When a client receives a `UserOperation`, it must first run some basic sanity ch

If the `UserOperation` object passes these sanity checks, the client must next run the first op simulation, and if the simulation succeeds, the client must add the op to the pool. A second simulation must also happen during bundling to make sure the UserOperation is still valid.

### Using Signature Aggregator

A signature aggregator exposes the following interface

```solidity
interface IAggregator {

function validateUserOpSignature(PackedUserOperation calldata userOp)
external view returns (bytes memory sigForUserOp);

function aggregateSignatures(PackedUserOperation[] calldata userOps) external view returns (bytes memory aggregatesSignature);

function validateSignatures(PackedUserOperation[] calldata userOps, bytes calldata signature) view external;
}
```

* An account signifies it uses signature aggregation returning its address from `validateUserOp`.
* During `simulateValidation`, this aggregator is returned to the bundler as part of the `aggregatorInfo` struct.
* The bundler should first accept the aggregator (aggregators must be staked. bundler should verify it is not throttled/banned)
* To accept the UserOp, the bundler must call **validateUserOpSignature()** to validate the userOp's signature.
This method returned an alternate signature (usually empty) that should be used during bundling.
* The bundler MUST call `validateUserOp` a second time on the account with the UserOperation using that returned signature, and make sure it returns the same value.
* **aggregateSignatures()** must aggregate all UserOp signatures into a single value.
* Note that the above methods are helper methods for the bundler. The bundler MAY use a native library to perform the same validation and aggregation logic.
* **validateSignatures()** MUST validate the aggregated signature matches for all UserOperations in the array, and revert otherwise.
This method is called on-chain by `handleOps()`

### Simulation

#### Simulation Rationale
Expand All @@ -357,7 +311,7 @@ To add a UserOperation into the mempool (and later to add it into a bundle) we n
In addition, we need to verify that the same will hold true when executed on-chain.
For this purpose, a UserOperation is not allowed to access any information that might change between simulation and execution, such as current block time, number, hash etc.
In addition, a UserOperation is only allowed to access data related to this sender address: Multiple UserOperations should not access the same storage, so it is impossible to invalidate a large number of UserOperations with a single state change.
There are 3 special contracts that interact with the account: the factory (initCode) that deploys the contract, the paymaster that can pay for the gas, and a signature aggregator (described later)
There are 2 special entity contracts that interact with the account: the factory (initCode) that deploys the contract, and the paymaster that can pay for the gas.
Each of these contracts is also restricted in its storage access, to make sure UserOperation validations are isolated.

#### Simulation Specification:
Expand Down Expand Up @@ -390,19 +344,15 @@ struct ReturnInfo {
bytes paymasterContext;
}

struct AggregatorStakeInfo {
address aggregator;
StakeInfo stakeInfo;
}

struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}


```

The `AggregatorStakeInfo` structure is further defined in [ERC-7766](./eip-7766.md).

This method returns `ValidationResult` or revert on validation failure.
The node should drop the UserOperation if the simulation fails (either by revert or by "signature failure")

Expand All @@ -413,8 +363,7 @@ The simulated call performs the full validation, by calling:
3. if specified a paymaster: `paymaster.validatePaymasterUserOp`.

The simulateValidation should validate the return value (validationData) returned by the account's `validateUserOp` and paymaster's `validatePaymasterUserOp`.
The account MAY return an aggregator. See [Using Signature Aggregator](#using-signature-aggregator)
The paymaster MUST return either "0" (success) or SIG_VALIDATION_FAILED for aggregator, and not an address.
The paymaster MUST return either "0" (success) or SIG_VALIDATION_FAILED.
Either return value may contain a "validAfter" and "validUntil" timestamps, which is the time-range that this UserOperation is valid on-chain.
A node MAY drop a UserOperation if it expires too soon (e.g. wouldn't make it to the next block) by either the account or paymaster.
If the `ValidationResult` includes `sigFail`, the client SHOULD drop the `UserOperation`.
Expand All @@ -424,8 +373,8 @@ For the complete procedure see [ERC-7562](./eip-7562.md)

### Alternative Mempools

The simulation rules above are strict and prevent the ability of paymasters and signature aggregators to grief the system.
However, there might be use cases where specific paymasters (and signature aggregators) can be validated
The simulation rules above are strict and prevent the ability of paymasters to grief the system.
However, there might be use cases where specific paymasters can be validated
(through manual auditing) and verified that they cannot cause any problem, while still require relaxing of the opcode rules.
A bundler cannot simply "whitelist" a request from a specific paymaster: if that paymaster is not accepted by all
bundlers, then its support will be sporadic at best.
Expand All @@ -442,8 +391,6 @@ During bundling, the bundler should:
* Exclude UserOps that access any sender address of another UserOp in the same batch.
* Exclude UserOps that access any address created by another UserOp validation in the same batch (via a factory).
* For each paymaster used in the batch, keep track of the balance while adding UserOps. Ensure that it has sufficient deposit to pay for all the UserOps that use it.
* Sort UserOps by aggregator, to create the lists of UserOps-per-aggregator.
* For each aggregator, run the aggregator-specific code to create aggregated signature, and update the UserOps

After creating the batch, before including the transaction in a block, the bundler should:

Expand Down Expand Up @@ -506,7 +453,7 @@ To prevent such rogue UserOperations, the bundler is required to follow a set of
### Reputation Rationale.

UserOperation's storage access rules prevent them from interfering with each other.
But "global" entities - paymasters, factories and aggregators are accessed by multiple UserOperations, and thus might invalidate multiple previously valid UserOperations.
But "global" entities - paymasters and factories are accessed by multiple UserOperations, and thus might invalidate multiple previously valid UserOperations.

To prevent abuse, we throttle down (or completely ban for a period of time) an entity that causes invalidation of a large number of UserOperations in the mempool.
To prevent such entities from "Sybil-attack", we require them to stake with the system, and thus make such DoS attack very expensive.
Expand Down Expand Up @@ -573,25 +520,14 @@ The result `SHOULD` be set to the **userOpHash** if and only if the request pass

* If the UserOperation is valid, the client MUST return the calculated **userOpHash** for it
* in case of failure, MUST return an `error` result object, with `code` and `message`. The error code and message SHOULD be set as follows:
* **code: -32602** - invalid UserOperation struct/fields
* **code: -32500** - transaction rejected by entryPoint's simulateValidation, during wallet creation or validation
* The `message` field MUST be set to the FailedOp's "`AAxx`" error message from the EntryPoint
* **code: -32501** - transaction rejected by paymaster's validatePaymasterUserOp
* The `message` field SHOULD be set to the revert message from the paymaster
* The `data` field MUST contain a `paymaster` value
* **code: -32502** - transaction rejected because of opcode validation
* **code: -32503** - UserOperation out of time-range: either wallet or paymaster returned a time-range, and it has already expired (or will expire soon)
* The `data` field SHOULD contain the `validUntil` and `validAfter` values
* The `data` field SHOULD contain a `paymaster` value, if this error was triggered by the paymaster
* **code: -32504** - transaction rejected because paymaster (or signature aggregator) is throttled/banned
* The `data` field SHOULD contain a `paymaster` or `aggregator` value, depending on the failed entity
* **code: -32505** - transaction rejected because paymaster (or signature aggregator) stake or unstake-delay is too low
* The `data` field SHOULD contain a `paymaster` or `aggregator` value, depending on the failed entity
* The `data` field SHOULD contain a `paymaster` value, depending on the failed entity
* The `data` field SHOULD contain a `paymaster` value, depending on the failed entity
* The `data` field SHOULD contain a `minimumStake` and `minimumUnstakeDelay`
* **code: -32506** - transaction rejected because wallet specified unsupported signature aggregator
* The `data` field SHOULD contain an `aggregator` value
* **code: -32507** - transaction rejected because of wallet signature check failed (or paymaster signature, if the paymaster uses its data as signature)
* **code: -32508** - transaction rejected because paymaster balance can't cover all pending UserOperations.

##### Example:

Expand Down Expand Up @@ -793,7 +729,7 @@ This api must only be available in testing mode and is required by the compatibi

#### * debug_bundler_clearState

Clears the bundler mempool and reputation data of paymasters/accounts/factories/aggregators.
Clears the bundler mempool and reputation data of paymasters/accounts/factories.

```json=
# Request
Expand Down
Loading