From 5d6f523907a76ea36ef99636d4f7037351a70a36 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Mon, 13 Oct 2025 11:41:24 -0600 Subject: [PATCH 1/5] Draft: ERC-7579 Atomic Module installation --- ERCS/erc-xxxx.md | 242 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 ERCS/erc-xxxx.md diff --git a/ERCS/erc-xxxx.md b/ERCS/erc-xxxx.md new file mode 100644 index 00000000000..01c82b3aae0 --- /dev/null +++ b/ERCS/erc-xxxx.md @@ -0,0 +1,242 @@ +--- +eip: XXXX +title: Atomic Module Installation for ERC-7579 Smart Accounts +description: Standard for installing and using ERC-7579 modules within a single user operation +author: Ernesto Garcia (@ernestognw), Taek Lee (@leekt) +discussions-to: TBD +status: Draft +type: Standards Track +category: ERC +created: 2025-10-02 +requires: 712, 1271, 4337, 7579 +--- + +## Abstract + +This ERC defines a standard mechanism for [ERC-7579] compliant smart accounts to install and immediately use modules within a single user operation. The standard specifies a magic nonce prefix, signature encoding format, and [EIP-712] domain structure to enable atomic module installation while maintaining security through account owner authorization. + +[ERC-7579]: ./eip-7579.md +[EIP-712]: ./eip-712.md +[ERC-1271]: ./eip-1271.md +[ERC-4337]: ./eip-4337.md +[ERC-7579]: ./eip-7579.md + +## Motivation + +Current [ERC-7579] implementations require separate transactions for module installation and usage, creating friction in user experience. Common use cases like session key modules and spending limit modules would benefit from atomic installation and immediate usage. + +This standard addresses several key issues: + +- **User Experience**: Reduces transaction count from 2+ to 1 for module installation and usage +- **Gas Efficiency**: Eliminates separate installation transactions +- **Interoperability**: Provides a standard pattern that works across different [ERC-7579] account implementations +- **Security**: Maintains strong authorization through [EIP-712] signatures from account owners + +Real-world usage in production systems like Kernel has validated the need and feasibility of this pattern, particularly for session key management and dynamic module installation. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Definitions + +- **Atomic Module Installation**: The process of installing and using an [ERC-7579] module within a single user operation +- **Enable Mode**: The operational mode triggered by the atomic module prefix in the nonce +- **Installation Signature**: An [EIP-712] signature authorizing module installation +- **User Operation Signature**: The signature for validating the user operation itself + +### Magic Nonce Prefix + +Smart accounts implementing this standard MUST detect atomic module installation by checking for the magic prefix `0x01` in the most significant byte of the user operation nonce. + +```solidity +bytes1 constant ATOMIC_MODULE_PREFIX = 0x01; + +bytes1 result; +uint256 nonce = userOp.nonce; +assembly ("memory-safe") { + result := and(nonce, shl(248, not(0))) +} +if (result == ATOMIC_MODULE_PREFIX) { + // Enter atomic module installation mode +} +``` + +### Signature Encoding Format + +When the atomic module prefix is detected, the user operation signature MUST be encoded as: + +```solidity +abi.encode( + uint256 moduleTypeId, + address module, + bytes initData, + bytes installationSignature, + bytes userOpSignature +) +``` + +Where: +- `moduleTypeId`: The [ERC-7579] module type identifier +- `module`: The address of the module to install +- `initData`: Initialization data to pass to the module's `onInstall` function +- `installationSignature`: [EIP-712] signature authorizing the module installation +- `userOpSignature`: Signature for validating the user operation + +### EIP-712 Message Structure + +The message structure MUST be: + +```solidity +bytes32 constant INSTALL_MODULE_TYPEHASH = keccak256( + "InstallAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" +); +``` + +### Validation Flow + +Smart accounts implementing this standard on top of [ERC-4337]'s `validateUserOp` function: + +1. MUST detect the atomic module prefix (`0x01`) in user operation nonces +2. MUST decode the signature according to the specified format +3. MUST validate the EIP-712 `installationSignature` using `isValidSignature` +4. SHOULD install the module using the standard ERC-7579 `installModule` flow using the `moduleTypeId`, `module`, and `initData` +5. MUST continue with normal user operation validation using the extracted `userOpSignature` +6. MUST return `SIG_VALIDATION_FAILED` (`1`) if signature decoding fails or installation signature is invalid + +## Rationale + +### Magic Nonce Prefix Choice + +The prefix `0x01` was chosen because: +- It's unlikely to conflict with existing nonce usage patterns +- It's easily detectable with bitwise operations +- It follows the pattern established by successful implementations like Kernel + +### Signature Structure Design + +The signature encoding separates concerns cleanly: +- Module installation data is explicitly structured +- Installation authorization is separate from user operation validation +- The format is extensible for future enhancements + +### EIP-712 Integration + +Using EIP-712 for installation authorization provides: +- Strong cryptographic guarantees +- Human-readable signature requests in wallets +- Standard domain separation +- Replay protection through nonce inclusion + +### Compatibility with ERC-7579 + +This standard extends ERC-7579 without breaking existing functionality: +- Normal user operations continue to work unchanged +- Standard module installation flows remain available +- All ERC-7579 security guarantees are preserved + +## Backwards Compatibility + +This standard is fully backwards compatible with existing ERC-7579 implementations: + +- Accounts not implementing this standard ignore the magic nonce prefix +- Standard module installation methods remain unchanged +- Existing modules work without modification +- User operations without the magic prefix function normally + +## Reference Implementation + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import {AccountERC7579} from "@openzeppelin/contracts/account/extensions/draft-AccountERC7579.sol"; +import {PackedUserOperation} from "@openzeppelin/contracts/interfaces/draft-IERC4337.sol"; +import {ERC4337Utils} from "@openzeppelin/contracts/account/utils/draft-ERC4337Utils.sol"; +import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; + +abstract contract AtomicModuleAccount is AccountERC7579, ... { + bytes1 private constant ATOMIC_MODULE_PREFIX = 0x01; + bytes32 private constant INSTALL_MODULE_TYPEHASH = + keccak256( + "InstallAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" + ); + + function _validateUserOp( + PackedUserOperation calldata userOp, + bytes32 userOpHash + ) internal virtual override returns (uint256) { + bytes1 result; + uint256 nonce = userOp.nonce; + assembly ("memory-safe") { + result := and(nonce, shl(248, not(0))) + } + if (result == ATOMIC_MODULE_PREFIX) { + return _validateAtomicModule(userOp, userOpHash); + } + return super._validateUserOp(userOp, userOpHash); + } + + function _validateAtomicModule( + PackedUserOperation calldata userOp, + bytes32 userOpHash + ) internal returns (uint256) { + ( + bool success, + uint256 moduleTypeId, + address module, + bytes calldata initData, + bytes calldata installationSignature, + bytes calldata userOpSignature + ) = _tryDecodeAtomicModule(userOp.signature); + + if (!success) { + return ERC4337Utils.SIG_VALIDATION_FAILED; + } + + bytes32 structHash = _installModuleStructHash( + moduleTypeId, + module, + initData, + userOp.nonce + ); + + if ( + isValidSignature(_hashTypedDataV4(structHash), installationSignature) == + IERC1271.isValidSignature.selector + ) { + _installModule(moduleTypeId, module, initData); + + // Create modified userOp with extracted signature + PackedUserOperation memory modifiedUserOp = PackedUserOperation({ + sender: userOp.sender, + nonce: userOp.nonce, + initCode: userOp.initCode, + callData: userOp.callData, + accountGasLimits: userOp.accountGasLimits, + preVerificationGas: userOp.preVerificationGas, + gasFees: userOp.gasFees, + paymasterAndData: userOp.paymasterAndData, + signature: userOpSignature + }); + + return super._validateUserOp(modifiedUserOp, userOpHash); + } + + return ERC4337Utils.SIG_VALIDATION_FAILED; + } + + // Additional implementation methods... +} +``` + +## Security Considerations + +- **Authorization**: Only account owners can authorize module installation through EIP-712 signatures +- **Signature Separation**: Installation authorization and user operation validation use separate signatures +- **Replay Protection**: Nonce inclusion in EIP-712 message prevents replay attacks +- **Module Validation**: Standard ERC-7579 module validation applies (type checking, duplicate prevention) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 0aa0165bcee4651f22a101c60ff2e5af21775d0c Mon Sep 17 00:00:00 2001 From: ernestognw Date: Mon, 13 Oct 2025 19:08:17 -0600 Subject: [PATCH 2/5] Draft: ERC-7579 Atomic Module installation --- ERCS/erc-xxxx.md | 62 +++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/ERCS/erc-xxxx.md b/ERCS/erc-xxxx.md index 01c82b3aae0..aa89fd61db3 100644 --- a/ERCS/erc-xxxx.md +++ b/ERCS/erc-xxxx.md @@ -1,7 +1,7 @@ --- eip: XXXX -title: Atomic Module Installation for ERC-7579 Smart Accounts -description: Standard for installing and using ERC-7579 modules within a single user operation +title: Enable Mode for module installation in ERC-7579 Smart Accounts +description: Install and use ERC-7579 modules within a single user operation author: Ernesto Garcia (@ernestognw), Taek Lee (@leekt) discussions-to: TBD status: Draft @@ -13,7 +13,7 @@ requires: 712, 1271, 4337, 7579 ## Abstract -This ERC defines a standard mechanism for [ERC-7579] compliant smart accounts to install and immediately use modules within a single user operation. The standard specifies a magic nonce prefix, signature encoding format, and [EIP-712] domain structure to enable atomic module installation while maintaining security through account owner authorization. +This ERC defines a standard mechanism for [ERC-7579] compliant smart accounts to install and immediately use modules within a single user operation. The standard specifies a magic nonce prefix, signature encoding format, and [EIP-712] domain structure to authorize _enable mode_ to install the module during the validation phase of the user operation while maintaining security through account owner authorization. [ERC-7579]: ./eip-7579.md [EIP-712]: ./eip-712.md @@ -23,13 +23,12 @@ This ERC defines a standard mechanism for [ERC-7579] compliant smart accounts to ## Motivation -Current [ERC-7579] implementations require separate transactions for module installation and usage, creating friction in user experience. Common use cases like session key modules and spending limit modules would benefit from atomic installation and immediate usage. +Current [ERC-7579] implementations require separate transactions for module installation and usage, creating friction in user experience. Common use cases like session key modules and spending limit modules would benefit from _enable mode_ and immediate usage. This standard addresses several key issues: - **User Experience**: Reduces transaction count from 2+ to 1 for module installation and usage -- **Gas Efficiency**: Eliminates separate installation transactions -- **Interoperability**: Provides a standard pattern that works across different [ERC-7579] account implementations +- **Consistency across implementations**: Provides a standard pattern that works across different [ERC-7579] account implementations - **Security**: Maintains strong authorization through [EIP-712] signatures from account owners Real-world usage in production systems like Kernel has validated the need and feasibility of this pattern, particularly for session key management and dynamic module installation. @@ -40,31 +39,30 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ### Definitions -- **Atomic Module Installation**: The process of installing and using an [ERC-7579] module within a single user operation -- **Enable Mode**: The operational mode triggered by the atomic module prefix in the nonce -- **Installation Signature**: An [EIP-712] signature authorizing module installation +- **Enable Mode**: The logic branch triggered during the validation phase allowing to install and use an [ERC-7579] module within a single user operation +- **Installation Signature**: An [EIP-712] signature authorizing module installatio - **User Operation Signature**: The signature for validating the user operation itself ### Magic Nonce Prefix -Smart accounts implementing this standard MUST detect atomic module installation by checking for the magic prefix `0x01` in the most significant byte of the user operation nonce. +Smart accounts implementing this standard MUST detect enable mode by checking for the magic prefix `0x01` in the most significant byte of the user operation nonce. ```solidity -bytes1 constant ATOMIC_MODULE_PREFIX = 0x01; +bytes1 constant ENABLE_MODE_PREFIX = 0x01; bytes1 result; uint256 nonce = userOp.nonce; assembly ("memory-safe") { result := and(nonce, shl(248, not(0))) } -if (result == ATOMIC_MODULE_PREFIX) { - // Enter atomic module installation mode +if (result == ENABLE_MODE_PREFIX) { + // Enter enable mode } ``` ### Signature Encoding Format -When the atomic module prefix is detected, the user operation signature MUST be encoded as: +When the enable mode prefix is detected, the user operation signature MUST be encoded as: ```solidity abi.encode( @@ -77,6 +75,7 @@ abi.encode( ``` Where: + - `moduleTypeId`: The [ERC-7579] module type identifier - `module`: The address of the module to install - `initData`: Initialization data to pass to the module's `onInstall` function @@ -88,8 +87,8 @@ Where: The message structure MUST be: ```solidity -bytes32 constant INSTALL_MODULE_TYPEHASH = keccak256( - "InstallAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" +bytes32 constant ENABLE_MODULE_TYPEHASH = keccak256( + "EnableAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" ); ``` @@ -97,18 +96,19 @@ bytes32 constant INSTALL_MODULE_TYPEHASH = keccak256( Smart accounts implementing this standard on top of [ERC-4337]'s `validateUserOp` function: -1. MUST detect the atomic module prefix (`0x01`) in user operation nonces +1. MUST detect the enable mode prefix (`0x01`) in user operation nonces 2. MUST decode the signature according to the specified format 3. MUST validate the EIP-712 `installationSignature` using `isValidSignature` 4. SHOULD install the module using the standard ERC-7579 `installModule` flow using the `moduleTypeId`, `module`, and `initData` 5. MUST continue with normal user operation validation using the extracted `userOpSignature` -6. MUST return `SIG_VALIDATION_FAILED` (`1`) if signature decoding fails or installation signature is invalid +6. MUST return `SIG_VALIDATION_FAILED` (`1`) if signature decoding fails or enabling signature is invalid ## Rationale ### Magic Nonce Prefix Choice The prefix `0x01` was chosen because: + - It's unlikely to conflict with existing nonce usage patterns - It's easily detectable with bitwise operations - It follows the pattern established by successful implementations like Kernel @@ -116,6 +116,7 @@ The prefix `0x01` was chosen because: ### Signature Structure Design The signature encoding separates concerns cleanly: + - Module installation data is explicitly structured - Installation authorization is separate from user operation validation - The format is extensible for future enhancements @@ -123,6 +124,7 @@ The signature encoding separates concerns cleanly: ### EIP-712 Integration Using EIP-712 for installation authorization provides: + - Strong cryptographic guarantees - Human-readable signature requests in wallets - Standard domain separation @@ -131,6 +133,7 @@ Using EIP-712 for installation authorization provides: ### Compatibility with ERC-7579 This standard extends ERC-7579 without breaking existing functionality: + - Normal user operations continue to work unchanged - Standard module installation flows remain available - All ERC-7579 security guarantees are preserved @@ -155,11 +158,11 @@ import {PackedUserOperation} from "@openzeppelin/contracts/interfaces/draft-IERC import {ERC4337Utils} from "@openzeppelin/contracts/account/utils/draft-ERC4337Utils.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; -abstract contract AtomicModuleAccount is AccountERC7579, ... { - bytes1 private constant ATOMIC_MODULE_PREFIX = 0x01; - bytes32 private constant INSTALL_MODULE_TYPEHASH = +abstract contract EnableModeAccount is AccountERC7579, ... { + bytes1 private constant ENABLE_MODE_PREFIX = 0x01; + bytes32 private constant ENABLE_MODULE_TYPEHASH = keccak256( - "InstallAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" + "EnableAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" ); function _validateUserOp( @@ -171,13 +174,13 @@ abstract contract AtomicModuleAccount is AccountERC7579, ... { assembly ("memory-safe") { result := and(nonce, shl(248, not(0))) } - if (result == ATOMIC_MODULE_PREFIX) { - return _validateAtomicModule(userOp, userOpHash); + if (result == ENABLE_MODE_PREFIX) { + return _validateEnableMode(userOp, userOpHash); } return super._validateUserOp(userOp, userOpHash); } - function _validateAtomicModule( + function _validateEnableMode( PackedUserOperation calldata userOp, bytes32 userOpHash ) internal returns (uint256) { @@ -188,7 +191,7 @@ abstract contract AtomicModuleAccount is AccountERC7579, ... { bytes calldata initData, bytes calldata installationSignature, bytes calldata userOpSignature - ) = _tryDecodeAtomicModule(userOp.signature); + ) = _tryDecodeEnableMode(userOp.signature); if (!success) { return ERC4337Utils.SIG_VALIDATION_FAILED; @@ -206,7 +209,7 @@ abstract contract AtomicModuleAccount is AccountERC7579, ... { IERC1271.isValidSignature.selector ) { _installModule(moduleTypeId, module, initData); - + // Create modified userOp with extracted signature PackedUserOperation memory modifiedUserOp = PackedUserOperation({ sender: userOp.sender, @@ -219,7 +222,7 @@ abstract contract AtomicModuleAccount is AccountERC7579, ... { paymasterAndData: userOp.paymasterAndData, signature: userOpSignature }); - + return super._validateUserOp(modifiedUserOp, userOpHash); } @@ -232,8 +235,7 @@ abstract contract AtomicModuleAccount is AccountERC7579, ... { ## Security Considerations -- **Authorization**: Only account owners can authorize module installation through EIP-712 signatures -- **Signature Separation**: Installation authorization and user operation validation use separate signatures +- **Authorization**: Only _account owners_ can authorize module installation through EIP-712 signatures - **Replay Protection**: Nonce inclusion in EIP-712 message prevents replay attacks - **Module Validation**: Standard ERC-7579 module validation applies (type checking, duplicate prevention) From 89347552d4add7aa0e6e21c25d30ab648e9fc706 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Mon, 20 Oct 2025 18:59:04 -0600 Subject: [PATCH 3/5] Update discussions and number --- ERCS/{erc-xxxx.md => erc-8043.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename ERCS/{erc-xxxx.md => erc-8043.md} (96%) diff --git a/ERCS/erc-xxxx.md b/ERCS/erc-8043.md similarity index 96% rename from ERCS/erc-xxxx.md rename to ERCS/erc-8043.md index aa89fd61db3..e80a8907f46 100644 --- a/ERCS/erc-xxxx.md +++ b/ERCS/erc-8043.md @@ -1,9 +1,9 @@ --- -eip: XXXX +eip: 8043 title: Enable Mode for module installation in ERC-7579 Smart Accounts description: Install and use ERC-7579 modules within a single user operation author: Ernesto Garcia (@ernestognw), Taek Lee (@leekt) -discussions-to: TBD +discussions-to: https://ethereum-magicians.org/t/erc-8043-enable-mode-for-module-installation-in-erc-7579-smart-accounts status: Draft type: Standards Track category: ERC @@ -88,7 +88,7 @@ The message structure MUST be: ```solidity bytes32 constant ENABLE_MODULE_TYPEHASH = keccak256( - "EnableAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" + "EnableModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" ); ``` @@ -162,7 +162,7 @@ abstract contract EnableModeAccount is AccountERC7579, ... { bytes1 private constant ENABLE_MODE_PREFIX = 0x01; bytes32 private constant ENABLE_MODULE_TYPEHASH = keccak256( - "EnableAtomicModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" + "EnableModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" ); function _validateUserOp( From 86944b484eb0390bb650e51d7472a064254635e7 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Mon, 20 Oct 2025 19:02:35 -0600 Subject: [PATCH 4/5] up --- ERCS/erc-8043.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-8043.md b/ERCS/erc-8043.md index e80a8907f46..3b616acbeda 100644 --- a/ERCS/erc-8043.md +++ b/ERCS/erc-8043.md @@ -1,9 +1,9 @@ --- eip: 8043 -title: Enable Mode for module installation in ERC-7579 Smart Accounts +title: Enable Mode for modules in ERC-7579 accounts description: Install and use ERC-7579 modules within a single user operation author: Ernesto Garcia (@ernestognw), Taek Lee (@leekt) -discussions-to: https://ethereum-magicians.org/t/erc-8043-enable-mode-for-module-installation-in-erc-7579-smart-accounts +discussions-to: https://ethereum-magicians.org/t/erc-8043-enable-mode-for-module-installation-in-erc-7579-smart-accounts/25901 status: Draft type: Standards Track category: ERC From 0a512a5a33b11e6563c60db02416b34f5ac825a5 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Thu, 23 Oct 2025 18:00:24 -0600 Subject: [PATCH 5/5] Update to support multiple modules --- ERCS/erc-8043.md | 56 ++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/ERCS/erc-8043.md b/ERCS/erc-8043.md index 3b616acbeda..1a5e5a6f316 100644 --- a/ERCS/erc-8043.md +++ b/ERCS/erc-8043.md @@ -23,11 +23,11 @@ This ERC defines a standard mechanism for [ERC-7579] compliant smart accounts to ## Motivation -Current [ERC-7579] implementations require separate transactions for module installation and usage, creating friction in user experience. Common use cases like session key modules and spending limit modules would benefit from _enable mode_ and immediate usage. +Current [ERC-7579] implementations require separate transactions for module installation and usage, which can hinder user experience. Use cases such as session key and spending limit modules would benefit from the ability to install and use modules in a single operation. This standard allows applications to opt in to install and use multiple modules under a single signature and user operation, enabling greater flexibility—particularly for scenarios where multiple modules need to work together atomically. This standard addresses several key issues: -- **User Experience**: Reduces transaction count from 2+ to 1 for module installation and usage +- **User Experience**: Reduces transaction count from N+1 to 1 for installing N modules and using them - **Consistency across implementations**: Provides a standard pattern that works across different [ERC-7579] account implementations - **Security**: Maintains strong authorization through [EIP-712] signatures from account owners @@ -39,8 +39,8 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ### Definitions -- **Enable Mode**: The logic branch triggered during the validation phase allowing to install and use an [ERC-7579] module within a single user operation -- **Installation Signature**: An [EIP-712] signature authorizing module installatio +- **Enable Mode**: The logic branch triggered during the validation phase allowing to install and use one or more [ERC-7579] modules within a single user operation +- **Installation Signature**: An [EIP-712] signature authorizing module installations - **User Operation Signature**: The signature for validating the user operation itself ### Magic Nonce Prefix @@ -66,9 +66,9 @@ When the enable mode prefix is detected, the user operation signature MUST be en ```solidity abi.encode( - uint256 moduleTypeId, - address module, - bytes initData, + uint256[] moduleTypeIds, + address[] modules, + bytes[] initDatas, bytes installationSignature, bytes userOpSignature ) @@ -76,10 +76,10 @@ abi.encode( Where: -- `moduleTypeId`: The [ERC-7579] module type identifier -- `module`: The address of the module to install -- `initData`: Initialization data to pass to the module's `onInstall` function -- `installationSignature`: [EIP-712] signature authorizing the module installation +- `moduleTypeIds`: Array of [ERC-7579] module type identifiers +- `modules`: Array of module addresses to install +- `initDatas`: Array of initialization data to pass to each module's `onInstall` function +- `installationSignature`: [EIP-712] signature authorizing the module installations - `userOpSignature`: Signature for validating the user operation ### EIP-712 Message Structure @@ -87,8 +87,8 @@ Where: The message structure MUST be: ```solidity -bytes32 constant ENABLE_MODULE_TYPEHASH = keccak256( - "EnableModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" +bytes32 constant ENABLE_MODULES_TYPEHASH = keccak256( + "EnableModules(uint256[] moduleTypeIds,address[] modules,bytes[] initDatas,uint256 nonce)" ); ``` @@ -99,9 +99,9 @@ Smart accounts implementing this standard on top of [ERC-4337]'s `validateUserOp 1. MUST detect the enable mode prefix (`0x01`) in user operation nonces 2. MUST decode the signature according to the specified format 3. MUST validate the EIP-712 `installationSignature` using `isValidSignature` -4. SHOULD install the module using the standard ERC-7579 `installModule` flow using the `moduleTypeId`, `module`, and `initData` +4. SHOULD install each module using the standard ERC-7579 `installModule` flow using the corresponding `moduleTypeIds[i]`, `modules[i]`, and `initDatas[i]` 5. MUST continue with normal user operation validation using the extracted `userOpSignature` -6. MUST return `SIG_VALIDATION_FAILED` (`1`) if signature decoding fails or enabling signature is invalid +6. MUST return `SIG_VALIDATION_FAILED` (`1`) if signature decoding fails, enabling signature is invalid, or any module installation fails ## Rationale @@ -160,9 +160,9 @@ import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; abstract contract EnableModeAccount is AccountERC7579, ... { bytes1 private constant ENABLE_MODE_PREFIX = 0x01; - bytes32 private constant ENABLE_MODULE_TYPEHASH = + bytes32 private constant ENABLE_MODULES_TYPEHASH = keccak256( - "EnableModule(uint256 moduleTypeId,address module,bytes initData,uint256 nonce)" + "EnableModules(uint256[] moduleTypeIds,address[] modules,bytes[] initDatas,uint256 nonce)" ); function _validateUserOp( @@ -186,9 +186,9 @@ abstract contract EnableModeAccount is AccountERC7579, ... { ) internal returns (uint256) { ( bool success, - uint256 moduleTypeId, - address module, - bytes calldata initData, + uint256[] calldata moduleTypeIds, + address[] calldata modules, + bytes[] calldata initDatas, bytes calldata installationSignature, bytes calldata userOpSignature ) = _tryDecodeEnableMode(userOp.signature); @@ -197,10 +197,10 @@ abstract contract EnableModeAccount is AccountERC7579, ... { return ERC4337Utils.SIG_VALIDATION_FAILED; } - bytes32 structHash = _installModuleStructHash( - moduleTypeId, - module, - initData, + bytes32 structHash = _installModulesStructHash( + moduleTypeIds, + modules, + initDatas, userOp.nonce ); @@ -208,7 +208,10 @@ abstract contract EnableModeAccount is AccountERC7579, ... { isValidSignature(_hashTypedDataV4(structHash), installationSignature) == IERC1271.isValidSignature.selector ) { - _installModule(moduleTypeId, module, initData); + // Install all modules + for (uint256 i = 0; i < modules.length; i++) { + _installModule(moduleTypeIds[i], modules[i], initDatas[i]); + } // Create modified userOp with extracted signature PackedUserOperation memory modifiedUserOp = PackedUserOperation({ @@ -235,9 +238,10 @@ abstract contract EnableModeAccount is AccountERC7579, ... { ## Security Considerations -- **Authorization**: Only _account owners_ can authorize module installation through EIP-712 signatures +- **Authorization**: Only _account owners_ can authorize module installations through EIP-712 signatures - **Replay Protection**: Nonce inclusion in EIP-712 message prevents replay attacks - **Module Validation**: Standard ERC-7579 module validation applies (type checking, duplicate prevention) +- **Array Length Validation**: Implementations MUST ensure that `moduleTypeIds`, `modules`, and `initDatas` arrays have the same length to prevent mismatched installations ## Copyright