#!/usr/bin/env node

/**
 * Calculate CREATE2 salt to produce a specific contract address
 * 
 * CREATE2 formula:
 * address = keccak256(0xff ++ deployer ++ salt ++ keccak256(bytecode))[12:]
 */

const { ethers } = require("ethers");
const fs = require("fs");
const path = require("path");

// Target addresses from genesis.json
const TARGET_WETH9 = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const TARGET_WETH10 = "0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F";

// Load contract bytecode from artifacts
function loadBytecode(contractName) {
    const artifactsPath = path.join(__dirname, "../../out", contractName, `${contractName}.sol`, `${contractName}.json`);
    if (!fs.existsSync(artifactsPath)) {
        throw new Error(`Artifact not found: ${artifactsPath}`);
    }
    const artifact = JSON.parse(fs.readFileSync(artifactsPath, "utf8"));
    return artifact.bytecode.object;
}

// Calculate CREATE2 address
function calculateCreate2Address(deployer, salt, bytecode) {
    const bytecodeHash = ethers.keccak256(bytecode);
    const initCodeHash = ethers.keccak256(
        ethers.concat([
            "0xff",
            deployer,
            salt,
            bytecodeHash,
        ])
    );
    return ethers.getAddress("0x" + initCodeHash.slice(26));
}

// Find salt that produces target address
function findSalt(deployer, bytecode, targetAddress, maxIterations = 1000000) {
    console.log(`Finding salt for target address: ${targetAddress}`);
    console.log(`Deployer: ${deployer}`);
    console.log(`Bytecode length: ${bytecode.length} bytes`);
    console.log(`Max iterations: ${maxIterations}`);
    console.log("");

    // Try common salts first
    const commonSalts = [
        "0x0000000000000000000000000000000000000000000000000000000000000000", // Zero
        "0x0000000000000000000000000000000000000000000000000000000000000001", // One
        "0x000000000000000000000000000000000000000000000000000000000000008a", // Chain ID 138
        ethers.keccak256(ethers.toUtf8Bytes("WETH9")),
        ethers.keccak256(ethers.toUtf8Bytes("WETH10")),
        ethers.keccak256(ethers.toUtf8Bytes("WETH")),
        ethers.keccak256(ethers.toUtf8Bytes("Wrapped Ether")),
        ethers.keccak256(ethers.toUtf8Bytes("ChainID-138-WETH9")),
        ethers.keccak256(ethers.toUtf8Bytes("ChainID-138-WETH10")),
    ];

    console.log("Trying common salts...");
    for (const salt of commonSalts) {
        const address = calculateCreate2Address(deployer, salt, bytecode);
        console.log(`  Salt: ${salt.slice(0, 20)}... → Address: ${address}`);
        if (address.toLowerCase() === targetAddress.toLowerCase()) {
            console.log(`\n✅ Found salt: ${salt}`);
            return salt;
        }
    }

    console.log("\nCommon salts didn't match. Brute forcing...");
    console.log("(This may take a while)");

    // Brute force
    for (let i = 0; i < maxIterations; i++) {
        const salt = ethers.zeroPadValue(ethers.toBeHex(i, 32), 32);
        const address = calculateCreate2Address(deployer, salt, bytecode);
        
        if (i % 10000 === 0) {
            process.stdout.write(`\rChecked ${i} salts...`);
        }

        if (address.toLowerCase() === targetAddress.toLowerCase()) {
            console.log(`\n✅ Found salt: ${salt} (iteration ${i})`);
            return salt;
        }
    }

    console.log(`\n❌ Could not find salt after ${maxIterations} iterations`);
    return null;
}

async function main() {
    const args = process.argv.slice(2);
    
    if (args.length < 2) {
        console.log("Usage: node calculate-create2-salt.js <contract-name> <deployer-address>");
        console.log("");
        console.log("Examples:");
        console.log("  node calculate-create2-salt.js WETH 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb");
        console.log("  node calculate-create2-salt.js WETH10 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb");
        process.exit(1);
    }

    const contractName = args[0];
    const deployer = ethers.getAddress(args[1]);

    let targetAddress;
    if (contractName === "WETH" || contractName === "WETH9") {
        targetAddress = TARGET_WETH9;
    } else if (contractName === "WETH10") {
        targetAddress = TARGET_WETH10;
    } else {
        console.error(`Unknown contract: ${contractName}`);
        process.exit(1);
    }

    try {
        const bytecode = loadBytecode(contractName);
        const salt = findSalt(deployer, bytecode, targetAddress);
        
        if (salt) {
            console.log("");
            console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
            console.log("✅ CREATE2 Salt Found");
            console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
            console.log(`Contract: ${contractName}`);
            console.log(`Target Address: ${targetAddress}`);
            console.log(`Deployer: ${deployer}`);
            console.log(`Salt: ${salt}`);
            console.log("");
            console.log("Use this salt in your CREATE2 deployment script!");
        } else {
            console.log("");
            console.log("❌ Could not find salt");
            console.log("You may need to:");
            console.log("  1. Increase maxIterations");
            console.log("  2. Check if bytecode matches");
            console.log("  3. Verify deployer address");
            process.exit(1);
        }
    } catch (error) {
        console.error(`Error: ${error.message}`);
        process.exit(1);
    }
}

if (require.main === module) {
    main().catch(console.error);
}

module.exports = { calculateCreate2Address, findSalt };

