From 0857b27e2f03a14ba2a38009bb440eec3d93a40f Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Tue, 4 Jun 2024 17:12:51 +0530 Subject: [PATCH 01/15] refactor: basic refactor of factory contracts + removed multiAccountFactory --- src/accounts/MultiAccountFactory.sol | 96 ---------------------- src/accounts/MultiAccountHelpers.sol | 12 --- src/accounts/erc7579/ERC7579Factory.sol | 12 +-- src/accounts/interface/IAccountFactory.sol | 17 ++++ src/accounts/kernel/KernelFactory.sol | 20 ++--- src/accounts/safe/SafeFactory.sol | 14 ++-- src/test/ModuleKitHelpers.sol | 3 +- src/test/RhinestoneModuleKit.sol | 56 +++++++++++-- src/test/utils/ERC7579Helpers.sol | 3 +- src/test/utils/SafeHelpers.sol | 4 +- 10 files changed, 90 insertions(+), 147 deletions(-) delete mode 100644 src/accounts/MultiAccountFactory.sol delete mode 100644 src/accounts/MultiAccountHelpers.sol create mode 100644 src/accounts/interface/IAccountFactory.sol diff --git a/src/accounts/MultiAccountFactory.sol b/src/accounts/MultiAccountFactory.sol deleted file mode 100644 index a62526d2..00000000 --- a/src/accounts/MultiAccountFactory.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { SafeFactory } from "./safe/SafeFactory.sol"; -import { ERC7579Factory } from "./erc7579/ERC7579Factory.sol"; -import { KernelFactory } from "./kernel/KernelFactory.sol"; -import { AccountType } from "./MultiAccountHelpers.sol"; -import { envOr } from "src/test/utils/Vm.sol"; - -enum AccountType { - DEFAULT, - SAFE, - KERNEL, - CUSTOM -} - -string constant DEFAULT = "DEFAULT"; -string constant SAFE = "SAFE"; -string constant KERNEL = "KERNEL"; - -address constant MULTI_ACCOUNT_FACTORY_ADDRESS = 0x864B12d347dafD27Ce36eD763a3D6764F182F835; - -contract MultiAccountFactory is SafeFactory, ERC7579Factory, KernelFactory { - AccountType public env; - - error InvalidAccountType(); - - function init() external { - string memory _env = envOr("ACCOUNT_TYPE", DEFAULT); - - if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(DEFAULT))) { - env = AccountType.DEFAULT; - } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(SAFE))) { - env = AccountType.SAFE; - } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(KERNEL))) { - env = AccountType.KERNEL; - } else { - revert InvalidAccountType(); - } - - initSafe(); - initERC7579(); - initKernel(); - } - - function createAccount( - bytes32 salt, - bytes calldata initCode - ) - public - returns (address account) - { - if (env == AccountType.SAFE) { - return createSafe(salt, initCode); - } else if (env == AccountType.KERNEL) { - return createKernel(initCode, salt); - } else { - return createERC7579(salt, initCode); - } - } - - function getAddress(bytes32 salt, bytes memory initCode) public view returns (address) { - if (env == AccountType.SAFE) { - return getAddressSafe(salt, initCode); - } else if (env == AccountType.KERNEL) { - return getAddressKernel(initCode, salt); - } else { - return getAddressERC7579(salt, initCode); - } - } - - function getInitData( - address validator, - bytes memory initData - ) - external - view - returns (bytes memory init) - { - if (env == AccountType.SAFE) { - init = getInitDataSafe(validator, initData); - } else if (env == AccountType.KERNEL) { - init = getInitDataKernel(validator, initData); - } else { - init = getInitDataERC7579(validator, initData); - } - } - - function setAccountType(AccountType _env) public { - env = _env; - } - - function getAccountType() public view returns (AccountType) { - return env; - } -} diff --git a/src/accounts/MultiAccountHelpers.sol b/src/accounts/MultiAccountHelpers.sol deleted file mode 100644 index e5ab670c..00000000 --- a/src/accounts/MultiAccountHelpers.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { - AccountType, - MULTI_ACCOUNT_FACTORY_ADDRESS, - MultiAccountFactory -} from "./MultiAccountFactory.sol"; - -function getAccountType() view returns (AccountType env) { - env = MultiAccountFactory(MULTI_ACCOUNT_FACTORY_ADDRESS).getAccountType(); -} diff --git a/src/accounts/erc7579/ERC7579Factory.sol b/src/accounts/erc7579/ERC7579Factory.sol index e4ffbe5a..b8b240a8 100644 --- a/src/accounts/erc7579/ERC7579Factory.sol +++ b/src/accounts/erc7579/ERC7579Factory.sol @@ -3,36 +3,38 @@ pragma solidity ^0.8.23; import "../../external/ERC7579.sol"; import { LibClone } from "solady/utils/LibClone.sol"; +import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; -abstract contract ERC7579Factory { +contract ERC7579Factory { ERC7579Account internal implementation; ERC7579Bootstrap internal bootstrapDefault; - function initERC7579() internal { + function init() public override { implementation = new ERC7579Account(); bootstrapDefault = new ERC7579Bootstrap(); } - function createERC7579(bytes32 salt, bytes memory initCode) public returns (address account) { + function createAccount(bytes32 salt, bytes memory initCode) public override returns (address account) { bytes32 _salt = _getSalt(salt, initCode); account = LibClone.cloneDeterministic(0, address(implementation), initCode, _salt); IMSA(account).initializeAccount(initCode); } - function getAddressERC7579(bytes32 salt, bytes memory initCode) public view returns (address) { + function getAddress(bytes32 salt, bytes memory initCode) public view override returns (address) { bytes32 _salt = _getSalt(salt, initCode); return LibClone.predictDeterministicAddress( address(implementation), initCode, _salt, address(this) ); } - function getInitDataERC7579( + function getInitData( address validator, bytes memory initData ) public view + override returns (bytes memory init) { ERC7579BootstrapConfig[] memory _validators = new ERC7579BootstrapConfig[](1); diff --git a/src/accounts/interface/IAccountFactory.sol b/src/accounts/interface/IAccountFactory.sol new file mode 100644 index 00000000..d7b768a4 --- /dev/null +++ b/src/accounts/interface/IAccountFactory.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +interface IAccountFactory { + function init() public; + + function createAccount(bytes32 salt, bytes memory initCode) public returns (address account); + + function getAddress(bytes32 salt, bytes memory initCode) public view returns (address); + + function getInitData( + address validator, + bytes memory initData + ) + public + returns (bytes memory init); +} diff --git a/src/accounts/kernel/KernelFactory.sol b/src/accounts/kernel/KernelFactory.sol index 92115334..7faeb61f 100644 --- a/src/accounts/kernel/KernelFactory.sol +++ b/src/accounts/kernel/KernelFactory.sol @@ -7,38 +7,32 @@ import { ENTRYPOINT_ADDR } from "../../test/predeploy/EntryPoint.sol"; import { ValidatorLib } from "kernel/utils/ValidationTypeLib.sol"; import { ValidationId } from "kernel/types/Types.sol"; import { IValidator, IHook } from "kernel/interfaces/IERC7579Modules.sol"; +import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; -abstract contract KernelFactory { +contract KernelFactory is IAccountFactory { KernelAccountFactory internal factory; Kernel internal kernalImpl; - function initKernel() internal { + function init() public override{ kernalImpl = new Kernel(IEntryPoint(ENTRYPOINT_ADDR)); factory = new KernelAccountFactory(address(kernalImpl)); } - function createKernel(bytes memory data, bytes32 salt) public returns (address account) { + function createAccount(bytes32 salt, bytes memory data) public override returns (address account) { account = factory.createAccount(data, salt); } - function getAddressKernel( - bytes memory data, - bytes32 salt - ) - public - view - virtual - returns (address) - { + function getAddress(bytes memory data, bytes32 salt) public view override returns (address) { return factory.getAddress(data, salt); } - function getInitDataKernel( + function getInitData( address validator, bytes memory initData ) public view + override returns (bytes memory init) { ValidationId rootValidator = ValidatorLib.validatorToIdentifier(IValidator(validator)); diff --git a/src/accounts/safe/SafeFactory.sol b/src/accounts/safe/SafeFactory.sol index cef8f9c6..1b75329c 100644 --- a/src/accounts/safe/SafeFactory.sol +++ b/src/accounts/safe/SafeFactory.sol @@ -11,15 +11,16 @@ import { ENTRYPOINT_ADDR } from "src/test/predeploy/EntryPoint.sol"; import { REGISTRY_ADDR } from "src/test/predeploy/Registry.sol"; import { makeAddr } from "src/test/utils/Vm.sol"; import { Solarray } from "solarray/Solarray.sol"; +import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; -abstract contract SafeFactory { +contract SafeFactory is IAccountFactory { // singletons Safe7579 internal safe7579; Safe7579Launchpad internal launchpad; Safe internal safeSingleton; SafeProxyFactory internal safeProxyFactory; - function initSafe() internal { + function init() public override{ // Set up MSA and Factory safe7579 = new Safe7579(); launchpad = new Safe7579Launchpad(ENTRYPOINT_ADDR, IERC7484(address(REGISTRY_ADDR))); @@ -27,7 +28,7 @@ abstract contract SafeFactory { safeProxyFactory = new SafeProxyFactory(); } - function createSafe(bytes32 salt, bytes calldata initCode) internal returns (address safe) { + function createAccount(bytes32 salt, bytes calldata initCode) public override returns (address safe) { Safe7579Launchpad.InitData memory initData = abi.decode(initCode, (Safe7579Launchpad.InitData)); bytes32 initHash = launchpad.hash(initData); @@ -42,13 +43,13 @@ abstract contract SafeFactory { ); } - function getAddressSafe( + function getAddress( bytes32 salt, bytes memory initCode ) public view - virtual + override returns (address) { Safe7579Launchpad.InitData memory initData = @@ -67,12 +68,13 @@ abstract contract SafeFactory { }); } - function getInitDataSafe( + function getInitData( address validator, bytes memory initData ) public view + override returns (bytes memory init) { ModuleInit[] memory validators = new ModuleInit[](1); diff --git a/src/test/ModuleKitHelpers.sol b/src/test/ModuleKitHelpers.sol index dcb5a805..5930c932 100644 --- a/src/test/ModuleKitHelpers.sol +++ b/src/test/ModuleKitHelpers.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; +import { AccountInstance, UserOpData, AccountType, getAccountType } from "./RhinestoneModuleKit.sol"; import { IEntryPoint } from "../external/ERC4337.sol"; import { IERC7579Account, @@ -16,7 +16,6 @@ import { ModuleKitCache } from "./utils/ModuleKitCache.sol"; import { writeExpectRevert, writeGasIdentifier } from "./utils/Log.sol"; import { KernelHelpers } from "./utils/KernelHelpers.sol"; import "./utils/Vm.sol"; -import { getAccountType, AccountType } from "src/accounts/MultiAccountHelpers.sol"; import { HookType } from "safe7579/DataTypes.sol"; library ModuleKitHelpers { diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index bc014b90..635cb8f9 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; +import { SafeFactory } from "src/accounts/safe/SafeFactory.sol"; +import { ERC7579Factory } from "src/accounts/erc7579/ERC7579Factory.sol"; +import { KernelFactory } from "src/accounts/kernel/KernelFactory.sol"; +import { envOr } from "src/test/utils/Vm.sol"; +import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; import { Auxiliary, AuxiliaryFactory } from "./Auxiliary.sol"; -import { - MultiAccountFactory, - AccountType, - MULTI_ACCOUNT_FACTORY_ADDRESS -} from "src/accounts/MultiAccountFactory.sol"; import { PackedUserOperation, IStakeManager } from "../external/ERC4337.sol"; import { ENTRYPOINT_ADDR } from "./predeploy/EntryPoint.sol"; import { @@ -34,25 +34,55 @@ struct UserOpData { bytes32 userOpHash; } +enum AccountType { + DEFAULT, + SAFE, + KERNEL, + CUSTOM +} + contract RhinestoneModuleKit is AuxiliaryFactory { - MultiAccountFactory public accountFactory; bool internal isInit; MockValidator public _defaultValidator; + IAccountFactory public accountFactory; + AccountType public env; /** * Initializes Auxiliary and /src/core * This function will run before any accounts can be created */ + modifier initializeModuleKit() { if (!isInit) { super.init(); isInit = true; - address _accountFactory = address(new MultiAccountFactory()); - etch(MULTI_ACCOUNT_FACTORY_ADDRESS, _accountFactory.code); - accountFactory = MultiAccountFactory(MULTI_ACCOUNT_FACTORY_ADDRESS); + string memory _env = envOr("ACCOUNT_TYPE", DEFAULT); + + initSafe(); + initERC7579(); + initKernel(); + + if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(DEFAULT))) { + env = AccountType.DEFAULT; + accountFactory = IAccountFactory(new ERC7579Factory()); + } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(SAFE))) { + env = AccountType.SAFE; + accountFactory = IAccountFactory(new SafeFactory()); + } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(KERNEL))) { + env = AccountType.KERNEL; + accountFactory = IAccountFactory(new KernelFactory()); + } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(CUSTOM))) { + env = AccountType.CUSTOM; + // TODO: What should happen in the custom case? + accountFactory = IAccountFactory(new ERC7579Factory()); + } else { + revert InvalidAccountType(); + } + accountFactory.init(); label(address(accountFactory), "AccountFactory"); + _defaultValidator = new MockValidator(); label(address(_defaultValidator), "DefaultValidator"); @@ -141,4 +171,12 @@ contract RhinestoneModuleKit is AuxiliaryFactory { ModuleKitCache.logEntrypoint(instance.account, auxiliary.entrypoint); } + + function setAccountType(AccountType _env) public { + env = _env; + } + + function getAccountType() public view returns (AccountType) { + return env; + } } diff --git a/src/test/utils/ERC7579Helpers.sol b/src/test/utils/ERC7579Helpers.sol index a2d77463..7461badb 100644 --- a/src/test/utils/ERC7579Helpers.sol +++ b/src/test/utils/ERC7579Helpers.sol @@ -10,12 +10,11 @@ import { import "erc7579/lib/ModeLib.sol"; import "erc7579/interfaces/IERC7579Module.sol"; import { PackedUserOperation, IEntryPoint } from "../../external/ERC4337.sol"; -import { AccountInstance } from "../RhinestoneModuleKit.sol"; +import { AccountInstance, AccountType, getAccountType } from "../RhinestoneModuleKit.sol"; import "./Vm.sol"; import { ValidationType } from "kernel/types/Types.sol"; import { VALIDATION_TYPE_ROOT, VALIDATION_TYPE_VALIDATOR } from "kernel/types/Constants.sol"; import { KernelHelpers } from "./KernelHelpers.sol"; -import { getAccountType, AccountType } from "src/accounts/MultiAccountHelpers.sol"; import { HookType } from "safe7579/DataTypes.sol"; import { SafeHelpers } from "./SafeHelpers.sol"; diff --git a/src/test/utils/SafeHelpers.sol b/src/test/utils/SafeHelpers.sol index 16996dd9..1c7f98e5 100644 --- a/src/test/utils/SafeHelpers.sol +++ b/src/test/utils/SafeHelpers.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.23; import { Safe7579Launchpad } from "safe7579/Safe7579Launchpad.sol"; -import { MultiAccountFactory } from "src/accounts/MultiAccountFactory.sol"; +import { SafeFactory } from "src/accounts/safe/SafeFactory.sol"; interface ISafeFactory { function getInitDataSafe( @@ -37,7 +37,7 @@ library SafeHelpers { // abi.decode(_initCode, (Safe7579Launchpad.InitData)); initData.callData = erc4337CallData; initCode = abi.encodePacked( - factory, abi.encodeCall(MultiAccountFactory.createAccount, (salt, abi.encode(initData))) + factory, abi.encodeCall(SafeFactory.createAccount, (salt, abi.encode(initData))) ); callData = abi.encodeCall(Safe7579Launchpad.setupSafe, (initData)); } From 3fcfa339085e4c1dd1ab2943435ce32aa84735eb Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Tue, 4 Jun 2024 17:57:39 +0530 Subject: [PATCH 02/15] chore: removed getAccountType, and added it to accountInstance --- src/accounts/erc7579/ERC7579Factory.sol | 4 +-- src/accounts/kernel/KernelFactory.sol | 4 +-- src/accounts/safe/SafeFactory.sol | 4 +-- src/test/ModuleKitHelpers.sol | 14 +++++----- src/test/RhinestoneModuleKit.sol | 36 ++++++++++++++++--------- src/test/utils/ERC7579Helpers.sol | 15 ++++++----- 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/accounts/erc7579/ERC7579Factory.sol b/src/accounts/erc7579/ERC7579Factory.sol index b8b240a8..ef530bb9 100644 --- a/src/accounts/erc7579/ERC7579Factory.sol +++ b/src/accounts/erc7579/ERC7579Factory.sol @@ -35,7 +35,7 @@ contract ERC7579Factory { public view override - returns (bytes memory init) + returns (bytes memory _init) { ERC7579BootstrapConfig[] memory _validators = new ERC7579BootstrapConfig[](1); _validators[0].module = validator; @@ -45,7 +45,7 @@ contract ERC7579Factory { ERC7579BootstrapConfig memory _hook; ERC7579BootstrapConfig[] memory _fallBacks = new ERC7579BootstrapConfig[](0); - init = abi.encode( + _init = abi.encode( address(bootstrapDefault), abi.encodeCall(ERC7579Bootstrap.initMSA, (_validators, _executors, _hook, _fallBacks)) ); diff --git a/src/accounts/kernel/KernelFactory.sol b/src/accounts/kernel/KernelFactory.sol index 7faeb61f..b57b862b 100644 --- a/src/accounts/kernel/KernelFactory.sol +++ b/src/accounts/kernel/KernelFactory.sol @@ -33,10 +33,10 @@ contract KernelFactory is IAccountFactory { public view override - returns (bytes memory init) + returns (bytes memory _init) { ValidationId rootValidator = ValidatorLib.validatorToIdentifier(IValidator(validator)); - init = abi.encodeCall(Kernel.initialize, (rootValidator, IHook(address(0)), initData, "")); + _init = abi.encodeCall(Kernel.initialize, (rootValidator, IHook(address(0)), initData, "")); } } diff --git a/src/accounts/safe/SafeFactory.sol b/src/accounts/safe/SafeFactory.sol index 1b75329c..e15c13de 100644 --- a/src/accounts/safe/SafeFactory.sol +++ b/src/accounts/safe/SafeFactory.sol @@ -75,7 +75,7 @@ contract SafeFactory is IAccountFactory { public view override - returns (bytes memory init) + returns (bytes memory _init) { ModuleInit[] memory validators = new ModuleInit[](1); validators[0] = ModuleInit({ module: address(validator), initData: initData }); @@ -103,6 +103,6 @@ contract SafeFactory is IAccountFactory { validators: validators, callData: "" }); - init = abi.encode(initDataSafe); + _init = abi.encode(initDataSafe); } } diff --git a/src/test/ModuleKitHelpers.sol b/src/test/ModuleKitHelpers.sol index 5930c932..6d87f138 100644 --- a/src/test/ModuleKitHelpers.sol +++ b/src/test/ModuleKitHelpers.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { AccountInstance, UserOpData, AccountType, getAccountType } from "./RhinestoneModuleKit.sol"; +import { AccountInstance, UserOpData, AccountType } from "./RhinestoneModuleKit.sol"; import { IEntryPoint } from "../external/ERC4337.sol"; import { IERC7579Account, @@ -44,7 +44,7 @@ library ModuleKitHelpers { internal returns (UserOpData memory userOpData) { - data = getInstallModuleData(moduleTypeId, module, data); + data = getInstallModuleData(instance, moduleTypeId, module, data); userOpData = instance.getInstallModuleOps( moduleTypeId, module, data, address(instance.defaultValidator) ); @@ -83,7 +83,8 @@ library ModuleKitHelpers { returns (bool) { bytes memory data; - AccountType env = getAccountType(); + AccountType env = instance.accountType; + // Each library should have isModuleInstalledData function if (env == AccountType.SAFE) { if (moduleTypeId == MODULE_TYPE_HOOK) { data = abi.encode(HookType.GLOBAL, bytes4(0x0), ""); @@ -106,7 +107,7 @@ library ModuleKitHelpers { view returns (bool) { - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.SAFE) { if (moduleTypeId == MODULE_TYPE_HOOK) { data = abi.encode(HookType.GLOBAL, bytes4(0x0), data); @@ -179,6 +180,7 @@ library ModuleKitHelpers { } function getInstallModuleData( + AccountInstance memory instance, uint256 moduleTypeId, address module, bytes memory data @@ -187,7 +189,7 @@ library ModuleKitHelpers { view returns (bytes memory) { - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.KERNEL) { if (moduleTypeId == MODULE_TYPE_EXECUTOR) { data = KernelHelpers.getDefaultInstallExecutorData(module, data); @@ -212,7 +214,7 @@ library ModuleKitHelpers { view returns (bytes memory) { - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.KERNEL) { if (moduleTypeId == MODULE_TYPE_EXECUTOR) { data = KernelHelpers.getDefaultUninstallExecutorData(module, data); diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 635cb8f9..8d5e91b0 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -20,8 +20,17 @@ import { MockValidator } from "../Mocks.sol"; import "./utils/Vm.sol"; import "./utils/ModuleKitCache.sol"; +enum AccountType { + DEFAULT, + SAFE, + KERNEL, + CUSTOM +} + struct AccountInstance { + AccountType accountType; address account; + address accountHelper; Auxiliary aux; IERC7579Validator defaultValidator; bytes32 salt; @@ -34,12 +43,11 @@ struct UserOpData { bytes32 userOpHash; } -enum AccountType { - DEFAULT, - SAFE, - KERNEL, - CUSTOM -} +string constant DEFAULT = "DEFAULT"; +string constant SAFE = "SAFE"; +string constant KERNEL = "KERNEL"; +string constant CUSTOM = "CUSTOM"; + contract RhinestoneModuleKit is AuxiliaryFactory { bool internal isInit; @@ -47,6 +55,9 @@ contract RhinestoneModuleKit is AuxiliaryFactory { IAccountFactory public accountFactory; AccountType public env; + + error InvalidAccountType(); + /** * Initializes Auxiliary and /src/core * This function will run before any accounts can be created @@ -59,10 +70,6 @@ contract RhinestoneModuleKit is AuxiliaryFactory { string memory _env = envOr("ACCOUNT_TYPE", DEFAULT); - initSafe(); - initERC7579(); - initKernel(); - if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(DEFAULT))) { env = AccountType.DEFAULT; accountFactory = IAccountFactory(new ERC7579Factory()); @@ -93,6 +100,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { } _; } + // TODO: create makeAccountInstance function which only requires account address. /** * create new AccountInstance with initCode @@ -112,7 +120,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { // Create AccountInstance struct with counterFactualAddress and initCode // The initcode will be set to 0, once the account was created by EntryPoint.sol instance = _makeAccountInstance( - salt, counterFactualAddress, initCode4337, address(_defaultValidator) + salt, env, counterFactualAddress, initCode4337, address(_defaultValidator) ); accountFactory.setAccountType(AccountType.CUSTOM); @@ -135,7 +143,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { ); label(address(account), toString(salt)); deal(account, 1 ether); - instance = _makeAccountInstance(salt, account, initCode4337, address(_defaultValidator)); + instance = _makeAccountInstance(salt,env, account, initCode4337, address(_defaultValidator)); } function makeAccountInstance( @@ -148,11 +156,12 @@ contract RhinestoneModuleKit is AuxiliaryFactory { initializeModuleKit returns (AccountInstance memory instance) { - instance = _makeAccountInstance(salt, account, initCode, defaultValidator); + instance = _makeAccountInstance(salt, env, account, initCode, defaultValidator); } function _makeAccountInstance( bytes32 salt, + AccountType accountType, address account, bytes memory initCode4337, address validator @@ -161,6 +170,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { returns (AccountInstance memory instance) { instance = AccountInstance({ + accountType: accountType, account: account, aux: auxiliary, salt: salt, diff --git a/src/test/utils/ERC7579Helpers.sol b/src/test/utils/ERC7579Helpers.sol index 7461badb..0cfd3ea2 100644 --- a/src/test/utils/ERC7579Helpers.sol +++ b/src/test/utils/ERC7579Helpers.sol @@ -10,7 +10,7 @@ import { import "erc7579/lib/ModeLib.sol"; import "erc7579/interfaces/IERC7579Module.sol"; import { PackedUserOperation, IEntryPoint } from "../../external/ERC4337.sol"; -import { AccountInstance, AccountType, getAccountType } from "../RhinestoneModuleKit.sol"; +import { AccountInstance, AccountType } from "../RhinestoneModuleKit.sol"; import "./Vm.sol"; import { ValidationType } from "kernel/types/Types.sol"; import { VALIDATION_TYPE_ROOT, VALIDATION_TYPE_VALIDATOR } from "kernel/types/Constants.sol"; @@ -92,7 +92,7 @@ library ERC7579Helpers { bytes memory callData = configModule(instance.account, moduleType, module, initData, fn); - if (getAccountType() == AccountType.SAFE) { + if (instance.accountType == AccountType.SAFE) { if (initCode.length != 0) { (initCode, callData) = SafeHelpers.getInitCallData(instance.salt, txValidator, initCode, callData); @@ -134,7 +134,7 @@ library ERC7579Helpers { initCode = instance.initCode; } - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.SAFE) { if (initCode.length != 0) { (initCode, callData) = @@ -234,6 +234,7 @@ library ERC7579Helpers { /** * get callData to uninstall validator on ERC7579 Account */ + // TODO: What is the reason we don't pass instance here? function uninstallValidator( address account, address validator, @@ -243,7 +244,7 @@ library ERC7579Helpers { view returns (bytes memory callData) { - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.DEFAULT || env == AccountType.SAFE) { // get previous validator in sentinel list address previous; @@ -297,7 +298,7 @@ library ERC7579Helpers { view returns (bytes memory callData) { - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.DEFAULT || env == AccountType.SAFE) { // get previous executor in sentinel list address previous; @@ -334,7 +335,7 @@ library ERC7579Helpers { view returns (bytes memory callData) { - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.SAFE) { callData = abi.encodeCall( IERC7579Account.installModule, @@ -470,7 +471,7 @@ library ERC7579Helpers { view returns (uint256 nonce) { - AccountType env = getAccountType(); + AccountType env = instance.accountType; if (env == AccountType.KERNEL) { ValidationType vType; if (validator == defaultValidator) { From 4a28790900510f337401f363e0e7b9d8ebb2b6e9 Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Wed, 5 Jun 2024 18:44:43 +0530 Subject: [PATCH 03/15] refactor: modify account helpers structure to allow custom contracts --- src/Helpers.sol | 2 +- src/accounts/erc7579/ERC7579Factory.sol | 21 +- src/accounts/kernel/KernelFactory.sol | 11 +- src/accounts/safe/SafeFactory.sol | 11 +- src/test/ModuleKitHelpers.sol | 78 +---- src/test/ModuleKitUserOp.sol | 2 +- src/test/RhinestoneModuleKit.sol | 84 +++-- .../{utils => helpers}/ERC7579Helpers.sol | 238 ++++++++------ src/test/helpers/IAccountHelpers.sol | 45 +++ src/test/helpers/KernelHelpers.sol | 292 ++++++++++++++++++ src/test/helpers/SafeHelpers.sol | 192 ++++++++++++ src/test/utils/KernelHelpers.sol | 146 --------- src/test/utils/SafeHelpers.sol | 44 --- 13 files changed, 781 insertions(+), 385 deletions(-) rename src/test/{utils => helpers}/ERC7579Helpers.sol (73%) create mode 100644 src/test/helpers/IAccountHelpers.sol create mode 100644 src/test/helpers/KernelHelpers.sol create mode 100644 src/test/helpers/SafeHelpers.sol delete mode 100644 src/test/utils/KernelHelpers.sol delete mode 100644 src/test/utils/SafeHelpers.sol diff --git a/src/Helpers.sol b/src/Helpers.sol index c065a048..66175594 100644 --- a/src/Helpers.sol +++ b/src/Helpers.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.23; /* solhint-disable no-unused-import */ import { ERC4337Helpers } from "./test/utils/ERC4337Helpers.sol"; -import { ERC7579Helpers } from "./test/utils/ERC7579Helpers.sol"; +import { ERC7579Helpers } from "./test/helpers/ERC7579Helpers.sol"; import { sign as vmSign } from "./test/utils/Vm.sol"; function ecdsaSign(uint256 privKey, bytes32 digest) pure returns (bytes memory signature) { diff --git a/src/accounts/erc7579/ERC7579Factory.sol b/src/accounts/erc7579/ERC7579Factory.sol index ef530bb9..f04b474e 100644 --- a/src/accounts/erc7579/ERC7579Factory.sol +++ b/src/accounts/erc7579/ERC7579Factory.sol @@ -5,7 +5,7 @@ import "../../external/ERC7579.sol"; import { LibClone } from "solady/utils/LibClone.sol"; import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; -contract ERC7579Factory { +contract ERC7579Factory is IAccountFactory{ ERC7579Account internal implementation; ERC7579Bootstrap internal bootstrapDefault; @@ -14,14 +14,29 @@ contract ERC7579Factory { bootstrapDefault = new ERC7579Bootstrap(); } - function createAccount(bytes32 salt, bytes memory initCode) public override returns (address account) { + function createAccount( + bytes32 salt, + bytes memory initCode + ) + public + override + returns (address account) + { bytes32 _salt = _getSalt(salt, initCode); account = LibClone.cloneDeterministic(0, address(implementation), initCode, _salt); IMSA(account).initializeAccount(initCode); } - function getAddress(bytes32 salt, bytes memory initCode) public view override returns (address) { + function getAddress( + bytes32 salt, + bytes memory initCode + ) + public + view + override + returns (address) + { bytes32 _salt = _getSalt(salt, initCode); return LibClone.predictDeterministicAddress( address(implementation), initCode, _salt, address(this) diff --git a/src/accounts/kernel/KernelFactory.sol b/src/accounts/kernel/KernelFactory.sol index b57b862b..d31ea9fd 100644 --- a/src/accounts/kernel/KernelFactory.sol +++ b/src/accounts/kernel/KernelFactory.sol @@ -13,12 +13,19 @@ contract KernelFactory is IAccountFactory { KernelAccountFactory internal factory; Kernel internal kernalImpl; - function init() public override{ + function init() public override { kernalImpl = new Kernel(IEntryPoint(ENTRYPOINT_ADDR)); factory = new KernelAccountFactory(address(kernalImpl)); } - function createAccount(bytes32 salt, bytes memory data) public override returns (address account) { + function createAccount( + bytes32 salt, + bytes memory data + ) + public + override + returns (address account) + { account = factory.createAccount(data, salt); } diff --git a/src/accounts/safe/SafeFactory.sol b/src/accounts/safe/SafeFactory.sol index e15c13de..04a9728f 100644 --- a/src/accounts/safe/SafeFactory.sol +++ b/src/accounts/safe/SafeFactory.sol @@ -20,7 +20,7 @@ contract SafeFactory is IAccountFactory { Safe internal safeSingleton; SafeProxyFactory internal safeProxyFactory; - function init() public override{ + function init() public override { // Set up MSA and Factory safe7579 = new Safe7579(); launchpad = new Safe7579Launchpad(ENTRYPOINT_ADDR, IERC7484(address(REGISTRY_ADDR))); @@ -28,7 +28,14 @@ contract SafeFactory is IAccountFactory { safeProxyFactory = new SafeProxyFactory(); } - function createAccount(bytes32 salt, bytes calldata initCode) public override returns (address safe) { + function createAccount( + bytes32 salt, + bytes calldata initCode + ) + public + override + returns (address safe) + { Safe7579Launchpad.InitData memory initData = abi.decode(initCode, (Safe7579Launchpad.InitData)); bytes32 initHash = launchpad.hash(initData); diff --git a/src/test/ModuleKitHelpers.sol b/src/test/ModuleKitHelpers.sol index 6d87f138..b2a89285 100644 --- a/src/test/ModuleKitHelpers.sol +++ b/src/test/ModuleKitHelpers.sol @@ -1,22 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { AccountInstance, UserOpData, AccountType } from "./RhinestoneModuleKit.sol"; +import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; import { IEntryPoint } from "../external/ERC4337.sol"; -import { - IERC7579Account, - MODULE_TYPE_EXECUTOR, - MODULE_TYPE_VALIDATOR, - MODULE_TYPE_HOOK, - MODULE_TYPE_FALLBACK -} from "../external/ERC7579.sol"; import { ModuleKitUserOp, UserOpData } from "./ModuleKitUserOp.sol"; import { ERC4337Helpers } from "./utils/ERC4337Helpers.sol"; import { ModuleKitCache } from "./utils/ModuleKitCache.sol"; import { writeExpectRevert, writeGasIdentifier } from "./utils/Log.sol"; -import { KernelHelpers } from "./utils/KernelHelpers.sol"; import "./utils/Vm.sol"; -import { HookType } from "safe7579/DataTypes.sol"; library ModuleKitHelpers { using ModuleKitUserOp for AccountInstance; @@ -63,7 +54,7 @@ library ModuleKitHelpers { internal returns (UserOpData memory userOpData) { - data = getUninstallModuleData(moduleTypeId, module, data); + data = getUninstallModuleData(instance, moduleTypeId, module, data); userOpData = instance.getUninstallModuleOps( moduleTypeId, module, data, address(instance.defaultValidator) ); @@ -82,19 +73,8 @@ library ModuleKitHelpers { view returns (bool) { - bytes memory data; - AccountType env = instance.accountType; - // Each library should have isModuleInstalledData function - if (env == AccountType.SAFE) { - if (moduleTypeId == MODULE_TYPE_HOOK) { - data = abi.encode(HookType.GLOBAL, bytes4(0x0), ""); - } - } else if (env == AccountType.KERNEL) { - if (moduleTypeId == MODULE_TYPE_HOOK) { - return true; - } - } - return isModuleInstalled(instance, moduleTypeId, module, data); + return + IAccountHelpers(instance.accountHelper).isModuleInstalled(instance, moduleTypeId, module); } function isModuleInstalled( @@ -107,17 +87,9 @@ library ModuleKitHelpers { view returns (bool) { - AccountType env = instance.accountType; - if (env == AccountType.SAFE) { - if (moduleTypeId == MODULE_TYPE_HOOK) { - data = abi.encode(HookType.GLOBAL, bytes4(0x0), data); - } - } else if (env == AccountType.KERNEL) { - if (moduleTypeId == MODULE_TYPE_HOOK) { - return true; - } - } - return IERC7579Account(instance.account).isModuleInstalled(moduleTypeId, module, data); + return IAccountHelpers(instance.accountHelper).isModuleInstalled( + instance, moduleTypeId, module, data + ); } function exec( @@ -180,7 +152,7 @@ library ModuleKitHelpers { } function getInstallModuleData( - AccountInstance memory instance, + AccountInstance memory instance, uint256 moduleTypeId, address module, bytes memory data @@ -189,23 +161,13 @@ library ModuleKitHelpers { view returns (bytes memory) { - AccountType env = instance.accountType; - if (env == AccountType.KERNEL) { - if (moduleTypeId == MODULE_TYPE_EXECUTOR) { - data = KernelHelpers.getDefaultInstallExecutorData(module, data); - } else if (moduleTypeId == MODULE_TYPE_VALIDATOR) { - data = KernelHelpers.getDefaultInstallValidatorData(module, data); - } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { - data = KernelHelpers.getDefaultInstallFallbackData(module, data); - } else { - //TODO fix hook encoding impl in kernel helpers lib - data = KernelHelpers.getDefaultInstallHookData(module, data); - } - } - return data; + return IAccountHelpers(instance.accountHelper).getInstallModuleData( + instance, moduleTypeId, module, data + ); } function getUninstallModuleData( + AccountInstance memory instance, uint256 moduleTypeId, address module, bytes memory data @@ -214,18 +176,8 @@ library ModuleKitHelpers { view returns (bytes memory) { - AccountType env = instance.accountType; - if (env == AccountType.KERNEL) { - if (moduleTypeId == MODULE_TYPE_EXECUTOR) { - data = KernelHelpers.getDefaultUninstallExecutorData(module, data); - } else if (moduleTypeId == MODULE_TYPE_VALIDATOR) { - data = KernelHelpers.getDefaultUninstallValidatorData(module, data); - } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { - data = KernelHelpers.getDefaultUninstallFallbackData(module, data); - } else { - //TODO handle for hook - } - } - return data; + return IAccountHelpers(instance.accountHelper).getUninstallModuleData( + instance, moduleTypeId, module, data + ); } } diff --git a/src/test/ModuleKitUserOp.sol b/src/test/ModuleKitUserOp.sol index 8d31d81f..b9ca7351 100644 --- a/src/test/ModuleKitUserOp.sol +++ b/src/test/ModuleKitUserOp.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.23; import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; -import { ERC7579Helpers } from "./utils/ERC7579Helpers.sol"; +import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; import { Execution } from "../external/ERC7579.sol"; library ModuleKitUserOp { diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 8d5e91b0..5b043ec9 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -6,14 +6,14 @@ import { ERC7579Factory } from "src/accounts/erc7579/ERC7579Factory.sol"; import { KernelFactory } from "src/accounts/kernel/KernelFactory.sol"; import { envOr } from "src/test/utils/Vm.sol"; import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; +import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; +import { SafeHelpers } from "./helpers/SafeHelpers.sol"; +import { KernelHelpers } from "./helpers/KernelHelpers.sol"; +import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; import { Auxiliary, AuxiliaryFactory } from "./Auxiliary.sol"; import { PackedUserOperation, IStakeManager } from "../external/ERC4337.sol"; import { ENTRYPOINT_ADDR } from "./predeploy/EntryPoint.sol"; import { - ERC7579BootstrapConfig, - IERC7579Account, - ERC7579Account, - ERC7579AccountFactory, IERC7579Validator } from "../external/ERC7579.sol"; import { MockValidator } from "../Mocks.sol"; @@ -48,59 +48,91 @@ string constant SAFE = "SAFE"; string constant KERNEL = "KERNEL"; string constant CUSTOM = "CUSTOM"; - contract RhinestoneModuleKit is AuxiliaryFactory { bool internal isInit; MockValidator public _defaultValidator; + IAccountFactory public accountFactory; + IAccountHelpers public accountHelper; + + IAccountFactory public safeFactory; + IAccountFactory public kernelFactory; + IAccountFactory public erc7579Factory; + + IAccountHelpers public safeHelper; + IAccountHelpers public kernelHelper; + IAccountHelpers public erc7579Helper; AccountType public env; error InvalidAccountType(); - + /** * Initializes Auxiliary and /src/core * This function will run before any accounts can be created */ - modifier initializeModuleKit() { if (!isInit) { super.init(); isInit = true; + safeFactory = new SafeFactory(); + kernelFactory = new KernelFactory(); + erc7579Factory = new ERC7579Factory(); + + safeHelper = new SafeHelpers(); + kernelHelper = new KernelHelpers(); + erc7579Helper = new ERC7579Helpers(); + + safeFactory.init(); + kernelFactory.init(); + erc7579Factory.init(); + + label(address(safeFactory), "SafeFactory"); + label(address(kernelFactory), "KernelFactory"); + label(address(erc7579Factory), "ERC7579Factory"); + + // Stake factory on EntryPoint + deal(address(safeFactory), 10 ether); + deal(address(kernelFactory), 10 ether); + deal(address(erc7579Factory), 10 ether); + + prank(address(safeFactory)); + IStakeManager(ENTRYPOINT_ADDR).addStake{ value: 10 ether }(100_000); + prank(address(kernelFactory)); + IStakeManager(ENTRYPOINT_ADDR).addStake{ value: 10 ether }(100_000); + prank(address(erc7579Factory)); + IStakeManager(ENTRYPOINT_ADDR).addStake{ value: 10 ether }(100_000); + string memory _env = envOr("ACCOUNT_TYPE", DEFAULT); if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(DEFAULT))) { env = AccountType.DEFAULT; - accountFactory = IAccountFactory(new ERC7579Factory()); + accountFactory = erc7579Factory; + accountHelper = erc7579Helper; } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(SAFE))) { env = AccountType.SAFE; - accountFactory = IAccountFactory(new SafeFactory()); + accountFactory = safeFactory; + accountHelper = safeHelper; } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(KERNEL))) { env = AccountType.KERNEL; - accountFactory = IAccountFactory(new KernelFactory()); + accountFactory = kernelFactory; + accountHelper = kernelHelper; } else if (keccak256(abi.encodePacked(_env)) == keccak256(abi.encodePacked(CUSTOM))) { env = AccountType.CUSTOM; - // TODO: What should happen in the custom case? - accountFactory = IAccountFactory(new ERC7579Factory()); + accountFactory = erc7579Factory; + accountHelper = erc7579Helper; } else { revert InvalidAccountType(); } - accountFactory.init(); label(address(accountFactory), "AccountFactory"); _defaultValidator = new MockValidator(); label(address(_defaultValidator), "DefaultValidator"); - - // Stake factory on EntryPoint - deal(address(accountFactory), 10 ether); - prank(address(accountFactory)); - IStakeManager(ENTRYPOINT_ADDR).addStake{ value: 10 ether }(100_000); } _; } - // TODO: create makeAccountInstance function which only requires account address. /** * create new AccountInstance with initCode @@ -110,6 +142,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { */ function makeAccountInstance( bytes32 salt, + address helper, address counterFactualAddress, bytes memory initCode4337 ) @@ -120,7 +153,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { // Create AccountInstance struct with counterFactualAddress and initCode // The initcode will be set to 0, once the account was created by EntryPoint.sol instance = _makeAccountInstance( - salt, env, counterFactualAddress, initCode4337, address(_defaultValidator) + salt, env, helper, counterFactualAddress, initCode4337, address(_defaultValidator) ); accountFactory.setAccountType(AccountType.CUSTOM); @@ -143,12 +176,15 @@ contract RhinestoneModuleKit is AuxiliaryFactory { ); label(address(account), toString(salt)); deal(account, 1 ether); - instance = _makeAccountInstance(salt,env, account, initCode4337, address(_defaultValidator)); + instance = _makeAccountInstance( + salt, env, address(accountHelper), account, initCode4337, address(_defaultValidator) + ); } function makeAccountInstance( bytes32 salt, address account, + address helper, address defaultValidator, bytes memory initCode ) @@ -156,12 +192,15 @@ contract RhinestoneModuleKit is AuxiliaryFactory { initializeModuleKit returns (AccountInstance memory instance) { - instance = _makeAccountInstance(salt, env, account, initCode, defaultValidator); + instance = _makeAccountInstance(salt, env, helper, account, initCode, defaultValidator); + + accountFactory.setAccountType(AccountType.CUSTOM); } function _makeAccountInstance( bytes32 salt, AccountType accountType, + address helper, address account, bytes memory initCode4337, address validator @@ -171,6 +210,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { { instance = AccountInstance({ accountType: accountType, + accountHelper: helper, account: account, aux: auxiliary, salt: salt, diff --git a/src/test/utils/ERC7579Helpers.sol b/src/test/helpers/ERC7579Helpers.sol similarity index 73% rename from src/test/utils/ERC7579Helpers.sol rename to src/test/helpers/ERC7579Helpers.sol index 0cfd3ea2..ba613599 100644 --- a/src/test/utils/ERC7579Helpers.sol +++ b/src/test/helpers/ERC7579Helpers.sol @@ -3,20 +3,14 @@ pragma solidity ^0.8.23; import { Execution, - IERC7579Account, - ERC7579BootstrapConfig, - IERC7579Validator + IERC7579Account } from "../../external/ERC7579.sol"; import "erc7579/lib/ModeLib.sol"; import "erc7579/interfaces/IERC7579Module.sol"; import { PackedUserOperation, IEntryPoint } from "../../external/ERC4337.sol"; -import { AccountInstance, AccountType } from "../RhinestoneModuleKit.sol"; -import "./Vm.sol"; -import { ValidationType } from "kernel/types/Types.sol"; -import { VALIDATION_TYPE_ROOT, VALIDATION_TYPE_VALIDATOR } from "kernel/types/Constants.sol"; -import { KernelHelpers } from "./KernelHelpers.sol"; -import { HookType } from "safe7579/DataTypes.sol"; -import { SafeHelpers } from "./SafeHelpers.sol"; +import { AccountInstance } from "../RhinestoneModuleKit.sol"; +import "../utils/Vm.sol"; +import { IAccountHelpers } from "./IAccountHelpers.sol"; interface IAccountModulesPaginated { function getValidatorPaginated( @@ -36,7 +30,7 @@ interface IAccountModulesPaginated { returns (address[] memory, address); } -library ERC7579Helpers { +contract ERC7579Helpers is IAccountHelpers { /** * @dev install/uninstall a module on an ERC7579 account * @@ -66,7 +60,8 @@ library ERC7579Helpers { internal returns (bytes memory) fn ) - internal + public + virtual returns (bytes memory erc7579Tx) { erc7579Tx = fn(account, moduleType, module, initData); @@ -82,7 +77,8 @@ library ERC7579Helpers { returns (bytes memory) fn, address txValidator ) - internal + public + virtual returns (PackedUserOperation memory userOp, bytes32 userOpHash) { bytes memory initCode; @@ -92,13 +88,6 @@ library ERC7579Helpers { bytes memory callData = configModule(instance.account, moduleType, module, initData, fn); - if (instance.accountType == AccountType.SAFE) { - if (initCode.length != 0) { - (initCode, callData) = - SafeHelpers.getInitCallData(instance.salt, txValidator, initCode, callData); - } - } - userOp = PackedUserOperation({ sender: instance.account, nonce: getNonce( @@ -124,8 +113,9 @@ library ERC7579Helpers { bytes memory callData, address txValidator ) - internal + public view + virtual returns (PackedUserOperation memory userOp, bytes32 userOpHash) { bytes memory initCode; @@ -134,14 +124,6 @@ library ERC7579Helpers { initCode = instance.initCode; } - AccountType env = instance.accountType; - if (env == AccountType.SAFE) { - if (initCode.length != 0) { - (initCode, callData) = - SafeHelpers.getInitCallData(instance.salt, txValidator, initCode, callData); - } - } - userOp = PackedUserOperation({ sender: instance.account, nonce: getNonce( @@ -171,8 +153,9 @@ library ERC7579Helpers { address module, bytes memory initData ) - internal + public view + virtual returns (bytes memory callData) { if (moduleType == MODULE_TYPE_VALIDATOR) { @@ -197,8 +180,9 @@ library ERC7579Helpers { address module, bytes memory initData ) - internal + public view + virtual returns (bytes memory callData) { if (moduleType == MODULE_TYPE_VALIDATOR) { @@ -222,8 +206,9 @@ library ERC7579Helpers { address validator, bytes memory initData ) - internal + public pure + virtual returns (bytes memory callData) { callData = abi.encodeCall( @@ -234,35 +219,33 @@ library ERC7579Helpers { /** * get callData to uninstall validator on ERC7579 Account */ - // TODO: What is the reason we don't pass instance here? + // TODO: What is the reason we don't pass instance here? Pass instance here function uninstallValidator( address account, address validator, bytes memory initData ) - internal + public view + virtual returns (bytes memory callData) { - AccountType env = instance.accountType; - if (env == AccountType.DEFAULT || env == AccountType.SAFE) { - // get previous validator in sentinel list - address previous; - - (address[] memory array,) = - IAccountModulesPaginated(account).getValidatorPaginated(address(0x1), 100); - - if (array.length == 1) { - previous = address(0x1); - } else if (array[0] == validator) { - previous = address(0x1); - } else { - for (uint256 i = 1; i < array.length; i++) { - if (array[i] == validator) previous = array[i - 1]; - } + // get previous validator in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getValidatorPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == validator) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == validator) previous = array[i - 1]; } - initData = abi.encode(previous, initData); } + initData = abi.encode(previous, initData); callData = abi.encodeCall( IERC7579Account.uninstallModule, (MODULE_TYPE_VALIDATOR, validator, initData) @@ -277,8 +260,9 @@ library ERC7579Helpers { address executor, bytes memory initData ) - internal + public pure + virtual returns (bytes memory callData) { callData = abi.encodeCall( @@ -289,34 +273,33 @@ library ERC7579Helpers { /** * get callData to uninstall executor on ERC7579 Account */ + // TODO: function uninstallExecutor( address account, address executor, bytes memory initData ) - internal + public view + virtual returns (bytes memory callData) { - AccountType env = instance.accountType; - if (env == AccountType.DEFAULT || env == AccountType.SAFE) { - // get previous executor in sentinel list - address previous; - - (address[] memory array,) = - IAccountModulesPaginated(account).getExecutorsPaginated(address(0x1), 100); - - if (array.length == 1) { - previous = address(0x1); - } else if (array[0] == executor) { - previous = address(0x1); - } else { - for (uint256 i = 1; i < array.length; i++) { - if (array[i] == executor) previous = array[i - 1]; - } + // get previous executor in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getExecutorsPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == executor) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == executor) previous = array[i - 1]; } - initData = abi.encode(previous, initData); } + initData = abi.encode(previous, initData); callData = abi.encodeCall( IERC7579Account.uninstallModule, (MODULE_TYPE_EXECUTOR, executor, initData) @@ -331,20 +314,12 @@ library ERC7579Helpers { address hook, bytes memory initData ) - internal + public view + virtual returns (bytes memory callData) { - AccountType env = instance.accountType; - if (env == AccountType.SAFE) { - callData = abi.encodeCall( - IERC7579Account.installModule, - (MODULE_TYPE_HOOK, hook, abi.encode(HookType.GLOBAL, bytes4(0x0), initData)) - ); - } else { - callData = - abi.encodeCall(IERC7579Account.installModule, (MODULE_TYPE_HOOK, hook, initData)); - } + callData = abi.encodeCall(IERC7579Account.installModule, (MODULE_TYPE_HOOK, hook, initData)); } /** @@ -355,8 +330,9 @@ library ERC7579Helpers { address hook, bytes memory initData ) - internal + public pure + virtual returns (bytes memory callData) { callData = abi.encodeCall( @@ -372,8 +348,9 @@ library ERC7579Helpers { address fallbackHandler, bytes memory initData ) - internal + public pure + virtual returns (bytes memory callData) { callData = abi.encodeCall( @@ -389,8 +366,9 @@ library ERC7579Helpers { address fallbackHandler, bytes memory initData ) - internal + public pure + virtual returns (bytes memory callData) { fallbackHandler = fallbackHandler; //avoid solhint-no-unused-vars @@ -410,8 +388,9 @@ library ERC7579Helpers { uint256 value, bytes memory callData ) - internal + public pure + virtual returns (bytes memory erc7579Tx) { ModeCode mode = ModeLib.encode({ @@ -428,7 +407,12 @@ library ERC7579Helpers { * Encode a batched ERC7579 Execution Transaction * @param executions ERC7579 batched executions */ - function encode(Execution[] memory executions) internal pure returns (bytes memory erc7579Tx) { + function encode(Execution[] memory executions) + public + pure + virtual + returns (bytes memory erc7579Tx) + { ModeCode mode = ModeLib.encode({ callType: CALLTYPE_BATCH, execType: EXECTYPE_DEFAULT, @@ -446,8 +430,9 @@ library ERC7579Helpers { uint256[] memory values, bytes[] memory callDatas ) - internal + public pure + virtual returns (Execution[] memory executions) { executions = new Execution[](targets.length); @@ -467,22 +452,73 @@ library ERC7579Helpers { address validator, address defaultValidator ) - internal + public view + virtual returns (uint256 nonce) { - AccountType env = instance.accountType; - if (env == AccountType.KERNEL) { - ValidationType vType; - if (validator == defaultValidator) { - vType = VALIDATION_TYPE_ROOT; - } else { - vType = VALIDATION_TYPE_VALIDATOR; - } - nonce = KernelHelpers.encodeNonce(vType, false, account, defaultValidator); - } else { - uint192 key = uint192(bytes24(bytes20(address(validator)))); - nonce = entrypoint.getNonce(address(account), key); - } + uint192 key = uint192(bytes24(bytes20(address(validator)))); + nonce = entrypoint.getNonce(address(account), key); + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module + ) + public + view + virtual + override + returns (bool) + { + bytes memory data; + + return isModuleInstalled(instance, moduleTypeId, module, data); + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bool) + { + return IERC7579Account(instance.account).isModuleInstalled(moduleTypeId, module, data); + } + + function getInstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bytes memory) + { + return data; + } + + function getUninstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bytes memory) + { + return data; } } diff --git a/src/test/helpers/IAccountHelpers.sol b/src/test/helpers/IAccountHelpers.sol new file mode 100644 index 00000000..82beb7e5 --- /dev/null +++ b/src/test/helpers/IAccountHelpers.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { AccountInstance } from "../RhinestoneModuleKit.sol"; + +interface IAccountHelpers { + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module + ) + external + view + returns (bool); + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + external + view + returns (bool); + + function getInstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + external + view + returns (bytes memory); + + function getUninstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + external + view + returns (bytes memory); +} diff --git a/src/test/helpers/KernelHelpers.sol b/src/test/helpers/KernelHelpers.sol new file mode 100644 index 00000000..da8d4fe8 --- /dev/null +++ b/src/test/helpers/KernelHelpers.sol @@ -0,0 +1,292 @@ +pragma solidity ^0.8.23; + +import { AccountInstance } from "../RhinestoneModuleKit.sol"; +import { ValidatorLib } from "kernel/utils/ValidationTypeLib.sol"; +import { ValidationType, ValidationMode } from "kernel/types/Types.sol"; +import "kernel/types/Constants.sol"; +import { ENTRYPOINT_ADDR } from "../predeploy/EntryPoint.sol"; +import { IEntryPoint } from "kernel/interfaces/IEntryPoint.sol"; +import { IERC7579Account } from "erc7579/interfaces/IERC7579Account.sol"; +import { MockFallback } from "kernel/mock/MockFallback.sol"; +import { ERC7579Helpers } from "./ERC7579Helpers.sol"; + +contract KernelHelpers is ERC7579Helpers { + function getNonce( + address account, + IEntryPoint entrypoint, + address validator, + address defaultValidator + ) + public + view + virtual + returns (uint256 nonce) + { + ValidationType vType; + if (validator == defaultValidator) { + vType = VALIDATION_TYPE_ROOT; + } else { + vType = VALIDATION_TYPE_VALIDATOR; + } + nonce = encodeNonce(vType, false, account, defaultValidator); + } + + /** + * get callData to uninstall executor on ERC7579 Account + */ + function uninstallExecutor( + address account, + address executor, + bytes memory initData + ) + public + view + virtual + override + returns (bytes memory callData) + { + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_EXECUTOR, executor, initData) + ); + } + + function uninstallValidator( + address account, + address validator, + bytes memory initData + ) + public + view + virtual + override + returns (bytes memory callData) + { + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_VALIDATOR, validator, initData) + ); + } + + function encodeNonce( + ValidationType vType, + bool enable, + address account, + address validator + ) + public + view + returns (uint256 nonce) + { + uint192 nonceKey = 0; + if (vType == VALIDATION_TYPE_ROOT) { + nonceKey = 0; + } else if (vType == VALIDATION_TYPE_VALIDATOR) { + ValidationMode mode = VALIDATION_MODE_DEFAULT; + if (enable) { + mode = VALIDATION_MODE_ENABLE; + } + nonceKey = ValidatorLib.encodeAsNonceKey( + ValidationMode.unwrap(mode), + ValidationType.unwrap(vType), + bytes20(validator), + 0 // parallel key + ); + } else { + revert("Invalid validation type"); + } + return IEntryPoint(ENTRYPOINT_ADDR).getNonce(account, nonceKey); + } + + /** + * @dev + * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L311-L321 + */ + function getDefaultInstallValidatorData( + address module, + bytes memory initData + ) + public + pure + returns (bytes memory data) + { + data = abi.encodePacked(address(0), abi.encode(initData, abi.encodePacked(""))); + } + + /** + * @dev + * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L324-L334 + */ + function getDefaultInstallExecutorData( + address module, + bytes memory initData + ) + public + pure + returns (bytes memory data) + { + data = abi.encodePacked(address(0), abi.encode(initData, abi.encodePacked(""))); + } + + /** + * @dev + * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L336-L345 + */ + function getDefaultInstallFallbackData( + address module, + bytes memory initData + ) + public + pure + returns (bytes memory data) + { + data = abi.encodePacked( + MockFallback.fallbackFunction.selector, + address(0), + abi.encode(initData, abi.encodePacked("")) + ); + } + + /** + * @dev + * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L311-L321 + */ + function getDefaultInstallHookData( + address module, + bytes memory initData + ) + public + pure + returns (bytes memory data) + { + data = initData; + } + + /** + * @dev + * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L397-L398 + */ + function getDefaultUninstallValidatorData( + address module, + bytes memory deinitData + ) + public + pure + returns (bytes memory data) + { } + + /** + * @dev + * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L400 + */ + function getDefaultUninstallExecutorData( + address module, + bytes memory deinitData + ) + public + pure + returns (bytes memory data) + { } + + /** + * @dev + * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L402-L403 + */ + function getDefaultUninstallFallbackData( + address module, + bytes memory deinitData + ) + public + pure + returns (bytes memory data) + { + data = abi.encodePacked(MockFallback.fallbackFunction.selector, deinitData); + } + + function getInstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bytes memory) + { + if (moduleTypeId == MODULE_TYPE_EXECUTOR) { + data = KernelHelpers.getDefaultInstallExecutorData(module, data); + } else if (moduleTypeId == MODULE_TYPE_VALIDATOR) { + data = KernelHelpers.getDefaultInstallValidatorData(module, data); + } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { + data = KernelHelpers.getDefaultInstallFallbackData(module, data); + } else { + //TODO fix hook encoding impl in kernel helpers lib + data = KernelHelpers.getDefaultInstallHookData(module, data); + } + + return data; + } + + function getUninstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bytes memory) + { + if (moduleTypeId == MODULE_TYPE_EXECUTOR) { + data = KernelHelpers.getDefaultUninstallExecutorData(module, data); + } else if (moduleTypeId == MODULE_TYPE_VALIDATOR) { + data = KernelHelpers.getDefaultUninstallValidatorData(module, data); + } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { + data = KernelHelpers.getDefaultUninstallFallbackData(module, data); + } else { + //TODO handle for hook + } + return data; + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module + ) + public + view + virtual + override + returns (bool) + { + bytes memory data; + + if (moduleTypeId == MODULE_TYPE_HOOK) { + return true; + } + + return isModuleInstalled(instance, moduleTypeId, module, data); + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bool) + { + if (moduleTypeId == MODULE_TYPE_HOOK) { + return true; + } + + return IERC7579Account(instance.account).isModuleInstalled(moduleTypeId, module, data); + } +} diff --git a/src/test/helpers/SafeHelpers.sol b/src/test/helpers/SafeHelpers.sol new file mode 100644 index 00000000..4ca91a9a --- /dev/null +++ b/src/test/helpers/SafeHelpers.sol @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { ERC7579Helpers } from "./ERC7579Helpers.sol"; +import { AccountInstance } from "../RhinestoneModuleKit.sol"; +import { Safe7579Launchpad } from "safe7579/Safe7579Launchpad.sol"; +import { SafeFactory } from "src/accounts/safe/SafeFactory.sol"; + + +import { PackedUserOperation } from "../../external/ERC4337.sol"; +import { + IERC7579Account, + MODULE_TYPE_HOOK +} from "../../external/ERC7579.sol"; +import { HookType } from "safe7579/DataTypes.sol"; + +contract SafeHelpers is ERC7579Helpers { + /** + * get callData to install hook on ERC7579 Account + */ + function installHook( + address, /* account */ + address hook, + bytes memory initData + ) + public + view + virtual + override + returns (bytes memory callData) + { + callData = abi.encodeCall( + IERC7579Account.installModule, + (MODULE_TYPE_HOOK, hook, abi.encode(HookType.GLOBAL, bytes4(0x0), initData)) + ); + } + + function execUserOp( + AccountInstance memory instance, + bytes memory callData, + address txValidator + ) + public + view + virtual + override + returns (PackedUserOperation memory userOp, bytes32 userOpHash) + { + bytes memory initCode; + bool notDeployedYet = instance.account.code.length == 0; + if (notDeployedYet) { + initCode = instance.initCode; + } + + if (initCode.length != 0) { + (initCode, callData) = getInitCallData(instance.salt, txValidator, initCode, callData); + } + + userOp = PackedUserOperation({ + sender: instance.account, + nonce: getNonce( + instance.account, + instance.aux.entrypoint, + txValidator, + address(instance.defaultValidator) + ), + initCode: initCode, + callData: callData, + accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), + preVerificationGas: 2e6, + gasFees: bytes32(abi.encodePacked(uint128(1), uint128(1))), + paymasterAndData: bytes(""), + signature: bytes("") + }); + + userOpHash = instance.aux.entrypoint.getUserOpHash(userOp); + } + + function configModuleUserOp( + AccountInstance memory instance, + uint256 moduleType, + address module, + bytes memory initData, + function(address, uint256, address, bytes memory) + internal + returns (bytes memory) fn, + address txValidator + ) + public + virtual + override + returns (PackedUserOperation memory userOp, bytes32 userOpHash) + { + bytes memory initCode; + if (instance.account.code.length == 0) { + initCode = instance.initCode; + } + + bytes memory callData = configModule(instance.account, moduleType, module, initData, fn); + + if (initCode.length != 0) { + (initCode, callData) = getInitCallData(instance.salt, txValidator, initCode, callData); + } + + userOp = PackedUserOperation({ + sender: instance.account, + nonce: getNonce( + instance.account, + instance.aux.entrypoint, + txValidator, + address(instance.defaultValidator) + ), + initCode: initCode, + callData: callData, + accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), + preVerificationGas: 2e6, + gasFees: bytes32(abi.encodePacked(uint128(1), uint128(1))), + paymasterAndData: bytes(""), + signature: bytes("") + }); + + userOpHash = instance.aux.entrypoint.getUserOpHash(userOp); + } + + function getInitCallData( + bytes32 salt, + address txValidator, + bytes memory originalInitCode, + bytes memory erc4337CallData + ) + public + view + returns (bytes memory initCode, bytes memory callData) + { + // TODO: refactor this to decode the initcode + address factory; + assembly { + factory := mload(add(originalInitCode, 20)) + } + Safe7579Launchpad.InitData memory initData = abi.decode( + // TODO: What is this decode used for, is it fine if the name is changed? + IAccountFactory(factory).getInitData(txValidator, ""), + (Safe7579Launchpad.InitData) + ); + // Safe7579Launchpad.InitData memory initData = + // abi.decode(_initCode, (Safe7579Launchpad.InitData)); + initData.callData = erc4337CallData; + initCode = abi.encodePacked( + factory, abi.encodeCall(SafeFactory.createAccount, (salt, abi.encode(initData))) + ); + callData = abi.encodeCall(Safe7579Launchpad.setupSafe, (initData)); + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module + ) + public + view + virtual + override + returns (bool) + { + bytes memory data; + + if (moduleTypeId == MODULE_TYPE_HOOK) { + data = abi.encode(HookType.GLOBAL, bytes4(0x0), ""); + } + + return isModuleInstalled(instance, moduleTypeId, module, data); + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bool) + { + if (moduleTypeId == MODULE_TYPE_HOOK) { + data = abi.encode(HookType.GLOBAL, bytes4(0x0), data); + } + + return IERC7579Account(instance.account).isModuleInstalled(moduleTypeId, module, data); + } +} diff --git a/src/test/utils/KernelHelpers.sol b/src/test/utils/KernelHelpers.sol deleted file mode 100644 index 61cda673..00000000 --- a/src/test/utils/KernelHelpers.sol +++ /dev/null @@ -1,146 +0,0 @@ -pragma solidity ^0.8.23; - -import { ValidatorLib } from "kernel/utils/ValidationTypeLib.sol"; -import { ValidationType, ValidationMode } from "kernel/types/Types.sol"; -import "kernel/types/Constants.sol"; -import { ENTRYPOINT_ADDR } from "../predeploy/EntryPoint.sol"; -import { IEntryPoint } from "kernel/interfaces/IEntryPoint.sol"; -import { IERC7579Account } from "erc7579/interfaces/IERC7579Account.sol"; -import { MockFallback } from "kernel/mock/MockFallback.sol"; - -library KernelHelpers { - function encodeNonce( - ValidationType vType, - bool enable, - address account, - address validator - ) - internal - view - returns (uint256 nonce) - { - uint192 nonceKey = 0; - if (vType == VALIDATION_TYPE_ROOT) { - nonceKey = 0; - } else if (vType == VALIDATION_TYPE_VALIDATOR) { - ValidationMode mode = VALIDATION_MODE_DEFAULT; - if (enable) { - mode = VALIDATION_MODE_ENABLE; - } - nonceKey = ValidatorLib.encodeAsNonceKey( - ValidationMode.unwrap(mode), - ValidationType.unwrap(vType), - bytes20(validator), - 0 // parallel key - ); - } else { - revert("Invalid validation type"); - } - return IEntryPoint(ENTRYPOINT_ADDR).getNonce(account, nonceKey); - } - - /** - * @dev - * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L311-L321 - */ - function getDefaultInstallValidatorData( - address module, - bytes memory initData - ) - internal - pure - returns (bytes memory data) - { - data = abi.encodePacked(address(0), abi.encode(initData, abi.encodePacked(""))); - } - - /** - * @dev - * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L324-L334 - */ - function getDefaultInstallExecutorData( - address module, - bytes memory initData - ) - internal - pure - returns (bytes memory data) - { - data = abi.encodePacked(address(0), abi.encode(initData, abi.encodePacked(""))); - } - - /** - * @dev - * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L336-L345 - */ - function getDefaultInstallFallbackData( - address module, - bytes memory initData - ) - internal - pure - returns (bytes memory data) - { - data = abi.encodePacked( - MockFallback.fallbackFunction.selector, - address(0), - abi.encode(initData, abi.encodePacked("")) - ); - } - - /** - * @dev - * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L311-L321 - */ - function getDefaultInstallHookData( - address module, - bytes memory initData - ) - internal - pure - returns (bytes memory data) - { - data = initData; - } - - /** - * @dev - * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L397-L398 - */ - function getDefaultUninstallValidatorData( - address module, - bytes memory deinitData - ) - internal - pure - returns (bytes memory data) - { } - - /** - * @dev - * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L400 - */ - function getDefaultUninstallExecutorData( - address module, - bytes memory deinitData - ) - internal - pure - returns (bytes memory data) - { } - - /** - * @dev - * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L402-L403 - */ - function getDefaultUninstallFallbackData( - address module, - bytes memory deinitData - ) - internal - pure - returns (bytes memory data) - { - data = abi.encodePacked(MockFallback.fallbackFunction.selector, deinitData); - } -} diff --git a/src/test/utils/SafeHelpers.sol b/src/test/utils/SafeHelpers.sol deleted file mode 100644 index 1c7f98e5..00000000 --- a/src/test/utils/SafeHelpers.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { Safe7579Launchpad } from "safe7579/Safe7579Launchpad.sol"; -import { SafeFactory } from "src/accounts/safe/SafeFactory.sol"; - -interface ISafeFactory { - function getInitDataSafe( - address validator, - bytes memory initData - ) - external - view - returns (bytes memory init); -} - -library SafeHelpers { - function getInitCallData( - bytes32 salt, - address txValidator, - bytes memory originalInitCode, - bytes memory erc4337CallData - ) - internal - view - returns (bytes memory initCode, bytes memory callData) - { - // TODO: refactor this to decode the initcode - address factory; - assembly { - factory := mload(add(originalInitCode, 20)) - } - Safe7579Launchpad.InitData memory initData = abi.decode( - ISafeFactory(factory).getInitDataSafe(txValidator, ""), (Safe7579Launchpad.InitData) - ); - // Safe7579Launchpad.InitData memory initData = - // abi.decode(_initCode, (Safe7579Launchpad.InitData)); - initData.callData = erc4337CallData; - initCode = abi.encodePacked( - factory, abi.encodeCall(SafeFactory.createAccount, (salt, abi.encode(initData))) - ); - callData = abi.encodeCall(Safe7579Launchpad.setupSafe, (initData)); - } -} From 50d0fd5da0cadfd9a22cbc170f5fa3b8f8c1a370 Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Wed, 5 Jun 2024 18:24:36 +0100 Subject: [PATCH 04/15] fix: build issues --- src/accounts/interface/IAccountFactory.sol | 13 +- src/accounts/kernel/KernelFactory.sol | 2 +- src/accounts/safe/SafeFactory.sol | 2 +- src/test/ModuleKitHelpers.sol | 6 +- src/test/RhinestoneModuleKit.sol | 12 +- src/test/helpers/ERC7579Helpers.sol | 30 +- src/test/helpers/HelperBase.sol | 521 +++++++++++++++++++++ src/test/helpers/KernelHelpers.sol | 4 +- src/test/helpers/SafeHelpers.sol | 14 +- 9 files changed, 570 insertions(+), 34 deletions(-) create mode 100644 src/test/helpers/HelperBase.sol diff --git a/src/accounts/interface/IAccountFactory.sol b/src/accounts/interface/IAccountFactory.sol index d7b768a4..395982ae 100644 --- a/src/accounts/interface/IAccountFactory.sol +++ b/src/accounts/interface/IAccountFactory.sol @@ -2,16 +2,21 @@ pragma solidity ^0.8.23; interface IAccountFactory { - function init() public; + function init() external; - function createAccount(bytes32 salt, bytes memory initCode) public returns (address account); + function createAccount( + bytes32 salt, + bytes memory initCode + ) + external + returns (address account); - function getAddress(bytes32 salt, bytes memory initCode) public view returns (address); + function getAddress(bytes32 salt, bytes memory initCode) external view returns (address); function getInitData( address validator, bytes memory initData ) - public + external returns (bytes memory init); } diff --git a/src/accounts/kernel/KernelFactory.sol b/src/accounts/kernel/KernelFactory.sol index d31ea9fd..59e239ad 100644 --- a/src/accounts/kernel/KernelFactory.sol +++ b/src/accounts/kernel/KernelFactory.sol @@ -29,7 +29,7 @@ contract KernelFactory is IAccountFactory { account = factory.createAccount(data, salt); } - function getAddress(bytes memory data, bytes32 salt) public view override returns (address) { + function getAddress(bytes32 salt, bytes memory data) public view override returns (address) { return factory.getAddress(data, salt); } diff --git a/src/accounts/safe/SafeFactory.sol b/src/accounts/safe/SafeFactory.sol index 04a9728f..23065609 100644 --- a/src/accounts/safe/SafeFactory.sol +++ b/src/accounts/safe/SafeFactory.sol @@ -30,7 +30,7 @@ contract SafeFactory is IAccountFactory { function createAccount( bytes32 salt, - bytes calldata initCode + bytes memory initCode ) public override diff --git a/src/test/ModuleKitHelpers.sol b/src/test/ModuleKitHelpers.sol index b2a89285..630438e4 100644 --- a/src/test/ModuleKitHelpers.sol +++ b/src/test/ModuleKitHelpers.sol @@ -8,6 +8,7 @@ import { ERC4337Helpers } from "./utils/ERC4337Helpers.sol"; import { ModuleKitCache } from "./utils/ModuleKitCache.sol"; import { writeExpectRevert, writeGasIdentifier } from "./utils/Log.sol"; import "./utils/Vm.sol"; +import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; library ModuleKitHelpers { using ModuleKitUserOp for AccountInstance; @@ -73,8 +74,9 @@ library ModuleKitHelpers { view returns (bool) { - return - IAccountHelpers(instance.accountHelper).isModuleInstalled(instance, moduleTypeId, module); + return IAccountHelpers(instance.accountHelper).isModuleInstalled( + instance, moduleTypeId, module + ); } function isModuleInstalled( diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 5b043ec9..f3f33be6 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -7,15 +7,13 @@ import { KernelFactory } from "src/accounts/kernel/KernelFactory.sol"; import { envOr } from "src/test/utils/Vm.sol"; import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; +import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; import { SafeHelpers } from "./helpers/SafeHelpers.sol"; import { KernelHelpers } from "./helpers/KernelHelpers.sol"; -import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; import { Auxiliary, AuxiliaryFactory } from "./Auxiliary.sol"; import { PackedUserOperation, IStakeManager } from "../external/ERC4337.sol"; import { ENTRYPOINT_ADDR } from "./predeploy/EntryPoint.sol"; -import { - IERC7579Validator -} from "../external/ERC7579.sol"; +import { IERC7579Validator } from "../external/ERC7579.sol"; import { MockValidator } from "../Mocks.sol"; import "./utils/Vm.sol"; import "./utils/ModuleKitCache.sol"; @@ -80,9 +78,9 @@ contract RhinestoneModuleKit is AuxiliaryFactory { kernelFactory = new KernelFactory(); erc7579Factory = new ERC7579Factory(); + erc7579Helper = new ERC7579Helpers(); safeHelper = new SafeHelpers(); kernelHelper = new KernelHelpers(); - erc7579Helper = new ERC7579Helpers(); safeFactory.init(); kernelFactory.init(); @@ -156,7 +154,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { salt, env, helper, counterFactualAddress, initCode4337, address(_defaultValidator) ); - accountFactory.setAccountType(AccountType.CUSTOM); + setAccountType(AccountType.CUSTOM); } /** @@ -194,7 +192,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { { instance = _makeAccountInstance(salt, env, helper, account, initCode, defaultValidator); - accountFactory.setAccountType(AccountType.CUSTOM); + setAccountType(AccountType.CUSTOM); } function _makeAccountInstance( diff --git a/src/test/helpers/ERC7579Helpers.sol b/src/test/helpers/ERC7579Helpers.sol index ba613599..d59c803e 100644 --- a/src/test/helpers/ERC7579Helpers.sol +++ b/src/test/helpers/ERC7579Helpers.sol @@ -1,16 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { - Execution, - IERC7579Account -} from "../../external/ERC7579.sol"; +import { Execution, IERC7579Account } from "../../external/ERC7579.sol"; import "erc7579/lib/ModeLib.sol"; import "erc7579/interfaces/IERC7579Module.sol"; import { PackedUserOperation, IEntryPoint } from "../../external/ERC4337.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import "../utils/Vm.sol"; -import { IAccountHelpers } from "./IAccountHelpers.sol"; +import { HelperBase } from "./HelperBase.sol"; interface IAccountModulesPaginated { function getValidatorPaginated( @@ -30,7 +27,7 @@ interface IAccountModulesPaginated { returns (address[] memory, address); } -contract ERC7579Helpers is IAccountHelpers { +contract ERC7579Helpers is HelperBase { /** * @dev install/uninstall a module on an ERC7579 account * @@ -57,11 +54,12 @@ contract ERC7579Helpers is IAccountHelpers { address module, bytes memory initData, function(address, uint256, address, bytes memory) - internal + external returns (bytes memory) fn ) public virtual + override returns (bytes memory erc7579Tx) { erc7579Tx = fn(account, moduleType, module, initData); @@ -73,12 +71,13 @@ contract ERC7579Helpers is IAccountHelpers { address module, bytes memory initData, function(address, uint256, address, bytes memory) - internal + external returns (bytes memory) fn, address txValidator ) public virtual + override returns (PackedUserOperation memory userOp, bytes32 userOpHash) { bytes memory initCode; @@ -116,6 +115,7 @@ contract ERC7579Helpers is IAccountHelpers { public view virtual + override returns (PackedUserOperation memory userOp, bytes32 userOpHash) { bytes memory initCode; @@ -156,6 +156,7 @@ contract ERC7579Helpers is IAccountHelpers { public view virtual + override returns (bytes memory callData) { if (moduleType == MODULE_TYPE_VALIDATOR) { @@ -183,6 +184,7 @@ contract ERC7579Helpers is IAccountHelpers { public view virtual + override returns (bytes memory callData) { if (moduleType == MODULE_TYPE_VALIDATOR) { @@ -209,6 +211,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (bytes memory callData) { callData = abi.encodeCall( @@ -228,6 +231,7 @@ contract ERC7579Helpers is IAccountHelpers { public view virtual + override returns (bytes memory callData) { // get previous validator in sentinel list @@ -263,6 +267,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (bytes memory callData) { callData = abi.encodeCall( @@ -282,6 +287,7 @@ contract ERC7579Helpers is IAccountHelpers { public view virtual + override returns (bytes memory callData) { // get previous executor in sentinel list @@ -317,6 +323,7 @@ contract ERC7579Helpers is IAccountHelpers { public view virtual + override returns (bytes memory callData) { callData = abi.encodeCall(IERC7579Account.installModule, (MODULE_TYPE_HOOK, hook, initData)); @@ -333,6 +340,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (bytes memory callData) { callData = abi.encodeCall( @@ -351,6 +359,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (bytes memory callData) { callData = abi.encodeCall( @@ -369,6 +378,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (bytes memory callData) { fallbackHandler = fallbackHandler; //avoid solhint-no-unused-vars @@ -391,6 +401,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (bytes memory erc7579Tx) { ModeCode mode = ModeLib.encode({ @@ -411,6 +422,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (bytes memory erc7579Tx) { ModeCode mode = ModeLib.encode({ @@ -433,6 +445,7 @@ contract ERC7579Helpers is IAccountHelpers { public pure virtual + override returns (Execution[] memory executions) { executions = new Execution[](targets.length); @@ -455,6 +468,7 @@ contract ERC7579Helpers is IAccountHelpers { public view virtual + override returns (uint256 nonce) { uint192 key = uint192(bytes24(bytes20(address(validator)))); diff --git a/src/test/helpers/HelperBase.sol b/src/test/helpers/HelperBase.sol new file mode 100644 index 00000000..cfb3d76c --- /dev/null +++ b/src/test/helpers/HelperBase.sol @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { Execution, IERC7579Account } from "../../external/ERC7579.sol"; +import "erc7579/lib/ModeLib.sol"; +import "erc7579/interfaces/IERC7579Module.sol"; +import { PackedUserOperation, IEntryPoint } from "../../external/ERC4337.sol"; +import { AccountInstance } from "../RhinestoneModuleKit.sol"; +import "../utils/Vm.sol"; +import { IAccountHelpers } from "./IAccountHelpers.sol"; + +interface IAccountModulesPaginated { + function getValidatorPaginated( + address, + uint256 + ) + external + view + returns (address[] memory, address); + + function getExecutorsPaginated( + address, + uint256 + ) + external + view + returns (address[] memory, address); +} + +contract HelperBase is IAccountHelpers { + /** + * @dev install/uninstall a module on an ERC7579 account + * + * @param account IERC7579Account address + * @param module IERC7579Module address + * @param initData bytes encoded initialization data. + * initData will be passed to fn + * @param fn function parameter that will yield the initData + * + * @return erc7579Tx bytes encoded single ERC7579Execution + * + * + * + * can be used like so: + * bytes memory installCallData = configModule( + * validator, + * initData, + * ERC7579Helpers.installValidator); + * + */ + function configModule( + address account, + uint256 moduleType, + address module, + bytes memory initData, + function(address, uint256, address, bytes memory) + external + returns (bytes memory) fn + ) + public + virtual + returns (bytes memory erc7579Tx) + { + erc7579Tx = fn(account, moduleType, module, initData); + } + + function configModuleUserOp( + AccountInstance memory instance, + uint256 moduleType, + address module, + bytes memory initData, + function(address, uint256, address, bytes memory) + external + returns (bytes memory) fn, + address txValidator + ) + public + virtual + returns (PackedUserOperation memory userOp, bytes32 userOpHash) + { + bytes memory initCode; + if (instance.account.code.length == 0) { + initCode = instance.initCode; + } + + bytes memory callData = configModule(instance.account, moduleType, module, initData, fn); + + userOp = PackedUserOperation({ + sender: instance.account, + nonce: getNonce( + instance.account, + instance.aux.entrypoint, + txValidator, + address(instance.defaultValidator) + ), + initCode: initCode, + callData: callData, + accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), + preVerificationGas: 2e6, + gasFees: bytes32(abi.encodePacked(uint128(1), uint128(1))), + paymasterAndData: bytes(""), + signature: bytes("") + }); + + userOpHash = instance.aux.entrypoint.getUserOpHash(userOp); + } + + function execUserOp( + AccountInstance memory instance, + bytes memory callData, + address txValidator + ) + public + view + virtual + returns (PackedUserOperation memory userOp, bytes32 userOpHash) + { + bytes memory initCode; + bool notDeployedYet = instance.account.code.length == 0; + if (notDeployedYet) { + initCode = instance.initCode; + } + + userOp = PackedUserOperation({ + sender: instance.account, + nonce: getNonce( + instance.account, + instance.aux.entrypoint, + txValidator, + address(instance.defaultValidator) + ), + initCode: initCode, + callData: callData, + accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), + preVerificationGas: 2e6, + gasFees: bytes32(abi.encodePacked(uint128(1), uint128(1))), + paymasterAndData: bytes(""), + signature: bytes("") + }); + + userOpHash = instance.aux.entrypoint.getUserOpHash(userOp); + } + + /** + * Router function to install a module on an ERC7579 account + */ + function installModule( + address account, + uint256 moduleType, + address module, + bytes memory initData + ) + public + view + virtual + returns (bytes memory callData) + { + if (moduleType == MODULE_TYPE_VALIDATOR) { + return installValidator(account, module, initData); + } else if (moduleType == MODULE_TYPE_EXECUTOR) { + return installExecutor(account, module, initData); + } else if (moduleType == MODULE_TYPE_HOOK) { + return installHook(account, module, initData); + } else if (moduleType == MODULE_TYPE_FALLBACK) { + return installFallback(account, module, initData); + } else { + revert("Invalid module type"); + } + } + + /** + * Router function to uninstall a module on an ERC7579 account + */ + function uninstallModule( + address account, + uint256 moduleType, + address module, + bytes memory initData + ) + public + view + virtual + returns (bytes memory callData) + { + if (moduleType == MODULE_TYPE_VALIDATOR) { + return uninstallValidator(account, module, initData); + } else if (moduleType == MODULE_TYPE_EXECUTOR) { + return uninstallExecutor(account, module, initData); + } else if (moduleType == MODULE_TYPE_HOOK) { + return uninstallHook(account, module, initData); + } else if (moduleType == MODULE_TYPE_FALLBACK) { + return uninstallFallback(account, module, initData); + } else { + revert("Invalid module type"); + } + } + + /** + * get callData to install validator on ERC7579 Account + */ + function installValidator( + address, /* account */ + address validator, + bytes memory initData + ) + public + pure + virtual + returns (bytes memory callData) + { + callData = abi.encodeCall( + IERC7579Account.installModule, (MODULE_TYPE_VALIDATOR, validator, initData) + ); + } + + /** + * get callData to uninstall validator on ERC7579 Account + */ + // TODO: What is the reason we don't pass instance here? Pass instance here + function uninstallValidator( + address account, + address validator, + bytes memory initData + ) + public + view + virtual + returns (bytes memory callData) + { + // get previous validator in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getValidatorPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == validator) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == validator) previous = array[i - 1]; + } + } + initData = abi.encode(previous, initData); + + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_VALIDATOR, validator, initData) + ); + } + + /** + * get callData to install executor on ERC7579 Account + */ + function installExecutor( + address, /* account */ + address executor, + bytes memory initData + ) + public + pure + virtual + returns (bytes memory callData) + { + callData = abi.encodeCall( + IERC7579Account.installModule, (MODULE_TYPE_EXECUTOR, executor, initData) + ); + } + + /** + * get callData to uninstall executor on ERC7579 Account + */ + // TODO: + function uninstallExecutor( + address account, + address executor, + bytes memory initData + ) + public + view + virtual + returns (bytes memory callData) + { + // get previous executor in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getExecutorsPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == executor) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == executor) previous = array[i - 1]; + } + } + initData = abi.encode(previous, initData); + + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_EXECUTOR, executor, initData) + ); + } + + /** + * get callData to install hook on ERC7579 Account + */ + function installHook( + address, /* account */ + address hook, + bytes memory initData + ) + public + view + virtual + returns (bytes memory callData) + { + callData = abi.encodeCall(IERC7579Account.installModule, (MODULE_TYPE_HOOK, hook, initData)); + } + + /** + * get callData to uninstall hook on ERC7579 Account + */ + function uninstallHook( + address, /* account */ + address hook, + bytes memory initData + ) + public + pure + virtual + returns (bytes memory callData) + { + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_HOOK, address(0), initData) + ); + } + + /** + * get callData to install fallback on ERC7579 Account + */ + function installFallback( + address, /* account */ + address fallbackHandler, + bytes memory initData + ) + public + pure + virtual + returns (bytes memory callData) + { + callData = abi.encodeCall( + IERC7579Account.installModule, (MODULE_TYPE_FALLBACK, fallbackHandler, initData) + ); + } + + /** + * get callData to uninstall fallback on ERC7579 Account + */ + function uninstallFallback( + address, /* account */ + address fallbackHandler, + bytes memory initData + ) + public + pure + virtual + returns (bytes memory callData) + { + fallbackHandler = fallbackHandler; //avoid solhint-no-unused-vars + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_FALLBACK, address(0), initData) + ); + } + + /** + * Encode a single ERC7579 Execution Transaction + * @param target target of the call + * @param value the value of the call + * @param callData the calldata of the call + */ + function encode( + address target, + uint256 value, + bytes memory callData + ) + public + pure + virtual + returns (bytes memory erc7579Tx) + { + ModeCode mode = ModeLib.encode({ + callType: CALLTYPE_SINGLE, + execType: EXECTYPE_DEFAULT, + mode: MODE_DEFAULT, + payload: ModePayload.wrap(bytes22(0)) + }); + bytes memory data = abi.encodePacked(target, value, callData); + return abi.encodeCall(IERC7579Account.execute, (mode, data)); + } + + /** + * Encode a batched ERC7579 Execution Transaction + * @param executions ERC7579 batched executions + */ + function encode(Execution[] memory executions) + public + pure + virtual + returns (bytes memory erc7579Tx) + { + ModeCode mode = ModeLib.encode({ + callType: CALLTYPE_BATCH, + execType: EXECTYPE_DEFAULT, + mode: MODE_DEFAULT, + payload: ModePayload.wrap(bytes22(0)) + }); + return abi.encodeCall(IERC7579Account.execute, (mode, abi.encode(executions))); + } + + /** + * convert arrays to batched IERC7579Account + */ + function toExecutions( + address[] memory targets, + uint256[] memory values, + bytes[] memory callDatas + ) + public + pure + virtual + returns (Execution[] memory executions) + { + executions = new Execution[](targets.length); + if (targets.length != values.length && values.length != callDatas.length) { + revert("Length Mismatch"); + } + + for (uint256 i; i < targets.length; i++) { + executions[i] = + Execution({ target: targets[i], value: values[i], callData: callDatas[i] }); + } + } + + function getNonce( + address account, + IEntryPoint entrypoint, + address validator, + address defaultValidator + ) + public + view + virtual + returns (uint256 nonce) + { + uint192 key = uint192(bytes24(bytes20(address(validator)))); + nonce = entrypoint.getNonce(address(account), key); + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module + ) + public + view + virtual + override + returns (bool) + { + bytes memory data; + + return isModuleInstalled(instance, moduleTypeId, module, data); + } + + function isModuleInstalled( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bool) + { + return IERC7579Account(instance.account).isModuleInstalled(moduleTypeId, module, data); + } + + function getInstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bytes memory) + { + return data; + } + + function getUninstallModuleData( + AccountInstance memory instance, + uint256 moduleTypeId, + address module, + bytes memory data + ) + public + view + virtual + override + returns (bytes memory) + { + return data; + } +} diff --git a/src/test/helpers/KernelHelpers.sol b/src/test/helpers/KernelHelpers.sol index da8d4fe8..8d3a45f8 100644 --- a/src/test/helpers/KernelHelpers.sol +++ b/src/test/helpers/KernelHelpers.sol @@ -8,9 +8,9 @@ import { ENTRYPOINT_ADDR } from "../predeploy/EntryPoint.sol"; import { IEntryPoint } from "kernel/interfaces/IEntryPoint.sol"; import { IERC7579Account } from "erc7579/interfaces/IERC7579Account.sol"; import { MockFallback } from "kernel/mock/MockFallback.sol"; -import { ERC7579Helpers } from "./ERC7579Helpers.sol"; +import { HelperBase } from "./HelperBase.sol"; -contract KernelHelpers is ERC7579Helpers { +contract KernelHelpers is HelperBase { function getNonce( address account, IEntryPoint entrypoint, diff --git a/src/test/helpers/SafeHelpers.sol b/src/test/helpers/SafeHelpers.sol index 4ca91a9a..353323bc 100644 --- a/src/test/helpers/SafeHelpers.sol +++ b/src/test/helpers/SafeHelpers.sol @@ -1,20 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { ERC7579Helpers } from "./ERC7579Helpers.sol"; +import { HelperBase } from "./HelperBase.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import { Safe7579Launchpad } from "safe7579/Safe7579Launchpad.sol"; import { SafeFactory } from "src/accounts/safe/SafeFactory.sol"; - - import { PackedUserOperation } from "../../external/ERC4337.sol"; -import { - IERC7579Account, - MODULE_TYPE_HOOK -} from "../../external/ERC7579.sol"; +import { IERC7579Account, MODULE_TYPE_HOOK } from "../../external/ERC7579.sol"; import { HookType } from "safe7579/DataTypes.sol"; +import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; -contract SafeHelpers is ERC7579Helpers { +contract SafeHelpers is HelperBase { /** * get callData to install hook on ERC7579 Account */ @@ -82,7 +78,7 @@ contract SafeHelpers is ERC7579Helpers { address module, bytes memory initData, function(address, uint256, address, bytes memory) - internal + external returns (bytes memory) fn, address txValidator ) From b71a60bd855e559f6bbb941127e97edcd1c4f92f Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Wed, 5 Jun 2024 18:50:44 +0100 Subject: [PATCH 05/15] fix: more issues --- src/test/ModuleKitUserOp.sol | 56 ++++++++++++++-------------- src/test/RhinestoneModuleKit.sol | 26 +++++++++++++ src/test/helpers/IAccountHelpers.sol | 23 ++++++++++++ 3 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/test/ModuleKitUserOp.sol b/src/test/ModuleKitUserOp.sol index b9ca7351..389f9d20 100644 --- a/src/test/ModuleKitUserOp.sol +++ b/src/test/ModuleKitUserOp.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.23; import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; +import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; +import { Execution, ERC7579ExecutionLib } from "../external/ERC7579.sol"; import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; -import { Execution } from "../external/ERC7579.sol"; library ModuleKitUserOp { function getInstallModuleOps( @@ -17,7 +18,8 @@ library ModuleKitUserOp { returns (UserOpData memory userOpData) { // get userOp with correct nonce for selected txValidator - (userOpData.userOp, userOpData.userOpHash) = ERC7579Helpers.configModuleUserOp({ + (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) + .configModuleUserOp({ instance: instance, moduleType: moduleType, module: module, @@ -38,7 +40,8 @@ library ModuleKitUserOp { returns (UserOpData memory userOpData) { // get userOp with correct nonce for selected txValidator - (userOpData.userOp, userOpData.userOpHash) = ERC7579Helpers.configModuleUserOp({ + (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) + .configModuleUserOp({ instance: instance, moduleType: moduleType, module: module, @@ -59,12 +62,9 @@ library ModuleKitUserOp { view returns (UserOpData memory userOpData) { - bytes memory erc7579ExecCall = ERC7579Helpers.encode(target, value, callData); - (userOpData.userOp, userOpData.userOpHash) = ERC7579Helpers.execUserOp({ - instance: instance, - callData: erc7579ExecCall, - txValidator: txValidator - }); + bytes memory erc7579ExecCall = ERC7579ExecutionLib.encodeSingle(target, value, callData); + (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) + .execUserOp({ instance: instance, callData: erc7579ExecCall, txValidator: txValidator }); } function getExecOps( @@ -76,26 +76,24 @@ library ModuleKitUserOp { view returns (UserOpData memory userOpData) { - bytes memory erc7579ExecCall = ERC7579Helpers.encode(executions); - (userOpData.userOp, userOpData.userOpHash) = ERC7579Helpers.execUserOp({ - instance: instance, - callData: erc7579ExecCall, - txValidator: txValidator - }); + bytes memory erc7579ExecCall = ERC7579ExecutionLib.encodeBatch(executions); + (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) + .execUserOp({ instance: instance, callData: erc7579ExecCall, txValidator: txValidator }); } - function getExecOps( - AccountInstance memory instance, - address[] memory targets, - uint256[] memory values, - bytes[] memory callDatas, - address txValidator - ) - internal - view - returns (UserOpData memory userOpData) - { - Execution[] memory executions = ERC7579Helpers.toExecutions(targets, values, callDatas); - return getExecOps(instance, executions, txValidator); - } + // function getExecOps( + // AccountInstance memory instance, + // address[] memory targets, + // uint256[] memory values, + // bytes[] memory callDatas, + // address txValidator + // ) + // internal + // view + // returns (UserOpData memory userOpData) + // { + // Execution[] memory executions = + // IAccountHelpers(instance.accountHelper).toExecutions(targets, values, callDatas); + // return getExecOps(instance, executions, txValidator); + // } } diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index f3f33be6..2b3bee7d 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -157,6 +157,32 @@ contract RhinestoneModuleKit is AuxiliaryFactory { setAccountType(AccountType.CUSTOM); } + /** + * create new AccountInstance with modulekit defaults + * + * @param salt account salt / name + */ + function makeAccountInstance( + bytes32 salt, + address counterFactualAddress, + bytes memory initCode4337 + ) + internal + initializeModuleKit + returns (AccountInstance memory instance) + { + label(address(counterFactualAddress), toString(salt)); + deal(counterFactualAddress, 1 ether); + instance = _makeAccountInstance( + salt, + env, + address(accountHelper), + counterFactualAddress, + initCode4337, + address(_defaultValidator) + ); + } + /** * create new AccountInstance with modulekit defaults * diff --git a/src/test/helpers/IAccountHelpers.sol b/src/test/helpers/IAccountHelpers.sol index 82beb7e5..23a07024 100644 --- a/src/test/helpers/IAccountHelpers.sol +++ b/src/test/helpers/IAccountHelpers.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.23; import { AccountInstance } from "../RhinestoneModuleKit.sol"; +import { PackedUserOperation } from "../../external/ERC4337.sol"; interface IAccountHelpers { function isModuleInstalled( @@ -42,4 +43,26 @@ interface IAccountHelpers { external view returns (bytes memory); + + function configModuleUserOp( + AccountInstance memory instance, + uint256 moduleType, + address module, + bytes memory initData, + function(address, uint256, address, bytes memory) + external + returns (bytes memory) fn, + address txValidator + ) + external + returns (PackedUserOperation memory userOp, bytes32 userOpHash); + + function execUserOp( + AccountInstance memory instance, + bytes memory callData, + address txValidator + ) + external + view + returns (PackedUserOperation memory userOp, bytes32 userOpHash); } From c1c1ec211cae29634d55ad8fa68c1fd78d65c385 Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Wed, 5 Jun 2024 20:28:25 +0100 Subject: [PATCH 06/15] feat: compilation works --- src/test/ModuleKitUserOp.sol | 6 +- src/test/helpers/ERC7579Helpers.sol | 83 ++++++++-------------------- src/test/helpers/HelperBase.sol | 83 ++++++++-------------------- src/test/helpers/IAccountHelpers.sol | 5 +- src/test/helpers/SafeHelpers.sol | 38 ++++++------- 5 files changed, 70 insertions(+), 145 deletions(-) diff --git a/src/test/ModuleKitUserOp.sol b/src/test/ModuleKitUserOp.sol index 389f9d20..d843ecd2 100644 --- a/src/test/ModuleKitUserOp.sol +++ b/src/test/ModuleKitUserOp.sol @@ -24,7 +24,7 @@ library ModuleKitUserOp { moduleType: moduleType, module: module, initData: initData, - fn: ERC7579Helpers.installModule, + isInstall: true, txValidator: txValidator }); } @@ -46,7 +46,7 @@ library ModuleKitUserOp { moduleType: moduleType, module: module, initData: initData, - fn: ERC7579Helpers.uninstallModule, + isInstall: false, txValidator: txValidator }); } @@ -59,7 +59,6 @@ library ModuleKitUserOp { address txValidator ) internal - view returns (UserOpData memory userOpData) { bytes memory erc7579ExecCall = ERC7579ExecutionLib.encodeSingle(target, value, callData); @@ -73,7 +72,6 @@ library ModuleKitUserOp { address txValidator ) internal - view returns (UserOpData memory userOpData) { bytes memory erc7579ExecCall = ERC7579ExecutionLib.encodeBatch(executions); diff --git a/src/test/helpers/ERC7579Helpers.sol b/src/test/helpers/ERC7579Helpers.sol index d59c803e..42476892 100644 --- a/src/test/helpers/ERC7579Helpers.sol +++ b/src/test/helpers/ERC7579Helpers.sol @@ -28,51 +28,12 @@ interface IAccountModulesPaginated { } contract ERC7579Helpers is HelperBase { - /** - * @dev install/uninstall a module on an ERC7579 account - * - * @param account IERC7579Account address - * @param module IERC7579Module address - * @param initData bytes encoded initialization data. - * initData will be passed to fn - * @param fn function parameter that will yield the initData - * - * @return erc7579Tx bytes encoded single ERC7579Execution - * - * - * - * can be used like so: - * bytes memory installCallData = configModule( - * validator, - * initData, - * ERC7579Helpers.installValidator); - * - */ - function configModule( - address account, - uint256 moduleType, - address module, - bytes memory initData, - function(address, uint256, address, bytes memory) - external - returns (bytes memory) fn - ) - public - virtual - override - returns (bytes memory erc7579Tx) - { - erc7579Tx = fn(account, moduleType, module, initData); - } - function configModuleUserOp( AccountInstance memory instance, uint256 moduleType, address module, bytes memory initData, - function(address, uint256, address, bytes memory) - external - returns (bytes memory) fn, + bool isInstall, address txValidator ) public @@ -85,16 +46,26 @@ contract ERC7579Helpers is HelperBase { initCode = instance.initCode; } - bytes memory callData = configModule(instance.account, moduleType, module, initData, fn); + bytes memory callData; + if (isInstall) { + callData = installModule({ + account: instance.account, + moduleType: moduleType, + module: module, + initData: initData + }); + } else { + callData = uninstallModule({ + account: instance.account, + moduleType: moduleType, + module: module, + initData: initData + }); + } userOp = PackedUserOperation({ sender: instance.account, - nonce: getNonce( - instance.account, - instance.aux.entrypoint, - txValidator, - address(instance.defaultValidator) - ), + nonce: getNonce(instance, callData, txValidator), initCode: initCode, callData: callData, accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), @@ -126,12 +97,7 @@ contract ERC7579Helpers is HelperBase { userOp = PackedUserOperation({ sender: instance.account, - nonce: getNonce( - instance.account, - instance.aux.entrypoint, - txValidator, - address(instance.defaultValidator) - ), + nonce: getNonce(instance, callData, txValidator), initCode: initCode, callData: callData, accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), @@ -460,10 +426,9 @@ contract ERC7579Helpers is HelperBase { } function getNonce( - address account, - IEntryPoint entrypoint, - address validator, - address defaultValidator + AccountInstance memory instance, + bytes memory callData, + address txValidator ) public view @@ -471,8 +436,8 @@ contract ERC7579Helpers is HelperBase { override returns (uint256 nonce) { - uint192 key = uint192(bytes24(bytes20(address(validator)))); - nonce = entrypoint.getNonce(address(account), key); + uint192 key = uint192(bytes24(bytes20(address(txValidator)))); + nonce = instance.aux.entrypoint.getNonce(address(instance.account), key); } function isModuleInstalled( diff --git a/src/test/helpers/HelperBase.sol b/src/test/helpers/HelperBase.sol index cfb3d76c..ac52f503 100644 --- a/src/test/helpers/HelperBase.sol +++ b/src/test/helpers/HelperBase.sol @@ -28,50 +28,12 @@ interface IAccountModulesPaginated { } contract HelperBase is IAccountHelpers { - /** - * @dev install/uninstall a module on an ERC7579 account - * - * @param account IERC7579Account address - * @param module IERC7579Module address - * @param initData bytes encoded initialization data. - * initData will be passed to fn - * @param fn function parameter that will yield the initData - * - * @return erc7579Tx bytes encoded single ERC7579Execution - * - * - * - * can be used like so: - * bytes memory installCallData = configModule( - * validator, - * initData, - * ERC7579Helpers.installValidator); - * - */ - function configModule( - address account, - uint256 moduleType, - address module, - bytes memory initData, - function(address, uint256, address, bytes memory) - external - returns (bytes memory) fn - ) - public - virtual - returns (bytes memory erc7579Tx) - { - erc7579Tx = fn(account, moduleType, module, initData); - } - function configModuleUserOp( AccountInstance memory instance, uint256 moduleType, address module, bytes memory initData, - function(address, uint256, address, bytes memory) - external - returns (bytes memory) fn, + bool isInstall, address txValidator ) public @@ -83,16 +45,26 @@ contract HelperBase is IAccountHelpers { initCode = instance.initCode; } - bytes memory callData = configModule(instance.account, moduleType, module, initData, fn); + bytes memory callData; + if (isInstall) { + callData = installModule({ + account: instance.account, + moduleType: moduleType, + module: module, + initData: initData + }); + } else { + callData = uninstallModule({ + account: instance.account, + moduleType: moduleType, + module: module, + initData: initData + }); + } userOp = PackedUserOperation({ sender: instance.account, - nonce: getNonce( - instance.account, - instance.aux.entrypoint, - txValidator, - address(instance.defaultValidator) - ), + nonce: getNonce(instance, callData, txValidator), initCode: initCode, callData: callData, accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), @@ -111,7 +83,6 @@ contract HelperBase is IAccountHelpers { address txValidator ) public - view virtual returns (PackedUserOperation memory userOp, bytes32 userOpHash) { @@ -123,12 +94,7 @@ contract HelperBase is IAccountHelpers { userOp = PackedUserOperation({ sender: instance.account, - nonce: getNonce( - instance.account, - instance.aux.entrypoint, - txValidator, - address(instance.defaultValidator) - ), + nonce: getNonce(instance, callData, txValidator), initCode: initCode, callData: callData, accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), @@ -444,18 +410,17 @@ contract HelperBase is IAccountHelpers { } function getNonce( - address account, - IEntryPoint entrypoint, - address validator, - address defaultValidator + AccountInstance memory instance, + bytes memory callData, + address txValidator ) public view virtual returns (uint256 nonce) { - uint192 key = uint192(bytes24(bytes20(address(validator)))); - nonce = entrypoint.getNonce(address(account), key); + uint192 key = uint192(bytes24(bytes20(address(txValidator)))); + nonce = instance.aux.entrypoint.getNonce(address(instance.account), key); } function isModuleInstalled( diff --git a/src/test/helpers/IAccountHelpers.sol b/src/test/helpers/IAccountHelpers.sol index 23a07024..911c7e3a 100644 --- a/src/test/helpers/IAccountHelpers.sol +++ b/src/test/helpers/IAccountHelpers.sol @@ -49,9 +49,7 @@ interface IAccountHelpers { uint256 moduleType, address module, bytes memory initData, - function(address, uint256, address, bytes memory) - external - returns (bytes memory) fn, + bool isInstall, address txValidator ) external @@ -63,6 +61,5 @@ interface IAccountHelpers { address txValidator ) external - view returns (PackedUserOperation memory userOp, bytes32 userOpHash); } diff --git a/src/test/helpers/SafeHelpers.sol b/src/test/helpers/SafeHelpers.sol index 353323bc..60338140 100644 --- a/src/test/helpers/SafeHelpers.sol +++ b/src/test/helpers/SafeHelpers.sol @@ -37,7 +37,6 @@ contract SafeHelpers is HelperBase { address txValidator ) public - view virtual override returns (PackedUserOperation memory userOp, bytes32 userOpHash) @@ -54,12 +53,7 @@ contract SafeHelpers is HelperBase { userOp = PackedUserOperation({ sender: instance.account, - nonce: getNonce( - instance.account, - instance.aux.entrypoint, - txValidator, - address(instance.defaultValidator) - ), + nonce: getNonce(instance, callData, txValidator), initCode: initCode, callData: callData, accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), @@ -77,9 +71,7 @@ contract SafeHelpers is HelperBase { uint256 moduleType, address module, bytes memory initData, - function(address, uint256, address, bytes memory) - external - returns (bytes memory) fn, + bool isInstall, address txValidator ) public @@ -92,20 +84,29 @@ contract SafeHelpers is HelperBase { initCode = instance.initCode; } - bytes memory callData = configModule(instance.account, moduleType, module, initData, fn); - + bytes memory callData; + if (isInstall) { + callData = installModule({ + account: instance.account, + moduleType: moduleType, + module: module, + initData: initData + }); + } else { + callData = uninstallModule({ + account: instance.account, + moduleType: moduleType, + module: module, + initData: initData + }); + } if (initCode.length != 0) { (initCode, callData) = getInitCallData(instance.salt, txValidator, initCode, callData); } userOp = PackedUserOperation({ sender: instance.account, - nonce: getNonce( - instance.account, - instance.aux.entrypoint, - txValidator, - address(instance.defaultValidator) - ), + nonce: getNonce(instance, callData, txValidator), initCode: initCode, callData: callData, accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), @@ -125,7 +126,6 @@ contract SafeHelpers is HelperBase { bytes memory erc4337CallData ) public - view returns (bytes memory initCode, bytes memory callData) { // TODO: refactor this to decode the initcode From b1f69ca38778caf8993aaecf96168ee17dd2100b Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Wed, 5 Jun 2024 20:55:26 +0100 Subject: [PATCH 07/15] feat: all tests passing --- src/test/ModuleKitHelpers.sol | 1 - src/test/ModuleKitUserOp.sol | 7 ++++--- src/test/RhinestoneModuleKit.sol | 4 ++-- src/test/helpers/IAccountHelpers.sol | 12 ++++++++++++ test/integrations/ExampleFactory.t.sol | 3 +-- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/test/ModuleKitHelpers.sol b/src/test/ModuleKitHelpers.sol index 630438e4..7fdd46c8 100644 --- a/src/test/ModuleKitHelpers.sol +++ b/src/test/ModuleKitHelpers.sol @@ -17,7 +17,6 @@ library ModuleKitHelpers { function execUserOps(UserOpData memory userOpData) internal { // send userOp to entrypoint - IEntryPoint entrypoint = ModuleKitCache.getEntrypoint(userOpData.userOp.sender); ERC4337Helpers.exec4337(userOpData.userOp, entrypoint); } diff --git a/src/test/ModuleKitUserOp.sol b/src/test/ModuleKitUserOp.sol index d843ecd2..71ea3e4a 100644 --- a/src/test/ModuleKitUserOp.sol +++ b/src/test/ModuleKitUserOp.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.23; import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; -import { Execution, ERC7579ExecutionLib } from "../external/ERC7579.sol"; +import { Execution } from "../external/ERC7579.sol"; import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; library ModuleKitUserOp { @@ -61,7 +61,8 @@ library ModuleKitUserOp { internal returns (UserOpData memory userOpData) { - bytes memory erc7579ExecCall = ERC7579ExecutionLib.encodeSingle(target, value, callData); + bytes memory erc7579ExecCall = + IAccountHelpers(instance.accountHelper).encode(target, value, callData); (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) .execUserOp({ instance: instance, callData: erc7579ExecCall, txValidator: txValidator }); } @@ -74,7 +75,7 @@ library ModuleKitUserOp { internal returns (UserOpData memory userOpData) { - bytes memory erc7579ExecCall = ERC7579ExecutionLib.encodeBatch(executions); + bytes memory erc7579ExecCall = IAccountHelpers(instance.accountHelper).encode(executions); (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) .execUserOp({ instance: instance, callData: erc7579ExecCall, txValidator: txValidator }); } diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 2b3bee7d..8b6a1958 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -172,7 +172,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { returns (AccountInstance memory instance) { label(address(counterFactualAddress), toString(salt)); - deal(counterFactualAddress, 1 ether); + deal(counterFactualAddress, 10 ether); instance = _makeAccountInstance( salt, env, @@ -199,7 +199,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { address(accountFactory), abi.encodeCall(accountFactory.createAccount, (salt, initData)) ); label(address(account), toString(salt)); - deal(account, 1 ether); + deal(account, 10 ether); instance = _makeAccountInstance( salt, env, address(accountHelper), account, initCode4337, address(_defaultValidator) ); diff --git a/src/test/helpers/IAccountHelpers.sol b/src/test/helpers/IAccountHelpers.sol index 911c7e3a..57052733 100644 --- a/src/test/helpers/IAccountHelpers.sol +++ b/src/test/helpers/IAccountHelpers.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.23; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import { PackedUserOperation } from "../../external/ERC4337.sol"; +import { Execution } from "../../external/ERC7579.sol"; interface IAccountHelpers { function isModuleInstalled( @@ -62,4 +63,15 @@ interface IAccountHelpers { ) external returns (PackedUserOperation memory userOp, bytes32 userOpHash); + + function encode( + address target, + uint256 value, + bytes memory callData + ) + external + pure + returns (bytes memory erc7579Tx); + + function encode(Execution[] memory executions) external pure returns (bytes memory erc7579Tx); } diff --git a/test/integrations/ExampleFactory.t.sol b/test/integrations/ExampleFactory.t.sol index 4c0d8b21..122492ba 100644 --- a/test/integrations/ExampleFactory.t.sol +++ b/test/integrations/ExampleFactory.t.sol @@ -50,10 +50,9 @@ contract ExampleFactoryTest is BaseTest { } function test_userOpFlow() public { - bytes32 salt = keccak256("newAccount"); + bytes32 salt = bytes32(bytes("newAccount")); address account = factory.getAddress(salt, address(instance.defaultValidator), ""); bytes memory initCode = factory.getInitCode(salt, address(instance.defaultValidator), ""); - vm.deal(account, 10 ether); instance = makeAccountInstance(salt, account, initCode); From 08675228d1ccca56e30a463bfbbd86bdbf7fb1f7 Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Thu, 6 Jun 2024 02:25:23 +0530 Subject: [PATCH 08/15] chore: fix compiler warnings --- src/accounts/kernel/KernelFactory.sol | 2 +- src/test/helpers/ERC7579Helpers.sol | 18 ++++++++---------- src/test/helpers/HelperBase.sol | 20 +++++++++----------- src/test/helpers/KernelHelpers.sol | 20 ++++++++++---------- src/test/helpers/SafeHelpers.sol | 1 - 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/accounts/kernel/KernelFactory.sol b/src/accounts/kernel/KernelFactory.sol index 59e239ad..16a541c8 100644 --- a/src/accounts/kernel/KernelFactory.sol +++ b/src/accounts/kernel/KernelFactory.sol @@ -38,7 +38,7 @@ contract KernelFactory is IAccountFactory { bytes memory initData ) public - view + pure override returns (bytes memory _init) { diff --git a/src/test/helpers/ERC7579Helpers.sol b/src/test/helpers/ERC7579Helpers.sol index c7bb8275..56797398 100644 --- a/src/test/helpers/ERC7579Helpers.sol +++ b/src/test/helpers/ERC7579Helpers.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.23; import { Execution, IERC7579Account } from "../../external/ERC7579.sol"; import "erc7579/lib/ModeLib.sol"; import "erc7579/interfaces/IERC7579Module.sol"; -import { PackedUserOperation, IEntryPoint } from "../../external/ERC4337.sol"; +import { PackedUserOperation } from "../../external/ERC4337.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import "../utils/Vm.sol"; import { HelperBase } from "./HelperBase.sol"; @@ -188,7 +188,6 @@ contract ERC7579Helpers is HelperBase { /** * get callData to uninstall validator on ERC7579 Account */ - // TODO: What is the reason we don't pass instance here? Pass instance here function uninstallValidator( address account, address validator, @@ -244,7 +243,6 @@ contract ERC7579Helpers is HelperBase { /** * get callData to uninstall executor on ERC7579 Account */ - // TODO: function uninstallExecutor( address account, address executor, @@ -426,7 +424,7 @@ contract ERC7579Helpers is HelperBase { function getNonce( AccountInstance memory instance, - bytes memory callData, + bytes memory, address txValidator ) public @@ -471,9 +469,9 @@ contract ERC7579Helpers is HelperBase { } function getInstallModuleData( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, + AccountInstance memory , + uint256 , + address , bytes memory data ) public @@ -486,9 +484,9 @@ contract ERC7579Helpers is HelperBase { } function getUninstallModuleData( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, + AccountInstance memory , + uint256 , + address , bytes memory data ) public diff --git a/src/test/helpers/HelperBase.sol b/src/test/helpers/HelperBase.sol index ac52f503..89a819f9 100644 --- a/src/test/helpers/HelperBase.sol +++ b/src/test/helpers/HelperBase.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.23; import { Execution, IERC7579Account } from "../../external/ERC7579.sol"; import "erc7579/lib/ModeLib.sol"; import "erc7579/interfaces/IERC7579Module.sol"; -import { PackedUserOperation, IEntryPoint } from "../../external/ERC4337.sol"; +import { PackedUserOperation } from "../../external/ERC4337.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import "../utils/Vm.sol"; import { IAccountHelpers } from "./IAccountHelpers.sol"; @@ -182,7 +182,6 @@ contract HelperBase is IAccountHelpers { /** * get callData to uninstall validator on ERC7579 Account */ - // TODO: What is the reason we don't pass instance here? Pass instance here function uninstallValidator( address account, address validator, @@ -236,7 +235,6 @@ contract HelperBase is IAccountHelpers { /** * get callData to uninstall executor on ERC7579 Account */ - // TODO: function uninstallExecutor( address account, address executor, @@ -290,7 +288,7 @@ contract HelperBase is IAccountHelpers { */ function uninstallHook( address, /* account */ - address hook, + address, /*hook*/ bytes memory initData ) public @@ -411,7 +409,7 @@ contract HelperBase is IAccountHelpers { function getNonce( AccountInstance memory instance, - bytes memory callData, + bytes memory, address txValidator ) public @@ -455,9 +453,9 @@ contract HelperBase is IAccountHelpers { } function getInstallModuleData( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, + AccountInstance memory , + uint256, + address, bytes memory data ) public @@ -470,9 +468,9 @@ contract HelperBase is IAccountHelpers { } function getUninstallModuleData( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, + AccountInstance memory, + uint256, + address, bytes memory data ) public diff --git a/src/test/helpers/KernelHelpers.sol b/src/test/helpers/KernelHelpers.sol index 8d3a45f8..6d867968 100644 --- a/src/test/helpers/KernelHelpers.sol +++ b/src/test/helpers/KernelHelpers.sol @@ -13,7 +13,7 @@ import { HelperBase } from "./HelperBase.sol"; contract KernelHelpers is HelperBase { function getNonce( address account, - IEntryPoint entrypoint, + IEntryPoint, address validator, address defaultValidator ) @@ -35,7 +35,7 @@ contract KernelHelpers is HelperBase { * get callData to uninstall executor on ERC7579 Account */ function uninstallExecutor( - address account, + address, address executor, bytes memory initData ) @@ -51,7 +51,7 @@ contract KernelHelpers is HelperBase { } function uninstallValidator( - address account, + address, address validator, bytes memory initData ) @@ -101,7 +101,7 @@ contract KernelHelpers is HelperBase { * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L311-L321 */ function getDefaultInstallValidatorData( - address module, + address, bytes memory initData ) public @@ -116,7 +116,7 @@ contract KernelHelpers is HelperBase { * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L324-L334 */ function getDefaultInstallExecutorData( - address module, + address, bytes memory initData ) public @@ -131,7 +131,7 @@ contract KernelHelpers is HelperBase { * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L336-L345 */ function getDefaultInstallFallbackData( - address module, + address, bytes memory initData ) public @@ -150,7 +150,7 @@ contract KernelHelpers is HelperBase { * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L311-L321 */ function getDefaultInstallHookData( - address module, + address, bytes memory initData ) public @@ -191,7 +191,7 @@ contract KernelHelpers is HelperBase { * https://github.com/zerodevapp/kernel/blob/a807c8ec354a77ebb7cdb73c5be9dd315cda0df2/src/Kernel.sol#L402-L403 */ function getDefaultUninstallFallbackData( - address module, + address, bytes memory deinitData ) public @@ -202,7 +202,7 @@ contract KernelHelpers is HelperBase { } function getInstallModuleData( - AccountInstance memory instance, + AccountInstance memory, uint256 moduleTypeId, address module, bytes memory data @@ -228,7 +228,7 @@ contract KernelHelpers is HelperBase { } function getUninstallModuleData( - AccountInstance memory instance, + AccountInstance memory, uint256 moduleTypeId, address module, bytes memory data diff --git a/src/test/helpers/SafeHelpers.sol b/src/test/helpers/SafeHelpers.sol index 60338140..c607423f 100644 --- a/src/test/helpers/SafeHelpers.sol +++ b/src/test/helpers/SafeHelpers.sol @@ -134,7 +134,6 @@ contract SafeHelpers is HelperBase { factory := mload(add(originalInitCode, 20)) } Safe7579Launchpad.InitData memory initData = abi.decode( - // TODO: What is this decode used for, is it fine if the name is changed? IAccountFactory(factory).getInitData(txValidator, ""), (Safe7579Launchpad.InitData) ); From dd034b80daa0bb234b3572bf0dcf4c43cb5f8554 Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Thu, 6 Jun 2024 02:53:47 +0530 Subject: [PATCH 09/15] chore: removed code duplication in helper libraries --- src/accounts/erc7579/ERC7579Factory.sol | 2 +- src/test/helpers/ERC7579Helpers.sol | 451 +----------------- src/test/helpers/HelperBase.sol | 21 +- src/test/helpers/IAccountModulesPaginated.sol | 20 + src/test/helpers/KernelHelpers.sol | 3 +- src/test/helpers/SafeHelpers.sol | 4 +- 6 files changed, 27 insertions(+), 474 deletions(-) create mode 100644 src/test/helpers/IAccountModulesPaginated.sol diff --git a/src/accounts/erc7579/ERC7579Factory.sol b/src/accounts/erc7579/ERC7579Factory.sol index f04b474e..3df3a1cf 100644 --- a/src/accounts/erc7579/ERC7579Factory.sol +++ b/src/accounts/erc7579/ERC7579Factory.sol @@ -5,7 +5,7 @@ import "../../external/ERC7579.sol"; import { LibClone } from "solady/utils/LibClone.sol"; import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; -contract ERC7579Factory is IAccountFactory{ +contract ERC7579Factory is IAccountFactory { ERC7579Account internal implementation; ERC7579Bootstrap internal bootstrapDefault; diff --git a/src/test/helpers/ERC7579Helpers.sol b/src/test/helpers/ERC7579Helpers.sol index 56797398..c0dbfa80 100644 --- a/src/test/helpers/ERC7579Helpers.sol +++ b/src/test/helpers/ERC7579Helpers.sol @@ -8,291 +8,9 @@ import { PackedUserOperation } from "../../external/ERC4337.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import "../utils/Vm.sol"; import { HelperBase } from "./HelperBase.sol"; - -interface IAccountModulesPaginated { - function getValidatorPaginated( - address, - uint256 - ) - external - view - returns (address[] memory, address); - - function getExecutorsPaginated( - address, - uint256 - ) - external - view - returns (address[] memory, address); -} +import { IAccountModulesPaginated } from "./IAccountModulesPaginated.sol"; contract ERC7579Helpers is HelperBase { - function configModuleUserOp( - AccountInstance memory instance, - uint256 moduleType, - address module, - bytes memory initData, - bool isInstall, - address txValidator - ) - public - virtual - override - returns (PackedUserOperation memory userOp, bytes32 userOpHash) - { - bytes memory initCode; - if (instance.account.code.length == 0) { - initCode = instance.initCode; - } - - bytes memory callData; - if (isInstall) { - callData = installModule({ - account: instance.account, - moduleType: moduleType, - module: module, - initData: initData - }); - } else { - callData = uninstallModule({ - account: instance.account, - moduleType: moduleType, - module: module, - initData: initData - }); - } - - userOp = PackedUserOperation({ - sender: instance.account, - nonce: getNonce(instance, callData, txValidator), - initCode: initCode, - callData: callData, - accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), - preVerificationGas: 2e6, - gasFees: bytes32(abi.encodePacked(uint128(1), uint128(1))), - paymasterAndData: bytes(""), - signature: bytes("") - }); - - userOpHash = instance.aux.entrypoint.getUserOpHash(userOp); - } - - function execUserOp( - AccountInstance memory instance, - bytes memory callData, - address txValidator - ) - public - view - virtual - override - returns (PackedUserOperation memory userOp, bytes32 userOpHash) - { - bytes memory initCode; - bool notDeployedYet = instance.account.code.length == 0; - if (notDeployedYet) { - initCode = instance.initCode; - } - - userOp = PackedUserOperation({ - sender: instance.account, - nonce: getNonce(instance, callData, txValidator), - initCode: initCode, - callData: callData, - accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))), - preVerificationGas: 2e6, - gasFees: bytes32(abi.encodePacked(uint128(1), uint128(1))), - paymasterAndData: bytes(""), - signature: bytes("") - }); - - userOpHash = instance.aux.entrypoint.getUserOpHash(userOp); - } - - /** - * Router function to install a module on an ERC7579 account - */ - function installModule( - address account, - uint256 moduleType, - address module, - bytes memory initData - ) - public - view - virtual - override - returns (bytes memory callData) - { - if (moduleType == MODULE_TYPE_VALIDATOR) { - return installValidator(account, module, initData); - } else if (moduleType == MODULE_TYPE_EXECUTOR) { - return installExecutor(account, module, initData); - } else if (moduleType == MODULE_TYPE_HOOK) { - return installHook(account, module, initData); - } else if (moduleType == MODULE_TYPE_FALLBACK) { - return installFallback(account, module, initData); - } else { - revert("Invalid module type"); - } - } - - /** - * Router function to uninstall a module on an ERC7579 account - */ - function uninstallModule( - address account, - uint256 moduleType, - address module, - bytes memory initData - ) - public - view - virtual - override - returns (bytes memory callData) - { - if (moduleType == MODULE_TYPE_VALIDATOR) { - return uninstallValidator(account, module, initData); - } else if (moduleType == MODULE_TYPE_EXECUTOR) { - return uninstallExecutor(account, module, initData); - } else if (moduleType == MODULE_TYPE_HOOK) { - return uninstallHook(account, module, initData); - } else if (moduleType == MODULE_TYPE_FALLBACK) { - return uninstallFallback(account, module, initData); - } else { - revert("Invalid module type"); - } - } - - /** - * get callData to install validator on ERC7579 Account - */ - function installValidator( - address, /* account */ - address validator, - bytes memory initData - ) - public - pure - virtual - override - returns (bytes memory callData) - { - callData = abi.encodeCall( - IERC7579Account.installModule, (MODULE_TYPE_VALIDATOR, validator, initData) - ); - } - - /** - * get callData to uninstall validator on ERC7579 Account - */ - function uninstallValidator( - address account, - address validator, - bytes memory initData - ) - public - view - virtual - override - returns (bytes memory callData) - { - // get previous validator in sentinel list - address previous; - - (address[] memory array,) = - IAccountModulesPaginated(account).getValidatorPaginated(address(0x1), 100); - - if (array.length == 1) { - previous = address(0x1); - } else if (array[0] == validator) { - previous = address(0x1); - } else { - for (uint256 i = 1; i < array.length; i++) { - if (array[i] == validator) previous = array[i - 1]; - } - } - initData = abi.encode(previous, initData); - - callData = abi.encodeCall( - IERC7579Account.uninstallModule, (MODULE_TYPE_VALIDATOR, validator, initData) - ); - } - - /** - * get callData to install executor on ERC7579 Account - */ - function installExecutor( - address, /* account */ - address executor, - bytes memory initData - ) - public - pure - virtual - override - returns (bytes memory callData) - { - callData = abi.encodeCall( - IERC7579Account.installModule, (MODULE_TYPE_EXECUTOR, executor, initData) - ); - } - - /** - * get callData to uninstall executor on ERC7579 Account - */ - function uninstallExecutor( - address account, - address executor, - bytes memory initData - ) - public - view - virtual - override - returns (bytes memory callData) - { - // get previous executor in sentinel list - address previous; - - (address[] memory array,) = - IAccountModulesPaginated(account).getExecutorsPaginated(address(0x1), 100); - - if (array.length == 1) { - previous = address(0x1); - } else if (array[0] == executor) { - previous = address(0x1); - } else { - for (uint256 i = 1; i < array.length; i++) { - if (array[i] == executor) previous = array[i - 1]; - } - } - initData = abi.encode(previous, initData); - - callData = abi.encodeCall( - IERC7579Account.uninstallModule, (MODULE_TYPE_EXECUTOR, executor, initData) - ); - } - - /** - * get callData to install hook on ERC7579 Account - */ - function installHook( - address, /* account */ - address hook, - bytes memory initData - ) - public - view - virtual - override - returns (bytes memory callData) - { - callData = abi.encodeCall(IERC7579Account.installModule, (MODULE_TYPE_HOOK, hook, initData)); - } - /** * get callData to uninstall hook on ERC7579 Account */ @@ -311,25 +29,6 @@ contract ERC7579Helpers is HelperBase { abi.encodeCall(IERC7579Account.uninstallModule, (MODULE_TYPE_HOOK, hook, initData)); } - /** - * get callData to install fallback on ERC7579 Account - */ - function installFallback( - address, /* account */ - address fallbackHandler, - bytes memory initData - ) - public - pure - virtual - override - returns (bytes memory callData) - { - callData = abi.encodeCall( - IERC7579Account.installModule, (MODULE_TYPE_FALLBACK, fallbackHandler, initData) - ); - } - /** * get callData to uninstall fallback on ERC7579 Account */ @@ -349,152 +48,4 @@ contract ERC7579Helpers is HelperBase { IERC7579Account.uninstallModule, (MODULE_TYPE_FALLBACK, fallbackHandler, initData) ); } - - /** - * Encode a single ERC7579 Execution Transaction - * @param target target of the call - * @param value the value of the call - * @param callData the calldata of the call - */ - function encode( - address target, - uint256 value, - bytes memory callData - ) - public - pure - virtual - override - returns (bytes memory erc7579Tx) - { - ModeCode mode = ModeLib.encode({ - callType: CALLTYPE_SINGLE, - execType: EXECTYPE_DEFAULT, - mode: MODE_DEFAULT, - payload: ModePayload.wrap(bytes22(0)) - }); - bytes memory data = abi.encodePacked(target, value, callData); - return abi.encodeCall(IERC7579Account.execute, (mode, data)); - } - - /** - * Encode a batched ERC7579 Execution Transaction - * @param executions ERC7579 batched executions - */ - function encode(Execution[] memory executions) - public - pure - virtual - override - returns (bytes memory erc7579Tx) - { - ModeCode mode = ModeLib.encode({ - callType: CALLTYPE_BATCH, - execType: EXECTYPE_DEFAULT, - mode: MODE_DEFAULT, - payload: ModePayload.wrap(bytes22(0)) - }); - return abi.encodeCall(IERC7579Account.execute, (mode, abi.encode(executions))); - } - - /** - * convert arrays to batched IERC7579Account - */ - function toExecutions( - address[] memory targets, - uint256[] memory values, - bytes[] memory callDatas - ) - public - pure - virtual - override - returns (Execution[] memory executions) - { - executions = new Execution[](targets.length); - if (targets.length != values.length && values.length != callDatas.length) { - revert("Length Mismatch"); - } - - for (uint256 i; i < targets.length; i++) { - executions[i] = - Execution({ target: targets[i], value: values[i], callData: callDatas[i] }); - } - } - - function getNonce( - AccountInstance memory instance, - bytes memory, - address txValidator - ) - public - view - virtual - override - returns (uint256 nonce) - { - uint192 key = uint192(bytes24(bytes20(address(txValidator)))); - nonce = instance.aux.entrypoint.getNonce(address(instance.account), key); - } - - function isModuleInstalled( - AccountInstance memory instance, - uint256 moduleTypeId, - address module - ) - public - view - virtual - override - returns (bool) - { - bytes memory data; - - return isModuleInstalled(instance, moduleTypeId, module, data); - } - - function isModuleInstalled( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, - bytes memory data - ) - public - view - virtual - override - returns (bool) - { - return IERC7579Account(instance.account).isModuleInstalled(moduleTypeId, module, data); - } - - function getInstallModuleData( - AccountInstance memory , - uint256 , - address , - bytes memory data - ) - public - view - virtual - override - returns (bytes memory) - { - return data; - } - - function getUninstallModuleData( - AccountInstance memory , - uint256 , - address , - bytes memory data - ) - public - view - virtual - override - returns (bytes memory) - { - return data; - } } diff --git a/src/test/helpers/HelperBase.sol b/src/test/helpers/HelperBase.sol index 89a819f9..9d2eaa9d 100644 --- a/src/test/helpers/HelperBase.sol +++ b/src/test/helpers/HelperBase.sol @@ -8,24 +8,7 @@ import { PackedUserOperation } from "../../external/ERC4337.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import "../utils/Vm.sol"; import { IAccountHelpers } from "./IAccountHelpers.sol"; - -interface IAccountModulesPaginated { - function getValidatorPaginated( - address, - uint256 - ) - external - view - returns (address[] memory, address); - - function getExecutorsPaginated( - address, - uint256 - ) - external - view - returns (address[] memory, address); -} +import { IAccountModulesPaginated } from "./IAccountModulesPaginated.sol"; contract HelperBase is IAccountHelpers { function configModuleUserOp( @@ -453,7 +436,7 @@ contract HelperBase is IAccountHelpers { } function getInstallModuleData( - AccountInstance memory , + AccountInstance memory, uint256, address, bytes memory data diff --git a/src/test/helpers/IAccountModulesPaginated.sol b/src/test/helpers/IAccountModulesPaginated.sol new file mode 100644 index 00000000..a224be71 --- /dev/null +++ b/src/test/helpers/IAccountModulesPaginated.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +interface IAccountModulesPaginated { + function getValidatorPaginated( + address, + uint256 + ) + external + view + returns (address[] memory, address); + + function getExecutorsPaginated( + address, + uint256 + ) + external + view + returns (address[] memory, address); +} diff --git a/src/test/helpers/KernelHelpers.sol b/src/test/helpers/KernelHelpers.sol index 6d867968..f83c3722 100644 --- a/src/test/helpers/KernelHelpers.sol +++ b/src/test/helpers/KernelHelpers.sol @@ -262,11 +262,10 @@ contract KernelHelpers is HelperBase { override returns (bool) { - bytes memory data; - if (moduleTypeId == MODULE_TYPE_HOOK) { return true; } + bytes memory data; return isModuleInstalled(instance, moduleTypeId, module, data); } diff --git a/src/test/helpers/SafeHelpers.sol b/src/test/helpers/SafeHelpers.sol index c607423f..e969ecb2 100644 --- a/src/test/helpers/SafeHelpers.sol +++ b/src/test/helpers/SafeHelpers.sol @@ -100,6 +100,7 @@ contract SafeHelpers is HelperBase { initData: initData }); } + if (initCode.length != 0) { (initCode, callData) = getInitCallData(instance.salt, txValidator, initCode, callData); } @@ -134,8 +135,7 @@ contract SafeHelpers is HelperBase { factory := mload(add(originalInitCode, 20)) } Safe7579Launchpad.InitData memory initData = abi.decode( - IAccountFactory(factory).getInitData(txValidator, ""), - (Safe7579Launchpad.InitData) + IAccountFactory(factory).getInitData(txValidator, ""), (Safe7579Launchpad.InitData) ); // Safe7579Launchpad.InitData memory initData = // abi.decode(_initCode, (Safe7579Launchpad.InitData)); From 72a0b8c35a770527a826116ac5bbcde72ab0f386 Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Thu, 6 Jun 2024 02:58:39 +0530 Subject: [PATCH 10/15] minor: added natspec for helper param --- src/test/RhinestoneModuleKit.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 8b6a1958..97a28c4e 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -135,6 +135,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { /** * create new AccountInstance with initCode * @param salt account salt / name + * @param helper address of the account helper, follows the IAccountHelpers interface * @param counterFactualAddress of the account * @param initCode4337 to be added to userOp:initCode */ From b5c7d10db50ac57e077bb4865ffc251fd26aa30d Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Thu, 6 Jun 2024 11:55:39 +0100 Subject: [PATCH 11/15] fix: helpers bugs --- src/test/RhinestoneModuleKit.sol | 13 +++---------- src/test/helpers/KernelHelpers.sol | 12 ++++++------ test/BaseTest.t.sol | 7 ------- test/Diff.t.sol | 7 +++++++ test/integrations/ExampleFactory.t.sol | 2 +- 5 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 97a28c4e..cef19fcc 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -149,6 +149,8 @@ contract RhinestoneModuleKit is AuxiliaryFactory { initializeModuleKit returns (AccountInstance memory instance) { + label(address(counterFactualAddress), toString(salt)); + deal(counterFactualAddress, 10 ether); // Create AccountInstance struct with counterFactualAddress and initCode // The initcode will be set to 0, once the account was created by EntryPoint.sol instance = _makeAccountInstance( @@ -172,16 +174,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { initializeModuleKit returns (AccountInstance memory instance) { - label(address(counterFactualAddress), toString(salt)); - deal(counterFactualAddress, 10 ether); - instance = _makeAccountInstance( - salt, - env, - address(accountHelper), - counterFactualAddress, - initCode4337, - address(_defaultValidator) - ); + makeAccountInstance(salt, address(accountHelper), counterFactualAddress, initCode4337); } /** diff --git a/src/test/helpers/KernelHelpers.sol b/src/test/helpers/KernelHelpers.sol index f83c3722..47680172 100644 --- a/src/test/helpers/KernelHelpers.sol +++ b/src/test/helpers/KernelHelpers.sol @@ -12,23 +12,23 @@ import { HelperBase } from "./HelperBase.sol"; contract KernelHelpers is HelperBase { function getNonce( - address account, - IEntryPoint, - address validator, - address defaultValidator + AccountInstance memory instance, + bytes memory, + address txValidator ) public view virtual + override returns (uint256 nonce) { ValidationType vType; - if (validator == defaultValidator) { + if (txValidator == address(instance.defaultValidator)) { vType = VALIDATION_TYPE_ROOT; } else { vType = VALIDATION_TYPE_VALIDATOR; } - nonce = encodeNonce(vType, false, account, defaultValidator); + nonce = encodeNonce(vType, false, instance.account, txValidator); } /** diff --git a/test/BaseTest.t.sol b/test/BaseTest.t.sol index db1746ac..3b603585 100644 --- a/test/BaseTest.t.sol +++ b/test/BaseTest.t.sol @@ -24,11 +24,4 @@ contract BaseTest is RhinestoneModuleKit, Test { recipient = makeAddr("recipient"); } - - function test_transfer() public { - UserOpData memory data = instance.exec({ target: recipient, value: 1 ether, callData: "" }); - assertTrue(data.userOpHash != ""); - assertTrue(recipient.balance == 1 ether); - assertTrue(data.userOp.sender == instance.account); - } } diff --git a/test/Diff.t.sol b/test/Diff.t.sol index c552e5bf..7cb7b523 100644 --- a/test/Diff.t.sol +++ b/test/Diff.t.sol @@ -55,6 +55,13 @@ contract ERC7579DifferentialModuleKitLibTest is BaseTest { // } // } + function test_transfer() public { + UserOpData memory data = instance.exec({ target: recipient, value: 1 ether, callData: "" }); + assertTrue(data.userOpHash != ""); + assertTrue(recipient.balance == 1 ether); + assertTrue(data.userOp.sender == instance.account); + } + /*////////////////////////////////////////////////////////////////////////// exec //////////////////////////////////////////////////////////////////////////*/ diff --git a/test/integrations/ExampleFactory.t.sol b/test/integrations/ExampleFactory.t.sol index 122492ba..e2dbdf8e 100644 --- a/test/integrations/ExampleFactory.t.sol +++ b/test/integrations/ExampleFactory.t.sol @@ -54,7 +54,7 @@ contract ExampleFactoryTest is BaseTest { address account = factory.getAddress(salt, address(instance.defaultValidator), ""); bytes memory initCode = factory.getInitCode(salt, address(instance.defaultValidator), ""); - instance = makeAccountInstance(salt, account, initCode); + instance = makeAccountInstance(salt, address(erc7579Helper), account, initCode); address target = makeAddr("target"); uint256 value = 1 ether; From 5bec4a756358150018ae0aef497c2836d86505b2 Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Thu, 6 Jun 2024 13:15:31 +0100 Subject: [PATCH 12/15] feat: restructure modulekit lib and helpers --- src/test/Auxiliary.sol | 5 - src/test/ModuleKitHelpers.sol | 111 ++++++++++++++++-- src/test/ModuleKitUserOp.sol | 96 +-------------- src/test/RhinestoneModuleKit.sol | 94 ++++++++------- src/test/helpers/ERC7579Helpers.sol | 64 +++++++--- src/test/helpers/HelperBase.sol | 61 ++-------- src/test/helpers/IAccountHelpers.sol | 77 ------------ src/test/helpers/SafeHelpers.sol | 100 ++++++++++++---- .../IAccountModulesPaginated.sol | 0 test/integrations/ExampleFactory.t.sol | 7 +- 10 files changed, 301 insertions(+), 314 deletions(-) delete mode 100644 src/test/helpers/IAccountHelpers.sol rename src/test/helpers/{ => interfaces}/IAccountModulesPaginated.sol (100%) diff --git a/src/test/Auxiliary.sol b/src/test/Auxiliary.sol index 1e02bffb..506e73cc 100644 --- a/src/test/Auxiliary.sol +++ b/src/test/Auxiliary.sol @@ -18,9 +18,7 @@ import "./utils/Log.sol"; struct Auxiliary { IEntryPoint entrypoint; UserOpGasLog gasSimulation; - ERC7579Bootstrap bootstrap; IERC7484 registry; - address initialTrustedAttester; MockFactory mockFactory; } @@ -33,10 +31,7 @@ contract AuxiliaryFactory { auxiliary.gasSimulation = new UserOpGasLog(); auxiliary.entrypoint = etchEntrypoint(); label(address(auxiliary.entrypoint), "EntryPoint"); - auxiliary.bootstrap = new ERC7579Bootstrap(); - label(address(auxiliary.bootstrap), "ERC7579Bootstrap"); auxiliary.registry = etchRegistry(); label(address(auxiliary.registry), "ERC7484Registry"); - auxiliary.initialTrustedAttester = makeAddr("Trusted Attester"); } } diff --git a/src/test/ModuleKitHelpers.sol b/src/test/ModuleKitHelpers.sol index 7fdd46c8..0486471a 100644 --- a/src/test/ModuleKitHelpers.sol +++ b/src/test/ModuleKitHelpers.sol @@ -3,15 +3,15 @@ pragma solidity ^0.8.23; import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; import { IEntryPoint } from "../external/ERC4337.sol"; -import { ModuleKitUserOp, UserOpData } from "./ModuleKitUserOp.sol"; +import { ModuleKitUserOp } from "./ModuleKitUserOp.sol"; import { ERC4337Helpers } from "./utils/ERC4337Helpers.sol"; import { ModuleKitCache } from "./utils/ModuleKitCache.sol"; import { writeExpectRevert, writeGasIdentifier } from "./utils/Log.sol"; import "./utils/Vm.sol"; -import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; +import { HelperBase } from "./helpers/HelperBase.sol"; +import { Execution } from "../external/ERC7579.sol"; library ModuleKitHelpers { - using ModuleKitUserOp for AccountInstance; using ModuleKitHelpers for AccountInstance; using ModuleKitHelpers for UserOpData; @@ -73,9 +73,7 @@ library ModuleKitHelpers { view returns (bool) { - return IAccountHelpers(instance.accountHelper).isModuleInstalled( - instance, moduleTypeId, module - ); + return HelperBase(instance.accountHelper).isModuleInstalled(instance, moduleTypeId, module); } function isModuleInstalled( @@ -88,7 +86,7 @@ library ModuleKitHelpers { view returns (bool) { - return IAccountHelpers(instance.accountHelper).isModuleInstalled( + return HelperBase(instance.accountHelper).isModuleInstalled( instance, moduleTypeId, module, data ); } @@ -162,7 +160,7 @@ library ModuleKitHelpers { view returns (bytes memory) { - return IAccountHelpers(instance.accountHelper).getInstallModuleData( + return HelperBase(instance.accountHelper).getInstallModuleData( instance, moduleTypeId, module, data ); } @@ -177,8 +175,103 @@ library ModuleKitHelpers { view returns (bytes memory) { - return IAccountHelpers(instance.accountHelper).getUninstallModuleData( + return HelperBase(instance.accountHelper).getUninstallModuleData( instance, moduleTypeId, module, data ); } + + function getInstallModuleOps( + AccountInstance memory instance, + uint256 moduleType, + address module, + bytes memory initData, + address txValidator + ) + internal + returns (UserOpData memory userOpData) + { + // get userOp with correct nonce for selected txValidator + (userOpData.userOp, userOpData.userOpHash) = HelperBase(instance.accountHelper) + .configModuleUserOp({ + instance: instance, + moduleType: moduleType, + module: module, + initData: initData, + isInstall: true, + txValidator: txValidator + }); + } + + function getUninstallModuleOps( + AccountInstance memory instance, + uint256 moduleType, + address module, + bytes memory initData, + address txValidator + ) + internal + returns (UserOpData memory userOpData) + { + // get userOp with correct nonce for selected txValidator + (userOpData.userOp, userOpData.userOpHash) = HelperBase(instance.accountHelper) + .configModuleUserOp({ + instance: instance, + moduleType: moduleType, + module: module, + initData: initData, + isInstall: false, + txValidator: txValidator + }); + } + + function getExecOps( + AccountInstance memory instance, + address target, + uint256 value, + bytes memory callData, + address txValidator + ) + internal + returns (UserOpData memory userOpData) + { + bytes memory erc7579ExecCall = + HelperBase(instance.accountHelper).encode(target, value, callData); + (userOpData.userOp, userOpData.userOpHash) = HelperBase(instance.accountHelper).execUserOp({ + instance: instance, + callData: erc7579ExecCall, + txValidator: txValidator + }); + } + + function getExecOps( + AccountInstance memory instance, + Execution[] memory executions, + address txValidator + ) + internal + returns (UserOpData memory userOpData) + { + bytes memory erc7579ExecCall = HelperBase(instance.accountHelper).encode(executions); + (userOpData.userOp, userOpData.userOpHash) = HelperBase(instance.accountHelper).execUserOp({ + instance: instance, + callData: erc7579ExecCall, + txValidator: txValidator + }); + } + + // function getExecOps( + // AccountInstance memory instance, + // address[] memory targets, + // uint256[] memory values, + // bytes[] memory callDatas, + // address txValidator + // ) + // internal + // view + // returns (UserOpData memory userOpData) + // { + // Execution[] memory executions = + // HelperBase(instance.accountHelper).toExecutions(targets, values, callDatas); + // return getExecOps(instance, executions, txValidator); + // } } diff --git a/src/test/ModuleKitUserOp.sol b/src/test/ModuleKitUserOp.sol index 71ea3e4a..dc93a45b 100644 --- a/src/test/ModuleKitUserOp.sol +++ b/src/test/ModuleKitUserOp.sol @@ -1,98 +1,4 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; -import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; -import { Execution } from "../external/ERC7579.sol"; -import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; - -library ModuleKitUserOp { - function getInstallModuleOps( - AccountInstance memory instance, - uint256 moduleType, - address module, - bytes memory initData, - address txValidator - ) - internal - returns (UserOpData memory userOpData) - { - // get userOp with correct nonce for selected txValidator - (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) - .configModuleUserOp({ - instance: instance, - moduleType: moduleType, - module: module, - initData: initData, - isInstall: true, - txValidator: txValidator - }); - } - - function getUninstallModuleOps( - AccountInstance memory instance, - uint256 moduleType, - address module, - bytes memory initData, - address txValidator - ) - internal - returns (UserOpData memory userOpData) - { - // get userOp with correct nonce for selected txValidator - (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) - .configModuleUserOp({ - instance: instance, - moduleType: moduleType, - module: module, - initData: initData, - isInstall: false, - txValidator: txValidator - }); - } - - function getExecOps( - AccountInstance memory instance, - address target, - uint256 value, - bytes memory callData, - address txValidator - ) - internal - returns (UserOpData memory userOpData) - { - bytes memory erc7579ExecCall = - IAccountHelpers(instance.accountHelper).encode(target, value, callData); - (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) - .execUserOp({ instance: instance, callData: erc7579ExecCall, txValidator: txValidator }); - } - - function getExecOps( - AccountInstance memory instance, - Execution[] memory executions, - address txValidator - ) - internal - returns (UserOpData memory userOpData) - { - bytes memory erc7579ExecCall = IAccountHelpers(instance.accountHelper).encode(executions); - (userOpData.userOp, userOpData.userOpHash) = IAccountHelpers(instance.accountHelper) - .execUserOp({ instance: instance, callData: erc7579ExecCall, txValidator: txValidator }); - } - - // function getExecOps( - // AccountInstance memory instance, - // address[] memory targets, - // uint256[] memory values, - // bytes[] memory callDatas, - // address txValidator - // ) - // internal - // view - // returns (UserOpData memory userOpData) - // { - // Execution[] memory executions = - // IAccountHelpers(instance.accountHelper).toExecutions(targets, values, callDatas); - // return getExecOps(instance, executions, txValidator); - // } -} +library ModuleKitUserOp { } diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index cef19fcc..802c585d 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -6,7 +6,7 @@ import { ERC7579Factory } from "src/accounts/erc7579/ERC7579Factory.sol"; import { KernelFactory } from "src/accounts/kernel/KernelFactory.sol"; import { envOr } from "src/test/utils/Vm.sol"; import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; -import { IAccountHelpers } from "./helpers/IAccountHelpers.sol"; +import { HelperBase } from "./helpers/HelperBase.sol"; import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; import { SafeHelpers } from "./helpers/SafeHelpers.sol"; import { KernelHelpers } from "./helpers/KernelHelpers.sol"; @@ -26,14 +26,13 @@ enum AccountType { } struct AccountInstance { - AccountType accountType; address account; + AccountType accountType; address accountHelper; Auxiliary aux; IERC7579Validator defaultValidator; bytes32 salt; bytes initCode; - bool gasLog; } struct UserOpData { @@ -51,15 +50,15 @@ contract RhinestoneModuleKit is AuxiliaryFactory { MockValidator public _defaultValidator; IAccountFactory public accountFactory; - IAccountHelpers public accountHelper; + HelperBase public accountHelper; IAccountFactory public safeFactory; IAccountFactory public kernelFactory; IAccountFactory public erc7579Factory; - IAccountHelpers public safeHelper; - IAccountHelpers public kernelHelper; - IAccountHelpers public erc7579Helper; + HelperBase public safeHelper; + HelperBase public kernelHelper; + HelperBase public erc7579Helper; AccountType public env; @@ -132,31 +131,27 @@ contract RhinestoneModuleKit is AuxiliaryFactory { _; } - /** - * create new AccountInstance with initCode - * @param salt account salt / name - * @param helper address of the account helper, follows the IAccountHelpers interface - * @param counterFactualAddress of the account - * @param initCode4337 to be added to userOp:initCode - */ function makeAccountInstance( bytes32 salt, - address helper, - address counterFactualAddress, - bytes memory initCode4337 + address account, + bytes memory initCode, + address helper ) internal initializeModuleKit returns (AccountInstance memory instance) { - label(address(counterFactualAddress), toString(salt)); - deal(counterFactualAddress, 10 ether); - // Create AccountInstance struct with counterFactualAddress and initCode - // The initcode will be set to 0, once the account was created by EntryPoint.sol - instance = _makeAccountInstance( - salt, env, helper, counterFactualAddress, initCode4337, address(_defaultValidator) - ); + label(address(account), toString(salt)); + deal(account, 10 ether); + instance = _makeAccountInstance({ + salt: salt, + accountType: env, + helper: helper, + account: account, + initCode: initCode, + validator: address(_defaultValidator) + }); setAccountType(AccountType.CUSTOM); } @@ -167,14 +162,19 @@ contract RhinestoneModuleKit is AuxiliaryFactory { */ function makeAccountInstance( bytes32 salt, - address counterFactualAddress, - bytes memory initCode4337 + address account, + bytes memory initCode ) internal initializeModuleKit returns (AccountInstance memory instance) { - makeAccountInstance(salt, address(accountHelper), counterFactualAddress, initCode4337); + makeAccountInstance({ + salt: salt, + helper: address(accountHelper), + account: account, + initCode: initCode + }); } /** @@ -189,39 +189,54 @@ contract RhinestoneModuleKit is AuxiliaryFactory { { bytes memory initData = accountFactory.getInitData(address(_defaultValidator), ""); address account = accountFactory.getAddress(salt, initData); - bytes memory initCode4337 = abi.encodePacked( + 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, env, address(accountHelper), account, initCode4337, address(_defaultValidator) - ); + instance = _makeAccountInstance({ + salt: salt, + accountType: env, + helper: address(accountHelper), + account: account, + initCode: initCode, + validator: address(_defaultValidator) + }); } function makeAccountInstance( bytes32 salt, address account, + bytes memory initCode, address helper, - address defaultValidator, - bytes memory initCode + address defaultValidator ) internal initializeModuleKit returns (AccountInstance memory instance) { - instance = _makeAccountInstance(salt, env, helper, account, initCode, defaultValidator); + label(address(account), toString(salt)); + deal(account, 10 ether); + instance = _makeAccountInstance({ + salt: salt, + accountType: env, + helper: helper, + account: account, + initCode: initCode, + validator: defaultValidator + }); setAccountType(AccountType.CUSTOM); } function _makeAccountInstance( bytes32 salt, - AccountType accountType, - address helper, address account, - bytes memory initCode4337, - address validator + bytes memory initCode, + address validator, + AccountType accountType, + address helper ) internal returns (AccountInstance memory instance) @@ -233,8 +248,7 @@ contract RhinestoneModuleKit is AuxiliaryFactory { aux: auxiliary, salt: salt, defaultValidator: IERC7579Validator(validator), - initCode: initCode4337, - gasLog: false + initCode: initCode }); ModuleKitCache.logEntrypoint(instance.account, auxiliary.entrypoint); diff --git a/src/test/helpers/ERC7579Helpers.sol b/src/test/helpers/ERC7579Helpers.sol index c0dbfa80..ed9ae5d0 100644 --- a/src/test/helpers/ERC7579Helpers.sol +++ b/src/test/helpers/ERC7579Helpers.sol @@ -8,44 +8,78 @@ import { PackedUserOperation } from "../../external/ERC4337.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import "../utils/Vm.sol"; import { HelperBase } from "./HelperBase.sol"; -import { IAccountModulesPaginated } from "./IAccountModulesPaginated.sol"; +import { IAccountModulesPaginated } from "./interfaces/IAccountModulesPaginated.sol"; contract ERC7579Helpers is HelperBase { /** - * get callData to uninstall hook on ERC7579 Account + * get callData to uninstall validator on ERC7579 Account */ - function uninstallHook( - address, /* account */ - address hook, + function uninstallValidator( + address account, + address validator, bytes memory initData ) public - pure + view virtual override returns (bytes memory callData) { - callData = - abi.encodeCall(IERC7579Account.uninstallModule, (MODULE_TYPE_HOOK, hook, initData)); + // get previous validator in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getValidatorPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == validator) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == validator) previous = array[i - 1]; + } + } + initData = abi.encode(previous, initData); + + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_VALIDATOR, validator, initData) + ); } /** - * get callData to uninstall fallback on ERC7579 Account + * get callData to uninstall executor on ERC7579 Account */ - function uninstallFallback( - address, /* account */ - address fallbackHandler, + function uninstallExecutor( + address account, + address executor, bytes memory initData ) public - pure + view virtual override returns (bytes memory callData) { - fallbackHandler = fallbackHandler; //avoid solhint-no-unused-vars + // get previous executor in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getExecutorsPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == executor) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == executor) previous = array[i - 1]; + } + } + initData = abi.encode(previous, initData); + callData = abi.encodeCall( - IERC7579Account.uninstallModule, (MODULE_TYPE_FALLBACK, fallbackHandler, initData) + IERC7579Account.uninstallModule, (MODULE_TYPE_EXECUTOR, executor, initData) ); } } diff --git a/src/test/helpers/HelperBase.sol b/src/test/helpers/HelperBase.sol index 9d2eaa9d..e246ed9b 100644 --- a/src/test/helpers/HelperBase.sol +++ b/src/test/helpers/HelperBase.sol @@ -7,10 +7,8 @@ import "erc7579/interfaces/IERC7579Module.sol"; import { PackedUserOperation } from "../../external/ERC4337.sol"; import { AccountInstance } from "../RhinestoneModuleKit.sol"; import "../utils/Vm.sol"; -import { IAccountHelpers } from "./IAccountHelpers.sol"; -import { IAccountModulesPaginated } from "./IAccountModulesPaginated.sol"; -contract HelperBase is IAccountHelpers { +abstract contract HelperBase { function configModuleUserOp( AccountInstance memory instance, uint256 moduleType, @@ -175,23 +173,6 @@ contract HelperBase is IAccountHelpers { virtual returns (bytes memory callData) { - // get previous validator in sentinel list - address previous; - - (address[] memory array,) = - IAccountModulesPaginated(account).getValidatorPaginated(address(0x1), 100); - - if (array.length == 1) { - previous = address(0x1); - } else if (array[0] == validator) { - previous = address(0x1); - } else { - for (uint256 i = 1; i < array.length; i++) { - if (array[i] == validator) previous = array[i - 1]; - } - } - initData = abi.encode(previous, initData); - callData = abi.encodeCall( IERC7579Account.uninstallModule, (MODULE_TYPE_VALIDATOR, validator, initData) ); @@ -228,23 +209,6 @@ contract HelperBase is IAccountHelpers { virtual returns (bytes memory callData) { - // get previous executor in sentinel list - address previous; - - (address[] memory array,) = - IAccountModulesPaginated(account).getExecutorsPaginated(address(0x1), 100); - - if (array.length == 1) { - previous = address(0x1); - } else if (array[0] == executor) { - previous = address(0x1); - } else { - for (uint256 i = 1; i < array.length; i++) { - if (array[i] == executor) previous = array[i - 1]; - } - } - initData = abi.encode(previous, initData); - callData = abi.encodeCall( IERC7579Account.uninstallModule, (MODULE_TYPE_EXECUTOR, executor, initData) ); @@ -271,7 +235,7 @@ contract HelperBase is IAccountHelpers { */ function uninstallHook( address, /* account */ - address, /*hook*/ + address hook, bytes memory initData ) public @@ -279,9 +243,8 @@ contract HelperBase is IAccountHelpers { virtual returns (bytes memory callData) { - callData = abi.encodeCall( - IERC7579Account.uninstallModule, (MODULE_TYPE_HOOK, address(0), initData) - ); + callData = + abi.encodeCall(IERC7579Account.uninstallModule, (MODULE_TYPE_HOOK, hook, initData)); } /** @@ -317,7 +280,7 @@ contract HelperBase is IAccountHelpers { { fallbackHandler = fallbackHandler; //avoid solhint-no-unused-vars callData = abi.encodeCall( - IERC7579Account.uninstallModule, (MODULE_TYPE_FALLBACK, address(0), initData) + IERC7579Account.uninstallModule, (MODULE_TYPE_FALLBACK, fallbackHandler, initData) ); } @@ -412,27 +375,25 @@ contract HelperBase is IAccountHelpers { public view virtual - override returns (bool) { - bytes memory data; - - return isModuleInstalled(instance, moduleTypeId, module, data); + return isModuleInstalled(instance, moduleTypeId, module, ""); } function isModuleInstalled( AccountInstance memory instance, uint256 moduleTypeId, address module, - bytes memory data + bytes memory additionalContext ) public view virtual - override returns (bool) { - return IERC7579Account(instance.account).isModuleInstalled(moduleTypeId, module, data); + return IERC7579Account(instance.account).isModuleInstalled( + moduleTypeId, module, additionalContext + ); } function getInstallModuleData( @@ -444,7 +405,6 @@ contract HelperBase is IAccountHelpers { public view virtual - override returns (bytes memory) { return data; @@ -459,7 +419,6 @@ contract HelperBase is IAccountHelpers { public view virtual - override returns (bytes memory) { return data; diff --git a/src/test/helpers/IAccountHelpers.sol b/src/test/helpers/IAccountHelpers.sol deleted file mode 100644 index 57052733..00000000 --- a/src/test/helpers/IAccountHelpers.sol +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { AccountInstance } from "../RhinestoneModuleKit.sol"; -import { PackedUserOperation } from "../../external/ERC4337.sol"; -import { Execution } from "../../external/ERC7579.sol"; - -interface IAccountHelpers { - function isModuleInstalled( - AccountInstance memory instance, - uint256 moduleTypeId, - address module - ) - external - view - returns (bool); - - function isModuleInstalled( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, - bytes memory data - ) - external - view - returns (bool); - - function getInstallModuleData( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, - bytes memory data - ) - external - view - returns (bytes memory); - - function getUninstallModuleData( - AccountInstance memory instance, - uint256 moduleTypeId, - address module, - bytes memory data - ) - external - view - returns (bytes memory); - - function configModuleUserOp( - AccountInstance memory instance, - uint256 moduleType, - address module, - bytes memory initData, - bool isInstall, - address txValidator - ) - external - returns (PackedUserOperation memory userOp, bytes32 userOpHash); - - function execUserOp( - AccountInstance memory instance, - bytes memory callData, - address txValidator - ) - external - returns (PackedUserOperation memory userOp, bytes32 userOpHash); - - function encode( - address target, - uint256 value, - bytes memory callData - ) - external - pure - returns (bytes memory erc7579Tx); - - function encode(Execution[] memory executions) external pure returns (bytes memory erc7579Tx); -} diff --git a/src/test/helpers/SafeHelpers.sol b/src/test/helpers/SafeHelpers.sol index e969ecb2..ca1a10a7 100644 --- a/src/test/helpers/SafeHelpers.sol +++ b/src/test/helpers/SafeHelpers.sol @@ -6,11 +6,89 @@ import { AccountInstance } from "../RhinestoneModuleKit.sol"; import { Safe7579Launchpad } from "safe7579/Safe7579Launchpad.sol"; import { SafeFactory } from "src/accounts/safe/SafeFactory.sol"; import { PackedUserOperation } from "../../external/ERC4337.sol"; -import { IERC7579Account, MODULE_TYPE_HOOK } from "../../external/ERC7579.sol"; +import { + IERC7579Account, + MODULE_TYPE_HOOK, + MODULE_TYPE_VALIDATOR, + MODULE_TYPE_EXECUTOR +} from "../../external/ERC7579.sol"; import { HookType } from "safe7579/DataTypes.sol"; import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; +import { IAccountModulesPaginated } from "./interfaces/IAccountModulesPaginated.sol"; contract SafeHelpers is HelperBase { + /** + * get callData to uninstall validator on ERC7579 Account + */ + function uninstallValidator( + address account, + address validator, + bytes memory initData + ) + public + view + virtual + override + returns (bytes memory callData) + { + // get previous validator in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getValidatorPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == validator) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == validator) previous = array[i - 1]; + } + } + initData = abi.encode(previous, initData); + + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_VALIDATOR, validator, initData) + ); + } + + /** + * get callData to uninstall executor on ERC7579 Account + */ + function uninstallExecutor( + address account, + address executor, + bytes memory initData + ) + public + view + virtual + override + returns (bytes memory callData) + { + // get previous executor in sentinel list + address previous; + + (address[] memory array,) = + IAccountModulesPaginated(account).getExecutorsPaginated(address(0x1), 100); + + if (array.length == 1) { + previous = address(0x1); + } else if (array[0] == executor) { + previous = address(0x1); + } else { + for (uint256 i = 1; i < array.length; i++) { + if (array[i] == executor) previous = array[i - 1]; + } + } + initData = abi.encode(previous, initData); + + callData = abi.encodeCall( + IERC7579Account.uninstallModule, (MODULE_TYPE_EXECUTOR, executor, initData) + ); + } + /** * get callData to install hook on ERC7579 Account */ @@ -146,26 +224,6 @@ contract SafeHelpers is HelperBase { callData = abi.encodeCall(Safe7579Launchpad.setupSafe, (initData)); } - function isModuleInstalled( - AccountInstance memory instance, - uint256 moduleTypeId, - address module - ) - public - view - virtual - override - returns (bool) - { - bytes memory data; - - if (moduleTypeId == MODULE_TYPE_HOOK) { - data = abi.encode(HookType.GLOBAL, bytes4(0x0), ""); - } - - return isModuleInstalled(instance, moduleTypeId, module, data); - } - function isModuleInstalled( AccountInstance memory instance, uint256 moduleTypeId, diff --git a/src/test/helpers/IAccountModulesPaginated.sol b/src/test/helpers/interfaces/IAccountModulesPaginated.sol similarity index 100% rename from src/test/helpers/IAccountModulesPaginated.sol rename to src/test/helpers/interfaces/IAccountModulesPaginated.sol diff --git a/test/integrations/ExampleFactory.t.sol b/test/integrations/ExampleFactory.t.sol index e2dbdf8e..ede5b593 100644 --- a/test/integrations/ExampleFactory.t.sol +++ b/test/integrations/ExampleFactory.t.sol @@ -54,7 +54,12 @@ contract ExampleFactoryTest is BaseTest { address account = factory.getAddress(salt, address(instance.defaultValidator), ""); bytes memory initCode = factory.getInitCode(salt, address(instance.defaultValidator), ""); - instance = makeAccountInstance(salt, address(erc7579Helper), account, initCode); + instance = makeAccountInstance({ + salt: salt, + helper: address(erc7579Helper), + account: account, + initCode: initCode + }); address target = makeAddr("target"); uint256 value = 1 ether; From 5e5c5e692075d42ed2d8a37e30753dd4c38afe41 Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Thu, 6 Jun 2024 16:08:16 +0100 Subject: [PATCH 13/15] fix: duplicate imports --- src/test/RhinestoneModuleKit.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 802c585d..550e9b8a 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.23; import { SafeFactory } from "src/accounts/safe/SafeFactory.sol"; import { ERC7579Factory } from "src/accounts/erc7579/ERC7579Factory.sol"; import { KernelFactory } from "src/accounts/kernel/KernelFactory.sol"; -import { envOr } from "src/test/utils/Vm.sol"; +import { envOr, prank, label, deal, toString } from "src/test/utils/Vm.sol"; import { IAccountFactory } from "src/accounts/interface/IAccountFactory.sol"; import { HelperBase } from "./helpers/HelperBase.sol"; import { ERC7579Helpers } from "./helpers/ERC7579Helpers.sol"; @@ -15,7 +15,6 @@ import { PackedUserOperation, IStakeManager } from "../external/ERC4337.sol"; import { ENTRYPOINT_ADDR } from "./predeploy/EntryPoint.sol"; import { IERC7579Validator } from "../external/ERC7579.sol"; import { MockValidator } from "../Mocks.sol"; -import "./utils/Vm.sol"; import "./utils/ModuleKitCache.sol"; enum AccountType { From 172e197881327dfda68371d2a09f8c826da8fd2b Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Thu, 6 Jun 2024 16:12:20 +0100 Subject: [PATCH 14/15] feat: add ep to userOpData --- src/test/RhinestoneModuleKit.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 550e9b8a..31d45328 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -37,6 +37,7 @@ struct AccountInstance { struct UserOpData { PackedUserOperation userOp; bytes32 userOpHash; + IEntryPoint entrypoint; } string constant DEFAULT = "DEFAULT"; From d7eb34be6c6b591476f2a82ebb56dcd9d053c862 Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Thu, 6 Jun 2024 16:24:24 +0100 Subject: [PATCH 15/15] feat: remove modulekit cache --- src/test/ModuleKitHelpers.sol | 12 +++++++++--- src/test/RhinestoneModuleKit.sol | 4 +--- src/test/utils/ModuleKitCache.sol | 30 ------------------------------ 3 files changed, 10 insertions(+), 36 deletions(-) delete mode 100644 src/test/utils/ModuleKitCache.sol diff --git a/src/test/ModuleKitHelpers.sol b/src/test/ModuleKitHelpers.sol index 0486471a..84b2b49d 100644 --- a/src/test/ModuleKitHelpers.sol +++ b/src/test/ModuleKitHelpers.sol @@ -5,7 +5,6 @@ import { AccountInstance, UserOpData } from "./RhinestoneModuleKit.sol"; import { IEntryPoint } from "../external/ERC4337.sol"; import { ModuleKitUserOp } from "./ModuleKitUserOp.sol"; import { ERC4337Helpers } from "./utils/ERC4337Helpers.sol"; -import { ModuleKitCache } from "./utils/ModuleKitCache.sol"; import { writeExpectRevert, writeGasIdentifier } from "./utils/Log.sol"; import "./utils/Vm.sol"; import { HelperBase } from "./helpers/HelperBase.sol"; @@ -17,8 +16,7 @@ library ModuleKitHelpers { function execUserOps(UserOpData memory userOpData) internal { // send userOp to entrypoint - IEntryPoint entrypoint = ModuleKitCache.getEntrypoint(userOpData.userOp.sender); - ERC4337Helpers.exec4337(userOpData.userOp, entrypoint); + ERC4337Helpers.exec4337(userOpData.userOp, userOpData.entrypoint); } function signDefault(UserOpData memory userOpData) internal pure returns (UserOpData memory) { @@ -41,6 +39,7 @@ library ModuleKitHelpers { ); // sign userOp with default signature userOpData = userOpData.signDefault(); + userOpData.entrypoint = instance.aux.entrypoint; // send userOp to entrypoint userOpData.execUserOps(); } @@ -60,6 +59,8 @@ library ModuleKitHelpers { ); // sign userOp with default signature userOpData = userOpData.signDefault(); + userOpData.entrypoint = instance.aux.entrypoint; + // send userOp to entrypoint userOpData.execUserOps(); } @@ -104,6 +105,7 @@ library ModuleKitHelpers { instance.getExecOps(target, value, callData, address(instance.defaultValidator)); // sign userOp with default signature userOpData = userOpData.signDefault(); + userOpData.entrypoint = instance.aux.entrypoint; // send userOp to entrypoint userOpData.execUserOps(); } @@ -200,6 +202,7 @@ library ModuleKitHelpers { isInstall: true, txValidator: txValidator }); + userOpData.entrypoint = instance.aux.entrypoint; } function getUninstallModuleOps( @@ -222,6 +225,7 @@ library ModuleKitHelpers { isInstall: false, txValidator: txValidator }); + userOpData.entrypoint = instance.aux.entrypoint; } function getExecOps( @@ -241,6 +245,7 @@ library ModuleKitHelpers { callData: erc7579ExecCall, txValidator: txValidator }); + userOpData.entrypoint = instance.aux.entrypoint; } function getExecOps( @@ -257,6 +262,7 @@ library ModuleKitHelpers { callData: erc7579ExecCall, txValidator: txValidator }); + userOpData.entrypoint = instance.aux.entrypoint; } // function getExecOps( diff --git a/src/test/RhinestoneModuleKit.sol b/src/test/RhinestoneModuleKit.sol index 31d45328..1c93e3a4 100644 --- a/src/test/RhinestoneModuleKit.sol +++ b/src/test/RhinestoneModuleKit.sol @@ -15,7 +15,7 @@ import { PackedUserOperation, IStakeManager } from "../external/ERC4337.sol"; import { ENTRYPOINT_ADDR } from "./predeploy/EntryPoint.sol"; import { IERC7579Validator } from "../external/ERC7579.sol"; import { MockValidator } from "../Mocks.sol"; -import "./utils/ModuleKitCache.sol"; +import { IEntryPoint } from "../external/ERC4337.sol"; enum AccountType { DEFAULT, @@ -250,8 +250,6 @@ contract RhinestoneModuleKit is AuxiliaryFactory { defaultValidator: IERC7579Validator(validator), initCode: initCode }); - - ModuleKitCache.logEntrypoint(instance.account, auxiliary.entrypoint); } function setAccountType(AccountType _env) public { diff --git a/src/test/utils/ModuleKitCache.sol b/src/test/utils/ModuleKitCache.sol deleted file mode 100644 index 4638317a..00000000 --- a/src/test/utils/ModuleKitCache.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import { IEntryPoint } from "../../external/ERC4337.sol"; - -struct TmpStorage { - mapping(address account => IEntryPoint entrypoint) entrypoint; -} - -bytes32 constant STORAGE_SLOT = keccak256("forge.rhinestone.ModuleKitHelpers"); - -library ModuleKitCache { - function getStorage() internal pure returns (TmpStorage storage _str) { - bytes32 _slot = STORAGE_SLOT; - - assembly { - _str.slot := _slot - } - } - - function logEntrypoint(address account, IEntryPoint entrypoint) internal { - TmpStorage storage str = getStorage(); - str.entrypoint[account] = entrypoint; - } - - function getEntrypoint(address account) internal view returns (IEntryPoint entrypoint) { - TmpStorage storage str = getStorage(); - return str.entrypoint[account]; - } -}