"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StaticJob = void 0;
const tslib_1 = require("tslib");
const inquirer = (0, tslib_1.__importStar)(require("inquirer"));
const axios_1 = (0, tslib_1.__importDefault)(require("axios"));
const ethers_1 = require("ethers");
const utils_1 = require("ethers/lib/utils");
const StaticJobs_json_1 = require("../artifacts/contracts/StaticJobs.sol/StaticJobs.json");
const network_1 = require("../utils/network");
const exec_sh_1 = (0, tslib_1.__importDefault)(require("exec-sh"));
// eslint-disable-next-line unicorn/prefer-module
const kill = require('kill-port');
class StaticJob {
    constructor() {
        this.complete = false;
        this.chainID = 1;
    }
    // constructor(_chainID: number) {
    //     this.chainID = _chainID;
    // }
    async run(dataDir) {
        // Get chain
        const supportedChains = ['ETH Mainnet:      1',
            'Goerli Testnet:   5'];
        const networkQuestion = [
            {
                type: 'autocomplete',
                name: 'network',
                message: 'Please select the network the contract you wish to call is deployed on',
                source: (answersSoFar, input) => supportedChains,
            },
        ];
        const { network } = await inquirer.prompt(networkQuestion);
        let chain = 0;
        if (network === supportedChains[0])
            chain = 1;
        else
            chain = 5;
        // Get contract address
        const targetAddressQuestion = [
            {
                type: 'input',
                name: 'targetAddress',
                message: 'What is the address of the contract being called?',
            },
        ];
        const { targetAddress } = await inquirer.prompt(targetAddressQuestion);
        // Get Abi
        let request = '';
        if (chain === 1) {
            request = ('https://api.etherscan.io/api?module=contract&action=getabi&address=' + targetAddress + '&apikey=NYCBE3Q78HB6DV91TWZMF716INRUFS6TRM');
        }
        else if (chain === 5) {
            request = ('https://api-goerli.etherscan.io/api?module=contract&action=getabi&address=' + targetAddress + '&apikey=NYCBE3Q78HB6DV91TWZMF716INRUFS6TRM');
        }
        const response = await axios_1.default.get(request);
        const _abi = response.data.result;
        // console.log(response.data.result)
        const cInterface = new ethers_1.ethers.utils.Interface(_abi);
        const selectors = [];
        for (let i = 0; i < cInterface.fragments.length; i++) {
            if (cInterface.fragments[i].type === 'constructor')
                continue;
            selectors.push(cInterface.fragments[i].format());
        }
        // console.log(selectors)
        const selectorQuestion = [
            {
                type: 'autocomplete',
                name: 'selector',
                message: 'Please pick the function selector of the function you would like to call',
                source: (answersSoFar, input) => selectors,
            },
        ];
        const { selector } = await inquirer.prompt(selectorQuestion);
        // Reverse to get types
        let fragment = cInterface.fragments[0];
        for (let i = 0; i < cInterface.fragments.length; i++) {
            if (cInterface.fragments[i].type === 'constructor')
                continue;
            if (cInterface.fragments[i].format() === selector) {
                fragment = cInterface.fragments[i];
                break;
            }
        }
        const typesArray = [];
        for (let i = 0; i < fragment.inputs.length; i++) {
            typesArray.push(fragment.inputs[i].baseType);
        }
        const valuesArray = [];
        // console.log(selector)
        // Get values
        for (let i = 0; i < typesArray.length; i++) {
            // Prompt for current type
            const paramQuestion = [
                {
                    type: 'input',
                    name: 'parameter',
                    message: 'What value should be passed in for \'' + fragment.inputs[i].name + '\' (' + typesArray[i] + ')?',
                },
            ];
            const { parameter } = await inquirer.prompt(paramQuestion);
            valuesArray.push(String(parameter));
        }
        // Encode and register
        // console.log(valuesArray)
        const sigHash = (0, utils_1.keccak256)('0x' + convertToHex(String(selector))).slice(0, 10);
        const encodedValues = utils_1.defaultAbiCoder.encode(typesArray, valuesArray).slice(2);
        const encodedData = sigHash + encodedValues;
        console.log('Doing some magic 🧙');
        await kill(8545);
        const web3Client = chain == 1
            ? 'https://eth-mainnet.alchemyapi.io/v2/Q115S9wBEp4Z2CZPmW8RTfwXCIjevA19' : 'https://eth-goerli.g.alchemy.com/v2/iWUTVgmaVBEJy-Bcq39RrWwuDUZwtCsW';
        new Promise((resolve, reject) => {
            (0, exec_sh_1.default)(`npx hardhat node --fork ` + web3Client, { cwd: dataDir, stdio: null }, function (err, _stdout, _stderr) {
                if (err) {
                    reject();
                }
                resolve(null);
            });
        });
        // Hardhat takes time to start, so we must wait
        // eslint-disable-next-line no-async-promise-executor
        await new Promise(async (resolve) => {
            let isOnline = false;
            while (!isOnline) {
                try {
                    await axios_1.default.get('http://localhost:8545');
                    console.log('Local node ready. Starting fork...');
                    isOnline = true;
                    resolve(true);
                }
                catch { }
                await new Promise(resolve => {
                    setTimeout(resolve, 5000);
                });
            }
        });
        // Execute and reveal info
        const provider = new ethers_1.ethers.providers.JsonRpcProvider('http://localhost:8545');
        const miscAddr = '0x0000000000000000133700000000000000000000';
        // Fund ETH
        await provider.send('hardhat_setBalance', [
            miscAddr,
            (0, utils_1.parseEther)('1000').toHexString(),
        ]);
        // Imitate sundry address
        const [mainActor, mainSigner] = await getImpersonationSigner(miscAddr, provider);
        // Deploy StaticJob.sol
        const staticFactory = new ethers_1.ethers.ContractFactory(StaticJobs_json_1.abi, StaticJobs_json_1.bytecode);
        const StaticJobs = await staticFactory.connect(mainSigner).deploy();
        const initTx = await StaticJobs.connect(mainSigner).initialize(miscAddr);
        await initTx.wait();
        const depGasTx = await StaticJobs.depositGas({ value: (0, utils_1.parseEther)('0.1') });
        await depGasTx.wait();
        const registerTx = await StaticJobs.registerJob([encodedData], [targetAddress], 'test job', ['Qm... ipfs hash']);
        await registerTx.wait();
        const executeTx = await StaticJobs.executeJob(miscAddr, 0, [encodedData], [targetAddress]);
        const execution = await executeTx.wait();
        // See if failed or no
        if (execution.events != null) {
            for (let index = 0; index < execution.events.length; index++) {
                if (execution.events[index].event === 'JobExecuted') {
                    console.log('🎉 Congrats the job successfully executed! 🎉');
                }
                if (execution.events[index].event === 'JobFailed') {
                    console.log('Oops! 💥 Something wasn\'t configured right, make sure:');
                    console.log('-This is the right contract\n-It is deployed on the network\n-The types are correct\n-The function is not pure or a view');
                    console.log('If access controls are required, use a dynamic job for proper security.');
                }
            }
        }
        const seeTxQ = [
            {
                type: 'confirm',
                name: 'seeTx',
                message: 'Would you like to see the transaction receipt?',
                default: true,
            },
        ];
        const { seeTx } = await inquirer.prompt(seeTxQ);
        if (seeTx)
            console.log(execution);
        return;
        // END
    }
}
exports.StaticJob = StaticJob;
const getImpersonationSigner = async (address, provider) => {
    const actor = new network_1.Impersonator(address);
    const signer = await actor.impersonate(provider);
    return [actor, signer];
};
function convertToHex(str) {
    var hex = '';
    for (var i = 0; i < str.length; i++) {
        hex += '' + str.charCodeAt(i).toString(16);
    }
    return hex;
}
