227 lines
9.3 KiB
Solidity
227 lines
9.3 KiB
Solidity
|
|
// 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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|