// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Script, console} from "forge-std/Script.sol"; import {WETH10} from "../contracts/tokens/WETH10.sol"; import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol"; /** * @title DeployWETH10ToExactAddress * @notice Deploy WETH10 to exact address 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f using CREATE2 * @dev This script uses CREATE2 to deploy to the exact address specified in genesis.json */ contract DeployWETH10ToExactAddress is Script { // Target address from genesis.json address constant TARGET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F; function run() external { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); address deployer = vm.addr(deployerPrivateKey); console.log("Deploying WETH10 to exact address using CREATE2"); console.log("Target address:", vm.toString(TARGET_WETH10)); console.log("Deployer:", vm.toString(deployer)); vm.startBroadcast(deployerPrivateKey); // Get WETH10 bytecode bytes memory weth10Bytecode = type(WETH10).creationCode; bytes32 bytecodeHash = keccak256(weth10Bytecode); console.log("WETH10 bytecode hash:", vm.toString(bytecodeHash)); // Deploy CREATE2Factory (or use existing if already deployed) CREATE2Factory factory = new CREATE2Factory(); address factoryAddress = address(factory); console.log("CREATE2Factory deployed at:", factoryAddress); // Check if contract already exists on-chain // Note: We'll try to interact with it - if it reverts, it doesn't exist try this.checkContractExists(TARGET_WETH10) returns (bool exists) { if (exists) { console.log("Contract already exists at target address"); WETH10 weth10 = WETH10(payable(TARGET_WETH10)); console.log("WETH10 name:", weth10.name()); console.log("WETH10 symbol:", weth10.symbol()); vm.stopBroadcast(); return; } } catch { // Contract doesn't exist, continue with deployment } // Try multiple deployer addresses: // 1. Current deployer // 2. CREATE2Factory address (as deployer) // 3. Standard CREATE2 deterministic deployer // 4. Addresses from genesis.json address[] memory deployers = new address[](5); deployers[0] = deployer; deployers[1] = factoryAddress; deployers[2] = 0x4e59b44847b379578588920cA78FbF26c0B4956C; // Standard CREATE2 deployer deployers[3] = 0x0742D35CC6634c0532925A3b844bc9E7595f0Beb; // Genesis address with initial balance (padded) deployers[4] = 0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882; // Genesis address with high balance uint256 salt = 0; address foundDeployer = address(0); for (uint256 i = 0; i < deployers.length; i++) { console.log("Trying deployer:", vm.toString(deployers[i])); // Try common salts first salt = findSaltForAddress(deployers[i], weth10Bytecode, TARGET_WETH10); if (salt != 0) { foundDeployer = deployers[i]; console.log("Found salt with common salts!"); break; } // Try brute force console.log("Brute forcing salt (this may take a while)..."); salt = bruteForceSalt(deployers[i], weth10Bytecode, TARGET_WETH10); if (salt != 0) { foundDeployer = deployers[i]; console.log("Found salt via brute force!"); break; } } if (salt != 0 && foundDeployer != address(0)) { console.log("Found salt:", vm.toString(salt)); console.log("Using deployer:", vm.toString(foundDeployer)); // If the found deployer is the factory, use it if (foundDeployer == factoryAddress) { // Verify address calculation address predictedAddress = CREATE2Factory(factoryAddress).computeAddress(weth10Bytecode, salt); console.log("Predicted address:", vm.toString(predictedAddress)); if (predictedAddress == TARGET_WETH10) { // Deploy using CREATE2Factory address weth10Address = factory.deploy(weth10Bytecode, salt); console.log("WETH10 deployed at:", weth10Address); require(weth10Address == TARGET_WETH10, "Address mismatch"); // Verify deployment WETH10 weth10 = WETH10(payable(weth10Address)); console.log("WETH10 name:", weth10.name()); console.log("WETH10 symbol:", weth10.symbol()); console.log("WETH10 decimals:", weth10.decimals()); } else { revert("Predicted address does not match target"); } } else { // Need to deploy using the found deployer address directly console.log("Deployer address found, but not using factory."); console.log("You may need to deploy using the deployer address:", vm.toString(foundDeployer)); console.log("Salt:", vm.toString(salt)); revert("Deployer is not the factory - manual deployment may be required"); } } else { revert("Could not find salt and deployer combination to produce target address"); } vm.stopBroadcast(); console.log("\n=== Deployment Summary ==="); console.log("WETH10 Address:", vm.toString(TARGET_WETH10)); console.log("CREATE2Factory:", vm.toString(factoryAddress)); console.log("Salt:", vm.toString(salt)); } function findSaltForAddress( address deployerAddr, bytes memory bytecode, address targetAddr ) internal pure returns (uint256) { // Try common salts bytes32 bytecodeHash = keccak256(bytecode); bytes32[] memory commonSalts = new bytes32[](10); commonSalts[0] = keccak256("WETH10"); commonSalts[1] = keccak256("WETH"); commonSalts[2] = keccak256("Wrapped Ether"); commonSalts[3] = keccak256("ChainID-138-WETH10"); commonSalts[4] = keccak256(abi.encodePacked("WETH10", uint256(138))); commonSalts[5] = bytes32(uint256(0)); // Zero salt commonSalts[6] = bytes32(uint256(1)); // Salt of 1 commonSalts[7] = bytes32(uint256(138)); // Chain ID commonSalts[8] = keccak256(abi.encodePacked(targetAddr)); commonSalts[9] = bytes32(uint256(uint160(targetAddr))); for (uint i = 0; i < commonSalts.length; i++) { bytes32 hash = keccak256( abi.encodePacked(bytes1(0xff), deployerAddr, commonSalts[i], bytecodeHash) ); address computedAddr = address(uint160(uint256(hash))); if (computedAddr == targetAddr) { return uint256(commonSalts[i]); } } return 0; } function bruteForceSalt( address deployerAddr, bytes memory bytecode, address targetAddr ) internal pure returns (uint256) { // Enhanced brute force: checks more values and different salt patterns bytes32 bytecodeHash = keccak256(bytecode); // Try sequential salts first (0 to 10,000) for (uint256 i = 0; i < 10000; i++) { bytes32 salt = bytes32(i); bytes32 hash = keccak256( abi.encodePacked(bytes1(0xff), deployerAddr, salt, bytecodeHash) ); address computedAddr = address(uint160(uint256(hash))); if (computedAddr == targetAddr) { return uint256(salt); } } // Try powers of 2 and common values uint256[] memory commonValues = new uint256[](20); commonValues[0] = 138; // Chain ID commonValues[1] = 1; commonValues[2] = 2; commonValues[3] = 10; commonValues[4] = 100; commonValues[5] = 1000; commonValues[6] = 10000; commonValues[7] = 100000; commonValues[8] = type(uint128).max; commonValues[9] = type(uint256).max; for (uint256 j = 10; j < 20; j++) { commonValues[j] = uint256(keccak256(abi.encodePacked("WETH10", j))); } for (uint256 k = 0; k < commonValues.length; k++) { bytes32 salt = bytes32(commonValues[k]); bytes32 hash = keccak256( abi.encodePacked(bytes1(0xff), deployerAddr, salt, bytecodeHash) ); address computedAddr = address(uint160(uint256(hash))); if (computedAddr == targetAddr) { return uint256(salt); } } return 0; } // Helper function to check if contract exists function checkContractExists(address addr) external view returns (bool) { uint256 size; assembly { size := extcodesize(addr) } return size > 0; } }