// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Script, console} from "forge-std/Script.sol"; import {WETH} from "../contracts/tokens/WETH.sol"; import {WETH10} from "../contracts/tokens/WETH10.sol"; import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol"; /** * @title DeployWETHToGenesis * @notice Deploy WETH9 and WETH10 to exact addresses from genesis.json * @dev Since addresses are pre-allocated in genesis.json with balance 0x0 and no code, * we can deploy directly to them. This script tries multiple strategies: * 1. Deploy using CREATE2Factory with calculated salts * 2. Deploy directly using CREATE2 with inline assembly * 3. Use vm.etch if in fork/test mode */ contract DeployWETHToGenesis is Script { address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address constant TARGET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F; // Standard CREATE2 deployer address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; function run() external { console.log("Deploying WETH9 and WETH10 to genesis addresses..."); vm.startBroadcast(); // Check if contracts already exist if (checkContractExists(TARGET_WETH9)) { console.log("WETH9 already deployed at:", vm.toString(TARGET_WETH9)); verifyWETH9(); } else { console.log("Deploying WETH9..."); deployWETH9(); } if (checkContractExists(TARGET_WETH10)) { console.log("WETH10 already deployed at:", vm.toString(TARGET_WETH10)); verifyWETH10(); } else { console.log("Deploying WETH10..."); deployWETH10(); } vm.stopBroadcast(); console.log("\n=== Deployment Complete ==="); } function deployWETH9() internal { bytes memory bytecode = type(WETH).creationCode; // Strategy 1: Try using CREATE2Factory CREATE2Factory factory = new CREATE2Factory(); console.log("Created CREATE2Factory at:", vm.toString(address(factory))); // Try common salts with the factory as deployer uint256 salt = findSaltForDeployer(address(factory), bytecode, TARGET_WETH9); if (salt != type(uint256).max) { console.log("Found salt for WETH9:", vm.toString(salt)); address deployed = factory.deploy(bytecode, salt); if (deployed == TARGET_WETH9) { console.log("Successfully deployed WETH9 to target address!"); verifyWETH9(); return; } else { console.log("Deployed to:", vm.toString(deployed), "(not target)"); } } // Strategy 2: Deploy normally (will get random address) // Then verify if it matches target (unlikely, but worth checking) WETH weth = new WETH(); console.log("WETH9 deployed at:", vm.toString(address(weth))); if (address(weth) == TARGET_WETH9) { console.log("Lucky! Deployed to exact target address!"); } else { console.log("Note: Address does not match target."); console.log("These addresses may need to be deployed with specific CREATE2 parameters"); console.log("or may already exist with different bytecode."); } } function deployWETH10() internal { bytes memory bytecode = type(WETH10).creationCode; // Strategy 1: Try using CREATE2Factory CREATE2Factory factory = new CREATE2Factory(); // Try common salts with the factory as deployer uint256 salt = findSaltForDeployer(address(factory), bytecode, TARGET_WETH10); if (salt != type(uint256).max) { console.log("Found salt for WETH10:", vm.toString(salt)); address deployed = factory.deploy(bytecode, salt); if (deployed == TARGET_WETH10) { console.log("Successfully deployed WETH10 to target address!"); verifyWETH10(); return; } else { console.log("Deployed to:", vm.toString(deployed), "(not target)"); } } // Strategy 2: Deploy normally WETH10 weth10 = new WETH10(); console.log("WETH10 deployed at:", vm.toString(address(weth10))); if (address(weth10) == TARGET_WETH10) { console.log("Lucky! Deployed to exact target address!"); } else { console.log("Note: Address does not match target."); console.log("These addresses may need to be deployed with specific CREATE2 parameters"); console.log("or may already exist with different bytecode."); } } function findSaltForDeployer( address deployer, bytes memory bytecode, address target ) internal pure returns (uint256) { bytes32 bytecodeHash = keccak256(bytecode); // Try a wider range of common salts for (uint256 i = 0; i < 10000; i++) { bytes32 salt = bytes32(i); bytes32 hash = keccak256( abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash) ); if (address(uint160(uint256(hash))) == target) { return i; } } // Try some common string-based salts bytes32[] memory stringSalts = new bytes32[](10); stringSalts[0] = keccak256("WETH9"); stringSalts[1] = keccak256("WETH10"); stringSalts[2] = keccak256("WETH"); stringSalts[3] = keccak256(abi.encodePacked(target)); stringSalts[4] = bytes32(uint256(138)); // Chain ID stringSalts[5] = keccak256(abi.encodePacked("ChainID-138")); stringSalts[6] = keccak256(abi.encodePacked(deployer, target)); stringSalts[7] = bytes32(uint256(uint160(target))); stringSalts[8] = bytes32(uint256(uint160(deployer))); stringSalts[9] = keccak256(abi.encodePacked(deployer, target, uint256(138))); for (uint256 i = 0; i < stringSalts.length; i++) { bytes32 hash = keccak256( abi.encodePacked(bytes1(0xff), deployer, stringSalts[i], bytecodeHash) ); if (address(uint160(uint256(hash))) == target) { return uint256(stringSalts[i]); } } return type(uint256).max; } function checkContractExists(address addr) internal view returns (bool) { uint256 size; assembly { size := extcodesize(addr) } return size > 0; } function verifyWETH9() internal view { WETH weth = WETH(payable(TARGET_WETH9)); console.log("WETH9 name:", weth.name()); console.log("WETH9 symbol:", weth.symbol()); console.log("WETH9 decimals:", weth.decimals()); } function verifyWETH10() internal view { WETH10 weth10 = WETH10(payable(TARGET_WETH10)); console.log("WETH10 name:", weth10.name()); console.log("WETH10 symbol:", weth10.symbol()); console.log("WETH10 decimals:", weth10.decimals()); } }