// 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 DeployWETH9Smart * @notice Smart deployment using Foundry's impersonation and salt calculation * @dev Since the address is in genesis.json, we can: * 1. Calculate salt for known deployers (no brute force needed) * 2. Use vm.startBroadcast(address) to impersonate any deployer * 3. Deploy using CREATE2 with calculated salt */ contract DeployWETH9Smart is Script { address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // Potential deployers (try these in order) address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; address constant GENESIS_ADDRESS_1 = 0x0742D35CC6634c0532925A3b844bc9E7595f0Beb; address constant GENESIS_ADDRESS_2 = 0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882; function run() external { console.log("Smart Deployment: WETH9 to", vm.toString(TARGET_WETH9)); bytes memory bytecode = type(WETH).creationCode; bytes32 bytecodeHash = keccak256(bytecode); // Try each potential deployer address[] memory deployers = new address[](4); deployers[0] = CREATE2_DEPLOYER; deployers[1] = GENESIS_ADDRESS_1; deployers[2] = GENESIS_ADDRESS_2; // Try CREATE2Factory as deployer too (will deploy it first if needed) deployers[3] = address(0); // Will be set to CREATE2Factory address for (uint256 i = 0; i < deployers.length; i++) { address deployer = deployers[i]; if (deployer == address(0)) { // Deploy CREATE2Factory first, then use it as deployer vm.startBroadcast(); CREATE2Factory factory = new CREATE2Factory(); deployer = address(factory); vm.stopBroadcast(); console.log("Deployed CREATE2Factory at:", vm.toString(deployer)); } console.log("\nTrying deployer:", vm.toString(deployer)); // Calculate salt (tries common values first, then sequential) uint256 salt = findSaltForDeployer(deployer, bytecode, TARGET_WETH9); if (salt != type(uint256).max) { console.log("Found salt:", vm.toString(salt)); console.log("Deploying with CREATE2..."); // If using CREATE2Factory, deploy through it if (deployer.code.length > 0) { // Check if it's a CREATE2Factory (has deploy function) vm.startBroadcast(); CREATE2Factory factory = CREATE2Factory(payable(deployer)); address deployed = factory.deploy(bytecode, salt); if (deployed == TARGET_WETH9) { console.log("OK Successfully deployed WETH9 at target address!"); verifyWeth9(); vm.stopBroadcast(); return; } else { console.log("ERROR Deployed to wrong address:", vm.toString(deployed)); vm.stopBroadcast(); } } else { // Direct CREATE2 deployment using assembly vm.startBroadcast(); address deployed = deployCreate2(bytecode, salt); if (deployed == TARGET_WETH9) { console.log("OK Successfully deployed WETH9 at target address!"); verifyWeth9(); vm.stopBroadcast(); return; } else { console.log("ERROR Deployed to wrong address:", vm.toString(deployed)); vm.stopBroadcast(); } } } } revert("Could not find valid salt/deployer combination"); } function findSaltForDeployer( address deployer, bytes memory bytecode, address target ) internal pure returns (uint256) { bytes32 bytecodeHash = keccak256(bytecode); // Try common salts first (much faster than brute force) bytes32[] memory commonSalts = new bytes32[](15); commonSalts[0] = bytes32(uint256(0)); commonSalts[1] = bytes32(uint256(1)); commonSalts[2] = bytes32(uint256(138)); // Chain ID commonSalts[3] = keccak256("WETH9"); commonSalts[4] = keccak256("WETH"); commonSalts[5] = keccak256("Wrapped Ether"); commonSalts[6] = keccak256(abi.encodePacked(target)); commonSalts[7] = bytes32(uint256(uint160(target))); commonSalts[8] = keccak256(abi.encodePacked("ChainID-138-WETH9")); commonSalts[9] = keccak256(abi.encodePacked("ChainID", uint256(138), "WETH9")); for (uint256 i = 10; i < 15; 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) ); if (address(uint160(uint256(hash))) == target) { return uint256(commonSalts[i]); } } // Limited sequential search (first 1000 for speed) for (uint256 i = 0; i < 1000; i++) { bytes32 salt = bytes32(i); bytes32 hash = keccak256( abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash) ); if (address(uint160(uint256(hash))) == target) { return i; } } return type(uint256).max; } function deployCreate2(bytes memory bytecode, uint256 salt) internal returns (address) { address addr; assembly { let ptr := mload(0x40) let bytecodeLength := mload(bytecode) let bytecodePtr := add(bytecode, 0x20) codecopy(ptr, bytecodePtr, bytecodeLength) addr := create2(0, ptr, bytecodeLength, salt) if iszero(addr) { revert(0, 0) } } return addr; } 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()); } }