diff --git a/ERCS/erc-4337.md b/ERCS/erc-4337.md index 72c0c4ddf2..3898524dc0 100644 --- a/ERCS/erc-4337.md +++ b/ERCS/erc-4337.md @@ -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 @@ -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 @@ -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 @@ -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-XXXX](link). * `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 @@ -222,11 +206,9 @@ 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` +There is one main entry point method: `handleOps`. -* `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. +* `handleOps` handles userOps of accounts. 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. @@ -322,33 +304,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 @@ -357,7 +312,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 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: @@ -390,19 +345,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-XXXX](link). + 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") @@ -413,8 +364,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`. @@ -424,8 +374,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. @@ -442,8 +392,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: @@ -506,7 +454,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. @@ -583,13 +531,11 @@ The result `SHOULD` be set to the **userOpHash** if and only if the request pass * **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 + * **code: -32504** - transaction rejected because paymaster is throttled/banned + * The `data` field SHOULD contain a `paymaster` value, depending on the failed entity + * **code: -32505** - transaction rejected because paymaster stake or unstake-delay is too low + * 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. @@ -793,7 +739,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