// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Script, console} from "forge-std/Script.sol"; import {WETH} from "../contracts/tokens/WETH.sol"; /** * @title DeployWETH9Direct * @notice Deploy WETH9 directly to the exact address from genesis.json * @dev Since the address is pre-allocated in genesis.json, we can: * 1. Calculate the salt if we know the deployer (reverse CREATE2 calculation) * 2. Use vm.startPrank to impersonate any deployer address * 3. Deploy using CREATE2 with the calculated salt * * Alternatively, if the address is just pre-allocated in genesis, we might * be able to deploy directly to it using vm.etch (for testing) or by * ensuring the deployment happens at the right nonce/conditions. */ contract DeployWETH9Direct is Script { // Target address from genesis.json address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // Standard CREATE2 deployer (commonly used for deterministic deployments) address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; function run() external { console.log("Deploying WETH9 to exact address:", vm.toString(TARGET_WETH9)); console.log("Strategy: Calculate salt or use direct deployment"); // Get WETH bytecode bytes memory wethBytecode = type(WETH).creationCode; bytes32 bytecodeHash = keccak256(wethBytecode); console.log("WETH9 bytecode hash:", vm.toString(bytecodeHash)); // Strategy 1: Try to calculate salt for known deployers // We'll try the standard CREATE2 deployer first uint256 salt = calculateSaltForAddress(CREATE2_DEPLOYER, wethBytecode, TARGET_WETH9); if (salt != type(uint256).max) { console.log("Found salt:", vm.toString(salt)); console.log("Using CREATE2 deployer:", vm.toString(CREATE2_DEPLOYER)); // Impersonate the CREATE2 deployer (if it exists on-chain) // If it doesn't exist, we'll need to deploy it first or use a different approach vm.startBroadcast(); // Deploy using CREATE2 with the calculated salt address deployedAddress = deployWithCreate2(CREATE2_DEPLOYER, wethBytecode, salt); require(deployedAddress == TARGET_WETH9, "Address mismatch!"); // Verify deployment WETH weth = WETH(payable(TARGET_WETH9)); console.log("WETH9 name:", weth.name()); console.log("WETH9 symbol:", weth.symbol()); console.log("WETH9 decimals:", weth.decimals()); vm.stopBroadcast(); console.log("\n=== Deployment Summary ==="); console.log("WETH9 Address:", vm.toString(TARGET_WETH9)); console.log("Deployer:", vm.toString(CREATE2_DEPLOYER)); console.log("Salt:", vm.toString(salt)); } else { // Strategy 2: Since address is in genesis.json, it might be a special case // We can try deploying directly using vm.etch (for testing) or // by ensuring we deploy with the right nonce console.log("Could not calculate salt for known deployers"); console.log("Trying alternative approach: deploy with vm.etch or direct deployment"); vm.startBroadcast(); // For testing: use vm.etch to set the bytecode directly // Note: This only works in fork mode or local testnets bytes memory deployedBytecode = abi.encodePacked(wethBytecode); // Verify if contract already exists uint256 codeSize; assembly { codeSize := extcodesize(TARGET_WETH9) } if (codeSize == 0) { // Deploy a new WETH contract - it will get a random address // But we want it at a specific address, so we need CREATE2 console.log("Contract does not exist yet at target address"); console.log("Need to use CREATE2 with correct salt and deployer"); revert("Cannot deploy to exact address without CREATE2 parameters"); } else { console.log("Contract already exists at target address"); WETH weth = WETH(payable(TARGET_WETH9)); console.log("WETH9 name:", weth.name()); } vm.stopBroadcast(); } } /** * @notice Calculate what salt would produce the target address * @dev This is a reverse CREATE2 calculation - we know the address, we need the salt * Unfortunately, CREATE2 is a one-way function, so we can't directly reverse it * But we can try common salts and see which one produces the target address * * CREATE2 formula: keccak256(0xff ++ deployer ++ salt ++ keccak256(bytecode))[12:] */ function calculateSaltForAddress( address deployer, bytes memory bytecode, address target ) internal pure returns (uint256) { bytes32 bytecodeHash = keccak256(bytecode); // Try common salts bytes32[] memory commonSalts = new bytes32[](20); commonSalts[0] = bytes32(uint256(0)); // Zero commonSalts[1] = bytes32(uint256(1)); // One commonSalts[2] = bytes32(uint256(138)); // Chain ID commonSalts[3] = keccak256("WETH9"); commonSalts[4] = keccak256("WETH"); commonSalts[5] = keccak256(abi.encodePacked(target)); commonSalts[6] = bytes32(uint256(uint160(target))); for (uint256 i = 7; i < 20; i++) { commonSalts[i] = keccak256(abi.encodePacked("WETH9", i)); } for (uint256 i = 0; i < commonSalts.length; i++) { bytes32 hash = keccak256( abi.encodePacked(bytes1(0xff), deployer, commonSalts[i], bytecodeHash) ); address computed = address(uint160(uint256(hash))); if (computed == target) { return uint256(commonSalts[i]); } } return type(uint256).max; // Not found } /** * @notice Deploy using CREATE2 with a specific deployer, bytecode, and salt * @dev This requires the deployer contract to exist or be deployed first */ function deployWithCreate2( address deployerAddr, bytes memory bytecode, uint256 salt ) internal returns (address) { // If deployer doesn't exist, we'd need to deploy it first // For now, we'll use inline assembly to deploy with CREATE2 address addr; assembly { let ptr := mload(0x40) // Copy bytecode to memory let bytecodeLength := mload(bytecode) let bytecodePtr := add(bytecode, 0x20) mstore(ptr, bytecodeLength) let codeDataPtr := add(ptr, 0x20) codecopy(codeDataPtr, bytecodePtr, bytecodeLength) // Deploy using CREATE2 addr := create2(0, codeDataPtr, bytecodeLength, salt) if iszero(addr) { revert(0, 0) } } return addr; } }