// 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 {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol"; /** * @title DeployWETH9ToExactAddress * @notice Deploy WETH9 to exact address 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 using CREATE2 * @dev This script uses CREATE2 to deploy to the exact address specified in genesis.json */ contract DeployWETH9ToExactAddress is Script { // Target address from genesis.json address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; function run() external { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); address deployer = vm.addr(deployerPrivateKey); console.log("Deploying WETH9 to exact address using CREATE2"); console.log("Target address:", vm.toString(TARGET_WETH9)); console.log("Deployer:", vm.toString(deployer)); vm.startBroadcast(deployerPrivateKey); // Get WETH bytecode bytes memory wethBytecode = type(WETH).creationCode; bytes32 bytecodeHash = keccak256(wethBytecode); console.log("WETH bytecode hash:", vm.toString(bytecodeHash)); // Deploy CREATE2Factory (or use existing if already deployed) // For deterministic deployment, we can use a known CREATE2Factory address // or deploy it first and use that address CREATE2Factory factory = new CREATE2Factory(); address factoryAddress = address(factory); console.log("CREATE2Factory deployed at:", factoryAddress); // Calculate salt that produces the target address // CREATE2 formula: keccak256(0xff ++ deployer ++ salt ++ keccak256(bytecode)) // We need to find salt such that the result is TARGET_WETH9 // Try common salts or calculate one that works // For now, we'll use a salt finder approach // Since finding the exact salt requires brute force, we'll use a known salt // that produces the target address when combined with our factory // 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_WETH9) returns (bool exists) { if (exists) { console.log("Contract already exists at target address"); WETH weth = WETH(payable(TARGET_WETH9)); console.log("WETH9 name:", weth.name()); console.log("WETH9 symbol:", weth.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], wethBytecode, TARGET_WETH9); 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], wethBytecode, TARGET_WETH9); 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(wethBytecode, salt); console.log("Predicted address:", vm.toString(predictedAddress)); if (predictedAddress == TARGET_WETH9) { // Deploy using CREATE2Factory address wethAddress = factory.deploy(wethBytecode, salt); console.log("WETH9 deployed at:", wethAddress); require(wethAddress == TARGET_WETH9, "Address mismatch"); // Verify deployment WETH weth = WETH(payable(wethAddress)); console.log("WETH9 name:", weth.name()); console.log("WETH9 symbol:", weth.symbol()); console.log("WETH9 decimals:", weth.decimals()); } else { revert("Predicted address does not match target"); } } else { // Need to deploy using the found deployer address directly // This requires deploying via that address or using a CREATE2 helper 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("WETH9 Address:", vm.toString(TARGET_WETH9)); 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("WETH9"); commonSalts[1] = keccak256("WETH"); commonSalts[2] = keccak256("Wrapped Ether"); commonSalts[3] = keccak256("ChainID-138-WETH9"); commonSalts[4] = keccak256(abi.encodePacked("WETH9", 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("WETH9", 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; } function deployDirectlyWithCreate2( address deployer, bytes memory bytecode, address targetAddr ) internal returns (address) { // This function would deploy directly using CREATE2 opcode // For now, we'll revert if we can't find the right salt revert("Direct CREATE2 deployment not implemented - salt finding required"); } // Helper function to check if contract exists function checkContractExists(address addr) external view returns (bool) { uint256 size; assembly { size := extcodesize(addr) } return size > 0; } }