"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const token_math_1 = require("@melonproject/token-math");
const settings_1 = require("../../settings");
const getWeb3Options_1 = require("../environment/getWeb3Options");
const ensure_1 = require("../guards/ensure");
const signTransaction_1 = require("../environment/signTransaction");
const EnsureError_1 = require("../guards/EnsureError");
const getLogCurried_1 = require("../environment/getLogCurried");
const getLog = getLogCurried_1.getLogCurried('melon:protocol:utils:solidity:deploy');
const prepare = (environment, pathToSolidityFile, args = []) => __awaiter(this, void 0, void 0, function* () {
    const log = getLog(environment);
    const txIdentifier = `${pathToSolidityFile}(${(args &&
        args.length &&
        args.join(',')) ||
        ''})`;
    const parsed = path.parse(pathToSolidityFile);
    const rawABI = fs.readFileSync(path.join(settings_1.solidityCompileTarget, parsed.dir, `${parsed.name}.abi`), { encoding: 'utf-8' });
    const bin = fs.readFileSync(path.join(settings_1.solidityCompileTarget, parsed.dir, `${parsed.name}.bin`), { encoding: 'utf-8' });
    ensure_1.ensure(bin.length !== 0, `Binary file for ${pathToSolidityFile} is empty`);
    const parsedABI = JSON.parse(rawABI);
    log.debug('Setup transaction for deployment of', txIdentifier, environment.wallet.address);
    try {
        const contract = new environment.eth.Contract(parsedABI);
        const transaction = contract.deploy({
            arguments: args,
            data: bin.indexOf('0x') === 0 ? bin : `0x${bin}`,
        });
        const options = getWeb3Options_1.getWeb3Options(environment);
        const gasEstimation = yield transaction.estimateGas({
            from: environment.wallet.address.toString(),
        });
        log.debug('Gas estimation:', gasEstimation, options);
        ensure_1.ensure(!token_math_1.greaterThan(token_math_1.toBI(gasEstimation), token_math_1.toBI(options.gas || 0)), [
            `Estimated gas consumption (${gasEstimation})`,
            `is higher than the provided gas limit: ${options.gas}`,
        ].join(' '));
        const encodedAbi = transaction.encodeABI();
        const unsignedTransaction = Object.assign({ data: encodedAbi }, options);
        return {
            txIdentifier,
            unsignedTransaction,
        };
    }
    catch (e) {
        if (e instanceof EnsureError_1.EnsureError) {
            throw e;
        }
        else {
            // tslint:disable-next-line:max-line-length
            throw new Error(`Error preparing deploy contract transaction: ${txIdentifier}\n${e.message}`);
        }
    }
});
const send = (environment, { txIdentifier = 'Unknown deployment', signedTransaction }) => __awaiter(this, void 0, void 0, function* () {
    const log = getLog(environment);
    try {
        let txHash;
        const receipt = yield environment.eth
            .sendSignedTransaction(signedTransaction)
            .on('error', error => {
            throw error;
        })
            .on('transactionHash', t => {
            txHash = t;
            log.debug('TxHash', txIdentifier, txHash);
        })
            .once('confirmation', c => log.debug('Confirmation', txIdentifier, c));
        log.info('Got receipt for:', txIdentifier, 'at contract address:', receipt.contractAddress, 'transaction hash:', txHash);
        return new token_math_1.Address(receipt.contractAddress);
    }
    catch (e) {
        // tslint:disable-next-line:max-line-length
        throw new Error(`Error deploying contract: ${txIdentifier}\n${e.message}`);
    }
});
const deployContract = (environment, pathToSolidityFile, args = []) => __awaiter(this, void 0, void 0, function* () {
    const { txIdentifier, unsignedTransaction } = yield prepare(environment, pathToSolidityFile, args);
    const signedTransaction = yield signTransaction_1.signTransaction(environment, unsignedTransaction);
    const address = yield send(environment, { txIdentifier, signedTransaction });
    return address;
});
exports.deployContract = deployContract;
deployContract.prepare = prepare;
deployContract.send = send;
