"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GethRPC = void 0;
const crypto_wallet_core_1 = require("@bitpay-labs/crypto-wallet-core");
const logger_1 = __importDefault(require("../../../../../logger"));
const transaction_1 = require("../../models/transaction");
class GethRPC {
    constructor(web3) {
        this.web3 = web3;
    }
    getBlock(blockNumber) {
        return this.web3.eth.getBlock(blockNumber, true);
    }
    async traceBlock(blockNumber) {
        let result = [];
        try {
            result = await this.send({
                method: 'debug_traceBlockByNumber',
                params: [crypto_wallet_core_1.Utils.toHex(blockNumber), { tracer: 'callTracer' }],
                jsonrpc: '2.0',
                id: Date.now() + Math.round(Math.random() * 1000)
            });
        }
        catch (e) {
            logger_1.default.debug('%o', e);
        }
        return result;
    }
    async getTransactionsFromBlock(blockNumber) {
        const tracedTxs = await this.traceBlock(blockNumber);
        const txs = tracedTxs && tracedTxs.length > 0 ? tracedTxs.filter(tx => tx.result) : [];
        return txs.map(tx => this.transactionFromGethTrace(tx));
    }
    send(data) {
        return new Promise((resolve, reject) => {
            const provider = this.web3.eth.currentProvider;
            provider.send(data, function (err, data) {
                if (err || data.error)
                    return reject(err || data.error);
                resolve(data.result);
            });
        });
    }
    transactionFromGethTrace(tx) {
        const convertedTx = tx.result;
        convertedTx.abiType = transaction_1.EVMTransactionStorage.abiDecode(tx.result.input);
        for (const call of convertedTx.calls || []) {
            call.abiType = transaction_1.EVMTransactionStorage.abiDecode(tx.result.input);
        }
        return convertedTx;
    }
    reconcileTraces(block, transactions, traces) {
        // TODO calculate total block reward including fees
        block;
        for (const i in traces) {
            if (traces[i].calls) {
                const tx = transactions[i];
                tx.calls = traces[i].calls.flatMap((call, idx) => this.flattenTraceCalls(call, idx.toString()));
            }
        }
        return transactions;
    }
    flattenTraceCalls(trace, depth) {
        const retval = [];
        const calls = trace.calls;
        delete trace.calls;
        trace.abiType = trace.input ? transaction_1.EVMTransactionStorage.abiDecode(trace.input) : undefined;
        if (trace.abiType) {
            for (const param of trace.abiType.params) {
                param.value = typeof param.value === 'string' ? param.value : JSON.stringify(param.value);
                if (param.value && param.value.length > 100) {
                    // Need to truncate this so it doesn't blow up the index.
                    param.value = param.value.substring(0, 100) + '...';
                }
            }
        }
        trace.depth = depth;
        retval.push(trace);
        if (calls) {
            retval.push(...calls.flatMap((call, idx) => this.flattenTraceCalls(call, depth + '_' + idx)));
        }
        return retval;
    }
}
exports.GethRPC = GethRPC;
//# sourceMappingURL=gethRpc.js.map