Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

NameTypeDescription
<none>stringaccountImplementationId 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

NameTypeDescription
encodedModebytes32the 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

NameTypeDescription
moduleTypeIduint256the 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

NameTypeDescription
moduleTypeIduint256the module type ID according to the ERC-7579 spec
moduleaddressthe module address
initDatabytesarbitrary 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

NameTypeDescription
moduleTypeIduint256the module type ID according the ERC-7579 spec
moduleaddressthe module address
deInitDatabytesarbitrary 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

NameTypeDescription
moduleTypeIduint256the module type ID according the ERC-7579 spec
moduleaddressthe module address
additionalContextbytesarbitrary 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

NameTypeDescription
modebytes32The encoded execution mode of the transaction. See ModeLib.sol for details
executionCalldatabytesThe 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

NameTypeDescription
modebytes32The encoded execution mode of the transaction. See ModeLib.sol for details
executionCalldatabytesThe encoded execution call data

Returns

NameTypeDescription
returnDatabytes[]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;
}