From b0705f67174d396a02557412c64f859531a70731 Mon Sep 17 00:00:00 2001 From: spengrah Date: Wed, 31 Jan 2024 12:39:15 -0600 Subject: [PATCH] add saltNonce --- script/HatsEligibilitiesChain.s.sol | 2 +- script/HatsTogglesChain.s.sol | 2 +- src/HatsModuleFactory.sol | 56 ++++++---- src/utils/DeployFunctions.sol | 7 +- test/HatsEligibilitiesChain.t.sol | 12 ++- test/HatsModule.t.sol | 17 ++-- test/HatsModuleFactory.t.sol | 152 +++++++++++++++++++++------- test/HatsTogglesChain.t.sol | 10 +- 8 files changed, 186 insertions(+), 72 deletions(-) diff --git a/script/HatsEligibilitiesChain.s.sol b/script/HatsEligibilitiesChain.s.sol index df2bde3..f039045 100644 --- a/script/HatsEligibilitiesChain.s.sol +++ b/script/HatsEligibilitiesChain.s.sol @@ -24,7 +24,7 @@ contract DeployImplementation is Script { address deployer = vm.rememberKey(privKey); vm.startBroadcast(deployer); - implementation = new HatsEligibilitiesChain{ salt: SALT}(version); + implementation = new HatsEligibilitiesChain{ salt: SALT }(version); vm.stopBroadcast(); diff --git a/script/HatsTogglesChain.s.sol b/script/HatsTogglesChain.s.sol index 836345a..2b9dd9e 100644 --- a/script/HatsTogglesChain.s.sol +++ b/script/HatsTogglesChain.s.sol @@ -24,7 +24,7 @@ contract DeployImplementation is Script { address deployer = vm.rememberKey(privKey); vm.startBroadcast(deployer); - implementation = new HatsTogglesChain{ salt: SALT}(version); + implementation = new HatsTogglesChain{ salt: SALT }(version); vm.stopBroadcast(); diff --git a/src/HatsModuleFactory.sol b/src/HatsModuleFactory.sol index f23abe9..7678d0b 100644 --- a/src/HatsModuleFactory.sol +++ b/src/HatsModuleFactory.sol @@ -12,10 +12,12 @@ contract HatsModuleFactory { //////////////////////////////////////////////////////////////*/ /** - * @notice Emitted if attempting to deploy a clone of `implementation` for a given `hatId` and `otherImmutableArgs` - * that already has a HatsModule deployment + * @notice Emitted if attempting to deploy a clone of `implementation` for a given `hatId`, `otherImmutableArgs`, and + * `saltNonce` that already has a HatsModule deployment */ - error HatsModuleFactory_ModuleAlreadyDeployed(address implementation, uint256 hatId, bytes otherImmutableArgs); + error HatsModuleFactory_ModuleAlreadyDeployed( + address implementation, uint256 hatId, bytes otherImmutableArgs, uint256 saltNonce + ); /// @notice Emitted when array arguments to a batch function have mismatching lengths error BatchArrayLengthMismatch(); @@ -24,9 +26,10 @@ contract HatsModuleFactory { EVENTS //////////////////////////////////////////////////////////////*/ - /// @notice Emitted when a HatsModule for `hatId` and `otherImmutableArgs` is deployed to address `instance` + /// @notice Emitted when a HatsModule for `hatId`, `otherImmutableArgs`, and `saltNonce` is deployed to address + /// `instance` event HatsModuleFactory_ModuleDeployed( - address implementation, address instance, uint256 hatId, bytes otherImmutableArgs, bytes initData + address implementation, address instance, uint256 hatId, bytes otherImmutableArgs, bytes initData, uint256 saltNonce ); /*////////////////////////////////////////////////////////////// @@ -64,21 +67,23 @@ contract HatsModuleFactory { * @param _otherImmutableArgs Other immutable args to pass to the clone as immutable storage. * @param _initData The encoded data to pass to the `setUp` function of the new HatsModule instance. Leave empty if no * {setUp} is required. + * @param _saltNonce The nonce to use when calculating the salt * @return _instance The address of the deployed HatsModule instance */ function createHatsModule( address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs, - bytes calldata _initData + bytes calldata _initData, + uint256 _saltNonce ) public returns (address _instance) { // calculate unique params that will be used to check for existing deployments and deploy the clone if none exists bytes memory args = _encodeArgs(_implementation, _hatId, _otherImmutableArgs); - bytes32 _salt = _calculateSalt(args); + bytes32 _salt = _calculateSalt(args, _saltNonce); // check if a HatsModule has already been deployed for these parameters if (_getHatsModuleAddress(_implementation, args, _salt).code.length > 0) { - revert HatsModuleFactory_ModuleAlreadyDeployed(_implementation, _hatId, _otherImmutableArgs); + revert HatsModuleFactory_ModuleAlreadyDeployed(_implementation, _hatId, _otherImmutableArgs, _saltNonce); } // deploy the clone to a deterministic address @@ -88,7 +93,9 @@ contract HatsModuleFactory { HatsModule(_instance).setUp(_initData); // log the deployment - emit HatsModuleFactory_ModuleDeployed(_implementation, address(_instance), _hatId, _otherImmutableArgs, _initData); + emit HatsModuleFactory_ModuleDeployed( + _implementation, address(_instance), _hatId, _otherImmutableArgs, _initData, _saltNonce + ); } /** @@ -101,13 +108,15 @@ contract HatsModuleFactory { * @param _otherImmutableArgsArray Other immutable args to pass to the clones as immutable storage. * @param _initDataArray The encoded data to pass to the `setUp` functions of the new HatsModule instances. Leave * empty if no {setUp} is required. + * @param _saltNonces The nonces to use when calculating the salts * @return success True if all modules were successfully created */ function batchCreateHatsModule( address[] calldata _implementations, uint256[] calldata _hatIds, bytes[] calldata _otherImmutableArgsArray, - bytes[] calldata _initDataArray + bytes[] calldata _initDataArray, + uint256[] calldata _saltNonces ) public returns (bool success) { uint256 length = _implementations.length; @@ -118,7 +127,7 @@ contract HatsModuleFactory { } for (uint256 i = 0; i < length;) { - createHatsModule(_implementations[i], _hatIds[i], _otherImmutableArgsArray[i], _initDataArray[i]); + createHatsModule(_implementations[i], _hatIds[i], _otherImmutableArgsArray[i], _initDataArray[i], _saltNonces[i]); unchecked { ++i; @@ -131,16 +140,19 @@ contract HatsModuleFactory { /** * @notice Predicts the address of a HatsModule instance for a given hat * @param _hatId The hat for which to predict the HatsModule instance address + * @param _otherImmutableArgs Other immutable args to pass to the clone as immutable storage. + * @param _saltNonce The nonce to use when calculating the salt * @return The predicted address of the deployed instance */ - function getHatsModuleAddress(address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs) - public - view - returns (address) - { + function getHatsModuleAddress( + address _implementation, + uint256 _hatId, + bytes calldata _otherImmutableArgs, + uint256 _saltNonce + ) public view returns (address) { // prepare the unique inputs bytes memory args = _encodeArgs(_implementation, _hatId, _otherImmutableArgs); - bytes32 _salt = _calculateSalt(args); + bytes32 _salt = _calculateSalt(args, _saltNonce); // predict the address return _getHatsModuleAddress(_implementation, args, _salt); } @@ -149,15 +161,16 @@ contract HatsModuleFactory { * @notice Checks if a HatsModule instance has already been deployed for a given hat * @param _hatId The hat for which to check for an existing instance * @param _otherImmutableArgs Other immutable args to pass to the clone as immutable storage. + * @param _saltNonce The nonce to use when calculating the salt * @return True if an instance has already been deployed for the given hat */ - function deployed(address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs) + function deployed(address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs, uint256 _saltNonce) public view returns (bool) { // check for contract code at the predicted address - return getHatsModuleAddress(_implementation, _hatId, _otherImmutableArgs).code.length > 0; + return getHatsModuleAddress(_implementation, _hatId, _otherImmutableArgs, _saltNonce).code.length > 0; } /*////////////////////////////////////////////////////////////// @@ -203,9 +216,10 @@ contract HatsModuleFactory { * - The chain ID of the current network, to avoid confusion across networks since the same hat trees * on different networks may have different wearers/admins * @param _args The encoded arguments to pass to the clone as immutable storage + * @param _saltNonce The nonce to use when calculating the salt * @return The salt to use when deploying the clone */ - function _calculateSalt(bytes memory _args) internal view returns (bytes32) { - return keccak256(abi.encodePacked(_args, block.chainid)); + function _calculateSalt(bytes memory _args, uint256 _saltNonce) internal view returns (bytes32) { + return keccak256(abi.encodePacked(_args, block.chainid, _saltNonce)); } } diff --git a/src/utils/DeployFunctions.sol b/src/utils/DeployFunctions.sol index 2ea9adf..d94ba54 100644 --- a/src/utils/DeployFunctions.sol +++ b/src/utils/DeployFunctions.sol @@ -5,7 +5,7 @@ import { console2 } from "forge-std/Test.sol"; import { HatsModule, HatsModuleFactory, IHats } from "../HatsModuleFactory.sol"; function deployModuleFactory(IHats _hats, bytes32 _salt, string memory _version) returns (HatsModuleFactory _factory) { - _factory = new HatsModuleFactory{ salt: _salt}(_hats, _version); + _factory = new HatsModuleFactory{ salt: _salt }(_hats, _version); } function deployModuleInstance( @@ -13,7 +13,8 @@ function deployModuleInstance( address _implementation, uint256 _hatId, bytes memory _otherImmutableArgs, - bytes memory _initData + bytes memory _initData, + uint256 _saltNonce ) returns (address _instance) { - _instance = _factory.createHatsModule(_implementation, _hatId, _otherImmutableArgs, _initData); + _instance = _factory.createHatsModule(_implementation, _hatId, _otherImmutableArgs, _initData, _saltNonce); } diff --git a/test/HatsEligibilitiesChain.t.sol b/test/HatsEligibilitiesChain.t.sol index 5c6e003..1a48569 100644 --- a/test/HatsEligibilitiesChain.t.sol +++ b/test/HatsEligibilitiesChain.t.sol @@ -34,6 +34,8 @@ contract DeployImplementationTest is DeployImplementation, Test { address[] expectedModules; + uint256 saltNonce = 1; + function deployInstanceTwoModules( uint256 targetHat, uint256 numClauses, @@ -43,8 +45,9 @@ contract DeployImplementationTest is DeployImplementation, Test { ) public returns (HatsEligibilitiesChain) { bytes memory otherImmutableArgs = abi.encodePacked(numClauses, lengths, _module1, _module2); // deploy the instance - return - HatsEligibilitiesChain(deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "")); + return HatsEligibilitiesChain( + deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "", saltNonce) + ); } function deployInstanceThreeModules( @@ -57,8 +60,9 @@ contract DeployImplementationTest is DeployImplementation, Test { ) public returns (HatsEligibilitiesChain) { bytes memory otherImmutableArgs = abi.encodePacked(numClauses, lengths, _module1, _module2, _module3); // deploy the instance - return - HatsEligibilitiesChain(deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "")); + return HatsEligibilitiesChain( + deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "", saltNonce) + ); } function setUp() public virtual { diff --git a/test/HatsModule.t.sol b/test/HatsModule.t.sol index 8218834..a623a17 100644 --- a/test/HatsModule.t.sol +++ b/test/HatsModule.t.sol @@ -59,16 +59,21 @@ contract DeployInstance is HatsModuleTest { // expect event emitted with the initData vm.expectEmit(true, true, true, true); emit HatsModuleFactory_ModuleDeployed( - address(impl), factory.getHatsModuleAddress(address(impl), hatId, otherArgs), hatId, otherArgs, initData + address(impl), + factory.getHatsModuleAddress(address(impl), hatId, otherArgs, saltNonce), + hatId, + otherArgs, + initData, + saltNonce ); // deploy an instance of HatsModuleHarness via the factory // inst = HatsModuleHarness(factory.createHatsModule(address(impl), hatId, otherArgs, initData)); - inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData)); + inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData, saltNonce)); } function test_immutables() public { // deploy an instance of HatsModuleHarness via the factory - inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData)); + inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData, saltNonce)); assertEq(address(inst.IMPLEMENTATION()), address(impl), "incorrect implementation address"); assertEq(address(inst.HATS()), address(hats), "incorrect hats address"); @@ -78,14 +83,14 @@ contract DeployInstance is HatsModuleTest { function test_version() public { // deploy an instance of HatsModuleHarness via the factory - inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData)); + inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData, saltNonce)); assertEq(inst.version(), MODULE_VERSION, "incorrect module version"); } function test_setUp_cannotBeCalledTwice() public { // deploy an instance of HatsModuleHarness via the factory - inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData)); + inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData, saltNonce)); // expect revert if setUp is called again vm.expectRevert(); @@ -97,6 +102,6 @@ contract DeployInstance is HatsModuleTest { vm.expectEmit(true, true, true, true); emit HatsModuleHarness_SetUp(initData); // deploy an instance of HatsModuleHarness via the factory - inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData)); + inst = HatsModuleHarness(deployModuleInstance(factory, address(impl), hatId, otherArgs, initData, saltNonce)); } } diff --git a/test/HatsModuleFactory.t.sol b/test/HatsModuleFactory.t.sol index e7999f7..4c3e77c 100644 --- a/test/HatsModuleFactory.t.sol +++ b/test/HatsModuleFactory.t.sol @@ -23,11 +23,14 @@ contract HatsModuleFactoryTest is Deploy, Test { bytes public otherArgs; bytes public initData; uint256 public hatId; + uint256 public saltNonce; - error HatsModuleFactory_ModuleAlreadyDeployed(address implementation, uint256 hatId, bytes otherImmutableArgs); + error HatsModuleFactory_ModuleAlreadyDeployed( + address implementation, uint256 hatId, bytes otherImmutableArgs, uint256 saltNonce + ); event HatsModuleFactory_ModuleDeployed( - address implementation, address instance, uint256 hatId, bytes otherImmutableArgs, bytes initData + address implementation, address instance, uint256 hatId, bytes otherImmutableArgs, bytes initData, uint256 saltNonce ); function setUp() public virtual { @@ -59,8 +62,8 @@ contract FactoryHarness is HatsModuleFactory { return _encodeArgs(_implementation, _hatId, _otherImmutableArgs); } - function calculateSalt(bytes memory args) public view returns (bytes32) { - return _calculateSalt(args); + function calculateSalt(bytes memory _args, uint256 _saltNonce) public view returns (bytes32) { + return _calculateSalt(_args, _saltNonce); } function getHatsModuleAddress(address _implementation, bytes memory _arg, bytes32 _salt) @@ -71,14 +74,14 @@ contract FactoryHarness is HatsModuleFactory { return _getHatsModuleAddress(_implementation, _arg, _salt); } - function createModule(address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs) + function createModule(address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs, uint256 _saltNonce) public returns (address) { // encode the Hats contract adddress and _hatId to pass as immutable args when deploying the clone bytes memory args = _encodeArgs(_implementation, _hatId, _otherImmutableArgs); // calculate the determinstic address salt based on the args - bytes32 _salt = _calculateSalt(args); + bytes32 _salt = _calculateSalt(args, _saltNonce); // deploy the clone to the deterministic address return LibClone.cloneDeterministic(_implementation, args, _salt); } @@ -125,20 +128,24 @@ contract Internal_encodeArgs is InternalTest { } contract Internal_calculateSalt is InternalTest { - function testFuzz_calculateSalt(bytes memory _args) public { - assertEq(harness.calculateSalt(_args), keccak256(abi.encodePacked(_args, block.chainid)), "incorrect calculateSalt"); + function testFuzz_calculateSalt(bytes memory _args, uint256 _saltNonce) public { + assertEq( + harness.calculateSalt(_args, _saltNonce), + keccak256(abi.encodePacked(_args, block.chainid, _saltNonce)), + "incorrect calculateSalt" + ); } function test_calculateSalt_0() public { - testFuzz_calculateSalt(hex"00"); + testFuzz_calculateSalt(hex"00", 0); } function test_calculateSalt_large() public { - testFuzz_calculateSalt(largeBytes); + testFuzz_calculateSalt(largeBytes, 0); } function test_calculateSalt_validHat() public { - testFuzz_calculateSalt(harness.encodeArgs(address(1), hat1_1, hex"00")); + testFuzz_calculateSalt(harness.encodeArgs(address(1), hat1_1, hex"00"), 0); } } @@ -160,7 +167,7 @@ contract Internal_getHatsModuleAddress is InternalTest { function test_getHatsModuleAddress_validHat() public { bytes memory args = harness.encodeArgs(address(1), hat1_1, hex"00"); - testFuzz_getHatsModuleAddress(address(1), args, harness.calculateSalt(args)); + testFuzz_getHatsModuleAddress(address(1), args, harness.calculateSalt(args, 0)); } } @@ -171,36 +178,64 @@ contract Internal_createModule is InternalTest { implementation = address(new HatsModule("test implementation")); } - function test_createToggle_1() public { + function test_createModule_1() public { hatId = 1; otherArgs = hex"01"; bytes memory args = harness.encodeArgs(implementation, hatId, otherArgs); - instance = harness.createModule(implementation, hatId, otherArgs); - assertEq(address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args))); + saltNonce = 1; + instance = harness.createModule(implementation, hatId, otherArgs, saltNonce); + assertEq( + address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args, saltNonce)) + ); } - function test_createToggle_0() public { + function test_createModule_0() public { hatId = 0; otherArgs = hex"00"; bytes memory args = harness.encodeArgs(implementation, hatId, otherArgs); - instance = harness.createModule(implementation, hatId, otherArgs); - assertEq(address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args))); + saltNonce = 1; + instance = harness.createModule(implementation, hatId, otherArgs, saltNonce); + assertEq( + address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args, saltNonce)) + ); } - function test_createToggle_max() public { + function test_createModule_max() public { hatId = type(uint256).max; otherArgs = largeBytes; bytes memory args = harness.encodeArgs(implementation, hatId, otherArgs); - instance = harness.createModule(implementation, hatId, otherArgs); - assertEq(address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args))); + saltNonce = 1; + instance = harness.createModule(implementation, hatId, otherArgs, saltNonce); + assertEq( + address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args, saltNonce)) + ); } - function test_createToggle_validHat() public { + function test_createModule_validHat() public { hatId = hat1_1; otherArgs = hex"01"; bytes memory args = harness.encodeArgs(implementation, hatId, otherArgs); - instance = harness.createModule(implementation, hatId, otherArgs); - assertEq(address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args))); + saltNonce = 1; + instance = harness.createModule(implementation, hatId, otherArgs, saltNonce); + assertEq( + address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args, saltNonce)) + ); + } + + function test_createModule_redeployWithNewNonce() public { + hatId = hat1_1; + otherArgs = hex"01"; + bytes memory args = harness.encodeArgs(implementation, hatId, otherArgs); + saltNonce = 1; + instance = harness.createModule(implementation, hatId, otherArgs, saltNonce); + assertEq( + address(instance), harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args, saltNonce)) + ); + // redeploy with a new nonce + uint256 newNonce = 2; + address instance2 = harness.createModule(implementation, hatId, otherArgs, newNonce); + assertEq(instance2, harness.getHatsModuleAddress(implementation, args, harness.calculateSalt(args, newNonce))); + assertNotEq(instance, instance2); } } @@ -219,14 +254,20 @@ contract CreateHatsModule is WithImplementationTest { hatId = hat1_1; otherArgs = hex"00"; initData = hex"00"; + saltNonce = 0; } function test_createHatsModule() public { vm.expectEmit(true, true, true, true); emit HatsModuleFactory_ModuleDeployed( - implementation, factory.getHatsModuleAddress(implementation, hatId, otherArgs), hatId, otherArgs, initData + implementation, + factory.getHatsModuleAddress(implementation, hatId, otherArgs, saltNonce), + hatId, + otherArgs, + initData, + saltNonce ); - instance = factory.createHatsModule(implementation, hatId, otherArgs, initData); + instance = factory.createHatsModule(implementation, hatId, otherArgs, initData, saltNonce); assertEq(HatsModule(instance).hatId(), hat1_1, "hat"); assertEq(HatsModule(instance).IMPLEMENTATION(), implementation, "IMPLEMENTATION"); @@ -239,11 +280,39 @@ contract CreateHatsModule is WithImplementationTest { } function test_createHatsModule_alreadyDeployed_reverts() public { - factory.createHatsModule(implementation, hatId, otherArgs, initData); + factory.createHatsModule(implementation, hatId, otherArgs, initData, saltNonce); vm.expectRevert( - abi.encodeWithSelector(HatsModuleFactory_ModuleAlreadyDeployed.selector, implementation, hatId, otherArgs) + abi.encodeWithSelector( + HatsModuleFactory_ModuleAlreadyDeployed.selector, implementation, hatId, otherArgs, saltNonce + ) + ); + factory.createHatsModule(implementation, hatId, otherArgs, initData, saltNonce); + } + + function test_createHatsModule_redeployWithNewNonce() public { + // initial deploy + instance = factory.createHatsModule(implementation, hatId, otherArgs, initData, saltNonce); + assertEq(HatsModule(instance).hatId(), hat1_1, "hat"); + assertEq(HatsModule(instance).IMPLEMENTATION(), implementation, "IMPLEMENTATION"); + assertEq(address(HatsModule(instance).HATS()), address(hats), "HATS"); + assertEq(HatsModule(instance).version(), MODULE_VERSION, "version"); + + // try to redeploy with a new nonce + uint256 newNonce = 1; + vm.expectEmit(true, true, true, true); + emit HatsModuleFactory_ModuleDeployed( + implementation, + factory.getHatsModuleAddress(implementation, hatId, otherArgs, newNonce), + hatId, + otherArgs, + initData, + newNonce ); - factory.createHatsModule(implementation, hatId, otherArgs, initData); + address instance2 = factory.createHatsModule(implementation, hatId, otherArgs, initData, newNonce); + assertEq(HatsModule(instance2).hatId(), hat1_1, "hat"); + assertEq(HatsModule(instance2).IMPLEMENTATION(), implementation, "IMPLEMENTATION"); + assertEq(address(HatsModule(instance2).HATS()), address(hats), "HATS"); + assertEq(HatsModule(instance2).version(), MODULE_VERSION, "version"); } } @@ -254,14 +323,15 @@ contract GetHatsModuleAddress is WithImplementationTest { hatId = hat1_1; otherArgs = hex"00"; initData = hex"00"; + saltNonce = 0; } function test_getHatsModuleAddress_validHat() public { bytes memory args = abi.encodePacked(address(implementation), hats, hatId, otherArgs); address expected = LibClone.predictDeterministicAddress( - address(implementation), args, keccak256(abi.encodePacked(args, block.chainid)), address(factory) + address(implementation), args, keccak256(abi.encodePacked(args, block.chainid, saltNonce)), address(factory) ); - assertEq(factory.getHatsModuleAddress(implementation, hatId, otherArgs), expected); + assertEq(factory.getHatsModuleAddress(implementation, hatId, otherArgs, saltNonce), expected); } } @@ -274,15 +344,29 @@ contract Deployed is InternalTest { hatId = hat1_1; otherArgs = hex"00"; initData = hex"00"; + saltNonce = 0; } // uses the FactoryHarness version for easy access to the internal _createHatsModule function function test_deployed_true() public { - harness.createHatsModule(implementation, hatId, otherArgs, initData); - assertTrue(harness.deployed(implementation, hatId, otherArgs)); + harness.createHatsModule(implementation, hatId, otherArgs, initData, saltNonce); + assertTrue(harness.deployed(implementation, hatId, otherArgs, saltNonce)); } function test_deployed_false() public { - assertFalse(harness.deployed(implementation, hatId, otherArgs)); + assertFalse(harness.deployed(implementation, hatId, otherArgs, saltNonce)); + + harness.createHatsModule(implementation, hatId, otherArgs, initData, saltNonce); + assertFalse(harness.deployed(implementation, hatId, otherArgs, saltNonce + 1)); + } + + function testFuzz_deployed_false(uint256 _saltNonce) public { + _saltNonce = bound(_saltNonce, 1, type(uint256).max); + + // deploy with nonce = 0 + harness.createHatsModule(implementation, hatId, otherArgs, initData, 0); + + // expect false for any nonce > 0 + assertFalse(harness.deployed(implementation, hatId, otherArgs, _saltNonce)); } } diff --git a/test/HatsTogglesChain.t.sol b/test/HatsTogglesChain.t.sol index d45f7b1..4cf4a51 100644 --- a/test/HatsTogglesChain.t.sol +++ b/test/HatsTogglesChain.t.sol @@ -30,6 +30,8 @@ contract DeployImplementationTest is DeployImplementation, Test { address[] expectedModules; + uint256 saltNonce = 1; + function deployInstanceTwoModules( uint256 targetHat, uint256 numClauses, @@ -39,7 +41,9 @@ contract DeployImplementationTest is DeployImplementation, Test { ) public returns (HatsTogglesChain) { bytes memory otherImmutableArgs = abi.encodePacked(numClauses, lengths, _module1, _module2); // deploy the instance - return HatsTogglesChain(deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "")); + return HatsTogglesChain( + deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "", saltNonce) + ); } function deployInstanceThreeModules( @@ -52,7 +56,9 @@ contract DeployImplementationTest is DeployImplementation, Test { ) public returns (HatsTogglesChain) { bytes memory otherImmutableArgs = abi.encodePacked(numClauses, lengths, _module1, _module2, _module3); // deploy the instance - return HatsTogglesChain(deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "")); + return HatsTogglesChain( + deployModuleInstance(FACTORY, address(implementation), targetHat, otherImmutableArgs, "", saltNonce) + ); } function setUp() public virtual {