| add love contract 9bf25a7 julienbrg 10h ago | 1 | // SPDX-License-Identifier: MIT |
| 2 | pragma solidity ^0.8.30; |
| 3 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 4 | import {IWETH} from "../src/IWETH.sol"; |
| add love contract 9bf25a7 julienbrg 10h ago | 5 | import {Love} from "../src/Love.sol"; |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 6 | import {WETHRegistry} from "../src/WETHRegistry.sol"; |
| add love contract 9bf25a7 julienbrg 10h ago | 7 | import {Script, console} from "forge-std/Script.sol"; |
| 8 | |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 9 | /// @title LoveScript |
| 10 | /// @author Julien Béranger |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 11 | /// @notice Brings LOVE up on a chain, in the order the design requires: |
| 12 | /// registry first, wETH registered second, token third. |
| 13 | /// @dev Both contracts go through the canonical deterministic deployer and |
| 14 | /// neither takes constructor arguments, so their creation code is |
| 15 | /// identical on every chain and so are their addresses. That is the whole |
| 16 | /// point — LOVE is one address everywhere, not one per wETH deployment. |
| 17 | /// Keep `solc`, the optimizer settings and `bytecode_hash = "none"` as |
| 18 | /// they are in `foundry.toml`, or both addresses move. |
| add love contract 9bf25a7 julienbrg 10h ago | 19 | contract LoveScript is Script { |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 20 | /// @notice Salt used for `Love` when `SALT` is not set in the environment. |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 21 | bytes32 public constant DEFAULT_SALT = keccak256("LOVE"); |
| 22 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 23 | /// @notice Salt the registry is deployed with. Not configurable: `Love` |
| 24 | /// has the resulting address compiled into it. |
| 25 | bytes32 public constant REGISTRY_SALT = keccak256("LOVE.WETHRegistry"); |
| 26 | |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 27 | /// @notice wETH address used when `WETH` is not set in the environment. |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 28 | /// @dev The OP Stack predeploy, which is where wETH sits on Optimism, |
| 29 | /// Base, Mode, Zora, Lisk, World Chain, Unichain, Soneium and Ink. |
| 30 | /// Chains outside that set need `WETH` set explicitly. |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 31 | address public constant DEFAULT_WETH = 0x4200000000000000000000000000000000000006; |
| 32 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 33 | /// @notice The registry, once `run()` has deployed or found it. |
| 34 | WETHRegistry public registry; |
| 35 | |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 36 | /// @notice The token, once `run()` has deployed or found it. |
| add love contract 9bf25a7 julienbrg 10h ago | 37 | Love public love; |
| 38 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 39 | /// @notice Deploy the registry, register wETH and deploy the token, |
| 40 | /// skipping whichever of those has already happened. |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 41 | /// @dev Reads `SALT` and `WETH` from the environment, falling back to the |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 42 | /// defaults, and asserts both deployments match their predictions. |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 43 | /// @return The deployed token. |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 44 | function run() public returns (Love) { |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 45 | address w = wethAddress(); |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 46 | address predictedRegistry = registryAddress(); |
| 47 | address predicted = predict(salt()); |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 48 | |
| 49 | console.log("deployer ", CREATE2_FACTORY); |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 50 | console.log("registry ", predictedRegistry); |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 51 | console.log("weth ", w); |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 52 | console.log("salt ", vm.toString(salt())); |
| 53 | console.log("initCodeHash ", vm.toString(initCodeHash())); |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 54 | console.log("predicted ", predicted); |
| 55 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 56 | _ensureRegistry(predictedRegistry); |
| 57 | _ensureRegistered(IWETH(w)); |
| 58 | _ensureLove(predicted); |
| add love contract 9bf25a7 julienbrg 10h ago | 59 | |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 60 | return love; |
| 61 | } |
| 62 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 63 | /// @notice Print what `run()` would deploy, without broadcasting. |
| 64 | /// @return predicted The address the current salt derives to. |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 65 | function predict() public view returns (address predicted) { |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 66 | predicted = predict(salt()); |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 67 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 68 | console.log("registry ", registryAddress()); |
| 69 | console.log("weth ", wethAddress()); |
| 70 | console.log("salt ", vm.toString(salt())); |
| 71 | console.log("initCodeHash ", vm.toString(initCodeHash())); |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 72 | console.log("predicted ", predicted); |
| 73 | } |
| 74 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 75 | /// @notice Derive the token's deployment address from a salt. |
| 76 | /// @dev Takes no wETH argument, unlike the address derivation this |
| 77 | /// replaces: wETH is no longer in the creation code, which is why the |
| 78 | /// answer is the same on every chain. |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 79 | /// @param s The CREATE2 salt. |
| 80 | /// @return The address `Love` would land at. |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 81 | function predict(bytes32 s) public pure returns (address) { |
| 82 | return vm.computeCreate2Address(s, initCodeHash(), CREATE2_FACTORY); |
| 83 | } |
| 84 | |
| 85 | /// @notice The address the registry lands at on every chain. |
| 86 | /// @return The registry's CREATE2 address. |
| 87 | function registryAddress() public pure returns (address) { |
| 88 | return vm.computeCreate2Address(REGISTRY_SALT, keccak256(registryInitCode()), CREATE2_FACTORY); |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 89 | } |
| 90 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 91 | /// @notice The token's creation code, which now carries no arguments. |
| 92 | /// @return The creation code handed to CREATE2. |
| 93 | function initCode() public pure returns (bytes memory) { |
| 94 | return type(Love).creationCode; |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 95 | } |
| 96 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 97 | /// @notice Hash of the token's creation code. |
| 98 | /// @return The keccak256 of `initCode()`. |
| 99 | function initCodeHash() public pure returns (bytes32) { |
| 100 | return keccak256(initCode()); |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 101 | } |
| 102 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 103 | /// @notice The registry's creation code. |
| 104 | /// @return The creation code handed to CREATE2. |
| 105 | function registryInitCode() public pure returns (bytes memory) { |
| 106 | return type(WETHRegistry).creationCode; |
| 107 | } |
| 108 | |
| 109 | /// @notice The salt to deploy the token with. |
| 110 | /// @dev `SALT` overrides the default, e.g. to mine a vanity address or to |
| 111 | /// route around a chain where the registry has been squatted. |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 112 | /// @return The configured salt, or `DEFAULT_SALT`. |
| Merge 1-add-main-logic into main dded29a rickub 9h ago | 113 | function salt() public view returns (bytes32) { |
| 114 | return vm.envOr("SALT", DEFAULT_SALT); |
| add love contract 9bf25a7 julienbrg 10h ago | 115 | } |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 116 | |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 117 | /// @notice The wETH address to register on this chain. |
| 118 | /// @dev `WETH` overrides the default, which only holds on OP Stack chains. |
| 119 | /// Ignored once a wETH is already registered. |
| Merge 3-peg-love-to-weth into main ce13509 rickub 8h ago | 120 | /// @return The configured wETH address, or `DEFAULT_WETH`. |
| 121 | function wethAddress() public view returns (address) { |
| 122 | return vm.envOr("WETH", DEFAULT_WETH); |
| 123 | } |
| Merge 5-weth-bytecode-verification into main 2f6da5e rickub 6h ago | 124 | |
| 125 | /// @dev Deploys the registry unless it is already there. |
| 126 | /// @param predicted Where it should land. |
| 127 | function _ensureRegistry(address predicted) private { |
| 128 | if (predicted.code.length > 0) { |
| 129 | console.log("registry already deployed"); |
| 130 | registry = WETHRegistry(payable(predicted)); |
| 131 | return; |
| 132 | } |
| 133 | |
| 134 | vm.startBroadcast(); |
| 135 | registry = new WETHRegistry{salt: REGISTRY_SALT}(); |
| 136 | vm.stopBroadcast(); |
| 137 | |
| 138 | require(address(registry) == predicted, "LoveScript: registry address mismatch"); |
| 139 | console.log("registry deployed at", address(registry)); |
| 140 | } |
| 141 | |
| 142 | /// @dev Registers `candidate` unless a wETH is already registered. Reverts |
| 143 | /// inside the registry if the candidate's code is not a reviewed wETH |
| 144 | /// — which is the intended outcome on a chain whose wETH has not been |
| 145 | /// reviewed, not a script failure to work around. |
| 146 | /// @param candidate The wETH to register. |
| 147 | function _ensureRegistered(IWETH candidate) private { |
| 148 | IWETH registered = registry.weth(); |
| 149 | |
| 150 | if (address(registered) != address(0)) { |
| 151 | console.log("weth already registered", address(registered)); |
| 152 | require( |
| 153 | address(registered) == address(candidate) || address(candidate) == DEFAULT_WETH, |
| 154 | "LoveScript: a different weth is registered on this chain" |
| 155 | ); |
| 156 | return; |
| 157 | } |
| 158 | |
| 159 | vm.startBroadcast(); |
| 160 | registry.register{value: registry.PROBE()}(candidate); |
| 161 | vm.stopBroadcast(); |
| 162 | |
| 163 | console.log("weth registered", address(registry.weth())); |
| 164 | } |
| 165 | |
| 166 | /// @dev Deploys the token unless it is already there. |
| 167 | /// @param predicted Where it should land. |
| 168 | function _ensureLove(address predicted) private { |
| 169 | if (predicted.code.length > 0) { |
| 170 | console.log("already deployed, nothing to do"); |
| 171 | love = Love(predicted); |
| 172 | return; |
| 173 | } |
| 174 | |
| 175 | vm.startBroadcast(); |
| 176 | love = new Love{salt: salt()}(); |
| 177 | vm.stopBroadcast(); |
| 178 | |
| 179 | require(address(love) == predicted, "LoveScript: address mismatch"); |
| 180 | |
| 181 | console.log("Love deployed at", address(love)); |
| 182 | console.log("name ", love.name()); |
| 183 | console.log("symbol ", love.symbol()); |
| 184 | console.log("weth ", address(love.WETH())); |
| 185 | console.log("rate ", love.RATE()); |
| 186 | console.log("totalSupply ", love.totalSupply()); |
| 187 | } |
| add love contract 9bf25a7 julienbrg 10h ago | 188 | } |