AccountERC7579Upgradeable
Inherits: Initializable, Account, IERC1271, IERC7579Execution, IERC7579AccountConfig, IERC7579ModuleConfig
*Extension of {Account} that implements support for ERC-7579 modules. To comply with the ERC-1271 support requirement, this contract defers signature validation to installed validator modules by calling {IERC7579Validator-isValidSignatureWithSender}. This contract does not implement validation logic for user operations since this functionality is often delegated to self-contained validation modules. Developers must install a validator module upon initialization (or any other mechanism to enable execution from the account):
contract MyAccountERC7579 is AccountERC7579, Initializable {
function initializeAccount(address validator, bytes calldata validatorData) public initializer {
_installModule(MODULE_TYPE_VALIDATOR, validator, validatorData);
}
}
[NOTE]
Hook support is not included. See {AccountERC7579Hooked} for a version that hooks to execution.
Validator selection, when verifying either ERC-1271 signature or ERC-4337 UserOperation is implemented in
internal virtual functions {_extractUserOpValidator} and {_extractSignatureValidator}. Both are implemented
following common practices. However, this part is not standardized in ERC-7579 (or in any follow-up ERC). Some
accounts may want to override these internal functions.
When combined with {ERC7739}, resolution ordering of {isValidSignature} may have an impact ({ERC7739} does not
call super). Manual resolution might be necessary.
Static calls (using callType 0xfe
) are currently NOT supported.
WARNING: Removing all validator modules will render the account inoperable, as no user operations can be validated thereafter.*
State Variables
AccountERC7579StorageLocation
bytes32 private constant AccountERC7579StorageLocation =
0x0a47d913d72b2639f4ca1c145cc07ddf7170b73b257c8e0d4fced7cc8e3e3900;
Functions
_getAccountERC7579Storage
function _getAccountERC7579Storage() private pure returns (AccountERC7579Storage storage $);
onlyModule
Modifier that checks if the caller is an installed module of the given type.
modifier onlyModule(uint256 moduleTypeId, bytes calldata additionalContext);
__AccountERC7579_init
function __AccountERC7579_init() internal onlyInitializing;
__AccountERC7579_init_unchained
function __AccountERC7579_init_unchained() internal onlyInitializing;
fallback
See _fallback.
fallback(bytes calldata) external payable virtual returns (bytes memory);
accountId
Returns the account id of the smart account
function accountId() public view virtual returns (string memory);
Returns
Name | Type | Description |
---|---|---|
<none> | string | 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 |
supportsExecutionMode
Supported call types:
Single (0x00
): A single transaction execution.
Batch (0x01
): A batch of transactions execution.
Delegate (0xff
): A delegate call execution.
Supported exec types:
Default (0x00
): Default execution type (revert on failure).
Try (0x01
): Try execution type (emits ERC7579TryExecuteFail on failure).
function supportsExecutionMode(bytes32 encodedMode) public view virtual returns (bool);
Parameters
Name | Type | Description |
---|---|---|
encodedMode | bytes32 | the encoded mode MUST return true if the account supports the mode and false otherwise |
supportsModule
Supported module types: Validator: A module used during the validation phase to determine if a transaction is valid and should be executed on the account. Executor: A module that can execute transactions on behalf of the smart account via a callback. Fallback Handler: A module that can extend the fallback functionality of a smart account.
function supportsModule(uint256 moduleTypeId) public view virtual returns (bool);
Parameters
Name | Type | Description |
---|---|---|
moduleTypeId | uint256 | the module type ID according to the ERC-7579 spec MUST return true if the account supports the module type and false otherwise |
installModule
Installs a Module of a certain type on the smart account
function installModule(uint256 moduleTypeId, address module, bytes calldata initData)
public
virtual
onlyEntryPointOrSelf;
Parameters
Name | Type | Description |
---|---|---|
moduleTypeId | uint256 | the module type ID according to the ERC-7579 spec |
module | address | the module address |
initData | bytes | arbitrary data that may be passed to 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 |
uninstallModule
Uninstalls a Module of a certain type on the smart account
function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData)
public
virtual
onlyEntryPointOrSelf;
Parameters
Name | Type | Description |
---|---|---|
moduleTypeId | uint256 | the module type ID according the ERC-7579 spec |
module | address | the module address |
deInitData | bytes | arbitrary data that may be passed to the module during onUninstall deinitialization. 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 |
isModuleInstalled
Returns whether a module is installed on the smart account
function isModuleInstalled(uint256 moduleTypeId, address module, bytes calldata additionalContext)
public
view
virtual
returns (bool);
Parameters
Name | Type | Description |
---|---|---|
moduleTypeId | uint256 | the module type ID according the ERC-7579 spec |
module | address | the module address |
additionalContext | bytes | arbitrary data that may be passed to determine if the module is installed MUST return true if the module is installed and false otherwise |
execute
Executes a transaction on behalf of the account.
function execute(bytes32 mode, bytes calldata executionCalldata) public payable virtual onlyEntryPointOrSelf;
Parameters
Name | Type | Description |
---|---|---|
mode | bytes32 | The encoded execution mode of the transaction. See ModeLib.sol for details |
executionCalldata | bytes | 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 |
executeFromExecutor
Executes a transaction on behalf of the account. This function is intended to be called by Executor Modules
function executeFromExecutor(bytes32 mode, bytes calldata executionCalldata)
public
payable
virtual
onlyModule(MODULE_TYPE_EXECUTOR, Calldata.emptyBytes())
returns (bytes[] memory returnData);
Parameters
Name | Type | Description |
---|---|---|
mode | bytes32 | The encoded execution mode of the transaction. See ModeLib.sol for details |
executionCalldata | bytes | The encoded execution call data |
Returns
Name | Type | Description |
---|---|---|
returnData | bytes[] | An array with the returned data of each executed subcall MUST ensure adequate authorization control: i.e. onlyExecutorModule If a mode is requested that is not supported by the Account, it MUST revert |
isValidSignature
Implement ERC-1271 through IERC7579Validator modules. If module based validation fails, fallback to "native" validation by the abstract signer. NOTE: when combined with {ERC7739}, resolution ordering may have an impact ({ERC7739} does not call super). Manual resolution might be necessary.
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4);
_validateUserOp
Validates a user operation with {_signableUserOpHash} and returns the validation data if the module specified by the first 20 bytes of the nonce key is installed. Falls back to {Account-_validateUserOp} otherwise. See {_extractUserOpValidator} for the module extraction logic.
function _validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
internal
virtual
override
returns (uint256);
_execute
ERC-7579 execution logic. See supportsExecutionMode for supported modes. Reverts if the call type is not supported.
function _execute(Mode mode, bytes calldata executionCalldata) internal virtual returns (bytes[] memory returnData);
_installModule
Installs a module of the given type with the given initialization data.
For the fallback module type, the initData
is expected to be the (packed) concatenation of a 4-byte
selector and the rest of the data to be sent to the handler when calling IERC7579Module-onInstall.
Requirements:
Module type must be supported. See {supportsModule}. Reverts with {ERC7579Utils-ERC7579UnsupportedModuleType}.
Module must be of the given type. Reverts with {ERC7579Utils-ERC7579MismatchedModuleTypeId}.
Module must not be already installed. Reverts with {ERC7579Utils-ERC7579AlreadyInstalledModule}.
Emits a {IERC7579ModuleConfig-ModuleInstalled} event.
function _installModule(uint256 moduleTypeId, address module, bytes memory initData) internal virtual;
_uninstallModule
Uninstalls a module of the given type with the given de-initialization data.
For the fallback module type, the deInitData
is expected to be the (packed) concatenation of a 4-byte
selector and the rest of the data to be sent to the handler when calling IERC7579Module-onUninstall.
Requirements:
Module must be already installed. Reverts with {ERC7579Utils-ERC7579UninstalledModule} otherwise.
function _uninstallModule(uint256 moduleTypeId, address module, bytes memory deInitData) internal virtual;
_fallback
Fallback function that delegates the call to the installed handler for the given selector.
Reverts with ERC7579MissingFallbackHandler if the handler is not installed.
Calls the handler with the original msg.sender
appended at the end of the calldata following
the ERC-2771 format.
function _fallback() internal virtual returns (bytes memory);
_fallbackHandler
Returns the fallback handler for the given selector. Returns address(0)
if not installed.
function _fallbackHandler(bytes4 selector) internal view virtual returns (address);
_checkModule
Checks if the module is installed. Reverts if the module is not installed.
function _checkModule(uint256 moduleTypeId, address module, bytes calldata additionalContext) internal view virtual;
_extractUserOpValidator
*Extracts the nonce validator from the user operation. To construct a nonce key, set nonce as follows:
<module address (20 bytes)> | <key (4 bytes)> | <nonce (8 bytes)>
NOTE: The default behavior of this function replicates the behavior of https://github.com/rhinestonewtf/safe7579/blob/bb29e8b1a66658790c4169e72608e27d220f79be/src/Safe7579.sol#L266[Safe adapter], https://github.com/etherspot/etherspot-prime-contracts/blob/cfcdb48c4172cea0d66038324c0bae3288aa8caa/src/modular-etherspot-wallet/wallet/ModularEtherspotWallet.sol#L227[Etherspot's Prime Account], and https://github.com/erc7579/erc7579-implementation/blob/16138d1afd4e9711f6c1425133538837bd7787b5/src/MSAAdvanced.sol#L247[ERC7579 reference implementation]. This is not standardized in ERC-7579 (or in any follow-up ERC). Some accounts may want to override these internal functions. For example, https://github.com/bcnmy/nexus/blob/54f4e19baaff96081a8843672977caf712ef19f4/contracts/lib/NonceLib.sol#L17[Biconomy's Nexus] uses a similar yet incompatible approach (the validator address is also part of the nonce, but not at the same location)*
function _extractUserOpValidator(PackedUserOperation calldata userOp) internal pure virtual returns (address);
_extractSignatureValidator
*Extracts the signature validator from the signature. To construct a signature, set the first 20 bytes as the module address and the remaining bytes as the signature data:
<module address (20 bytes)> | <signature data>
NOTE: The default behavior of this function replicates the behavior of https://github.com/rhinestonewtf/safe7579/blob/bb29e8b1a66658790c4169e72608e27d220f79be/src/Safe7579.sol#L350[Safe adapter], https://github.com/bcnmy/nexus/blob/54f4e19baaff96081a8843672977caf712ef19f4/contracts/Nexus.sol#L239[Biconomy's Nexus], https://github.com/etherspot/etherspot-prime-contracts/blob/cfcdb48c4172cea0d66038324c0bae3288aa8caa/src/modular-etherspot-wallet/wallet/ModularEtherspotWallet.sol#L252[Etherspot's Prime Account], and https://github.com/erc7579/erc7579-implementation/blob/16138d1afd4e9711f6c1425133538837bd7787b5/src/MSAAdvanced.sol#L296[ERC7579 reference implementation]. This is not standardized in ERC-7579 (or in any follow-up ERC). Some accounts may want to override these internal functions.*
function _extractSignatureValidator(bytes calldata signature)
internal
pure
virtual
returns (address module, bytes calldata innerSignature);
_decodeFallbackData
Extract the function selector from initData/deInitData for MODULE_TYPE_FALLBACK
NOTE: If we had calldata here, we could use calldata slice which are cheaper to manipulate and don't require
actual copy. However, this would require _installModule
to get a calldata bytes object instead of a memory
bytes object. This would prevent calling _installModule
from a contract constructor and would force the use
of external initializers. That may change in the future, as most accounts will probably be deployed as
clones/proxy/ERC-7702 delegates and therefore rely on initializers anyway.
function _decodeFallbackData(bytes memory data)
internal
pure
virtual
returns (bytes4 selector, bytes memory remaining);
_rawSignatureValidation
By default, only use the modules for validation of userOp and signature. Disable raw signatures.
function _rawSignatureValidation(bytes32, bytes calldata) internal view virtual override returns (bool);
Errors
ERC7579MissingFallbackHandler
The account's {fallback} was called with a selector that doesn't have an installed handler.
error ERC7579MissingFallbackHandler(bytes4 selector);
Structs
AccountERC7579Storage
Note: storage-location: erc7201:openzeppelin.storage.AccountERC7579
struct AccountERC7579Storage {
EnumerableSet.AddressSet _validators;
EnumerableSet.AddressSet _executors;
mapping(bytes4 selector => address) _fallbacks;
}