"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var bn_js_1 = __importDefault(require("bn.js"));
var assert_1 = __importDefault(require("assert"));
var ethereumjs_util_1 = __importDefault(require("ethereumjs-util"));
var axios_1 = __importDefault(require("axios"));
var ContractsBase_1 = __importDefault(require("./ContractsBase"));
var ProofsUtil_1 = __importDefault(require("../libs/ProofsUtil"));
var web3_1 = __importDefault(require("web3"));
var web3 = new web3_1.default();
var hash = web3.utils.soliditySha3;
var logger = {
    info: require('debug')('maticjs:WithdrawManager'),
    debug: require('debug')('maticjs:debug:WithdrawManager'),
};
var ExitManager = /** @class */ (function (_super) {
    __extends(ExitManager, _super);
    function ExitManager(rootChain, options, web3Client) {
        var _this = _super.call(this, web3Client, options.network) || this;
        _this.rootChain = rootChain;
        _this.networkApiUrl = options.network.Matic.NetworkAPI;
        return _this;
    }
    ExitManager.prototype.buildPayloadForExit = function (burnTxHash, logEventSig) {
        return __awaiter(this, void 0, void 0, function () {
            var lastChildBlock, burnTx, receipt, block, headerBlockNumber, headerBlock, blockProof, receiptProof, logIndex;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.rootChain.getLastChildBlock()];
                    case 1:
                        lastChildBlock = _a.sent();
                        return [4 /*yield*/, this.web3Client.getMaticWeb3().eth.getTransaction(burnTxHash)];
                    case 2:
                        burnTx = _a.sent();
                        return [4 /*yield*/, this.web3Client.getMaticWeb3().eth.getTransactionReceipt(burnTxHash)];
                    case 3:
                        receipt = _a.sent();
                        return [4 /*yield*/, this.web3Client
                                .getMaticWeb3()
                                .eth.getBlock(burnTx.blockNumber, true /* returnTransactionObjects */)];
                    case 4:
                        block = _a.sent();
                        logger.info({ 'burnTx.blockNumber': burnTx.blockNumber, lastCheckPointedBlockNumber: lastChildBlock });
                        assert_1.default.ok(new bn_js_1.default(lastChildBlock).gte(new bn_js_1.default(burnTx.blockNumber)), 'Burn transaction has not been checkpointed as yet');
                        return [4 /*yield*/, this.rootChain.findHeaderBlockNumber(burnTx.blockNumber)];
                    case 5:
                        headerBlockNumber = _a.sent();
                        return [4 /*yield*/, this.web3Client.call(this.rootChain.rootChain.methods.headerBlocks(this.encode(headerBlockNumber)))];
                    case 6:
                        headerBlock = _a.sent();
                        logger.info({ headerBlockNumber: headerBlockNumber.toString(), headerBlock: headerBlock });
                        return [4 /*yield*/, ProofsUtil_1.default.buildBlockProof(this.web3Client.getMaticWeb3(), parseInt(headerBlock.start, 10), parseInt(headerBlock.end, 10), parseInt(burnTx.blockNumber + '', 10))];
                    case 7:
                        blockProof = _a.sent();
                        return [4 /*yield*/, ProofsUtil_1.default.getReceiptProof(receipt, block, this.web3Client.getMaticWeb3())];
                    case 8:
                        receiptProof = _a.sent();
                        logIndex = -1;
                        switch (logEventSig) {
                            case '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef':
                                logIndex = receipt.logs.findIndex(function (log) {
                                    return log.topics[0].toLowerCase() == logEventSig.toLowerCase() &&
                                        log.topics[2].toLowerCase() == '0x0000000000000000000000000000000000000000000000000000000000000000';
                                });
                                break;
                            case '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62':
                            case '0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb':
                                logIndex = receipt.logs.findIndex(function (log) {
                                    return log.topics[0].toLowerCase() == logEventSig.toLowerCase() &&
                                        log.topics[3].toLowerCase() == '0x0000000000000000000000000000000000000000000000000000000000000000';
                                });
                                break;
                            default:
                                logIndex = receipt.logs.findIndex(function (log) { return log.topics[0].toLowerCase() == logEventSig.toLowerCase(); });
                        }
                        assert_1.default.ok(logIndex > -1, 'Log not found in receipt');
                        return [2 /*return*/, this._encodePayload(headerBlockNumber, blockProof, burnTx.blockNumber, block.timestamp, Buffer.from(block.transactionsRoot.slice(2), 'hex'), Buffer.from(block.receiptsRoot.slice(2), 'hex'), ProofsUtil_1.default.getReceiptBytes(receipt), // rlp encoded
                            receiptProof.parentNodes, receiptProof.path, logIndex)];
                }
            });
        });
    };
    ExitManager.prototype.buildPayloadForExitFastMerkle = function (start, end, blockNumber) {
        return __awaiter(this, void 0, void 0, function () {
            var blockProof;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, ProofsUtil_1.default.buildBlockProof(this.web3Client.getMaticWeb3(), parseInt(start, 10), parseInt(end, 10), parseInt(blockNumber + '', 10))];
                    case 1:
                        blockProof = _a.sent();
                        return [2 /*return*/, blockProof];
                }
            });
        });
    };
    ExitManager.prototype.buildPayloadForExitHermoine = function (burnTxHash, logEventSig) {
        return __awaiter(this, void 0, void 0, function () {
            var lastChildBlock, receipt, block, blockIncludedResponse, headerBlock, start, end, number, blockProofResponse, blockProof, receiptProof, logIndex;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.rootChain.getLastChildBlock()];
                    case 1:
                        lastChildBlock = _a.sent();
                        return [4 /*yield*/, this.web3Client.getMaticWeb3().eth.getTransactionReceipt(burnTxHash)];
                    case 2:
                        receipt = _a.sent();
                        return [4 /*yield*/, this.web3Client
                                .getMaticWeb3()
                                .eth.getBlock(receipt.blockNumber, true /* returnTransactionObjects */)];
                    case 3:
                        block = _a.sent();
                        logger.info({ 'receipt.blockNumber': receipt.blockNumber, lastCheckPointedBlockNumber: lastChildBlock });
                        assert_1.default.ok(new bn_js_1.default(lastChildBlock).gte(new bn_js_1.default(receipt.blockNumber)), 'Burn transaction has not been checkpointed as yet');
                        return [4 /*yield*/, axios_1.default.get(this.networkApiUrl + '/block-included/' + receipt.blockNumber)];
                    case 4:
                        blockIncludedResponse = _a.sent();
                        headerBlock = blockIncludedResponse.data;
                        start = parseInt(headerBlock.start, 10);
                        end = parseInt(headerBlock.end, 10);
                        number = parseInt(receipt.blockNumber + '', 10);
                        return [4 /*yield*/, axios_1.default.get(this.networkApiUrl + "/fast-merkle-proof?start=" + start + "&end=" + end + "&number=" + number)];
                    case 5:
                        blockProofResponse = _a.sent();
                        blockProof = blockProofResponse.data.proof;
                        return [4 /*yield*/, ProofsUtil_1.default.getReceiptProof(receipt, block, this.web3Client.getMaticWeb3())];
                    case 6:
                        receiptProof = _a.sent();
                        logIndex = -1;
                        switch (logEventSig) {
                            case '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef':
                                logIndex = receipt.logs.findIndex(function (log) {
                                    return log.topics[0].toLowerCase() == logEventSig.toLowerCase() &&
                                        log.topics[2].toLowerCase() == '0x0000000000000000000000000000000000000000000000000000000000000000';
                                });
                                break;
                            case '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62':
                            case '0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb':
                                logIndex = receipt.logs.findIndex(function (log) {
                                    return log.topics[0].toLowerCase() == logEventSig.toLowerCase() &&
                                        log.topics[3].toLowerCase() == '0x0000000000000000000000000000000000000000000000000000000000000000';
                                });
                                break;
                            default:
                                logIndex = receipt.logs.findIndex(function (log) { return log.topics[0].toLowerCase() == logEventSig.toLowerCase(); });
                        }
                        assert_1.default.ok(logIndex > -1, 'Log not found in receipt');
                        return [2 /*return*/, this._encodePayload(headerBlock.headerBlockNumber, blockProof, receipt.blockNumber, block.timestamp, Buffer.from(block.transactionsRoot.slice(2), 'hex'), Buffer.from(block.receiptsRoot.slice(2), 'hex'), ProofsUtil_1.default.getReceiptBytes(receipt), // rlp encoded
                            receiptProof.parentNodes, receiptProof.path, logIndex)];
                }
            });
        });
    };
    ExitManager.prototype.getExitHash = function (burnTxHash, logEventSig) {
        return __awaiter(this, void 0, void 0, function () {
            var lastChildBlock, receipt, block, receiptProof, logIndex, nibbleArr;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.rootChain.getLastChildBlock()];
                    case 1:
                        lastChildBlock = _a.sent();
                        return [4 /*yield*/, this.web3Client.getMaticWeb3().eth.getTransactionReceipt(burnTxHash)];
                    case 2:
                        receipt = _a.sent();
                        return [4 /*yield*/, this.web3Client
                                .getMaticWeb3()
                                .eth.getBlock(receipt.blockNumber, true /* returnTransactionObjects */)];
                    case 3:
                        block = _a.sent();
                        assert_1.default.ok(new bn_js_1.default(lastChildBlock).gte(new bn_js_1.default(receipt.blockNumber)), 'Burn transaction has not been checkpointed as yet');
                        return [4 /*yield*/, ProofsUtil_1.default.getReceiptProof(receipt, block, this.web3Client.getMaticWeb3())];
                    case 4:
                        receiptProof = _a.sent();
                        logIndex = -1;
                        switch (logEventSig) {
                            case '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef':
                                logIndex = receipt.logs.findIndex(function (log) {
                                    return log.topics[0].toLowerCase() == logEventSig.toLowerCase() &&
                                        log.topics[2].toLowerCase() == '0x0000000000000000000000000000000000000000000000000000000000000000';
                                });
                                break;
                            case '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62':
                            case '0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb':
                                logIndex = receipt.logs.findIndex(function (log) {
                                    return log.topics[0].toLowerCase() == logEventSig.toLowerCase() &&
                                        log.topics[3].toLowerCase() == '0x0000000000000000000000000000000000000000000000000000000000000000';
                                });
                                break;
                            default:
                            // let's not do anything, this is going to be fail in
                            // following `assert`
                        }
                        assert_1.default.ok(logIndex > -1, 'Log not found in receipt');
                        nibbleArr = [];
                        receiptProof.path.forEach(function (byte) {
                            nibbleArr.push(Buffer.from('0' + (byte / 0x10).toString(16), 'hex'));
                            nibbleArr.push(Buffer.from('0' + (byte % 0x10).toString(16), 'hex'));
                        });
                        return [2 /*return*/, hash(receipt.blockNumber, ethereumjs_util_1.default.bufferToHex(Buffer.concat(nibbleArr)), logIndex)];
                }
            });
        });
    };
    ExitManager.prototype._encodePayload = function (headerNumber, buildBlockProof, blockNumber, timestamp, transactionsRoot, receiptsRoot, receipt, receiptParentNodes, path, logIndex) {
        return ethereumjs_util_1.default.bufferToHex(ethereumjs_util_1.default.rlp.encode([
            headerNumber,
            buildBlockProof,
            blockNumber,
            timestamp,
            ethereumjs_util_1.default.bufferToHex(transactionsRoot),
            ethereumjs_util_1.default.bufferToHex(receiptsRoot),
            ethereumjs_util_1.default.bufferToHex(receipt),
            ethereumjs_util_1.default.bufferToHex(ethereumjs_util_1.default.rlp.encode(receiptParentNodes)),
            ethereumjs_util_1.default.bufferToHex(Buffer.concat([Buffer.from('00', 'hex'), path])),
            logIndex,
        ]));
    };
    return ExitManager;
}(ContractsBase_1.default));
exports.default = ExitManager;
