"use strict";
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 __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const api_1 = require("@polkadot/api");
const index_1 = require("../../../../../src/index");
const factory_1 = require("../../../../../src/factory");
const chai_1 = require("../../../../chai");
const config_1 = require("../../../../config");
const utils_1 = require("../../../../../src/utils");
const bitcoin_core_client_1 = require("../../../../../src/utils/bitcoin-core-client");
const src_1 = require("../../../../../src");
const issueRedeem_1 = require("../../../../../src/utils/issueRedeem");
const big_js_1 = __importDefault(require("big.js"));
const helpers_1 = require("../../../../utils/helpers");
describe("redeem", () => {
    let api;
    let keyring;
    let userAccount;
    const randomBtcAddress = "bcrt1qujs29q4gkyn2uj6y570xl460p4y43ruayxu8ry";
    let bitcoinCoreClient;
    let vault_1;
    let vault_2;
    const collateralTickerToVaultIdsMap = new Map();
    let wrappedCurrency;
    let interBtcAPI;
    const fetchBtcTxIdFromOpReturn = (redeemRequestId) => __awaiter(void 0, void 0, void 0, function* () {
        const opreturnData = utils_1.stripHexPrefix(redeemRequestId);
        return interBtcAPI.electrsAPI.waitForOpreturn(opreturnData, 5 * 60 * 1000, 5000).catch((_) => {
            throw new Error(`Could not fetch BTC transaction id for redeem request id ${redeemRequestId}`);
        });
    });
    before(() => __awaiter(void 0, void 0, void 0, function* () {
        api = yield factory_1.createSubstrateAPI(config_1.PARACHAIN_ENDPOINT);
        keyring = new api_1.Keyring({ type: "sr25519" });
        userAccount = keyring.addFromUri(config_1.USER_1_URI);
        interBtcAPI = new index_1.DefaultInterBtcApi(api, "regtest", userAccount, config_1.ESPLORA_BASE_PATH);
        const collateralCurrencies = utils_1.getCorrespondingCollateralCurrencies(interBtcAPI.getGovernanceCurrency());
        wrappedCurrency = interBtcAPI.getWrappedCurrency();
        vault_1 = keyring.addFromUri(config_1.VAULT_1_URI);
        vault_2 = keyring.addFromUri(config_1.VAULT_2_URI);
        collateralCurrencies.forEach((collateralCurrency) => {
            const vault_1_id = src_1.newVaultId(api, vault_1.address, collateralCurrency, wrappedCurrency);
            const vault_2_id = src_1.newVaultId(api, vault_2.address, collateralCurrency, wrappedCurrency);
            collateralTickerToVaultIdsMap.set(collateralCurrency.ticker, [vault_1_id, vault_2_id]);
        });
        bitcoinCoreClient = new bitcoin_core_client_1.BitcoinCoreClient(config_1.BITCOIN_CORE_NETWORK, config_1.BITCOIN_CORE_HOST, config_1.BITCOIN_CORE_USERNAME, config_1.BITCOIN_CORE_PASSWORD, config_1.BITCOIN_CORE_PORT, config_1.BITCOIN_CORE_WALLET);
    }));
    after(() => {
        return api.disconnect();
    });
    it("should fail if no account is set", () => __awaiter(void 0, void 0, void 0, function* () {
        const amount = utils_1.newMonetaryAmount(10, wrappedCurrency);
        yield chai_1.assert.isRejected(interBtcAPI.redeem.request(amount, randomBtcAddress));
    })).timeout(3 * 60000);
    it("should issue and request redeem", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const [vault_1_id] of collateralTickerToVaultIdsMap.values()) {
            const issueAmount = utils_1.newMonetaryAmount(0.00005, wrappedCurrency, true);
            const redeemAmount = utils_1.newMonetaryAmount(0.00003, wrappedCurrency, true);
            yield utils_1.issueAndRedeem(interBtcAPI, bitcoinCoreClient, userAccount, vault_1_id, issueAmount, redeemAmount, false, issueRedeem_1.ExecuteRedeem.False);
        }
    })).timeout(12 * 60000);
    it("should load existing redeem requests", () => __awaiter(void 0, void 0, void 0, function* () {
        const redeemRequests = yield interBtcAPI.redeem.list();
        chai_1.assert.isAtLeast(redeemRequests.length, 1, "Error in initialization setup. Should have at least 1 issue request");
    }));
    it("should have applied oracle fee rate to redeem transaction", () => __awaiter(void 0, void 0, void 0, function* () {
        const oracleBtcFeePerByte = yield interBtcAPI.oracle.getBitcoinFees();
        const redeemRequests = yield interBtcAPI.redeem.list();
        for (const redeemRequest of redeemRequests) {
            const txId = yield fetchBtcTxIdFromOpReturn(redeemRequest.id);
            // get the actual values on the BTC transaction
            const btcTx = yield interBtcAPI.electrsAPI.getTx(txId);
            // TODO: remove at later stage. print debug info while we seem to have occasional failures
            console.log(`BTC transaction details:\n${JSON.stringify(btcTx)} `);
            const actualTxFeeSatoshi = new big_js_1.default(btcTx.fee || 0);
            const actualTxVsize = helpers_1.calculateBtcTxVsize(btcTx);
            if (actualTxVsize.eq(0)) {
                chai_1.assert.fail(`Invalid actual tx vsize of 0, cannot calculate fee rate for redeem request id ${redeemRequest.id}`);
                return;
            }
            // allowable delta from observations: now and then, the fee is off by 1 satoshi,
            // so allow for that difference plus some epsilon (1e-5)
            const expectedFees = oracleBtcFeePerByte.mul(actualTxVsize);
            const allowedFeesMax = expectedFees.plus(1);
            const allowedFeeDelta = allowedFeesMax.div(expectedFees).plus(0.00001);
            const actualFeeRateSatoshiPerByte = actualTxFeeSatoshi.div(actualTxVsize);
            chai_1.expect(actualFeeRateSatoshiPerByte.toNumber()).to.be.closeTo(allowedFeeDelta.toNumber(), oracleBtcFeePerByte.toNumber(), `BTC fee rate for redeem request id ${redeemRequest.id} is not close to expected value.
                    Maximum allowed delta is ${allowedFeeDelta.toNumber()}.
                    BTC tx rate is ${actualFeeRateSatoshiPerByte.toString()}, but oracle rate is ${oracleBtcFeePerByte.toString()}`);
        }
    })).timeout(10 * 60000);
    // The goal of this test is to check that a vault sends the redeem BTC transaction with RBF (replace by fee) enabled.
    // And while we have the data, we might as well check if the new fee is indeed elevated.
    it("should be able to perform RBF on a redeem transaction", () => __awaiter(void 0, void 0, void 0, function* () {
        // grab only first entry (collateral currency), and only vault_1_id
        const [vault_1_id] = collateralTickerToVaultIdsMap.values().next().value;
        const issueAmount = utils_1.newMonetaryAmount(0.0001, wrappedCurrency, true);
        const redeemAmount = utils_1.newMonetaryAmount(0.00007, wrappedCurrency, true);
        const [, redeemRequest] = yield utils_1.issueAndRedeem(interBtcAPI, bitcoinCoreClient, userAccount, vault_1_id, issueAmount, redeemAmount, false, issueRedeem_1.ExecuteRedeem.False);
        // get BTC tx id
        const btcTxId = yield fetchBtcTxIdFromOpReturn(redeemRequest.id);
        const collateralCurrency = index_1.currencyIdToMonetaryCurrency(vault_1_id.currencies.collateral);
        const vaultBitcoinCoreClient = new bitcoin_core_client_1.BitcoinCoreClient(config_1.BITCOIN_CORE_NETWORK, config_1.BITCOIN_CORE_HOST, config_1.BITCOIN_CORE_USERNAME, config_1.BITCOIN_CORE_PASSWORD, config_1.BITCOIN_CORE_PORT, `vault_1-${collateralCurrency.ticker}-${wrappedCurrency.ticker}`);
        // try to bump fees
        const result = yield helpers_1.bumpFeesForBtcTx(vaultBitcoinCoreClient, btcTxId);
        if (result.errors && result.errors.length > 0) {
            // concatenate errors for print
            const errorMessage = result.errors.join("; ");
            chai_1.assert.fail(`Could not bump fees for redeem request id ${redeemRequest.id}, error(s): ${errorMessage}`);
        }
        const feesBefore = result.origfee;
        const feesAfter = result.fee;
        chai_1.assert.isAbove(feesAfter, feesBefore, `Fees did not increase after bumpfee for redeem request id ${redeemRequest.id}`);
    })).timeout(10 * 60000);
    // TODO: maybe add this to redeem API
    it("should get redeemBtcDustValue", () => __awaiter(void 0, void 0, void 0, function* () {
        const dust = yield interBtcAPI.api.query.redeem.redeemBtcDustValue();
        chai_1.assert.equal(dust.toString(), "1000");
    }));
    it("should getFeesToPay", () => __awaiter(void 0, void 0, void 0, function* () {
        const amount = utils_1.newMonetaryAmount(2, wrappedCurrency, true);
        const feesToPay = yield interBtcAPI.redeem.getFeesToPay(amount);
        chai_1.assert.equal(feesToPay.str.BTC(), "0.01");
    }));
    it("should getFeeRate", () => __awaiter(void 0, void 0, void 0, function* () {
        const feePercentage = yield interBtcAPI.redeem.getFeeRate();
        chai_1.assert.equal(feePercentage.toString(), "0.005");
    }));
    it("should getPremiumRedeemFeeRate", () => __awaiter(void 0, void 0, void 0, function* () {
        const premiumRedeemFee = yield interBtcAPI.redeem.getPremiumRedeemFeeRate();
        chai_1.assert.equal(premiumRedeemFee.toString(), "0.05");
    }));
    it("should getCurrentInclusionFee", () => __awaiter(void 0, void 0, void 0, function* () {
        const currentInclusionFee = yield interBtcAPI.redeem.getCurrentInclusionFee();
        chai_1.assert.isTrue(!currentInclusionFee.isZero());
    }));
    it("should getDustValue", () => __awaiter(void 0, void 0, void 0, function* () {
        const dustValue = yield interBtcAPI.redeem.getDustValue();
        chai_1.assert.equal(dustValue.str.BTC(), "0.00001");
    }));
});
//# sourceMappingURL=redeem.test.js.map