'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const es6_promisify_1 = require("es6-promisify");
const configService_1 = require("../configService");
const iContractWrapperBase_1 = require("../iContractWrapperBase");
const loggingService_1 = require("../loggingService");
const schemeWrapperBase_1 = require("../schemeWrapperBase");
const transactionService_1 = require("../transactionService");
const utils_1 = require("../utils");
const utilsInternal_1 = require("../utilsInternal");
class BootstrappingWrapperBase extends schemeWrapperBase_1.SchemeWrapperBase {
    /**
     * Wrap code that creates a transaction in the given transaction event. This is a helper
     * just for the common case of generating a single transaction.
     * Rethrows exceptions that occur.
     *
     * @param functionName Should look like [contractName].[functionName]
     * @param options Options that will be passed in the event payload, and
     * potentially containing a txEventContext
     * @param generateTx Callback that will  the contract function
     * @param func The contract function
     * @param params The contract function parameters
     * @param legalContractHash: Hash
     * @param web3Params Optional web params, like `from`
     */
    wrapTransactionInvocationWithPayload(functionName, options, func, params, legalContractHash, web3Params) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // if (!referrerAddress) {
            //   return this.wrapTransactionInvocation(
            //     functionName,
            //     options,
            //     func,
            //     params,
            //     web3Params
            //   );
            // }
            if (typeof (legalContractHash) === 'undefined') {
                throw new Error('legalContractHash is undefined');
            }
            const payload = transactionService_1.TransactionService.publishKickoffEvent(functionName, options, 1);
            const eventContext = transactionService_1.TransactionService.newTxEventContext(functionName, payload, options);
            try {
                const txHash = yield this.sendTransactionWithPayload(eventContext, functionName, func, params, legalContractHash, web3Params);
                transactionService_1.TransactionService.publishTxLifecycleEvents(eventContext, txHash, this.contract);
                return new iContractWrapperBase_1.ArcTransactionResult(txHash, this.contract);
            }
            catch (ex) {
                loggingService_1.LoggingService.error(
                // tslint:disable-next-line: max-line-length
                `BootstrappingWrapperBase.wrapTransactionInvocationWithPayload: An error occurred calling ${functionName}: ${JSON.stringify(ex)}`);
                throw ex;
            }
        });
    }
    /**
     * Invoke sendTransaction on the function.  Properly publish TxTracking events.
     * Rethrows exceptions that occur.
     *
     * If `ConfigService.get("estimateGas")` and gas was not already supplied,
     * then we estimate gas.
     *
     * @param eventContext The TxTracking context
     * @param functionName The contract function name (*just* the function name, on prepended contract name)
     * @param func The contract function
     * @param params The contract function parameters
     * @param legalContractHash: Hash
     * @param web3Params Optional web params, like `from`
     */
    sendTransactionWithPayload(eventContext, functionName, func, params, legalContractHash, web3Params = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                let error;
                const gasPriceComputed = configService_1.ConfigService.get('gasPriceAdjustment');
                const web3 = yield utils_1.Utils.getWeb3();
                if (gasPriceComputed && !web3Params.gasPrice) {
                    if (gasPriceComputed) {
                        const defaultGasPrice = yield es6_promisify_1.promisify((callback) => { web3.eth.getGasPrice(callback); })();
                        web3Params.gasPrice = yield gasPriceComputed(defaultGasPrice);
                    }
                    loggingService_1.LoggingService.debug(`invoking function with configured gasPrice: ${web3.fromWei(web3Params.gasPrice, 'gwei')}`);
                }
                const maxGasLimit = yield utilsInternal_1.UtilsInternal.computeMaxGasLimit();
                if (configService_1.ConfigService.get('estimateGas') && !web3Params.gas) {
                    yield this.estimateGas(func, params, web3Params)
                        .then((gas) => {
                        // add 130 gas to account for legalContractHash
                        gas = Math.min(gas + 130, maxGasLimit);
                        // side-effect of altering web3Params allows caller to know what we used
                        Object.assign(web3Params, { gas });
                        loggingService_1.LoggingService.debug(`invoking function with estimated gas: ${gas}`);
                    })
                        .catch((ex) => {
                        loggingService_1.LoggingService.error(`estimateGas failed: ${ex}`);
                        error = ex;
                    });
                }
                else if (web3Params.gas) {
                    // cap any already-given gas limit
                    web3Params.gas = Math.min(web3Params.gas, maxGasLimit);
                }
                if (error) {
                    // don't attempt sending the tx
                    throw error;
                }
                return this._sendTransactionWithPayload(web3, functionName.substring(functionName.indexOf('.') + 1), legalContractHash, params, web3Params);
            }
            catch (ex) {
                // catch every possible error
                transactionService_1.TransactionService.publishTxFailed(eventContext, transactionService_1.TransactionStage.sent, ex);
                throw ex;
            }
        });
    }
    _sendTransactionWithPayload(web3, functionName, legalContractHash, functionParameters, web3Params) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const contractName = this.name;
            const contractAddress = this.address;
            const truffleContract = yield utils_1.Utils.requireContract(contractName);
            const abi = truffleContract.abi.filter((entry) => entry.name === functionName)[0];
            // tslint:disable-next-line: variable-name
            const SolidityFunction = require('web3/lib/web3/function');
            const func = new SolidityFunction(web3.eth, abi, contractAddress);
            const coder = require('web3/lib/solidity/coder');
            const signature = func.signature();
            const payload = {};
            payload.to = contractAddress;
            payload.from = yield utils_1.Utils.getDefaultAccount();
            payload.data =
                '0x' +
                    signature +
                    coder.encodeParams([...func._inputTypes, 'bytes32'], [...functionParameters, legalContractHash]);
            if (web3Params) {
                Object.assign(payload, web3Params);
            }
            return es6_promisify_1.promisify((callback) => web3.eth.sendTransaction(payload, callback))();
        });
    }
}
exports.BootstrappingWrapperBase = BootstrappingWrapperBase;
//# sourceMappingURL=bootstrappingWrapperBase.js.map