diff --git a/.gas-snapshot b/.gas-snapshot index d18764e..b022842 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -48,20 +48,20 @@ TrailsIntentEntrypointTest:testPermitAmountExcessiveWithFee() (gas: 83027) TrailsIntentEntrypointTest:testPermitAmountInsufficientWithFee() (gas: 82059) TrailsIntentEntrypointTest:testUsedIntentsMapping() (gas: 113770) TrailsIntentEntrypointTest:testVersionConstant() (gas: 10381) -TrailsRouterDeploymentTest:test_DeployTrailsRouter_SameAddress() (gas: 1773856) -TrailsRouterDeploymentTest:test_DeployTrailsRouter_Success() (gas: 1764409) -TrailsRouterDeploymentTest:test_DeployedRouter_HasCorrectConfiguration() (gas: 1764205) -TrailsRouterShimDeploymentTest:test_DeployRouterShim_SameAddress() (gas: 7285038) -TrailsRouterShimDeploymentTest:test_DeployRouterShim_Success() (gas: 4709602) -TrailsRouterShimDeploymentTest:test_DeployedContract_HasCorrectConfiguration() (gas: 4709648) +TrailsRouterDeploymentTest:test_DeployTrailsRouter_SameAddress() (gas: 1784084) +TrailsRouterDeploymentTest:test_DeployTrailsRouter_Success() (gas: 1771019) +TrailsRouterDeploymentTest:test_DeployedRouter_HasCorrectConfiguration() (gas: 1770815) +TrailsRouterShimDeploymentTest:test_DeployRouterShim_SameAddress() (gas: 7469868) +TrailsRouterShimDeploymentTest:test_DeployRouterShim_Success() (gas: 4803591) +TrailsRouterShimDeploymentTest:test_DeployedContract_HasCorrectConfiguration() (gas: 4803637) 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: 1766894) +TrailsRouterShimTest:test_delegatecall_forwards_and_sets_sentinel_sstore_inactive() (gas: 1767156) 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: 1749120) +TrailsRouterShimTest:test_delegatecall_sets_sentinel_with_sstore_when_no_tstore() (gas: 1749382) 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) diff --git a/script/TrailsRouter.s.sol b/script/TrailsRouter.s.sol index 174b52e..77186c5 100644 --- a/script/TrailsRouter.s.sol +++ b/script/TrailsRouter.s.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.30; import {SingletonDeployer, console} from "erc2470-libs/script/SingletonDeployer.s.sol"; import {TrailsRouter} from "../src/TrailsRouter.sol"; +import {MULTICALL3_ADDRESS} from "../test/mocks/MockMulticall3.sol"; contract Deploy is SingletonDeployer { // ------------------------------------------------------------------------- @@ -14,7 +15,10 @@ contract Deploy is SingletonDeployer { address deployerAddress = vm.addr(pk); console.log("Deployer Address:", deployerAddress); - address router = deployRouter(pk); + address multicall3 = vm.envOr("MULTICALL3_ADDRESS", MULTICALL3_ADDRESS); + console.log("Multicall3 Address:", multicall3); + + address router = deployRouter(pk, multicall3); console.log("TrailsRouter deployed at:", router); } @@ -22,11 +26,11 @@ contract Deploy is SingletonDeployer { // Deploy Router // ------------------------------------------------------------------------- - function deployRouter(uint256 pk) public returns (address) { + function deployRouter(uint256 pk, address multicall3) public returns (address) { bytes32 salt = bytes32(0); // Deploy TrailsRouter - bytes memory initCode = type(TrailsRouter).creationCode; + bytes memory initCode = abi.encodePacked(type(TrailsRouter).creationCode, abi.encode(multicall3)); address router = _deployIfNotAlready("TrailsRouter", initCode, salt, pk); return router; diff --git a/script/TrailsRouterShim.s.sol b/script/TrailsRouterShim.s.sol index afe0b4d..7617f3a 100644 --- a/script/TrailsRouterShim.s.sol +++ b/script/TrailsRouterShim.s.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.30; import {SingletonDeployer, console} from "erc2470-libs/script/SingletonDeployer.s.sol"; import {TrailsRouterShim} from "../src/TrailsRouterShim.sol"; +import {MULTICALL3_ADDRESS} from "../test/mocks/MockMulticall3.sol"; import {Deploy as TrailsRouterDeploy} from "./TrailsRouter.s.sol"; contract Deploy is SingletonDeployer { @@ -21,14 +22,14 @@ contract Deploy is SingletonDeployer { address deployerAddress = vm.addr(pk); console.log("Deployer Address:", deployerAddress); + address multicall3 = vm.envOr("MULTICALL3_ADDRESS", MULTICALL3_ADDRESS); + console.log("Multicall3 Address:", multicall3); + bytes32 salt = bytes32(0); // Deploy TrailsRouter using the TrailsRouter deployment script TrailsRouterDeploy routerDeploy = new TrailsRouterDeploy(); - routerDeploy.run(); - - // Get the deployed router address from the deployment script - routerAddress = routerDeploy.deployRouter(pk); + routerAddress = routerDeploy.deployRouter(pk, multicall3); console.log("TrailsRouter deployed at:", routerAddress); // Deploy TrailsRouterShim with the router address diff --git a/src/TrailsRouter.sol b/src/TrailsRouter.sol index 22f24b9..31eef3c 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 d16e4f7..e90c33c 100644 --- a/test/TrailsRouter.t.sol +++ b/test/TrailsRouter.t.sol @@ -6,7 +6,7 @@ import {TrailsRouter} from "src/TrailsRouter.sol"; import {DelegatecallGuard} from "src/guards/DelegatecallGuard.sol"; import {MockSenderGetter} from "test/mocks/MockSenderGetter.sol"; import {MockERC20} from "test/mocks/MockERC20.sol"; -import {MockMulticall3} from "test/mocks/MockMulticall3.sol"; +import {MockMulticall3, MULTICALL3_ADDRESS} from "test/mocks/MockMulticall3.sol"; import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; import {IDelegatedExtension} from "wallet-contracts-v3/modules/interfaces/IDelegatedExtension.sol"; @@ -197,9 +197,9 @@ contract TrailsRouterTest is Test { function setUp() public { // Deploy mock multicall3 at the expected address MockMulticall3 mockMulticall3 = new MockMulticall3(); - vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, address(mockMulticall3).code); + vm.etch(MULTICALL3_ADDRESS, address(mockMulticall3).code); - router = new TrailsRouter(); + router = new TrailsRouter(MULTICALL3_ADDRESS); getter = new MockSenderGetter(); mockToken = new MockERC20("MockToken", "MTK", 18); failingToken = new FailingToken(); @@ -346,7 +346,7 @@ contract TrailsRouterTest is Test { } function test_Multicall3Address_IsCorrect() public view { - assertEq(router.MULTICALL3(), 0xcA11bde05977b3631167028862bE2a173976CA11); + assertEq(router.MULTICALL3(), MULTICALL3_ADDRESS); } // ------------------------------------------------------------------------- @@ -795,14 +795,14 @@ contract TrailsRouterTest is Test { function testExecute_WithFailingMulticall() public { // Save original multicall code - bytes memory originalCode = 0xcA11bde05977b3631167028862bE2a173976CA11.code; + bytes memory originalCode = MULTICALL3_ADDRESS.code; // Deploy and etch failing multicall MockMulticall3 failingMulticall = new MockMulticall3(); - vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, address(failingMulticall).code); + vm.etch(MULTICALL3_ADDRESS, address(failingMulticall).code); // Verify the etch worked - assertEq(keccak256(0xcA11bde05977b3631167028862bE2a173976CA11.code), keccak256(address(failingMulticall).code)); + assertEq(keccak256(MULTICALL3_ADDRESS.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 @@ -824,18 +824,18 @@ contract TrailsRouterTest is Test { router.execute(callData); // Restore original code - vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, originalCode); + vm.etch(MULTICALL3_ADDRESS, originalCode); } function test_pullAndExecute_WithFailingMulticall() public { uint256 transferAmount = 100e18; // Save original multicall code - bytes memory originalCode = 0xcA11bde05977b3631167028862bE2a173976CA11.code; + bytes memory originalCode = MULTICALL3_ADDRESS.code; // Mock multicall3 to return failure MockMulticall3 failingMulticall = new MockMulticall3(); - vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, address(failingMulticall).code); + vm.etch(MULTICALL3_ADDRESS, 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 @@ -860,7 +860,7 @@ contract TrailsRouterTest is Test { router.pullAndExecute(address(mockToken), callData); // Restore original code - vm.etch(0xcA11bde05977b3631167028862bE2a173976CA11, originalCode); + vm.etch(MULTICALL3_ADDRESS, originalCode); } function testInjectSweepAndCall_WithETH_ZeroBalance() public { diff --git a/test/TrailsRouterShim.t.sol b/test/TrailsRouterShim.t.sol index 0e2bc55..a946617 100644 --- a/test/TrailsRouterShim.t.sol +++ b/test/TrailsRouterShim.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.30; import {Test} from "forge-std/Test.sol"; +import {MULTICALL3_ADDRESS} from "test/mocks/MockMulticall3.sol"; import {TrailsRouterShim} from "src/TrailsRouterShim.sol"; import {DelegatecallGuard} from "src/guards/DelegatecallGuard.sol"; import {TrailsSentinelLib} from "src/libraries/TrailsSentinelLib.sol"; @@ -253,7 +254,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(MULTICALL3_ADDRESS)).code); address payable recipient = payable(address(0x111)); vm.deal(holder, callValue); @@ -309,7 +310,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(MULTICALL3_ADDRESS)).code); address payable recipient = payable(address(0x112)); vm.deal(holder, 1 ether); bytes memory data = diff --git a/test/mocks/MockMulticall3.sol b/test/mocks/MockMulticall3.sol index a13d7e5..95d7ea5 100644 --- a/test/mocks/MockMulticall3.sol +++ b/test/mocks/MockMulticall3.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.30; import {IMulticall3} from "src/interfaces/IMulticall3.sol"; +address constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11; + // ----------------------------------------------------------------------------- // Mock Contract // ----------------------------------------------------------------------------- diff --git a/test/script/TrailsRouter.s.t.sol b/test/script/TrailsRouter.s.t.sol index af22ad3..2623bd9 100644 --- a/test/script/TrailsRouter.s.t.sol +++ b/test/script/TrailsRouter.s.t.sol @@ -5,6 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {Deploy as TrailsRouterDeploy} from "script/TrailsRouter.s.sol"; import {TrailsRouter} from "src/TrailsRouter.sol"; import {Create2Utils} from "../utils/Create2Utils.sol"; +import {MULTICALL3_ADDRESS} from "../mocks/MockMulticall3.sol"; // ----------------------------------------------------------------------------- // Test Contract @@ -25,8 +26,10 @@ 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()); + function expectedRouterAddress(address multicall3) internal pure returns (address payable) { + return Create2Utils.calculateCreate2Address( + abi.encodePacked(type(TrailsRouter).creationCode, abi.encode(multicall3)), Create2Utils.standardSalt() + ); } // ------------------------------------------------------------------------- @@ -52,7 +55,7 @@ contract TrailsRouterDeploymentTest is Test { _deployScript.run(); // Verify TrailsRouter was deployed at the expected address - address payable expectedAddr = expectedRouterAddress(); + address payable expectedAddr = expectedRouterAddress(MULTICALL3_ADDRESS); assertEq(expectedAddr.code.length > 0, true, "TrailsRouter should be deployed"); // Verify the deployed contract is functional @@ -68,7 +71,7 @@ contract TrailsRouterDeploymentTest is Test { _deployScript.run(); // Verify first deployment address - address payable expectedAddr = expectedRouterAddress(); + address payable expectedAddr = expectedRouterAddress(MULTICALL3_ADDRESS); assertEq(expectedAddr.code.length > 0, true, "First deployment: TrailsRouter deployed"); // Re-set the PRIVATE_KEY for second deployment @@ -89,7 +92,7 @@ contract TrailsRouterDeploymentTest is Test { _deployScript.run(); // Get reference to deployed contract - address payable expectedAddr = expectedRouterAddress(); + address payable expectedAddr = expectedRouterAddress(MULTICALL3_ADDRESS); TrailsRouter router = TrailsRouter(expectedAddr); // Verify contract is deployed and functional diff --git a/test/script/TrailsRouterShim.s.t.sol b/test/script/TrailsRouterShim.s.t.sol index 219854a..9e66a51 100644 --- a/test/script/TrailsRouterShim.s.t.sol +++ b/test/script/TrailsRouterShim.s.t.sol @@ -6,6 +6,7 @@ import {Deploy as TrailsRouterShimDeploy} from "script/TrailsRouterShim.s.sol"; import {TrailsRouterShim} from "src/TrailsRouterShim.sol"; import {TrailsRouter} from "src/TrailsRouter.sol"; import {Create2Utils} from "../utils/Create2Utils.sol"; +import {MULTICALL3_ADDRESS} from "../mocks/MockMulticall3.sol"; // ----------------------------------------------------------------------------- // Test Contract @@ -26,12 +27,14 @@ 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()); + function expectedRouterAddress(address multicall3) internal pure returns (address payable) { + return Create2Utils.calculateCreate2Address( + abi.encodePacked(type(TrailsRouter).creationCode, abi.encode(multicall3)), Create2Utils.standardSalt() + ); } function expectedShimAddress() internal pure returns (address payable) { - address routerAddr = expectedRouterAddress(); + address routerAddr = expectedRouterAddress(MULTICALL3_ADDRESS); bytes memory shimInitCode = abi.encodePacked(type(TrailsRouterShim).creationCode, abi.encode(routerAddr)); return Create2Utils.calculateCreate2Address(shimInitCode, Create2Utils.standardSalt()); }