// SPDX-License-Identifier: MIT pragma solidity ^0.8.30; import {Love} from "../src/Love.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Script, console} from "forge-std/Script.sol"; /// @title LoveScript /// @author Julien Béranger /// @notice Deploys `Love` with CREATE2 through the canonical deterministic /// deployer. The address depends on the salt and the creation code, and /// the creation code embeds the wETH address — so the token only gets /// the same address on chains where wETH sits at the same address. Keep /// `solc`, the optimizer settings and `bytecode_hash = "none"` as they /// are in `foundry.toml`, or the address changes too. contract LoveScript is Script { /// @notice Salt used when `SALT` is not set in the environment. bytes32 public constant DEFAULT_SALT = keccak256("LOVE"); /// @notice wETH address used when `WETH` is not set in the environment. /// @dev wETH on Base, Optimism and every other OP-Stack chain. address public constant DEFAULT_WETH = 0x4200000000000000000000000000000000000006; /// @notice The token, once `run()` has deployed or found it. Love public love; /// @notice Deploy `Love`, or return it untouched if it is already there. /// @dev Reads `SALT` and `WETH` from the environment, falling back to the /// defaults, and asserts the deployed address matches the prediction. /// @return The deployed token. function run() public returns (Love) { bytes32 s = salt(); address w = wethAddress(); address predicted = predict(s, w); console.log("deployer ", CREATE2_FACTORY); console.log("salt ", vm.toString(s)); console.log("weth ", w); console.log("initCodeHash ", vm.toString(initCodeHash(w))); console.log("predicted ", predicted); if (predicted.code.length > 0) { console.log("already deployed, nothing to do"); love = Love(predicted); return love; } vm.startBroadcast(); love = new Love{salt: s}(IERC20(w)); vm.stopBroadcast(); require(address(love) == predicted, "LoveScript: address mismatch"); console.log("Love deployed at", address(love)); console.log("name ", love.name()); console.log("symbol ", love.symbol()); console.log("weth ", address(love.WETH())); console.log("rate ", love.RATE()); console.log("totalSupply ", love.totalSupply()); return love; } /// @notice Print the address `run()` would deploy to, without broadcasting. /// @return predicted The address the current salt and wETH derive to. function predict() public view returns (address predicted) { bytes32 s = salt(); address w = wethAddress(); predicted = predict(s, w); console.log("salt ", vm.toString(s)); console.log("weth ", w); console.log("initCodeHash ", vm.toString(initCodeHash(w))); console.log("predicted ", predicted); } /// @notice Derive the deployment address from a salt and a wETH address. /// @param s The CREATE2 salt. /// @param w The wETH the token would be pegged to. /// @return The address `Love` would land at. function predict(bytes32 s, address w) public pure returns (address) { return vm.computeCreate2Address(s, initCodeHash(w), CREATE2_FACTORY); } /// @notice The creation code CREATE2 is handed, constructor argument included. /// @param w The wETH the token would be pegged to. /// @return The creation code, with `w` ABI-encoded onto it. function initCode(address w) public pure returns (bytes memory) { return abi.encodePacked(type(Love).creationCode, abi.encode(w)); } /// @notice Hash of the creation code, the second input to the address derivation. /// @param w The wETH the token would be pegged to. /// @return The keccak256 of `initCode(w)`. function initCodeHash(address w) public pure returns (bytes32) { return keccak256(initCode(w)); } /// @notice The salt to deploy with. /// @dev `SALT` overrides the default, e.g. to mine a vanity address. /// @return The configured salt, or `DEFAULT_SALT`. function salt() public view returns (bytes32) { return vm.envOr("SALT", DEFAULT_SALT); } /// @notice The wETH address to peg the deployment to. /// @dev `WETH` overrides the default, which only holds on OP-Stack chains. /// @return The configured wETH address, or `DEFAULT_WETH`. function wethAddress() public view returns (address) { return vm.envOr("WETH", DEFAULT_WETH); } }