From f5961d35af3894f74c924c180f2b4037d2beefe4 Mon Sep 17 00:00:00 2001 From: highskore Date: Thu, 13 Mar 2025 18:20:08 +0100 Subject: [PATCH 1/5] feat(factories): add mulitmodule init functions --- src/accounts/erc7579/ERC7579Factory.sol | 38 ++++++++++++ src/accounts/kernel/KernelFactory.sol | 60 +++++++++++++++++++ src/accounts/nexus/NexusFactory.sol | 50 ++++++++++++++++ src/accounts/safe/SafeFactory.sol | 62 ++++++++++++++++++++ src/integrations/registry/ExampleFactory.sol | 31 +++++++++- 5 files changed, 240 insertions(+), 1 deletion(-) diff --git a/src/accounts/erc7579/ERC7579Factory.sol b/src/accounts/erc7579/ERC7579Factory.sol index 0db4257c..826a16a1 100644 --- a/src/accounts/erc7579/ERC7579Factory.sol +++ b/src/accounts/erc7579/ERC7579Factory.sol @@ -26,6 +26,44 @@ contract ERC7579Factory is IAccountFactory, ERC7579Precompiles { return deployMSAPRoxy(salt, address(implementation), initCode); } + function createAccountWithModules( + bytes32 salt, + ERC7579BootstrapConfig[] calldata validators, + ERC7579BootstrapConfig[] calldata executors, + ERC7579BootstrapConfig calldata _fallback, + ERC7579BootstrapConfig[] calldata hooks + ) + public + payable + virtual + returns (address) + { + bytes memory initData = abi.encode( + bootstrapDefault, + abi.encodeCall(IERC7579Bootstrap.initMSA, (validators, executors, _fallback, hooks)) + ); + + address account = deployMSAPRoxy(salt, address(implementation), initData); + + return account; + } + + function getInitData( + ERC7579BootstrapConfig[] memory _validators, + ERC7579BootstrapConfig[] memory _executors, + ERC7579BootstrapConfig memory hook, + ERC7579BootstrapConfig[] memory fallbacks + ) + public + view + returns (bytes memory _init) + { + _init = abi.encode( + address(bootstrapDefault), + abi.encodeCall(IERC7579Bootstrap.initMSA, (_validators, _executors, hook, fallbacks)) + ); + } + function getAddress( bytes32 salt, bytes memory initCode diff --git a/src/accounts/kernel/KernelFactory.sol b/src/accounts/kernel/KernelFactory.sol index 214fc854..581c8046 100644 --- a/src/accounts/kernel/KernelFactory.sol +++ b/src/accounts/kernel/KernelFactory.sol @@ -12,6 +12,16 @@ import { IHook } from "../../accounts/kernel/interfaces/IERC7579Module.sol"; import { IAccountFactory } from "../../accounts/factory/interface/IAccountFactory.sol"; import { MockHookMultiPlexer } from "../../Mocks.sol"; import { KernelPrecompiles } from "../../deployment/precompiles/KernelPrecompiles.sol"; +import { + MODULE_TYPE_VALIDATOR, + MODULE_TYPE_EXECUTOR, + MODULE_TYPE_FALLBACK +} from "./types/Constants.sol"; + +struct ModuleBootstrapConfig { + address module; + bytes initData; +} contract KernelFactory is IAccountFactory, KernelPrecompiles { IKernelAccountFactory internal factory; @@ -56,6 +66,56 @@ contract KernelFactory is IAccountFactory, KernelPrecompiles { ); } + function getInitData( + ModuleBootstrapConfig[] memory _validators, + ModuleBootstrapConfig[] memory _executors, + ModuleBootstrapConfig memory hook, + ModuleBootstrapConfig[] memory fallbacks + ) + public + pure + returns (bytes memory _init) + { + ValidationId rootValidator = + ValidatorLib.validatorToIdentifier(IValidator(_validators[0].module)); + // Encode the rest of the validators, executors and fallbacks are onInstall calls with the + // appropriate address and initData + bytes[] memory otherModules = + new bytes[](_validators.length - 1 + _executors.length + fallbacks.length); + uint256 index = 0; + for (uint256 i = 1; i < _validators.length; i++) { + otherModules[index] = abi.encodeCall( + IKernel.installModule, + (MODULE_TYPE_VALIDATOR, _validators[i].module, _validators[i].initData) + ); + index++; + } + for (uint256 i = 0; i < _executors.length; i++) { + otherModules[index] = abi.encodeCall( + IKernel.installModule, + (MODULE_TYPE_EXECUTOR, _executors[i].module, _executors[i].initData) + ); + index++; + } + for (uint256 i = 0; i < fallbacks.length; i++) { + otherModules[index] = abi.encodeCall( + IKernel.installModule, + (MODULE_TYPE_FALLBACK, fallbacks[i].module, fallbacks[i].initData) + ); + index++; + } + _init = abi.encodeCall( + IKernel.initialize, + ( + rootValidator, + IHook(address(hook.module)), + _validators[0].initData, + hook.initData, + otherModules + ) + ); + } + function setHookMultiPlexer(address hook) public { hookMultiPlexer = MockHookMultiPlexer(hook); } diff --git a/src/accounts/nexus/NexusFactory.sol b/src/accounts/nexus/NexusFactory.sol index a7beb078..32f2ee79 100644 --- a/src/accounts/nexus/NexusFactory.sol +++ b/src/accounts/nexus/NexusFactory.sol @@ -88,4 +88,54 @@ contract NexusFactory is IAccountFactory, NexusPrecompiles { config, IERC7484(REGISTRY_ADDR), attesters, 1 ); } + + function createAccountWithModules( + bytes32 salt, + NexusBootstrapConfig[] calldata validators, + NexusBootstrapConfig[] calldata executors, + NexusBootstrapConfig calldata hook, + NexusBootstrapConfig[] calldata fallbacks + ) + public + payable + virtual + returns (address) + { + address[] memory attesters = new address[](1); + attesters[0] = address(0x000000333034E9f539ce08819E12c1b8Cb29084d); + + bytes memory initData = abi.encode( + bootstrapDefault, + abi.encodeCall( + INexusBootstrap.initNexus, + (validators, executors, hook, fallbacks, IERC7484(REGISTRY_ADDR), attesters, 1) + ) + ); + + address account = deployNexusProxy(salt, nexusImpl, initData); + + return account; + } + + function getInitDataWithModules( + NexusBootstrapConfig[] memory validators, + NexusBootstrapConfig[] memory executors, + NexusBootstrapConfig memory hook, + NexusBootstrapConfig[] memory fallbacks + ) + public + view + returns (bytes memory _init) + { + address[] memory attesters = new address[](1); + attesters[0] = address(0x000000333034E9f539ce08819E12c1b8Cb29084d); + + _init = abi.encode( + address(bootstrapDefault), + abi.encodeCall( + INexusBootstrap.initNexus, + (validators, executors, hook, fallbacks, IERC7484(REGISTRY_ADDR), attesters, 1) + ) + ); + } } diff --git a/src/accounts/safe/SafeFactory.sol b/src/accounts/safe/SafeFactory.sol index b8953e94..062b3ad9 100644 --- a/src/accounts/safe/SafeFactory.sol +++ b/src/accounts/safe/SafeFactory.sol @@ -116,4 +116,66 @@ contract SafeFactory is IAccountFactory, Safe7579Precompiles { }); _init = abi.encode(initDataSafe); } + + function createAccountWithModules( + bytes32 salt, + ModuleInit[] calldata validators, + ModuleInit[] calldata executors, + ModuleInit[] calldata fallbacks, + ModuleInit[] calldata hooks + ) + public + payable + virtual + returns (address safe) + { + ISafe7579Launchpad.InitData memory initDataSafe = abi.decode( + getInitDataWithModules(validators, executors, fallbacks, hooks), + (ISafe7579Launchpad.InitData) + ); + bytes32 initHash = launchpad.hash(initDataSafe); + + bytes memory factoryInitializer = + abi.encodeCall(ISafe7579Launchpad.preValidationSetup, (initHash, address(0), "")); + + safe = address( + safeProxyFactory.createProxyWithNonce( + address(launchpad), factoryInitializer, uint256(salt) + ) + ); + } + + function getInitDataWithModules( + ModuleInit[] memory validators, + ModuleInit[] memory executors, + ModuleInit[] memory fallbacks, + ModuleInit[] memory hooks + ) + public + view + returns (bytes memory _init) + { + ISafe7579Launchpad.InitData memory initDataSafe = ISafe7579Launchpad.InitData({ + singleton: address(safeSingleton), + owners: Solarray.addresses(makeAddr("owner1")), + threshold: 1, + setupTo: address(launchpad), + setupData: abi.encodeCall( + ISafe7579Launchpad.initSafe7579, + ( + address(safe7579), + executors, + fallbacks, + hooks, + Solarray.addresses(makeAddr("attester1"), makeAddr("attester2")), + 2 + ) + ), + safe7579: ISafe7579(safe7579), + validators: validators, + callData: "" + }); + + _init = abi.encode(initDataSafe); + } } diff --git a/src/integrations/registry/ExampleFactory.sol b/src/integrations/registry/ExampleFactory.sol index 900f520e..70d77a10 100644 --- a/src/integrations/registry/ExampleFactory.sol +++ b/src/integrations/registry/ExampleFactory.sol @@ -3,7 +3,10 @@ pragma solidity >=0.8.23 <0.9.0; // Interfaces import { IMSA } from "../../accounts/erc7579/interfaces/IMSA.sol"; -import { IERC7579Bootstrap } from "../../accounts/erc7579/interfaces/IERC7579Bootstrap.sol"; +import { + IERC7579Bootstrap, + BootstrapConfig +} from "../../accounts/erc7579/interfaces/IERC7579Bootstrap.sol"; import { IModule as IERC7579Module } from "../../accounts/common/interfaces/IERC7579Module.sol"; // Dependencies @@ -88,6 +91,32 @@ contract ExampleFactory is FactoryBase, ERC7579Precompiles { return address(uint160(uint256(hash))); } + function getAddress( + bytes32 salt, + bytes memory initData + ) + public + view + virtual + returns (address) + { + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + salt, + keccak256( + abi.encodePacked( + MSAPROXY_BYTECODE, + abi.encode(IMPLEMENTATION, abi.encodeCall(IMSA.initializeAccount, initData)) + ) + ) + ) + ); + + return address(uint160(uint256(hash))); + } + function getInitCode( bytes32 salt, address validator, From e82f1af0725bc8c0d856b97da4d226d38f1f59ea Mon Sep 17 00:00:00 2001 From: highskore Date: Thu, 13 Mar 2025 18:24:22 +0100 Subject: [PATCH 2/5] fix: fix stack too deep --- src/accounts/safe/SafeFactory.sol | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/accounts/safe/SafeFactory.sol b/src/accounts/safe/SafeFactory.sol index 062b3ad9..ea46d7ff 100644 --- a/src/accounts/safe/SafeFactory.sol +++ b/src/accounts/safe/SafeFactory.sol @@ -135,12 +135,11 @@ contract SafeFactory is IAccountFactory, Safe7579Precompiles { ); bytes32 initHash = launchpad.hash(initDataSafe); - bytes memory factoryInitializer = - abi.encodeCall(ISafe7579Launchpad.preValidationSetup, (initHash, address(0), "")); - - safe = address( + return address( safeProxyFactory.createProxyWithNonce( - address(launchpad), factoryInitializer, uint256(salt) + address(launchpad), + abi.encodeCall(ISafe7579Launchpad.preValidationSetup, (initHash, address(0), "")), + uint256(salt) ) ); } From f54e7fc2db6d999245b3ef5e21f8f647005843f2 Mon Sep 17 00:00:00 2001 From: highskore Date: Thu, 13 Mar 2025 20:17:58 +0100 Subject: [PATCH 3/5] feat: add new makeAccountInstance and test --- src/accounts/erc7579/ERC7579Factory.sol | 39 +++++++-- .../factory/interface/IAccountFactory.sol | 14 ++++ src/accounts/kernel/KernelFactory.sol | 81 ++++++++++++++++--- src/accounts/nexus/NexusFactory.sol | 46 +++++++++-- src/accounts/safe/SafeFactory.sol | 72 ++++++++++------- src/test/RhinestoneModuleKit.sol | 40 +++++++++ test/Diff.t.sol | 43 +++++++++- 7 files changed, 282 insertions(+), 53 deletions(-) diff --git a/src/accounts/erc7579/ERC7579Factory.sol b/src/accounts/erc7579/ERC7579Factory.sol index 826a16a1..21db05d3 100644 --- a/src/accounts/erc7579/ERC7579Factory.sol +++ b/src/accounts/erc7579/ERC7579Factory.sol @@ -48,19 +48,48 @@ contract ERC7579Factory is IAccountFactory, ERC7579Precompiles { return account; } + function getInitData(bytes memory initCode) public view returns (bytes memory _init) { + ( + ERC7579BootstrapConfig[] memory _validators, + ERC7579BootstrapConfig[] memory _executors, + ERC7579BootstrapConfig memory hook, + ERC7579BootstrapConfig[] memory fallbacks + ) = abi.decode( + initCode, + ( + ERC7579BootstrapConfig[], + ERC7579BootstrapConfig[], + ERC7579BootstrapConfig, + ERC7579BootstrapConfig[] + ) + ); + _init = abi.encode( + address(bootstrapDefault), + abi.encodeCall(IERC7579Bootstrap.initMSA, (_validators, _executors, hook, fallbacks)) + ); + } + function getInitData( - ERC7579BootstrapConfig[] memory _validators, - ERC7579BootstrapConfig[] memory _executors, - ERC7579BootstrapConfig memory hook, - ERC7579BootstrapConfig[] memory fallbacks + IAccountFactory.ModuleInitData[] memory _validators, + IAccountFactory.ModuleInitData[] memory _executors, + IAccountFactory.ModuleInitData memory _hook, + IAccountFactory.ModuleInitData[] memory _fallbacks ) public view returns (bytes memory _init) { + ERC7579BootstrapConfig[] memory validators = + abi.decode(abi.encode(_validators), (ERC7579BootstrapConfig[])); + ERC7579BootstrapConfig[] memory executors = + abi.decode(abi.encode(_executors), (ERC7579BootstrapConfig[])); + ERC7579BootstrapConfig memory hook = abi.decode(abi.encode(_hook), (ERC7579BootstrapConfig)); + ERC7579BootstrapConfig[] memory fallbacks = + abi.decode(abi.encode(_fallbacks), (ERC7579BootstrapConfig[])); + _init = abi.encode( address(bootstrapDefault), - abi.encodeCall(IERC7579Bootstrap.initMSA, (_validators, _executors, hook, fallbacks)) + abi.encodeCall(IERC7579Bootstrap.initMSA, (validators, executors, hook, fallbacks)) ); } diff --git a/src/accounts/factory/interface/IAccountFactory.sol b/src/accounts/factory/interface/IAccountFactory.sol index 1f3422d2..5bbb32a1 100644 --- a/src/accounts/factory/interface/IAccountFactory.sol +++ b/src/accounts/factory/interface/IAccountFactory.sol @@ -2,6 +2,11 @@ pragma solidity >=0.8.23 <0.9.0; interface IAccountFactory { + struct ModuleInitData { + address module; + bytes data; + } + function init() external; function createAccount( @@ -19,4 +24,13 @@ interface IAccountFactory { ) external returns (bytes memory init); + + function getInitData( + IAccountFactory.ModuleInitData[] memory validators, + IAccountFactory.ModuleInitData[] memory executors, + IAccountFactory.ModuleInitData memory hook, + IAccountFactory.ModuleInitData[] memory fallbacks + ) + external + returns (bytes memory _init); } diff --git a/src/accounts/kernel/KernelFactory.sol b/src/accounts/kernel/KernelFactory.sol index 581c8046..dfa65c87 100644 --- a/src/accounts/kernel/KernelFactory.sol +++ b/src/accounts/kernel/KernelFactory.sol @@ -67,33 +67,92 @@ contract KernelFactory is IAccountFactory, KernelPrecompiles { } function getInitData( - ModuleBootstrapConfig[] memory _validators, - ModuleBootstrapConfig[] memory _executors, - ModuleBootstrapConfig memory hook, - ModuleBootstrapConfig[] memory fallbacks + IAccountFactory.ModuleInitData[] memory validators, + IAccountFactory.ModuleInitData[] memory executors, + IAccountFactory.ModuleInitData memory hook, + IAccountFactory.ModuleInitData[] memory fallbacks ) public pure + override returns (bytes memory _init) { + address[] memory attesters = new address[](1); + attesters[0] = address(0x000000333034E9f539ce08819E12c1b8Cb29084d); + + ValidationId rootValidator = + ValidatorLib.validatorToIdentifier(IValidator(validators[0].module)); + // Encode the rest of the validators, executors and fallbacks are onInstall calls with the + // appropriate address and initData + bytes[] memory otherModules = + new bytes[](validators.length - 1 + executors.length + fallbacks.length); + uint256 index = 0; + for (uint256 i = 1; i < validators.length; i++) { + otherModules[index] = abi.encodeCall( + IKernel.installModule, + (MODULE_TYPE_VALIDATOR, validators[i].module, validators[i].data) + ); + index++; + } + for (uint256 i = 0; i < executors.length; i++) { + otherModules[index] = abi.encodeCall( + IKernel.installModule, + (MODULE_TYPE_EXECUTOR, executors[i].module, executors[i].data) + ); + index++; + } + for (uint256 i = 0; i < fallbacks.length; i++) { + otherModules[index] = abi.encodeCall( + IKernel.installModule, + (MODULE_TYPE_FALLBACK, fallbacks[i].module, fallbacks[i].data) + ); + index++; + } + _init = abi.encodeCall( + IKernel.initialize, + ( + rootValidator, + IHook(address(hook.module)), + validators[0].data, + hook.data, + otherModules + ) + ); + } + + function getInitData(bytes memory initData) public pure returns (bytes memory _init) { + ( + ModuleBootstrapConfig[] memory validators, + ModuleBootstrapConfig[] memory executors, + ModuleBootstrapConfig memory hook, + ModuleBootstrapConfig[] memory fallbacks + ) = abi.decode( + initData, + ( + ModuleBootstrapConfig[], + ModuleBootstrapConfig[], + ModuleBootstrapConfig, + ModuleBootstrapConfig[] + ) + ); ValidationId rootValidator = - ValidatorLib.validatorToIdentifier(IValidator(_validators[0].module)); + ValidatorLib.validatorToIdentifier(IValidator(validators[0].module)); // Encode the rest of the validators, executors and fallbacks are onInstall calls with the // appropriate address and initData bytes[] memory otherModules = - new bytes[](_validators.length - 1 + _executors.length + fallbacks.length); + new bytes[](validators.length - 1 + executors.length + fallbacks.length); uint256 index = 0; - for (uint256 i = 1; i < _validators.length; i++) { + for (uint256 i = 1; i < validators.length; i++) { otherModules[index] = abi.encodeCall( IKernel.installModule, - (MODULE_TYPE_VALIDATOR, _validators[i].module, _validators[i].initData) + (MODULE_TYPE_VALIDATOR, validators[i].module, validators[i].initData) ); index++; } - for (uint256 i = 0; i < _executors.length; i++) { + for (uint256 i = 0; i < executors.length; i++) { otherModules[index] = abi.encodeCall( IKernel.installModule, - (MODULE_TYPE_EXECUTOR, _executors[i].module, _executors[i].initData) + (MODULE_TYPE_EXECUTOR, executors[i].module, executors[i].initData) ); index++; } @@ -109,7 +168,7 @@ contract KernelFactory is IAccountFactory, KernelPrecompiles { ( rootValidator, IHook(address(hook.module)), - _validators[0].initData, + validators[0].initData, hook.initData, otherModules ) diff --git a/src/accounts/nexus/NexusFactory.sol b/src/accounts/nexus/NexusFactory.sol index 32f2ee79..d9df9f57 100644 --- a/src/accounts/nexus/NexusFactory.sol +++ b/src/accounts/nexus/NexusFactory.sol @@ -117,16 +117,52 @@ contract NexusFactory is IAccountFactory, NexusPrecompiles { return account; } - function getInitDataWithModules( - NexusBootstrapConfig[] memory validators, - NexusBootstrapConfig[] memory executors, - NexusBootstrapConfig memory hook, - NexusBootstrapConfig[] memory fallbacks + function getInitData( + IAccountFactory.ModuleInitData[] memory _validators, + IAccountFactory.ModuleInitData[] memory _executors, + IAccountFactory.ModuleInitData memory _hook, + IAccountFactory.ModuleInitData[] memory _fallbacks ) public view + override returns (bytes memory _init) { + NexusBootstrapConfig[] memory validators = + abi.decode(abi.encode(_validators), (NexusBootstrapConfig[])); + NexusBootstrapConfig[] memory executors = + abi.decode(abi.encode(_executors), (NexusBootstrapConfig[])); + NexusBootstrapConfig memory hook = abi.decode(abi.encode(_hook), (NexusBootstrapConfig)); + NexusBootstrapConfig[] memory fallbacks = + abi.decode(abi.encode(_fallbacks), (NexusBootstrapConfig[])); + + address[] memory attesters = new address[](1); + attesters[0] = address(0x000000333034E9f539ce08819E12c1b8Cb29084d); + + _init = abi.encode( + address(bootstrapDefault), + abi.encodeCall( + INexusBootstrap.initNexus, + (validators, executors, hook, fallbacks, IERC7484(REGISTRY_ADDR), attesters, 1) + ) + ); + } + + function getInitData(bytes memory initData) public view returns (bytes memory _init) { + ( + NexusBootstrapConfig[] memory validators, + NexusBootstrapConfig[] memory executors, + NexusBootstrapConfig memory hook, + NexusBootstrapConfig[] memory fallbacks + ) = abi.decode( + initData, + ( + NexusBootstrapConfig[], + NexusBootstrapConfig[], + NexusBootstrapConfig, + NexusBootstrapConfig[] + ) + ); address[] memory attesters = new address[](1); attesters[0] = address(0x000000333034E9f539ce08819E12c1b8Cb29084d); diff --git a/src/accounts/safe/SafeFactory.sol b/src/accounts/safe/SafeFactory.sol index ea46d7ff..0b9a0256 100644 --- a/src/accounts/safe/SafeFactory.sol +++ b/src/accounts/safe/SafeFactory.sol @@ -117,43 +117,53 @@ contract SafeFactory is IAccountFactory, Safe7579Precompiles { _init = abi.encode(initDataSafe); } - function createAccountWithModules( - bytes32 salt, - ModuleInit[] calldata validators, - ModuleInit[] calldata executors, - ModuleInit[] calldata fallbacks, - ModuleInit[] calldata hooks + function getInitData( + IAccountFactory.ModuleInitData[] memory _validators, + IAccountFactory.ModuleInitData[] memory _executors, + IAccountFactory.ModuleInitData memory _hook, + IAccountFactory.ModuleInitData[] memory _fallbacks ) public - payable - virtual - returns (address safe) + view + returns (bytes memory _init) { - ISafe7579Launchpad.InitData memory initDataSafe = abi.decode( - getInitDataWithModules(validators, executors, fallbacks, hooks), - (ISafe7579Launchpad.InitData) - ); - bytes32 initHash = launchpad.hash(initDataSafe); + ModuleInit[] memory validators = abi.decode(abi.encode(_validators), (ModuleInit[])); + ModuleInit[] memory executors = abi.decode(abi.encode(_executors), (ModuleInit[])); + ModuleInit memory hook = abi.decode(abi.encode(_hook), (ModuleInit)); + ModuleInit[] memory hooks = new ModuleInit[](1); + hooks[0] = hook; + ModuleInit[] memory fallbacks = abi.decode(abi.encode(_fallbacks), (ModuleInit[])); - return address( - safeProxyFactory.createProxyWithNonce( - address(launchpad), - abi.encodeCall(ISafe7579Launchpad.preValidationSetup, (initHash, address(0), "")), - uint256(salt) - ) - ); + ISafe7579Launchpad.InitData memory initDataSafe = ISafe7579Launchpad.InitData({ + singleton: address(safeSingleton), + owners: Solarray.addresses(makeAddr("owner1")), + threshold: 1, + setupTo: address(launchpad), + setupData: abi.encodeCall( + ISafe7579Launchpad.initSafe7579, + ( + address(safe7579), + executors, + fallbacks, + hooks, + Solarray.addresses(makeAddr("attester1"), makeAddr("attester2")), + 2 + ) + ), + safe7579: ISafe7579(safe7579), + validators: validators, + callData: "" + }); + _init = abi.encode(initDataSafe); } - function getInitDataWithModules( - ModuleInit[] memory validators, - ModuleInit[] memory executors, - ModuleInit[] memory fallbacks, - ModuleInit[] memory hooks - ) - public - view - returns (bytes memory _init) - { + function getInitData(bytes memory initData) public view returns (bytes memory _init) { + ( + ModuleInit[] memory validators, + ModuleInit[] memory executors, + ModuleInit[] memory hooks, + ModuleInit[] memory fallbacks + ) = abi.decode(initData, (ModuleInit[], ModuleInit[], ModuleInit[], ModuleInit[])); ISafe7579Launchpad.InitData memory initDataSafe = ISafe7579Launchpad.InitData({ singleton: address(safeSingleton), owners: Solarray.addresses(makeAddr("owner1")), diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index feed8c7a..b76afb90 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -266,6 +266,46 @@ contract RhinestoneModuleKit is AuxiliaryFactory { }); } + /// @notice Create an account instance with the provided salt, account,executors, validators, + /// hook, and fallback arrays + /// @param salt The salt used to create the account + /// @param validators The array of ModuleInitData for validators + /// @param executors The array of ModuleInitData for executors + /// @param hook The ModuleInitData for the hook + /// @param fallbacks The array of ModuleInitData for fallbacks + /// @return instance The account instance + function makeAccountInstance( + bytes32 salt, + IAccountFactory.ModuleInitData[] memory validators, + IAccountFactory.ModuleInitData[] memory executors, + IAccountFactory.ModuleInitData memory hook, + IAccountFactory.ModuleInitData[] memory fallbacks + ) + internal + initializeModuleKit + returns (AccountInstance memory instance) + { + (AccountType env, address accountFactoryAddress, address accountHelper) = + ModuleKitHelpers.getAccountEnv(); + IAccountFactory accountFactory = IAccountFactory(accountFactoryAddress); + bytes memory initData = + IAccountFactory(getFactory(DEFAULT)).getInitData(validators, executors, hook, fallbacks); + address account = accountFactory.getAddress(salt, initData); + bytes memory initCode = abi.encodePacked( + address(accountFactory), abi.encodeCall(accountFactory.createAccount, (salt, initData)) + ); + label(address(account), toString(salt)); + deal(account, 10 ether); + instance = makeAccountInstance({ + salt: salt, + helper: accountHelper, + account: account, + initCode: initCode, + defaultValidator: address(validators[0].module), + defaultSessionValidator: address(executors[0].module) + }); + } + /// @notice Create an account instance with the provided salt, account, init code, and helper. /// Funds the account with 10 ether. /// @param salt The salt used to create the account diff --git a/test/Diff.t.sol b/test/Diff.t.sol index 22bd5f41..f652b464 100644 --- a/test/Diff.t.sol +++ b/test/Diff.t.sol @@ -21,6 +21,7 @@ import { MockK1Validator, VALIDATION_SUCCESS } from "test/mocks/MockK1Validator. import { MockK1ValidatorUncompliantUninstall } from "test/mocks/MockK1ValidatorUncompliantUninstall.sol"; import { VmSafe } from "src/test/utils/Vm.sol"; +import { IAccountFactory } from "src/accounts/factory/interface/IAccountFactory.sol"; contract ERC7579DifferentialModuleKitLibTest is BaseTest { using ModuleKitHelpers for *; @@ -64,7 +65,47 @@ contract ERC7579DifferentialModuleKitLibTest is BaseTest { } /*////////////////////////////////////////////////////////////////////////// - exec + make instance + //////////////////////////////////////////////////////////////////////////*/ + + function test_makeAccountInstance() public { + AccountInstance memory newInstance = makeAccountInstance("newSalt"); + assertTrue(newInstance.account.code.length == 0); + newInstance.deployAccount(); + assertTrue(newInstance.account.code.length > 0); + } + + function test_makeAccountInstance__RevertWhen__AccountExists() public { + AccountInstance memory newInstance = makeAccountInstance("newSalt"); + newInstance.deployAccount(); + vm.expectRevert(); + makeAccountInstance("newSalt"); + } + + function test_makeAccountInstance_withModules() public { + // Deploy executors, validators, hooks and fallbacks and setup ModuleInit data + IAccountFactory.ModuleInitData[] memory validators = new IAccountFactory.ModuleInitData[](1); + validators[0] = IAccountFactory.ModuleInitData({ module: address(validator), data: "" }); + IAccountFactory.ModuleInitData[] memory executors = new IAccountFactory.ModuleInitData[](1); + executors[0] = IAccountFactory.ModuleInitData({ module: address(executor), data: "" }); + IAccountFactory.ModuleInitData memory _hook = + IAccountFactory.ModuleInitData({ module: address(hook), data: "" }); + IAccountFactory.ModuleInitData[] memory fallbacks = new IAccountFactory.ModuleInitData[](0); + // Create account instance + AccountInstance memory newInstance2 = makeAccountInstance({ + salt: "newSalt2", + validators: validators, + executors: executors, + hook: _hook, + fallbacks: fallbacks + }); + assertTrue(newInstance2.account.code.length == 0); + newInstance2.deployAccount(); + assertTrue(newInstance2.account.code.length > 0); + } + + /*////////////////////////////////////////////////////////////////////////// + exec //////////////////////////////////////////////////////////////////////////*/ function testexec__Given__TwoInputs() public { From e569e9835de22b9d5fc2f010a5fe6da5f43bcd30 Mon Sep 17 00:00:00 2001 From: highskore Date: Tue, 18 Mar 2025 17:15:11 +0000 Subject: [PATCH 4/5] test: add multiinit tests --- src/test/RhinestoneModuleKit.sol | 3 +-- test/Diff.t.sol | 32 ++++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index b76afb90..ab26620d 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -288,8 +288,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { (AccountType env, address accountFactoryAddress, address accountHelper) = ModuleKitHelpers.getAccountEnv(); IAccountFactory accountFactory = IAccountFactory(accountFactoryAddress); - bytes memory initData = - IAccountFactory(getFactory(DEFAULT)).getInitData(validators, executors, hook, fallbacks); + bytes memory initData = accountFactory.getInitData(validators, executors, hook, fallbacks); address account = accountFactory.getAddress(salt, initData); bytes memory initCode = abi.encodePacked( address(accountFactory), abi.encodeCall(accountFactory.createAccount, (salt, initData)) diff --git a/test/Diff.t.sol b/test/Diff.t.sol index f652b464..ec7609f2 100644 --- a/test/Diff.t.sol +++ b/test/Diff.t.sol @@ -75,22 +75,34 @@ contract ERC7579DifferentialModuleKitLibTest is BaseTest { assertTrue(newInstance.account.code.length > 0); } - function test_makeAccountInstance__RevertWhen__AccountExists() public { + function test_makeAccountInstance_withModules() public { + AccountType env = ModuleKitHelpers.getAccountType(); AccountInstance memory newInstance = makeAccountInstance("newSalt"); + assertTrue(newInstance.account.code.length == 0); newInstance.deployAccount(); - vm.expectRevert(); - makeAccountInstance("newSalt"); - } - - function test_makeAccountInstance_withModules() public { + assertTrue(newInstance.account.code.length > 0); // Deploy executors, validators, hooks and fallbacks and setup ModuleInit data IAccountFactory.ModuleInitData[] memory validators = new IAccountFactory.ModuleInitData[](1); - validators[0] = IAccountFactory.ModuleInitData({ module: address(validator), data: "" }); + validators[0] = + IAccountFactory.ModuleInitData({ module: address(validator), data: abi.encode(0x123) }); IAccountFactory.ModuleInitData[] memory executors = new IAccountFactory.ModuleInitData[](1); - executors[0] = IAccountFactory.ModuleInitData({ module: address(executor), data: "" }); + executors[0] = IAccountFactory.ModuleInitData({ + module: address(executor), + data: env == AccountType.KERNEL ? abi.encodePacked(bytes20(0)) : abi.encodePacked("") + }); IAccountFactory.ModuleInitData memory _hook = - IAccountFactory.ModuleInitData({ module: address(hook), data: "" }); - IAccountFactory.ModuleInitData[] memory fallbacks = new IAccountFactory.ModuleInitData[](0); + IAccountFactory.ModuleInitData({ module: address(hook), data: bytes("0") }); + IAccountFactory.ModuleInitData[] memory fallbacks = new IAccountFactory.ModuleInitData[](1); + fallbacks[0] = IAccountFactory.ModuleInitData({ + module: address(fallbackHandler), + data: env == AccountType.KERNEL + ? abi.encodePacked( + bytes4(0), + bytes20(0), + abi.encode(abi.encodePacked(hex"00", "fallbackData"), abi.encodePacked("")) + ) + : abi.encodePacked(bytes4(0x01234123), bytes28(0)) + }); // Create account instance AccountInstance memory newInstance2 = makeAccountInstance({ salt: "newSalt2", From 22aa200d413105c555f01a3f5fc0e4d2cf896cd7 Mon Sep 17 00:00:00 2001 From: highskore Date: Wed, 19 Mar 2025 12:00:58 +0000 Subject: [PATCH 5/5] feat(factories): test mulitmodule init functions --- src/test/RhinestoneModuleKit.sol | 3 +-- src/test/helpers/SafeHelpers.sol | 19 ++++++++++--------- test/Diff.t.sol | 20 +++++++++++++------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index ab26620d..069e9e53 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -285,8 +285,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { initializeModuleKit returns (AccountInstance memory instance) { - (AccountType env, address accountFactoryAddress, address accountHelper) = - ModuleKitHelpers.getAccountEnv(); + (, address accountFactoryAddress, address accountHelper) = ModuleKitHelpers.getAccountEnv(); IAccountFactory accountFactory = IAccountFactory(accountFactoryAddress); bytes memory initData = accountFactory.getInitData(validators, executors, hook, fallbacks); address account = accountFactory.getAddress(salt, initData); diff --git a/src/test/helpers/SafeHelpers.sol b/src/test/helpers/SafeHelpers.sol index 8186a055..8e671c45 100644 --- a/src/test/helpers/SafeHelpers.sol +++ b/src/test/helpers/SafeHelpers.sol @@ -25,6 +25,9 @@ import { IAccountFactory } from "../../accounts/factory/interface/IAccountFactor import { IAccountModulesPaginated } from "./interfaces/IAccountModulesPaginated.sol"; import { IERC1271, EIP1271_MAGIC_VALUE, IERC712 } from "../../Interfaces.sol"; +// Libraries +import { LibBytes } from "solady/utils/LibBytes.sol"; + // Utils import { startPrank, stopPrank } from "../utils/Vm.sol"; @@ -65,7 +68,7 @@ contract SafeHelpers is HelperBase { } if (initCode.length != 0) { - (initCode, callData) = _getInitCallData(instance.salt, txValidator, initCode, callData); + (initCode, callData) = _getInitCallData(instance.salt, initCode, callData); } userOp = PackedUserOperation({ @@ -135,7 +138,7 @@ contract SafeHelpers is HelperBase { } if (initCode.length != 0) { - (initCode, callData) = _getInitCallData(instance.salt, txValidator, initCode, callData); + (initCode, callData) = _getInitCallData(instance.salt, initCode, callData); } userOp = PackedUserOperation({ @@ -383,9 +386,8 @@ contract SafeHelpers is HelperBase { } else { (bytes memory initCode, bytes memory callData) = _getInitCallData( instance.salt, - address(instance.defaultValidator), instance.initCode, - encode({ target: address(1), value: 1 wei, callData: "" }) + encode({ target: address(0), value: 0 wei, callData: "" }) ); assembly { let factory := mload(add(initCode, 20)) @@ -428,16 +430,15 @@ contract SafeHelpers is HelperBase { /// @notice Gets the initCode and callData for a new account instance /// @param salt bytes32 the salt for the account instance - /// @param txValidator address the address of the validator /// @param originalInitCode bytes the original initCode for the account instance /// @param erc4337CallData bytes the callData for the ERC4337 call function _getInitCallData( bytes32 salt, - address txValidator, bytes memory originalInitCode, bytes memory erc4337CallData ) public + pure returns (bytes memory initCode, bytes memory callData) { // TODO: refactor this to decode the initcode @@ -445,9 +446,9 @@ contract SafeHelpers is HelperBase { assembly { factory := mload(add(originalInitCode, 20)) } - ISafe7579Launchpad.InitData memory initData = abi.decode( - IAccountFactory(factory).getInitData(txValidator, ""), (ISafe7579Launchpad.InitData) - ); + bytes memory initData2 = LibBytes.slice(originalInitCode, 120, originalInitCode.length); + ISafe7579Launchpad.InitData memory initData = + abi.decode(initData2, (ISafe7579Launchpad.InitData)); initData.callData = erc4337CallData; initCode = abi.encodePacked( factory, abi.encodeCall(SafeFactory.createAccount, (salt, abi.encode(initData))) diff --git a/test/Diff.t.sol b/test/Diff.t.sol index ec7609f2..f66e88aa 100644 --- a/test/Diff.t.sol +++ b/test/Diff.t.sol @@ -77,10 +77,10 @@ contract ERC7579DifferentialModuleKitLibTest is BaseTest { function test_makeAccountInstance_withModules() public { AccountType env = ModuleKitHelpers.getAccountType(); - AccountInstance memory newInstance = makeAccountInstance("newSalt"); - assertTrue(newInstance.account.code.length == 0); - newInstance.deployAccount(); - assertTrue(newInstance.account.code.length > 0); + // AccountInstance memory newInstance = makeAccountInstance("newSalt"); + // assertTrue(newInstance.account.code.length == 0); + // newInstance.deployAccount(); + // assertTrue(newInstance.account.code.length > 0); // Deploy executors, validators, hooks and fallbacks and setup ModuleInit data IAccountFactory.ModuleInitData[] memory validators = new IAccountFactory.ModuleInitData[](1); validators[0] = @@ -90,8 +90,12 @@ contract ERC7579DifferentialModuleKitLibTest is BaseTest { module: address(executor), data: env == AccountType.KERNEL ? abi.encodePacked(bytes20(0)) : abi.encodePacked("") }); - IAccountFactory.ModuleInitData memory _hook = - IAccountFactory.ModuleInitData({ module: address(hook), data: bytes("0") }); + IAccountFactory.ModuleInitData memory _hook = IAccountFactory.ModuleInitData({ + module: address(hook), + data: env == AccountType.SAFE + ? abi.encode(bytes1(0), bytes4(0), abi.encode(0x123)) + : abi.encode(0x123) + }); IAccountFactory.ModuleInitData[] memory fallbacks = new IAccountFactory.ModuleInitData[](1); fallbacks[0] = IAccountFactory.ModuleInitData({ module: address(fallbackHandler), @@ -101,7 +105,9 @@ contract ERC7579DifferentialModuleKitLibTest is BaseTest { bytes20(0), abi.encode(abi.encodePacked(hex"00", "fallbackData"), abi.encodePacked("")) ) - : abi.encodePacked(bytes4(0x01234123), bytes28(0)) + : env == AccountType.SAFE || env == AccountType.NEXUS + ? abi.encode(bytes4(0x12345678), bytes1(0), abi.encode(0x123)) + : abi.encode(0x123) }); // Create account instance AccountInstance memory newInstance2 = makeAccountInstance({