| Merge 1-add-main-logic into main dded29a rickub 6h ago | 1 | // SPDX-License-Identifier: MIT |
| 2 | pragma solidity ^0.8.30; |
| 3 | |
| 4 | import {LoveScript} from "../script/Love.s.sol"; |
| 5 | import {Love} from "../src/Love.sol"; |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 6 | import {Fixtures} from "./Fixtures.sol"; |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 7 | import {Test} from "forge-std/Test.sol"; |
| 8 | |
| Merge 3-peg-love-to-weth into main ce13509 rickub 5h ago | 9 | /// @title LoveCreate2Test |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 10 | /// @notice The deployment address now derives from the salt and the creation |
| 11 | /// code alone. wETH used to be a constructor argument and so part of |
| 12 | /// that creation code, which gave the token a different address on |
| 13 | /// every chain whose wETH sat elsewhere; it is read from the registry |
| 14 | /// instead. These tests pin the consequence: same salt, same address, |
| 15 | /// on any chain, whatever its wETH is and wherever it lives. |
| 16 | contract LoveCreate2Test is Test, Fixtures { |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 17 | bytes32 constant SALT = keccak256("LOVE"); |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 18 | address constant OP_STACK_WETH = 0x4200000000000000000000000000000000000006; |
| 19 | address constant MAINNET_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 20 | |
| 21 | LoveScript script; |
| 22 | |
| 23 | function setUp() public { |
| 24 | script = new LoveScript(); |
| 25 | |
| 26 | // The canonical deterministic deployer, as it exists on live networks. |
| 27 | assertGt(CREATE2_FACTORY.code.length, 0, "create2 deployer missing"); |
| 28 | } |
| 29 | |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 30 | /*////////////////////////////////////////////////////////////// |
| 31 | THE REGISTRY |
| 32 | //////////////////////////////////////////////////////////////*/ |
| 33 | |
| 34 | /// @dev `Love` has the registry's address compiled in, so if the registry's |
| 35 | /// creation code moves — an edit to the contract, a solc bump, a |
| 36 | /// change of optimizer settings — this fails rather than shipping a |
| 37 | /// token pointing at an empty address. |
| 38 | function test_LoveRegistryConstantMatchesDeterministicAddress() public { |
| 39 | setUpChain(OP_STACK_WETH, WETH9_OP_LEGACY); |
| 40 | |
| 41 | assertEq(new Love().REGISTRY(), script.registryAddress()); |
| 42 | } |
| 43 | |
| 44 | function test_RegistryLandsAtTheAddressLoveExpects() public { |
| 45 | assertEq(address(deployRegistry()), script.registryAddress()); |
| 46 | } |
| 47 | |
| 48 | /// @dev The registry takes no constructor arguments, which is what makes |
| 49 | /// its address the same everywhere. |
| 50 | function test_RegistryAddressIsChainAgnostic() public { |
| 51 | address onThisChain = script.registryAddress(); |
| 52 | |
| 53 | vm.chainId(137); |
| 54 | assertEq(script.registryAddress(), onThisChain); |
| 55 | |
| 56 | vm.chainId(42_161); |
| 57 | assertEq(script.registryAddress(), onThisChain); |
| 58 | } |
| 59 | |
| 60 | /*////////////////////////////////////////////////////////////// |
| 61 | THE DERIVATION |
| 62 | //////////////////////////////////////////////////////////////*/ |
| 63 | |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 64 | function test_DeploysAtPredictedAddress() public { |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 65 | setUpChain(OP_STACK_WETH, WETH9_OP_LEGACY); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 66 | |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 67 | address predicted = vm.computeCreate2Address(SALT, keccak256(script.initCode()), CREATE2_FACTORY); |
| 68 | Love deployed = Love(_deploy(SALT)); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 69 | |
| 70 | assertEq(address(deployed), predicted); |
| 71 | assertEq(deployed.symbol(), "LOVE"); |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 72 | assertEq(address(deployed.WETH()), OP_STACK_WETH); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 73 | } |
| 74 | |
| 75 | /// @dev What the deploy script prints must be what the chain gives back. |
| 76 | function test_ScriptPredictionMatchesDeployment() public { |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 77 | setUpChain(OP_STACK_WETH, WETH9_OP_LEGACY); |
| 78 | |
| 79 | assertEq(script.predict(SALT), _deploy(SALT)); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 80 | } |
| 81 | |
| 82 | function test_ScriptUsesKeccakOfLoveAsDefaultSalt() public view { |
| 83 | assertEq(script.DEFAULT_SALT(), SALT); |
| 84 | } |
| 85 | |
| Merge 3-peg-love-to-weth into main ce13509 rickub 5h ago | 86 | function test_ScriptDefaultsToOpStackWeth() public view { |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 87 | assertEq(script.DEFAULT_WETH(), OP_STACK_WETH); |
| Merge 3-peg-love-to-weth into main ce13509 rickub 5h ago | 88 | } |
| 89 | |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 90 | /// @dev Same salt, same code, same address — and unlike before, no wETH |
| 91 | /// argument that could make the answer differ. |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 92 | function test_PredictionIsChainAgnostic() public { |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 93 | address onThisChain = script.predict(SALT); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 94 | |
| 95 | vm.chainId(137); |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 96 | assertEq(script.predict(SALT), onThisChain); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 97 | |
| 98 | vm.chainId(42_161); |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 99 | assertEq(script.predict(SALT), onThisChain); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 100 | } |
| 101 | |
| 102 | function test_DifferentSaltsGiveDifferentAddresses() public view { |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 103 | assertTrue(script.predict(SALT) != script.predict(keccak256("LOVE2"))); |
| Merge 3-peg-love-to-weth into main ce13509 rickub 5h ago | 104 | } |
| 105 | |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 106 | /*////////////////////////////////////////////////////////////// |
| 107 | THE POINT OF ALL THIS |
| 108 | //////////////////////////////////////////////////////////////*/ |
| 109 | |
| 110 | /// @dev The test that replaces `test_DifferentWethGivesDifferentAddress`. |
| 111 | /// Two chains, two different wETH implementations at two different |
| 112 | /// addresses, one LOVE address. This is what the registry buys. |
| 113 | function test_SameAddressOnChainsWithDifferentWeth() public { |
| 114 | // A snapshot stands in for a second chain. Etching the code away is |
| 115 | // not enough: a deployed account keeps its nonce, and CREATE2 refuses |
| 116 | // to build over that, so the registry could never be redeployed. |
| 117 | uint256 freshChain = vm.snapshotState(); |
| 118 | |
| 119 | setUpChain(OP_STACK_WETH, WETH9_OP_LEGACY); |
| 120 | address onOpStack = _deploy(SALT); |
| 121 | assertEq(address(Love(onOpStack).WETH()), OP_STACK_WETH); |
| 122 | |
| 123 | vm.revertToState(freshChain); |
| 124 | |
| 125 | // Different chain id, a different wETH implementation, at a different |
| 126 | // address. |
| 127 | vm.chainId(1); |
| 128 | setUpChain(MAINNET_WETH, WETH9_CANONICAL); |
| 129 | address onMainnet = _deploy(SALT); |
| 130 | assertEq(address(Love(onMainnet).WETH()), MAINNET_WETH); |
| 131 | |
| 132 | assertEq(onOpStack, onMainnet, "LOVE must land at one address on both chains"); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 133 | } |
| 134 | |
| 135 | /// @dev Redeploying with the same salt must fail, not silently return the |
| 136 | /// existing token. |
| 137 | function test_RevertWhen_RedeployingWithSameSalt() public { |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 138 | setUpChain(OP_STACK_WETH, WETH9_OP_LEGACY); |
| 139 | _deploy(SALT); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 140 | |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 141 | (bool ok,) = CREATE2_FACTORY.call(abi.encodePacked(SALT, script.initCode())); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 142 | assertFalse(ok); |
| 143 | } |
| 144 | |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 145 | function testFuzz_PredictionMatchesDeployment(bytes32 salt) public { |
| 146 | setUpChain(OP_STACK_WETH, WETH9_OP_LEGACY); |
| 147 | |
| 148 | assertEq(script.predict(salt), _deploy(salt)); |
| 149 | } |
| 150 | |
| 151 | /*////////////////////////////////////////////////////////////// |
| 152 | CONSTRUCTOR GUARDS |
| 153 | //////////////////////////////////////////////////////////////*/ |
| 154 | |
| 155 | function test_RevertWhen_RegistryNotDeployed() public { |
| 156 | vm.expectRevert(abi.encodeWithSelector(Love.RegistryNotDeployed.selector, script.registryAddress())); |
| 157 | new Love(); |
| 158 | } |
| 159 | |
| 160 | function test_RevertWhen_WethNotRegistered() public { |
| 161 | deployRegistry(); |
| 162 | |
| 163 | vm.expectRevert(abi.encodeWithSelector(Love.WethNotRegistered.selector, script.registryAddress())); |
| 164 | new Love(); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 165 | } |
| 166 | |
| Merge 3-peg-love-to-weth into main ce13509 rickub 5h ago | 167 | /// @dev Deploys through the canonical deterministic deployer, as the script does. |
| read weth from the registry instead of a constructor arg ffa4716 julienbrg 4h ago | 168 | function _deploy(bytes32 salt) internal returns (address deployed) { |
| 169 | (bool ok, bytes memory ret) = CREATE2_FACTORY.call(abi.encodePacked(salt, script.initCode())); |
| Merge 1-add-main-logic into main dded29a rickub 6h ago | 170 | require(ok, "create2 deployment failed"); |
| 171 | // casting to 'bytes20' is safe because the deployer returns the 20-byte address, raw |
| 172 | // forge-lint: disable-next-line(unsafe-typecast) |
| 173 | deployed = address(bytes20(ret)); |
| 174 | } |
| 175 | } |