"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());
    });
};
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 config_1 = require("../../../config");
const bitcoin_core_client_1 = require("../../../../src/utils/bitcoin-core-client");
const utils_1 = require("../../../../src/utils");
const src_1 = require("../../../../src");
const chai_1 = require("../../../chai");
const helpers_1 = require("../../../utils/helpers");
describe("redeem", () => {
    let api;
    let keyring;
    let userAccount;
    let vaultToLiquidate;
    let vaultToLiquidateIds;
    let vaultToBan;
    let vaultToBanIds;
    let userBitcoinCoreClient;
    let bitcoinCoreClient;
    let userInterBtcAPI;
    let oracleInterBtcAPI;
    let reporterInterBtcAPI;
    let assetRegistry;
    let loansAPI;
    let collateralCurrencies;
    let wrappedCurrency;
    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);
        const oracleAccount = keyring.addFromUri(config_1.ORACLE_URI);
        const reportingVaultAccount = keyring.addFromUri(config_1.VAULT_1_URI);
        const transactionAPI = new src_1.DefaultTransactionAPI(api);
        assetRegistry = new index_1.DefaultAssetRegistryAPI(api);
        userInterBtcAPI = new index_1.DefaultInterBtcApi(api, "regtest", userAccount, config_1.ESPLORA_BASE_PATH);
        loansAPI = new index_1.DefaultLoansAPI(api, assetRegistry, transactionAPI);
        oracleInterBtcAPI = new index_1.DefaultInterBtcApi(api, "regtest", oracleAccount, config_1.ESPLORA_BASE_PATH);
        reporterInterBtcAPI = new index_1.DefaultInterBtcApi(api, "regtest", reportingVaultAccount, config_1.ESPLORA_BASE_PATH);
        collateralCurrencies = helpers_1.getCorrespondingCollateralCurrenciesForTests(userInterBtcAPI.getGovernanceCurrency());
        wrappedCurrency = userInterBtcAPI.getWrappedCurrency();
        vaultToLiquidate = keyring.addFromUri(config_1.VAULT_TO_LIQUIDATE_URI);
        vaultToLiquidateIds = collateralCurrencies.map((collateralCurrency) => src_1.newVaultId(api, vaultToLiquidate.address, collateralCurrency, wrappedCurrency));
        vaultToBan = keyring.addFromUri(config_1.VAULT_TO_BAN_URI);
        vaultToBanIds = collateralCurrencies.map((collateralCurrency) => src_1.newVaultId(api, vaultToBan.address, collateralCurrency, wrappedCurrency));
        userBitcoinCoreClient = 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);
        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();
    });
    // TODO: discuss where to test this. Should be tested in the vault client rather than on the lib
    // TODO: check with greg how to use instant seal for this test
    it.skip("should liquidate a vault that committed theft", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const vaultToLiquidateId of vaultToLiquidateIds) {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vaultToLiquidateId.currencies.collateral);
            const regularExchangeRate = yield oracleInterBtcAPI.oracle.getExchangeRate(collateralCurrency);
            // There should be no burnable tokens
            yield chai_1.expect(userInterBtcAPI.redeem.getBurnExchangeRate(collateralCurrency)).to.be.rejected;
            // issue a small amount of tokens (10x dust)
            const dustValue = yield userInterBtcAPI.issue.getDustValue();
            const issuedTokens = dustValue.mul(10);
            yield utils_1.issueSingle(userInterBtcAPI, userBitcoinCoreClient, userAccount, issuedTokens, vaultToLiquidateId, true, false);
            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_to_liquidate-${collateralCurrency.ticker}-${wrappedCurrency.ticker}`);
            // Steal some bitcoin (spend from the vault's account)
            const foreignBitcoinAddress = "bcrt1qefxeckts7tkgz7uach9dnwer4qz5nyehl4sjcc";
            const amountToSteal = dustValue;
            const { txid: btcTxId } = yield vaultBitcoinCoreClient.sendBtcTxAndMine(foreignBitcoinAddress, amountToSteal, 3);
            // wait for tx inclusion and block finalization.
            yield src_1.waitForBlockFinalization(bitcoinCoreClient, userInterBtcAPI.btcRelay);
            // report theft
            yield reporterInterBtcAPI.vaults.reportVaultTheft(vaultToLiquidateId, btcTxId);
            const finalizedPromise = new Promise((resolve, _) => userInterBtcAPI.system.subscribeToFinalizedBlockHeads((header) => __awaiter(void 0, void 0, void 0, function* () {
                const events = yield userInterBtcAPI.api.query.system.events.at(header.parentHash);
                if (src_1.DefaultTransactionAPI.doesArrayContainEvent(events, api.events.relay.VaultTheft)) {
                    resolve();
                }
            })));
            // wait for theft reported event
            yield finalizedPromise;
            const flaggedForTheft = yield userInterBtcAPI.vaults.isVaultFlaggedForTheft(vaultToLiquidateId, btcTxId);
            chai_1.assert.isTrue(flaggedForTheft, `Expected vault (collateral: ${collateralCurrency.ticker}) to be flagged for theft, but it was not.`);
            const maxBurnableTokens = yield userInterBtcAPI.redeem.getMaxBurnableTokens(collateralCurrency);
            chai_1.assert.isTrue(maxBurnableTokens.gte(issuedTokens), `Expected max burnable tokens to be greater than or equal to issued tokens
                (issued tokens: ${issuedTokens.toHuman()}; collateral: ${collateralCurrency.ticker}) 
                but was ${maxBurnableTokens.toHuman()}`);
            const burnExchangeRate = yield userInterBtcAPI.redeem.getBurnExchangeRate(collateralCurrency);
            chai_1.assert.isTrue(regularExchangeRate.toBig().lt(burnExchangeRate.toBig()), `Burn exchange rate (${burnExchangeRate.toHuman()}) is not better than 
                the regular one (${regularExchangeRate.toHuman()}; context: ${collateralCurrency.ticker})`);
            // Burn InterBtc for a premium, to restore peg
            yield userInterBtcAPI.redeem.burn(amountToSteal, collateralCurrency);
        }
    })).timeout(10 * 60000);
    // TODO: Unskip after `subscribeToRedeemExpiry` is reimplemented
    // TODO: rewrite test to ban one vault, and check the other currency still works
    it.skip("should cancel a redeem request", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const vaultToBanId of vaultToBanIds) {
            yield helpers_1.runWhileMiningBTCBlocks(bitcoinCoreClient, () => __awaiter(void 0, void 0, void 0, function* () {
                const issueAmount = utils_1.newMonetaryAmount(0.00005, wrappedCurrency, true);
                const redeemAmount = utils_1.newMonetaryAmount(0.00003, wrappedCurrency, true);
                const initialRedeemPeriod = yield userInterBtcAPI.redeem.getRedeemPeriod();
                yield helpers_1.sudo(userInterBtcAPI, () => userInterBtcAPI.redeem.setRedeemPeriod(1));
                const [, redeemRequest] = yield src_1.issueAndRedeem(userInterBtcAPI, userBitcoinCoreClient, userAccount, vaultToBanId, issueAmount, redeemAmount, false, src_1.ExecuteRedeem.False);
                // TODO: in the promise:
                // grab head
                //  wait for head + redeem period + 1
                // then resolve
                // Wait for redeem expiry callback
                // await new Promise<void>((resolve, _) => {
                //     redeemAPI.subscribeToRedeemExpiry(newAccountId(api, userAccount.address), (requestId) => {
                //         if (stripHexPrefix(redeemRequest.id.toString()) === stripHexPrefix(requestId.toString())) {
                //             resolve();
                //         }
                //     });
                // });
                yield userInterBtcAPI.redeem.cancel(redeemRequest.id.toString(), true);
                const redeemRequestAfterCancellation = yield userInterBtcAPI.redeem.getRequestById(redeemRequest.id);
                chai_1.assert.isTrue(redeemRequestAfterCancellation.status === src_1.RedeemStatus.Reimbursed, "Failed to cancel issue request");
                // TODO: check vault status changed to "banned"
                // Set issue period back to its initial value to minimize side effects.
                yield helpers_1.sudo(userInterBtcAPI, () => userInterBtcAPI.redeem.setRedeemPeriod(initialRedeemPeriod));
            }));
        }
    })).timeout(10 * 60000);
    // TODO: discuss if we need this test ehre since it tests vault behavior
    it.skip("should issue and auto-execute redeem", () => __awaiter(void 0, void 0, void 0, function* () {
        yield helpers_1.runWhileMiningBTCBlocks(bitcoinCoreClient, () => __awaiter(void 0, void 0, void 0, function* () {
            const issueAmount = utils_1.newMonetaryAmount(0.000013, wrappedCurrency, true);
            const redeemAmount = utils_1.newMonetaryAmount(0.000011, wrappedCurrency, true);
            yield src_1.issueAndRedeem(userInterBtcAPI, bitcoinCoreClient, userAccount, undefined, issueAmount, redeemAmount, false);
        }));
        // The `ExecuteRedeem` event has been emitted at this point.
        // Do not check balances as this is already checked in the parachain integration tests.
    })).timeout(10 * 60 * 1000);
    // TODO: double the timeout and run on `master` branch only, before a release
    it.skip("should issue and manually execute redeem", () => __awaiter(void 0, void 0, void 0, function* () {
        yield helpers_1.runWhileMiningBTCBlocks(bitcoinCoreClient, () => __awaiter(void 0, void 0, void 0, function* () {
            const issueAmount = utils_1.newMonetaryAmount(0.00013, wrappedCurrency, true);
            const redeemAmount = utils_1.newMonetaryAmount(0.00011, wrappedCurrency, true);
            yield src_1.issueAndRedeem(userInterBtcAPI, bitcoinCoreClient, userAccount, undefined, issueAmount, redeemAmount, false, src_1.ExecuteRedeem.Manually);
        }));
        // The `ExecuteRedeem` event has been emitted at this point.
        // Do not check balances as this is already checked in the parachain integration tests.
    })).timeout(10 * 60 * 1000);
});
//# sourceMappingURL=redeem.test.js.map