Merge account abstraction work into master (#5274)
Co-authored-by: Ernesto García <ernestognw@gmail.com> Co-authored-by: Elias Rad <146735585+nnsW3@users.noreply.github.com> Co-authored-by: cairo <cairoeth@protonmail.com> Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com>
This commit is contained in:
215
contracts/interfaces/draft-IERC4337.sol
Normal file
215
contracts/interfaces/draft-IERC4337.sol
Normal file
@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
/**
|
||||
* @dev A https://github.com/ethereum/ercs/blob/master/ERCS/erc-4337.md#useroperation[user operation] is composed of the following elements:
|
||||
* - `sender` (`address`): The account making the operation
|
||||
* - `nonce` (`uint256`): Anti-replay parameter (see “Semi-abstracted Nonce Support” )
|
||||
* - `factory` (`address`): account factory, only for new accounts
|
||||
* - `factoryData` (`bytes`): data for account factory (only if account factory exists)
|
||||
* - `callData` (`bytes`): The data to pass to the sender during the main execution call
|
||||
* - `callGasLimit` (`uint256`): The amount of gas to allocate the main execution call
|
||||
* - `verificationGasLimit` (`uint256`): The amount of gas to allocate for the verification step
|
||||
* - `preVerificationGas` (`uint256`): Extra gas to pay the bunder
|
||||
* - `maxFeePerGas` (`uint256`): Maximum fee per gas (similar to EIP-1559 max_fee_per_gas)
|
||||
* - `maxPriorityFeePerGas` (`uint256`): Maximum priority fee per gas (similar to EIP-1559 max_priority_fee_per_gas)
|
||||
* - `paymaster` (`address`): Address of paymaster contract, (or empty, if account pays for itself)
|
||||
* - `paymasterVerificationGasLimit` (`uint256`): The amount of gas to allocate for the paymaster validation code
|
||||
* - `paymasterPostOpGasLimit` (`uint256`): The amount of gas to allocate for the paymaster post-operation code
|
||||
* - `paymasterData` (`bytes`): Data for paymaster (only if paymaster exists)
|
||||
* - `signature` (`bytes`): Data passed into the account to verify authorization
|
||||
*
|
||||
* When passed to on-chain contacts, the following packed version is used.
|
||||
* - `sender` (`address`)
|
||||
* - `nonce` (`uint256`)
|
||||
* - `initCode` (`bytes`): concatenation of factory address and factoryData (or empty)
|
||||
* - `callData` (`bytes`)
|
||||
* - `accountGasLimits` (`bytes32`): concatenation of verificationGas (16 bytes) and callGas (16 bytes)
|
||||
* - `preVerificationGas` (`uint256`)
|
||||
* - `gasFees` (`bytes32`): concatenation of maxPriorityFee (16 bytes) and maxFeePerGas (16 bytes)
|
||||
* - `paymasterAndData` (`bytes`): concatenation of paymaster fields (or empty)
|
||||
* - `signature` (`bytes`)
|
||||
*/
|
||||
struct PackedUserOperation {
|
||||
address sender;
|
||||
uint256 nonce;
|
||||
bytes initCode; // `abi.encodePacked(factory, factoryData)`
|
||||
bytes callData;
|
||||
bytes32 accountGasLimits; // `abi.encodePacked(verificationGasLimit, callGasLimit)` 16 bytes each
|
||||
uint256 preVerificationGas;
|
||||
bytes32 gasFees; // `abi.encodePacked(maxPriorityFee, maxFeePerGas)` 16 bytes each
|
||||
bytes paymasterAndData; // `abi.encodePacked(paymaster, paymasterVerificationGasLimit, paymasterPostOpGasLimit, paymasterData)`
|
||||
bytes signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Aggregates and validates multiple signatures for a batch of user operations.
|
||||
*/
|
||||
interface IAggregator {
|
||||
/**
|
||||
* @dev Validates the signature for a user operation.
|
||||
*/
|
||||
function validateUserOpSignature(
|
||||
PackedUserOperation calldata userOp
|
||||
) external view returns (bytes memory sigForUserOp);
|
||||
|
||||
/**
|
||||
* @dev Returns an aggregated signature for a batch of user operation's signatures.
|
||||
*/
|
||||
function aggregateSignatures(
|
||||
PackedUserOperation[] calldata userOps
|
||||
) external view returns (bytes memory aggregatesSignature);
|
||||
|
||||
/**
|
||||
* @dev Validates that the aggregated signature is valid for the user operations.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - The aggregated signature MUST match the given list of operations.
|
||||
*/
|
||||
function validateSignatures(PackedUserOperation[] calldata userOps, bytes calldata signature) external view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Handle nonce management for accounts.
|
||||
*/
|
||||
interface IEntryPointNonces {
|
||||
/**
|
||||
* @dev Returns the nonce for a `sender` account and a `key`.
|
||||
*
|
||||
* Nonces for a certain `key` are always increasing.
|
||||
*/
|
||||
function getNonce(address sender, uint192 key) external view returns (uint256 nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Handle stake management for accounts.
|
||||
*/
|
||||
interface IEntryPointStake {
|
||||
/**
|
||||
* @dev Returns the balance of the account.
|
||||
*/
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev Deposits `msg.value` to the account.
|
||||
*/
|
||||
function depositTo(address account) external payable;
|
||||
|
||||
/**
|
||||
* @dev Withdraws `withdrawAmount` from the account to `withdrawAddress`.
|
||||
*/
|
||||
function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external;
|
||||
|
||||
/**
|
||||
* @dev Adds stake to the account with an unstake delay of `unstakeDelaySec`.
|
||||
*/
|
||||
function addStake(uint32 unstakeDelaySec) external payable;
|
||||
|
||||
/**
|
||||
* @dev Unlocks the stake of the account.
|
||||
*/
|
||||
function unlockStake() external;
|
||||
|
||||
/**
|
||||
* @dev Withdraws the stake of the account to `withdrawAddress`.
|
||||
*/
|
||||
function withdrawStake(address payable withdrawAddress) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Entry point for user operations.
|
||||
*/
|
||||
interface IEntryPoint is IEntryPointNonces, IEntryPointStake {
|
||||
/**
|
||||
* @dev A user operation at `opIndex` failed with `reason`.
|
||||
*/
|
||||
error FailedOp(uint256 opIndex, string reason);
|
||||
|
||||
/**
|
||||
* @dev A user operation at `opIndex` failed with `reason` and `inner` returned data.
|
||||
*/
|
||||
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
|
||||
|
||||
/**
|
||||
* @dev Batch of aggregated user operations per aggregator.
|
||||
*/
|
||||
struct UserOpsPerAggregator {
|
||||
PackedUserOperation[] userOps;
|
||||
IAggregator aggregator;
|
||||
bytes signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Executes a batch of user operations.
|
||||
*/
|
||||
function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary) external;
|
||||
|
||||
/**
|
||||
* @dev Executes a batch of aggregated user operations per aggregator.
|
||||
*/
|
||||
function handleAggregatedOps(
|
||||
UserOpsPerAggregator[] calldata opsPerAggregator,
|
||||
address payable beneficiary
|
||||
) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Base interface for an account.
|
||||
*/
|
||||
interface IAccount {
|
||||
/**
|
||||
* @dev Validates a user operation.
|
||||
*/
|
||||
function validateUserOp(
|
||||
PackedUserOperation calldata userOp,
|
||||
bytes32 userOpHash,
|
||||
uint256 missingAccountFunds
|
||||
) external returns (uint256 validationData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Support for executing user operations by prepending the {executeUserOp} function selector
|
||||
* to the UserOperation's `callData`.
|
||||
*/
|
||||
interface IAccountExecute {
|
||||
/**
|
||||
* @dev Executes a user operation.
|
||||
*/
|
||||
function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Interface for a paymaster contract that agrees to pay for the gas costs of a user operation.
|
||||
*
|
||||
* NOTE: A paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction.
|
||||
*/
|
||||
interface IPaymaster {
|
||||
enum PostOpMode {
|
||||
opSucceeded,
|
||||
opReverted,
|
||||
postOpReverted
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates whether the paymaster is willing to pay for the user operation.
|
||||
*
|
||||
* NOTE: Bundlers will reject this method if it modifies the state, unless it's whitelisted.
|
||||
*/
|
||||
function validatePaymasterUserOp(
|
||||
PackedUserOperation calldata userOp,
|
||||
bytes32 userOpHash,
|
||||
uint256 maxCost
|
||||
) external returns (bytes memory context, uint256 validationData);
|
||||
|
||||
/**
|
||||
* @dev Verifies the sender is the entrypoint.
|
||||
*/
|
||||
function postOp(
|
||||
PostOpMode mode,
|
||||
bytes calldata context,
|
||||
uint256 actualGasCost,
|
||||
uint256 actualUserOpFeePerGas
|
||||
) external;
|
||||
}
|
||||
196
contracts/interfaces/draft-IERC7579.sol
Normal file
196
contracts/interfaces/draft-IERC7579.sol
Normal file
@ -0,0 +1,196 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {PackedUserOperation} from "./draft-IERC4337.sol";
|
||||
|
||||
uint256 constant VALIDATION_SUCCESS = 0;
|
||||
uint256 constant VALIDATION_FAILED = 1;
|
||||
uint256 constant MODULE_TYPE_VALIDATOR = 1;
|
||||
uint256 constant MODULE_TYPE_EXECUTOR = 2;
|
||||
uint256 constant MODULE_TYPE_FALLBACK = 3;
|
||||
uint256 constant MODULE_TYPE_HOOK = 4;
|
||||
|
||||
interface IERC7579Module {
|
||||
/**
|
||||
* @dev This function is called by the smart account during installation of the module
|
||||
* @param data arbitrary data that may be required on the module during `onInstall` initialization
|
||||
*
|
||||
* MUST revert on error (e.g. if module is already enabled)
|
||||
*/
|
||||
function onInstall(bytes calldata data) external;
|
||||
|
||||
/**
|
||||
* @dev This function is called by the smart account during uninstallation of the module
|
||||
* @param data arbitrary data that may be required on the module during `onUninstall` de-initialization
|
||||
*
|
||||
* MUST revert on error
|
||||
*/
|
||||
function onUninstall(bytes calldata data) external;
|
||||
|
||||
/**
|
||||
* @dev Returns boolean value if module is a certain type
|
||||
* @param moduleTypeId the module type ID according the ERC-7579 spec
|
||||
*
|
||||
* MUST return true if the module is of the given type and false otherwise
|
||||
*/
|
||||
function isModuleType(uint256 moduleTypeId) external view returns (bool);
|
||||
}
|
||||
|
||||
interface IERC7579Validator is IERC7579Module {
|
||||
/**
|
||||
* @dev Validates a UserOperation
|
||||
* @param userOp the ERC-4337 PackedUserOperation
|
||||
* @param userOpHash the hash of the ERC-4337 PackedUserOperation
|
||||
*
|
||||
* MUST validate that the signature is a valid signature of the userOpHash
|
||||
* SHOULD return ERC-4337's SIG_VALIDATION_FAILED (and not revert) on signature mismatch
|
||||
*/
|
||||
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev Validates a signature using ERC-1271
|
||||
* @param sender the address that sent the ERC-1271 request to the smart account
|
||||
* @param hash the hash of the ERC-1271 request
|
||||
* @param signature the signature of the ERC-1271 request
|
||||
*
|
||||
* MUST return the ERC-1271 `MAGIC_VALUE` if the signature is valid
|
||||
* MUST NOT modify state
|
||||
*/
|
||||
function isValidSignatureWithSender(
|
||||
address sender,
|
||||
bytes32 hash,
|
||||
bytes calldata signature
|
||||
) external view returns (bytes4);
|
||||
}
|
||||
|
||||
interface IERC7579Hook is IERC7579Module {
|
||||
/**
|
||||
* @dev Called by the smart account before execution
|
||||
* @param msgSender the address that called the smart account
|
||||
* @param value the value that was sent to the smart account
|
||||
* @param msgData the data that was sent to the smart account
|
||||
*
|
||||
* MAY return arbitrary data in the `hookData` return value
|
||||
*/
|
||||
function preCheck(
|
||||
address msgSender,
|
||||
uint256 value,
|
||||
bytes calldata msgData
|
||||
) external returns (bytes memory hookData);
|
||||
|
||||
/**
|
||||
* @dev Called by the smart account after execution
|
||||
* @param hookData the data that was returned by the `preCheck` function
|
||||
*
|
||||
* MAY validate the `hookData` to validate transaction context of the `preCheck` function
|
||||
*/
|
||||
function postCheck(bytes calldata hookData) external;
|
||||
}
|
||||
|
||||
struct Execution {
|
||||
address target;
|
||||
uint256 value;
|
||||
bytes callData;
|
||||
}
|
||||
|
||||
interface IERC7579Execution {
|
||||
/**
|
||||
* @dev Executes a transaction on behalf of the account.
|
||||
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
|
||||
* @param executionCalldata The encoded execution call data
|
||||
*
|
||||
* MUST ensure adequate authorization control: e.g. onlyEntryPointOrSelf if used with ERC-4337
|
||||
* If a mode is requested that is not supported by the Account, it MUST revert
|
||||
*/
|
||||
function execute(bytes32 mode, bytes calldata executionCalldata) external;
|
||||
|
||||
/**
|
||||
* @dev Executes a transaction on behalf of the account.
|
||||
* This function is intended to be called by Executor Modules
|
||||
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
|
||||
* @param executionCalldata The encoded execution call data
|
||||
*
|
||||
* MUST ensure adequate authorization control: i.e. onlyExecutorModule
|
||||
* If a mode is requested that is not supported by the Account, it MUST revert
|
||||
*/
|
||||
function executeFromExecutor(
|
||||
bytes32 mode,
|
||||
bytes calldata executionCalldata
|
||||
) external returns (bytes[] memory returnData);
|
||||
}
|
||||
|
||||
interface IERC7579AccountConfig {
|
||||
/**
|
||||
* @dev Returns the account id of the smart account
|
||||
* @return accountImplementationId the account id of the smart account
|
||||
*
|
||||
* MUST return a non-empty string
|
||||
* The accountId SHOULD be structured like so:
|
||||
* "vendorname.accountname.semver"
|
||||
* The id SHOULD be unique across all smart accounts
|
||||
*/
|
||||
function accountId() external view returns (string memory accountImplementationId);
|
||||
|
||||
/**
|
||||
* @dev Function to check if the account supports a certain execution mode (see above)
|
||||
* @param encodedMode the encoded mode
|
||||
*
|
||||
* MUST return true if the account supports the mode and false otherwise
|
||||
*/
|
||||
function supportsExecutionMode(bytes32 encodedMode) external view returns (bool);
|
||||
|
||||
/**
|
||||
* @dev Function to check if the account supports a certain module typeId
|
||||
* @param moduleTypeId the module type ID according to the ERC-7579 spec
|
||||
*
|
||||
* MUST return true if the account supports the module type and false otherwise
|
||||
*/
|
||||
function supportsModule(uint256 moduleTypeId) external view returns (bool);
|
||||
}
|
||||
|
||||
interface IERC7579ModuleConfig {
|
||||
event ModuleInstalled(uint256 moduleTypeId, address module);
|
||||
event ModuleUninstalled(uint256 moduleTypeId, address module);
|
||||
|
||||
/**
|
||||
* @dev Installs a Module of a certain type on the smart account
|
||||
* @param moduleTypeId the module type ID according to the ERC-7579 spec
|
||||
* @param module the module address
|
||||
* @param initData arbitrary data that may be required on the module during `onInstall`
|
||||
* initialization.
|
||||
*
|
||||
* MUST implement authorization control
|
||||
* MUST call `onInstall` on the module with the `initData` parameter if provided
|
||||
* MUST emit ModuleInstalled event
|
||||
* MUST revert if the module is already installed or the initialization on the module failed
|
||||
*/
|
||||
function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external;
|
||||
|
||||
/**
|
||||
* @dev Uninstalls a Module of a certain type on the smart account
|
||||
* @param moduleTypeId the module type ID according the ERC-7579 spec
|
||||
* @param module the module address
|
||||
* @param deInitData arbitrary data that may be required on the module during `onInstall`
|
||||
* initialization.
|
||||
*
|
||||
* MUST implement authorization control
|
||||
* MUST call `onUninstall` on the module with the `deInitData` parameter if provided
|
||||
* MUST emit ModuleUninstalled event
|
||||
* MUST revert if the module is not installed or the deInitialization on the module failed
|
||||
*/
|
||||
function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external;
|
||||
|
||||
/**
|
||||
* @dev Returns whether a module is installed on the smart account
|
||||
* @param moduleTypeId the module type ID according the ERC-7579 spec
|
||||
* @param module the module address
|
||||
* @param additionalContext arbitrary data that may be required to determine if the module is installed
|
||||
*
|
||||
* MUST return true if the module is installed and false otherwise
|
||||
*/
|
||||
function isModuleInstalled(
|
||||
uint256 moduleTypeId,
|
||||
address module,
|
||||
bytes calldata additionalContext
|
||||
) external view returns (bool);
|
||||
}
|
||||
Reference in New Issue
Block a user