diff --git a/.gas-snapshot b/.gas-snapshot index 44c8b8f..98d41a3 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -52,20 +52,20 @@ TrailsIntentEntrypointTest:testNonceIncrementsOnDeposit() (gas: 91830) TrailsIntentEntrypointTest:testPermitAmountExcessiveWithFee() (gas: 61879) TrailsIntentEntrypointTest:testPermitAmountInsufficientWithFee() (gas: 60889) TrailsIntentEntrypointTest:testVersionConstant() (gas: 10506) -TrailsRouterDeploymentTest:test_DeployTrailsRouter_SameAddress() (gas: 1856117) -TrailsRouterDeploymentTest:test_DeployTrailsRouter_Success() (gas: 1846511) -TrailsRouterDeploymentTest:test_DeployedRouter_HasCorrectConfiguration() (gas: 1846307) -TrailsRouterShimDeploymentTest:test_DeployRouterShim_SameAddress() (gas: 7530847) -TrailsRouterShimDeploymentTest:test_DeployRouterShim_Success() (gas: 4873481) -TrailsRouterShimDeploymentTest:test_DeployedContract_HasCorrectConfiguration() (gas: 4873526) +TrailsRouterDeploymentTest:test_DeployTrailsRouter_SameAddress() (gas: 1864438) +TrailsRouterDeploymentTest:test_DeployTrailsRouter_Success() (gas: 1852254) +TrailsRouterDeploymentTest:test_DeployedRouter_HasCorrectConfiguration() (gas: 1852050) +TrailsRouterShimDeploymentTest:test_DeployRouterShim_SameAddress() (gas: 7613006) +TrailsRouterShimDeploymentTest:test_DeployRouterShim_Success() (gas: 4916218) +TrailsRouterShimDeploymentTest:test_DeployedContract_HasCorrectConfiguration() (gas: 4916264) TrailsRouterShimTest:testConstructorValidation() (gas: 69336) TrailsRouterShimTest:testForwardToRouterReturnValue() (gas: 713355) TrailsRouterShimTest:testRouterAddressImmutable() (gas: 1391679) TrailsRouterShimTest:test_constructor_revert_zeroRouter() (gas: 68984) -TrailsRouterShimTest:test_delegatecall_forwards_and_sets_sentinel_sstore_inactive() (gas: 1848663) +TrailsRouterShimTest:test_delegatecall_forwards_and_sets_sentinel_sstore_inactive() (gas: 1848930) TrailsRouterShimTest:test_delegatecall_forwards_and_sets_sentinel_tstore_active() (gas: 38392) TrailsRouterShimTest:test_delegatecall_router_revert_bubbles_as_RouterCallFailed() (gas: 82109) -TrailsRouterShimTest:test_delegatecall_sets_sentinel_with_sstore_when_no_tstore() (gas: 1830888) +TrailsRouterShimTest:test_delegatecall_sets_sentinel_with_sstore_when_no_tstore() (gas: 1831155) TrailsRouterShimTest:test_delegatecall_sets_sentinel_with_tstore_when_supported() (gas: 20706) TrailsRouterShimTest:test_direct_handleSequenceDelegateCall_reverts_not_delegatecall() (gas: 9840) TrailsRouterShimTest:test_forwardToRouter_return_data_handling() (gas: 729052) @@ -78,7 +78,7 @@ TrailsRouterShimTest:test_handleSequenceDelegateCall_with_eth_value() (gas: 3210 TrailsRouterShimTest:test_handleSequenceDelegateCall_zero_call_value() (gas: 26873) TrailsRouterShimTest:test_sentinel_setting_with_different_op_hashes() (gas: 36405) TrailsRouterTest:testDelegateCallWithETH() (gas: 326226) -TrailsRouterTest:testExecute_WithFailingMulticall() (gas: 450176) +TrailsRouterTest:testExecute_WithFailingMulticall() (gas: 156745) TrailsRouterTest:testHandleSequenceDelegateCall_InjectAndCall() (gas: 72655) TrailsRouterTest:testHandleSequenceDelegateCall_RefundAndSweep() (gas: 85388) TrailsRouterTest:testHandleSequenceDelegateCall_Sweep() (gas: 48693) @@ -133,7 +133,7 @@ TrailsRouterTest:test_pullAmountAndExecute_WithToken_ShouldTransferAndExecute() TrailsRouterTest:test_pullAmountAndExecute_WithValidToken_ShouldTransferAndExecute() (gas: 75490) TrailsRouterTest:test_pullAndExecute_WithETH_NoEthSent() (gas: 18454) TrailsRouterTest:test_pullAndExecute_WithETH_ShouldTransferAndExecute() (gas: 72439) -TrailsRouterTest:test_pullAndExecute_WithFailingMulticall() (gas: 494404) +TrailsRouterTest:test_pullAndExecute_WithFailingMulticall() (gas: 202286) TrailsRouterTest:test_pullAndExecute_WithToken_IncorrectValue() (gas: 60552) TrailsRouterTest:test_pullAndExecute_WithValidToken_ShouldTransferFullBalanceAndExecute() (gas: 77324) TrailsRouterTest:test_refundAndSweep_erc20_partialRefund() (gas: 110365) diff --git a/script/TrailsRouter.s.sol b/script/TrailsRouter.s.sol index 174b52e..14b3405 100644 --- a/script/TrailsRouter.s.sol +++ b/script/TrailsRouter.s.sol @@ -24,9 +24,10 @@ contract Deploy is SingletonDeployer { function deployRouter(uint256 pk) public returns (address) { bytes32 salt = bytes32(0); + address multicall3 = 0xcA11bde05977b3631167028862bE2a173976CA11; - // Deploy TrailsRouter - bytes memory initCode = type(TrailsRouter).creationCode; + // Deploy TrailsRouter with constructor arguments + bytes memory initCode = abi.encodePacked(type(TrailsRouter).creationCode, abi.encode(multicall3)); address router = _deployIfNotAlready("TrailsRouter", initCode, salt, pk); return router; diff --git a/src/TrailsRouter.sol b/src/TrailsRouter.sol index 10584d0..33f01d6 100644 --- a/src/TrailsRouter.sol +++ b/src/TrailsRouter.sol @@ -24,7 +24,15 @@ contract TrailsRouter is IDelegatedExtension, ITrailsRouter, DelegatecallGuard, // Immutable Variables // ------------------------------------------------------------------------- - address public immutable MULTICALL3 = 0xcA11bde05977b3631167028862bE2a173976CA11; + address public immutable MULTICALL3; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(address _multicall3) { + MULTICALL3 = _multicall3; + } // ------------------------------------------------------------------------- // Errors diff --git a/test/TrailsRouter.t.sol b/test/TrailsRouter.t.sol index 15c49ee..0de51f7 100644 --- a/test/TrailsRouter.t.sol +++ b/test/TrailsRouter.t.sol @@ -49,6 +49,17 @@ contract RevertingReceiver { } } +// Mock multicall that always fails (for testing storage conflicts) +contract AlwaysFailingMulticall3 { + function aggregate3(IMulticall3.Call3[] calldata) external payable { + revert("MockMulticall3: forced failure"); + } + + function aggregate3Value(IMulticall3.Call3Value[] calldata) external payable { + revert("MockMulticall3: forced failure"); + } +} + contract MockTarget { uint256 public lastAmount; bool public shouldRevert; @@ -199,7 +210,7 @@ contract TrailsRouterTest is Test { MockMulticall3 mockMulticall3 = new MockMulticall3(); vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, address(mockMulticall3).code); - router = new TrailsRouter(); + router = new TrailsRouter(0xcA11bde05977b3631167028862bE2a173976CA11); getter = new MockSenderGetter(); mockToken = new MockERC20("MockToken", "MTK", 18); failingToken = new FailingToken(); @@ -865,17 +876,13 @@ contract TrailsRouterTest is Test { // Save original multicall code bytes memory originalCode = 0xcA11bde05977b3631167028862bE2a173976CA11.code; - // Deploy and etch failing multicall - MockMulticall3 failingMulticall = new MockMulticall3(); + // Deploy and etch always-failing multicall (doesn't use storage, always reverts) + AlwaysFailingMulticall3 failingMulticall = new AlwaysFailingMulticall3(); vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, address(failingMulticall).code); // Verify the etch worked assertEq(keccak256(0xcA11bde05977b3631167028862bE2a173976CA11.code), keccak256(address(failingMulticall).code)); - // Set the failure flag directly in storage since delegatecall uses caller's storage - // The shouldFail variable is at slot 0 in MockMulticall3 - vm.store(address(router), bytes32(0), bytes32(uint256(1))); // Set shouldFail = true in router's storage - IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](1); calls[0] = IMulticall3.Call3({ target: address(getter), allowFailure: false, callData: abi.encodeWithSignature("getSender()") @@ -901,13 +908,10 @@ contract TrailsRouterTest is Test { // Save original multicall code bytes memory originalCode = 0xcA11bde05977b3631167028862bE2a173976CA11.code; - // Mock multicall3 to return failure - MockMulticall3 failingMulticall = new MockMulticall3(); + // Deploy and etch always-failing multicall (doesn't use storage, always reverts) + AlwaysFailingMulticall3 failingMulticall = new AlwaysFailingMulticall3(); vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, address(failingMulticall).code); - // Set the failure flag directly in storage since delegatecall uses caller's storage - vm.store(address(router), bytes32(0), bytes32(uint256(1))); // Set shouldFail = true in router's storage - IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](1); calls[0] = IMulticall3.Call3({ target: address(getter), allowFailure: false, callData: abi.encodeWithSignature("getSender()") diff --git a/test/TrailsRouterShim.t.sol b/test/TrailsRouterShim.t.sol index 0e2bc55..efa59e7 100644 --- a/test/TrailsRouterShim.t.sol +++ b/test/TrailsRouterShim.t.sol @@ -253,7 +253,7 @@ contract TrailsRouterShimTest is Test { // Verify sentinel by re-etching TrailsRouter and validating via delegated entrypoint bytes memory original = address(shimImpl).code; - vm.etch(holder, address(new TrailsRouter()).code); + vm.etch(holder, address(new TrailsRouter(0xcA11bde05977b3631167028862bE2a173976CA11)).code); address payable recipient = payable(address(0x111)); vm.deal(holder, callValue); @@ -309,7 +309,7 @@ contract TrailsRouterShimTest is Test { // Verify via TrailsRouter delegated validation bytes memory original = address(shimImpl).code; - vm.etch(holder, address(new TrailsRouter()).code); + vm.etch(holder, address(new TrailsRouter(0xcA11bde05977b3631167028862bE2a173976CA11)).code); address payable recipient = payable(address(0x112)); vm.deal(holder, 1 ether); bytes memory data = diff --git a/test/script/TrailsRouter.s.t.sol b/test/script/TrailsRouter.s.t.sol index af22ad3..825463e 100644 --- a/test/script/TrailsRouter.s.t.sol +++ b/test/script/TrailsRouter.s.t.sol @@ -26,7 +26,9 @@ contract TrailsRouterDeploymentTest is Test { // Expected predetermined address (calculated using CREATE2) function expectedRouterAddress() internal pure returns (address payable) { - return Create2Utils.calculateCreate2Address(type(TrailsRouter).creationCode, Create2Utils.standardSalt()); + address multicall3 = 0xcA11bde05977b3631167028862bE2a173976CA11; + bytes memory initCode = abi.encodePacked(type(TrailsRouter).creationCode, abi.encode(multicall3)); + return Create2Utils.calculateCreate2Address(initCode, Create2Utils.standardSalt()); } // ------------------------------------------------------------------------- diff --git a/test/script/TrailsRouterShim.s.t.sol b/test/script/TrailsRouterShim.s.t.sol index 219854a..c3ba6fb 100644 --- a/test/script/TrailsRouterShim.s.t.sol +++ b/test/script/TrailsRouterShim.s.t.sol @@ -27,7 +27,9 @@ contract TrailsRouterShimDeploymentTest is Test { // Expected predetermined addresses (calculated using CREATE2) function expectedRouterAddress() internal pure returns (address payable) { - return Create2Utils.calculateCreate2Address(type(TrailsRouter).creationCode, Create2Utils.standardSalt()); + address multicall3 = 0xcA11bde05977b3631167028862bE2a173976CA11; + bytes memory initCode = abi.encodePacked(type(TrailsRouter).creationCode, abi.encode(multicall3)); + return Create2Utils.calculateCreate2Address(initCode, Create2Utils.standardSalt()); } function expectedShimAddress() internal pure returns (address payable) {