"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 big_js_1 = __importDefault(require("big.js"));
const index_1 = require("../../../../../src/index");
const factory_1 = require("../../../../../src/factory");
const chai_1 = require("../../../../chai");
const config_1 = require("../../../../config");
const src_1 = require("../../../../../src");
const utils_1 = require("../../../../../src/utils");
const helpers_1 = require("../../../../utils/helpers");
const sinon_1 = __importDefault(require("sinon"));
describe("vaultsAPI", () => {
    let vault_to_liquidate;
    let vault_to_ban;
    let vault_1;
    let vault_1_ids;
    let vault_2;
    let vault_3;
    let api;
    let wrappedCurrency;
    let collateralCurrencies;
    let governanceCurrency;
    let interBtcAPI;
    let assetRegistry;
    let loansAPI;
    before(() => __awaiter(void 0, void 0, void 0, function* () {
        api = yield factory_1.createSubstrateAPI(config_1.PARACHAIN_ENDPOINT);
        const ss58Prefix = utils_1.getSS58Prefix(api);
        const keyring = new api_1.Keyring({ type: "sr25519", ss58Format: ss58Prefix });
        assetRegistry = new index_1.DefaultAssetRegistryAPI(api);
        const transactionAPI = new index_1.DefaultTransactionAPI(api);
        loansAPI = new index_1.DefaultLoansAPI(api, assetRegistry, transactionAPI);
        interBtcAPI = new index_1.DefaultInterBtcApi(api, "regtest", undefined, config_1.ESPLORA_BASE_PATH);
        wrappedCurrency = interBtcAPI.getWrappedCurrency();
        governanceCurrency = interBtcAPI.getGovernanceCurrency();
        collateralCurrencies = helpers_1.getCorrespondingCollateralCurrenciesForTests(governanceCurrency);
        const aUSD = yield helpers_1.getAUSDForeignAsset(assetRegistry);
        if (aUSD !== undefined) {
            // also add aUSD collateral vaults if they exist (ie. the foreign asset exists)
            collateralCurrencies.push(aUSD);
        }
        vault_1 = keyring.addFromUri(config_1.VAULT_1_URI);
        vault_1_ids = collateralCurrencies.map((collateralCurrency) => src_1.newVaultId(api, vault_1.address, collateralCurrency, wrappedCurrency));
        vault_2 = keyring.addFromUri(config_1.VAULT_2_URI);
        vault_3 = keyring.addFromUri(config_1.VAULT_3_URI);
        vault_to_ban = keyring.addFromUri(config_1.VAULT_TO_BAN_URI);
        vault_to_liquidate = keyring.addFromUri(config_1.VAULT_TO_LIQUIDATE_URI);
    }));
    after(() => {
        return api.disconnect();
    });
    afterEach(() => {
        // discard any stubbed methods after each test
        sinon_1.default.restore();
    });
    function vaultIsATestVault(vaultAddress) {
        return (vaultAddress === vault_2.address ||
            vaultAddress === vault_1.address ||
            vaultAddress === vault_3.address ||
            vaultAddress === vault_to_ban.address ||
            vaultAddress === vault_to_liquidate.address);
    }
    // FIXME: this should be tested in a way that in doesn't use magic numbers
    it("should get issuable", () => __awaiter(void 0, void 0, void 0, function* () {
        const issuableInterBTC = yield interBtcAPI.vaults.getTotalIssuableAmount();
        const minExpectedIssuableInterBTC = utils_1.newMonetaryAmount(0.002, wrappedCurrency, true);
        chai_1.assert.isTrue(issuableInterBTC.gte(minExpectedIssuableInterBTC), `Issuable ${issuableInterBTC.toHuman()}`);
    }));
    it("should get the required collateral for the vault", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const vault_1_id of vault_1_ids) {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vault_1_id.currencies.collateral);
            const requiredCollateralForVault = yield interBtcAPI.vaults.getRequiredCollateralForVault(vault_1_id.accountId, collateralCurrency);
            const vault = yield interBtcAPI.vaults.get(vault_1_id.accountId, collateralCurrency);
            // The numeric value of the required collateral should be greater than that of issued tokens.
            // e.g. we require `0.8096` KSM for `0.00014` kBTC
            // edge case: we require 0 KSM for 0 kBTC, so check greater than or equal to
            chai_1.assert.isTrue(requiredCollateralForVault.toBig().gte(vault.getBackedTokens().toBig()), `Expect required collateral (${requiredCollateralForVault.toHuman()})
                to be greater than or equal to backed tokens (${vault.getBackedTokens().toHuman()})`);
        }
    }));
    // WARNING: this test is not idempotent
    // PRECONDITION: vault_1 must have issued some tokens against all collateral currencies
    it("should deposit and withdraw collateral", () => __awaiter(void 0, void 0, void 0, function* () {
        const prevAccount = interBtcAPI.account;
        for (const vault_1_id of vault_1_ids) {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vault_1_id.currencies.collateral);
            const currencyTicker = collateralCurrency.ticker;
            interBtcAPI.setAccount(vault_1);
            const amount = utils_1.newMonetaryAmount(100, collateralCurrency, true);
            const collateralizationBeforeDeposit = yield interBtcAPI.vaults.getVaultCollateralization(src_1.newAccountId(api, vault_1.address), collateralCurrency);
            yield interBtcAPI.vaults.depositCollateral(amount);
            const collateralizationAfterDeposit = yield interBtcAPI.vaults.getVaultCollateralization(src_1.newAccountId(api, vault_1.address), collateralCurrency);
            if (collateralizationBeforeDeposit === undefined || collateralizationAfterDeposit == undefined) {
                chai_1.assert.fail(`Collateralization is undefined for vault with collateral currency ${currencyTicker}
                    - potential cause: the vault may not have any issued tokens secured by ${currencyTicker}`);
                return;
            }
            chai_1.assert.isTrue(collateralizationAfterDeposit.gt(collateralizationBeforeDeposit), `Depositing did not increase collateralization (${currencyTicker} vault),
                expected ${collateralizationAfterDeposit} greater than ${collateralizationBeforeDeposit}`);
            yield interBtcAPI.vaults.withdrawCollateral(amount);
            const collateralizationAfterWithdrawal = yield interBtcAPI.vaults.getVaultCollateralization(src_1.newAccountId(api, vault_1.address), collateralCurrency);
            if (collateralizationAfterWithdrawal === undefined) {
                chai_1.assert.fail(`Collateralization is undefined for vault with collateral currency ${currencyTicker}`);
                return;
            }
            chai_1.assert.isTrue(collateralizationAfterDeposit.gt(collateralizationAfterWithdrawal), `Withdrawing did not decrease collateralization (${currencyTicker} vault), expected
                ${collateralizationAfterDeposit} greater than ${collateralizationAfterWithdrawal}`);
            chai_1.assert.equal(collateralizationBeforeDeposit.toString(), collateralizationAfterWithdrawal.toString(), `Collateralization after identical deposit and withdrawal changed (${currencyTicker} vault)`);
        }
        if (prevAccount) {
            interBtcAPI.setAccount(prevAccount);
        }
    }));
    it("should getLiquidationCollateralThreshold", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const collateralCurrency of collateralCurrencies) {
            const currencyTicker = collateralCurrency.ticker;
            const threshold = yield interBtcAPI.vaults.getLiquidationCollateralThreshold(collateralCurrency);
            chai_1.assert.isTrue(threshold.gt(0), `Expected liquidation threshold for ${currencyTicker} to be greater than 0, but was ${threshold.toString()}`);
        }
    }));
    it("should getPremiumRedeemThreshold", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const collateralCurrency of collateralCurrencies) {
            const currencyTicker = collateralCurrency.ticker;
            const threshold = yield interBtcAPI.vaults.getPremiumRedeemThreshold(collateralCurrency);
            chai_1.assert.isTrue(threshold.gt(0), `Expected premium redeem threshold for ${currencyTicker} to be greater than 0, but was ${threshold.toString()}`);
        }
    }));
    it("should select random vault for issue", () => __awaiter(void 0, void 0, void 0, function* () {
        const randomVault = yield interBtcAPI.vaults.selectRandomVaultIssue(utils_1.newMonetaryAmount(0, wrappedCurrency));
        chai_1.assert.isTrue(vaultIsATestVault(randomVault.accountId.toHuman()));
    }));
    it("should fail if no vault for issuing is found", () => __awaiter(void 0, void 0, void 0, function* () {
        chai_1.assert.isRejected(interBtcAPI.vaults.selectRandomVaultIssue(utils_1.newMonetaryAmount(9000000, wrappedCurrency, true)));
    }));
    it("should select random vault for redeem", () => __awaiter(void 0, void 0, void 0, function* () {
        const randomVault = yield interBtcAPI.vaults.selectRandomVaultRedeem(utils_1.newMonetaryAmount(0, wrappedCurrency));
        chai_1.assert.isTrue(vaultIsATestVault(randomVault.accountId.toHuman()));
    }));
    it("should fail if no vault for redeeming is found", () => __awaiter(void 0, void 0, void 0, function* () {
        const amount = utils_1.newMonetaryAmount(9000000, wrappedCurrency, true);
        chai_1.assert.isRejected(interBtcAPI.vaults.selectRandomVaultRedeem(amount));
    }));
    it("should fail to get vault collateralization for vault with zero collateral", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const vault_1_id of vault_1_ids) {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vault_1_id.currencies.collateral);
            const currencyTicker = collateralCurrency.ticker;
            const vault1Id = src_1.newAccountId(api, vault_1.address);
            chai_1.assert.isRejected(interBtcAPI.vaults.getVaultCollateralization(vault1Id, collateralCurrency), `Collateralization should not be available (${currencyTicker} vault)`);
        }
    }));
    it("should get the issuable InterBtc for a vault", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const vault_1_id of vault_1_ids) {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vault_1_id.currencies.collateral);
            const currencyTicker = collateralCurrency.ticker;
            const vault = yield interBtcAPI.vaults.get(vault_1_id.accountId, collateralCurrency);
            const issuableTokens = yield vault.getIssuableTokens();
            chai_1.assert.isTrue(issuableTokens.gt(utils_1.newMonetaryAmount(0, wrappedCurrency)), `Issuable tokens should be greater than 0 (${currencyTicker} vault)`);
        }
    }));
    it("should get the issuable InterBtc", () => __awaiter(void 0, void 0, void 0, function* () {
        const issuableInterBtc = yield interBtcAPI.vaults.getTotalIssuableAmount();
        chai_1.assert.isTrue(issuableInterBtc.gt(utils_1.newMonetaryAmount(0, wrappedCurrency)));
    }));
    // TODO: revisit after next publish why intrReward is always zero
    // PRECONDITION: vaults must have issued some tokens against all collateral currencies
    it("should getFees", () => __awaiter(void 0, void 0, void 0, function* () {
        const vaultIdsInScope = vault_1_ids;
        let countSkippedVaults = 0;
        for (const vaultId of vaultIdsInScope) {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vaultId.currencies.collateral);
            const wrappedCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vaultId.currencies.wrapped);
            const currencyTicker = collateralCurrency.ticker;
            const vault = yield interBtcAPI.vaults.get(vaultId.accountId, collateralCurrency);
            if (vault.issuedTokens.toBig().eq(0)) {
                // no issued tokens => no rewards => nothing to check
                countSkippedVaults++;
                continue;
            }
            const feesWrapped = yield interBtcAPI.vaults.getWrappedReward(vaultId.accountId, collateralCurrency, wrappedCurrency);
            chai_1.assert.isTrue(feesWrapped.gt(utils_1.newMonetaryAmount(0, wrappedCurrency)), `Fees should be greater than 0 (${currencyTicker} vault)`);
            const intrReward = yield interBtcAPI.vaults.getGovernanceReward(vaultId.accountId, collateralCurrency, governanceCurrency);
            chai_1.assert.isTrue(intrReward.gt(utils_1.newMonetaryAmount(0, governanceCurrency)), `Governance reward should be greater than 0 (${currencyTicker} vault)`);
        }
        // make sure not every vault has been skipped (due to no issued tokens)
        chai_1.assert.notEqual(countSkippedVaults, vaultIdsInScope.length, `Unexpected test behavior: skipped all ${vaultIdsInScope.length} vaults in the test; all vaults lacking issued tokens`);
    }));
    it("should getAPY", () => __awaiter(void 0, void 0, void 0, function* () {
        for (const vault_1_id of vault_1_ids) {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, vault_1_id.currencies.collateral);
            const currencyTicker = collateralCurrency.ticker;
            const apy = yield interBtcAPI.vaults.getAPY(src_1.newAccountId(api, vault_1.address), collateralCurrency);
            const apyBig = new big_js_1.default(apy);
            const apyBenchmark = new big_js_1.default("0");
            chai_1.assert.isTrue(apyBig.gte(apyBenchmark), `APY should be greater than or equal to ${apyBenchmark.toString()},
                    but was ${apyBig.toString()} (${currencyTicker} vault)`);
        }
    }));
    it("should getPunishmentFee", () => __awaiter(void 0, void 0, void 0, function* () {
        const punishmentFee = yield interBtcAPI.vaults.getPunishmentFee();
        chai_1.assert.equal(punishmentFee.toString(), "0.1");
    }));
    it("should get vault list", () => __awaiter(void 0, void 0, void 0, function* () {
        const vaults = (yield interBtcAPI.vaults.list()).map((vault) => vault.id.toHuman());
        chai_1.assert.isAbove(vaults.length, 0, "Vault list should not be empty");
    }));
    it("should disable and enable issuing with vault", () => __awaiter(void 0, void 0, void 0, function* () {
        const assertVaultStatus = (id, expectedStatus) => __awaiter(void 0, void 0, void 0, function* () {
            const collateralCurrency = yield index_1.currencyIdToMonetaryCurrency(assetRegistry, loansAPI, id.currencies.collateral);
            const currencyTicker = collateralCurrency.ticker;
            const { status } = yield interBtcAPI.vaults.get(id.accountId, collateralCurrency);
            const assertionMessage = `Vault with id ${id.toString()} (collateral: ${currencyTicker}) was expected to have
                    status: ${helpers_1.vaultStatusToLabel(expectedStatus)}, but got status: ${helpers_1.vaultStatusToLabel(status)}`;
            chai_1.assert.isTrue(status === expectedStatus, assertionMessage);
        });
        const ACCEPT_NEW_ISSUES = true;
        const REJECT_NEW_ISSUES = false;
        for (const vault_1_id of vault_1_ids) {
            // Check that vault 1 is active.
            yield assertVaultStatus(vault_1_id, index_1.VaultStatusExt.Active);
            // Disables vault 1 which is active.
            yield interBtcAPI.vaults.toggleIssueRequests(vault_1_id, REJECT_NEW_ISSUES);
            // Check that vault 1 is inactive.
            yield assertVaultStatus(vault_1_id, index_1.VaultStatusExt.Inactive);
            // Re-enable issuing with vault 1.
            yield interBtcAPI.vaults.toggleIssueRequests(vault_1_id, ACCEPT_NEW_ISSUES);
            // Check that vault 1 is again active.
            yield assertVaultStatus(vault_1_id, index_1.VaultStatusExt.Active);
        }
    }));
});
//# sourceMappingURL=vaults.test.js.map