"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 });
exports.DefaultVaultsAPI = void 0;
const big_js_1 = __importDefault(require("big.js"));
const utils_1 = require("../utils");
const types_1 = require("../types");
const index_1 = require("./index");
class DefaultVaultsAPI {
    constructor(api, electrsAPI, wrappedCurrency, governanceCurrency, tokensAPI, oracleAPI, feeAPI, rewardsAPI, systemAPI, transactionAPI, assetRegistryAPI, loansAPI) {
        this.api = api;
        this.electrsAPI = electrsAPI;
        this.wrappedCurrency = wrappedCurrency;
        this.governanceCurrency = governanceCurrency;
        this.tokensAPI = tokensAPI;
        this.oracleAPI = oracleAPI;
        this.feeAPI = feeAPI;
        this.rewardsAPI = rewardsAPI;
        this.systemAPI = systemAPI;
        this.transactionAPI = transactionAPI;
        this.assetRegistryAPI = assetRegistryAPI;
        this.loansAPI = loansAPI;
    }
    getWrappedCurrency() {
        return this.wrappedCurrency;
    }
    registerNewCollateralVault(collateralAmount) {
        return __awaiter(this, void 0, void 0, function* () {
            // check the vault account is set
            const vaultAccount = this.transactionAPI.getAccount();
            if (vaultAccount === undefined) {
                return Promise.reject("Failed to read account in vaults API; account must be set in interbtc API");
            }
            const extrinsic = this.buildRegisterVaultExtrinsic(collateralAmount);
            const registerVaultEvent = this.getRegisterVaultEvent();
            yield this.transactionAPI.sendLogged(extrinsic, registerVaultEvent, true);
        });
    }
    // helper method; mainly for easier mocking in unit tests without an active ApiPromise instance
    getRegisterVaultEvent() {
        return this.api.events.vaultRegistry.RegisterVault;
    }
    buildRegisterVaultExtrinsic(collateralAmount) {
        const currencyPair = utils_1.newVaultCurrencyPair(this.api, collateralAmount.currency, this.getWrappedCurrency());
        const amountAtomicUnit = this.api.createType("Balance", collateralAmount.toString(true));
        return this.api.tx.vaultRegistry.registerVault(currencyPair, amountAtomicUnit);
    }
    buildRegisterPublicKeyExtrinsic(publicKey) {
        return this.api.tx.vaultRegistry.registerPublicKey(publicKey);
    }
    register(amount, publicKey) {
        return __awaiter(this, void 0, void 0, function* () {
            yield Promise.all([
                this.transactionAPI.sendLogged(this.buildRegisterPublicKeyExtrinsic(publicKey), this.api.events.vaultRegistry.UpdatePublicKey, true),
                this.transactionAPI.sendLogged(this.buildRegisterVaultExtrinsic(amount), this.api.events.vaultRegistry.RegisterVault, true),
            ]);
        });
    }
    buildWithdrawCollateralExtrinsic(amount) {
        return __awaiter(this, void 0, void 0, function* () {
            const account = this.transactionAPI.getAccount();
            if (account == undefined) {
                throw new Error("Account must be connected to create a collateral withdrawal request.");
            }
            const vaultAccountId = utils_1.addressOrPairAsAccountId(this.api, account);
            return yield index_1.DefaultNominationAPI.buildWithdrawCollateralExtrinsic(this.api, this.rewardsAPI, vaultAccountId, amount, this.wrappedCurrency);
        });
    }
    withdrawCollateral(amount) {
        return __awaiter(this, void 0, void 0, function* () {
            const tx = yield this.buildWithdrawCollateralExtrinsic(amount);
            yield this.transactionAPI.sendLogged(tx, this.api.events.vaultRegistry.WithdrawCollateral, true);
        });
    }
    buildDepositCollateralExtrinsic(amount) {
        const account = this.transactionAPI.getAccount();
        if (account == undefined) {
            throw new Error("Account must be connected to create a collateral deposit request.");
        }
        const vaultAccountId = utils_1.addressOrPairAsAccountId(this.api, account);
        return index_1.DefaultNominationAPI.buildDepositCollateralExtrinsic(this.api, vaultAccountId, amount, this.wrappedCurrency);
    }
    depositCollateral(amount) {
        return __awaiter(this, void 0, void 0, function* () {
            const tx = this.buildDepositCollateralExtrinsic(amount);
            yield this.transactionAPI.sendLogged(tx, this.api.events.vaultRegistry.DepositCollateral, true);
        });
    }
    list(atBlock) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultsMap = yield (atBlock
                ? this.api.query.vaultRegistry.vaults.entriesAt(atBlock)
                : this.api.query.vaultRegistry.vaults.entries());
            return Promise.all(vaultsMap.filter((v) => v[1].isSome).map((v) => this.parseVault(v[1].value)));
        });
    }
    getOrNull(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const vaultId = utils_1.newVaultId(this.api, vaultAccountId.toString(), collateralCurrency, this.wrappedCurrency);
                const vault = yield this.api.query.vaultRegistry.vaults(vaultId);
                if (!vault.isSome) {
                    return null;
                }
                return this.parseVault(vault.value);
            }
            catch (error) {
                return Promise.reject(error);
            }
        });
    }
    get(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vault = yield this.getOrNull(vaultAccountId, collateralCurrency);
            if (vault === null) {
                return Promise.reject(new Error(`Vault does not exist for id '${vaultAccountId}' and collateral '${collateralCurrency.name}'`));
            }
            return vault;
        });
    }
    getCollateral(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.rewardsAPI.computeCollateralInStakingPool(utils_1.newVaultId(this.api, vaultAccountId.toString(), collateralCurrency, this.wrappedCurrency), vaultAccountId);
        });
    }
    getMinimumCollateral(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const collateralCurrencyId = utils_1.newCurrencyId(this.api, collateralCurrency);
            const minimumCollateral = yield this.api.query.vaultRegistry.minimumCollateralVault(collateralCurrencyId);
            return utils_1.newMonetaryAmount(minimumCollateral.toString(), collateralCurrency);
        });
    }
    getMaxNominationRatio(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const [premiumRedeemThreshold, secureCollateralThreshold] = yield Promise.all([
                this.getPremiumRedeemThreshold(collateralCurrency),
                this.getSecureCollateralThreshold(collateralCurrency),
            ]);
            return secureCollateralThreshold.div(premiumRedeemThreshold);
        });
    }
    computeBackingCollateral(vaultId, nonce) {
        return __awaiter(this, void 0, void 0, function* () {
            const collateralCurrency = yield utils_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vaultId.currencies.collateral);
            if (nonce === undefined) {
                nonce = yield this.rewardsAPI.getStakingPoolNonce(collateralCurrency, vaultId.accountId);
            }
            const rawBackingCollateral = yield this.api.query.vaultStaking.totalCurrentStake(nonce, vaultId);
            return utils_1.newMonetaryAmount(utils_1.decodeFixedPointType(rawBackingCollateral), collateralCurrency);
        });
    }
    backingCollateralProportion(vaultAccountId, nominatorId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vault = yield this.get(vaultAccountId, collateralCurrency);
            const nominatorCollateral = yield this.rewardsAPI.computeCollateralInStakingPool(utils_1.newVaultId(this.api, vaultAccountId.toString(), collateralCurrency, this.wrappedCurrency), nominatorId);
            // short-circuit a potential 0 div 0 scenario where
            // the nominator is equal to the vault and has zero collateral
            if (nominatorCollateral.isZero()) {
                return nominatorCollateral.toBig();
            }
            const backingCollateral = vault.backingCollateral.toBig();
            if (backingCollateral.eq(0)) {
                return Promise.reject(new Error("No backing collateral"));
            }
            return nominatorCollateral.toBig().div(backingCollateral);
        });
    }
    getBlockRewardAPY(vaultAccountId, nominatorId, collateralCurrency, governanceCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const [globalRewardPerBlock, globalStake, vaultStake, vaultRewardShare, lockedCollateral, minimumBlockPeriod] = yield Promise.all([
                this.rewardsAPI.getRewardPerBlock(governanceCurrency),
                this.getTotalIssuedAmount(),
                this.getIssuedAmount(vaultAccountId, collateralCurrency),
                this.backingCollateralProportion(vaultAccountId, nominatorId, collateralCurrency),
                this.getLockedCollateral(vaultAccountId, collateralCurrency),
                this.api.consts.timestamp.minimumPeriod,
            ]);
            if (globalStake.toBig().eq(0)) {
                return Promise.reject(new Error("No issued kBTC"));
            }
            const globalRewardShare = vaultStake.toBig().div(globalStake.toBig());
            const vaultRewardPerBlock = globalRewardPerBlock.mul(globalRewardShare);
            const ownRewardPerBlock = vaultRewardPerBlock.mul(vaultRewardShare);
            const rewardAsWrapped = yield this.oracleAPI.convertCollateralToWrapped(ownRewardPerBlock);
            const blockTime = minimumBlockPeriod.toNumber() * 2; // ms
            const blocksPerYear = (86400 * 365 * 1000) / blockTime;
            const annualisedReward = rewardAsWrapped.mul(blocksPerYear);
            return this.feeAPI.calculateAPY(annualisedReward, lockedCollateral);
        });
    }
    getLockedCollateral(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            // TODO: This might be inaccurate if the "reserved" value is incremented by other protocols
            // See comment: https://github.com/interlay/interbtc-api/pull/464#discussion_r954909315
            return (yield this.tokensAPI.balance(collateralCurrency, vaultAccountId)).reserved;
        });
    }
    computeReward(vaultAccountId, nominatorId, collateralCurrency, rewardCurrency, nonce) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.rewardsAPI.computeRewardInRewardsPool(rewardCurrency, collateralCurrency, vaultAccountId);
        });
    }
    getWrappedReward(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.computeReward(vaultAccountId, vaultAccountId, collateralCurrency, this.wrappedCurrency);
        });
    }
    getGovernanceReward(vaultAccountId, vaultCollateral, governanceCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.computeReward(vaultAccountId, vaultAccountId, vaultCollateral, governanceCurrency);
        });
    }
    getStakingCapacity(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vault = yield this.get(vaultAccountId, collateralCurrency);
            const [collateral, maxNominationRatio] = yield Promise.all([
                this.getCollateral(vaultAccountId, collateralCurrency),
                this.getMaxNominationRatio(yield utils_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vault.id.currencies.collateral)),
            ]);
            return collateral.mul(maxNominationRatio).sub(vault.backingCollateral);
        });
    }
    getLiquidationVault(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultCurrencyPair = utils_1.newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency);
            const liquidationVault = yield this.api.query.vaultRegistry.liquidationVault(vaultCurrencyPair);
            if (!liquidationVault.isSome) {
                return Promise.reject("System vault could not be fetched");
            }
            return yield utils_1.parseSystemVault(this.assetRegistryAPI, this.loansAPI, liquidationVault.value, this.wrappedCurrency, collateralCurrency);
        });
    }
    isNoTokensIssuedError(e) {
        return e !== undefined && e.includes("No tokens issued");
    }
    isBelowPremiumThreshold(vaultId) {
        return __awaiter(this, void 0, void 0, function* () {
            const [premiumRedeemThreshold, vaultCollateralization] = yield Promise.all([
                this.getPremiumRedeemThreshold(yield utils_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vaultId.currencies.collateral)),
                this.getCollateralizationFromVault(vaultId),
            ]);
            return vaultCollateralization.lt(premiumRedeemThreshold);
        });
    }
    getVaultCollateralization(vaultAccountId, collateralCurrency, newCollateral, onlyIssued = false) {
        return __awaiter(this, void 0, void 0, function* () {
            let collateralization = undefined;
            const vaultId = utils_1.newVaultId(this.api, vaultAccountId.toString(), collateralCurrency, this.wrappedCurrency);
            try {
                if (newCollateral) {
                    collateralization = yield this.getCollateralizationFromVaultAndCollateral(vaultId, newCollateral, onlyIssued);
                }
                else {
                    collateralization = yield this.getCollateralizationFromVault(vaultId, onlyIssued);
                }
            }
            catch (e) {
                if (this.isNoTokensIssuedError(e)) {
                    return Promise.resolve(undefined);
                }
                return Promise.reject(new Error(`Error during collateralization computation: ${e}`));
            }
            if (!collateralization) {
                return Promise.resolve(undefined);
            }
            return collateralization;
        });
    }
    getCollateralizationFromVault(vaultId, onlyIssued = false) {
        return __awaiter(this, void 0, void 0, function* () {
            const collateral = yield this.computeBackingCollateral(vaultId);
            return this.getCollateralizationFromVaultAndCollateral(vaultId, collateral, onlyIssued);
        });
    }
    getCollateralizationFromVaultAndCollateral(vaultId, newCollateral, onlyIssued) {
        return __awaiter(this, void 0, void 0, function* () {
            const vault = yield this.get(vaultId.accountId, yield utils_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vaultId.currencies.collateral));
            const issuedTokens = yield (onlyIssued ? Promise.resolve(vault.issuedTokens) : vault.getBackedTokens());
            if (issuedTokens.isZero()) {
                return Promise.reject("No tokens issued");
            }
            const collateralInWrapped = yield this.oracleAPI.convertCollateralToWrapped(newCollateral);
            return collateralInWrapped.toBig().div(issuedTokens.toBig());
        });
    }
    getSystemCollateralization() {
        return __awaiter(this, void 0, void 0, function* () {
            // TODO: Implement once method of calculation is decided on
            return Promise.resolve(undefined);
        });
    }
    getRequiredCollateralForVault(vaultAccountId, currency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vault = yield this.get(vaultAccountId, currency);
            const issuedTokens = vault.getBackedTokens();
            return yield this.getRequiredCollateralForWrapped(issuedTokens, currency);
        });
    }
    getRequiredCollateralForWrapped(wrappedAmount, currency) {
        return __awaiter(this, void 0, void 0, function* () {
            const secureCollateralThreshold = yield this.getSecureCollateralThreshold(currency);
            const requiredCollateralInWrappedCurrency = wrappedAmount.mul(secureCollateralThreshold);
            return yield this.oracleAPI.convertWrappedToCurrency(requiredCollateralInWrappedCurrency, currency);
        });
    }
    getIssuedAmount(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vault = yield this.get(vaultAccountId, collateralCurrency);
            return vault.issuedTokens;
        });
    }
    getTotalIssuedAmount() {
        return __awaiter(this, void 0, void 0, function* () {
            const issuedTokens = yield this.tokensAPI.total(this.wrappedCurrency);
            return issuedTokens;
        });
    }
    getTotalIssuableAmount() {
        return __awaiter(this, void 0, void 0, function* () {
            const issuableVaults = yield this.api.rpc.vaultRegistry.getVaultsWithIssuableTokens();
            const totalBalanceAtomic = issuableVaults.reduce((acc, [_, balanceWrapper]) => acc.add(balanceWrapper.amount.toString()), new big_js_1.default(0));
            const wrappedCurrency = this.getWrappedCurrency();
            return utils_1.newMonetaryAmount(totalBalanceAtomic, wrappedCurrency);
        });
    }
    calculateCapacity(collateral) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const [exchangeRate, secureCollateralThreshold] = yield Promise.all([
                    this.oracleAPI.getExchangeRate(collateral.currency),
                    this.getSecureCollateralThreshold(collateral.currency),
                ]);
                const unusedCollateral = collateral.div(secureCollateralThreshold);
                return exchangeRate.toBase(unusedCollateral);
            }
            catch (error) {
                return utils_1.newMonetaryAmount(0, this.wrappedCurrency);
            }
        });
    }
    getIssuableTokensFromVault(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultId = utils_1.newVaultId(this.api, vaultAccountId.toString(), collateralCurrency, this.getWrappedCurrency());
            const balance = yield this.api.rpc.vaultRegistry.getIssueableTokensFromVault({
                account_id: vaultId.accountId,
                currencies: vaultId.currencies,
            });
            const wrappedCurrencyPrimitive = utils_1.newCurrencyId(this.api, this.getWrappedCurrency());
            const currency = yield utils_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, wrappedCurrencyPrimitive);
            const amount = utils_1.newMonetaryAmount(balance.amount.toString(), currency);
            return amount;
        });
    }
    selectRandomVaultIssue(amount) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaults = yield this.getVaultsWithIssuableTokens();
            for (const [id, issuableAmount] of vaults) {
                if (issuableAmount.gte(amount)) {
                    return id;
                }
            }
            return Promise.reject(new Error("Did not find vault with sufficient collateral"));
        });
    }
    selectRandomVaultRedeem(amount) {
        return __awaiter(this, void 0, void 0, function* () {
            // Selects the first vault with sufficient tokens
            const vaults = yield this.list();
            for (const vault of vaults) {
                if (vault.issuedTokens.gte(amount)) {
                    return vault.id;
                }
            }
            return Promise.reject(new Error("Did not find vault with sufficient locked BTC"));
        });
    }
    getPremiumRedeemVaults() {
        return __awaiter(this, void 0, void 0, function* () {
            const map = new Map();
            const premiumRedeemVaults = yield this.api.rpc.vaultRegistry.getPremiumRedeemVaults();
            for (const [vaultId, balanceWrapper] of premiumRedeemVaults) {
                const amount = utils_1.newMonetaryAmount(balanceWrapper.amount.toString(), this.getWrappedCurrency());
                const ibtcPrimitivesVaultId = yield utils_1.decodeRpcVaultId(this.api, this.assetRegistryAPI, this.loansAPI, vaultId);
                map.set(ibtcPrimitivesVaultId, amount);
            }
            return map;
        });
    }
    getVaultsWithIssuableTokens() {
        return __awaiter(this, void 0, void 0, function* () {
            const issuableVaults = yield this.api.rpc.vaultRegistry.getVaultsWithIssuableTokens();
            const vaultIdsToAmountsMap = new Map();
            for (const [vaultId, balanceWrapper] of issuableVaults) {
                const amount = utils_1.newMonetaryAmount(balanceWrapper.amount.toString(), this.getWrappedCurrency());
                const ibtcPrimitivesVaultId = yield utils_1.decodeRpcVaultId(this.api, this.assetRegistryAPI, this.loansAPI, vaultId);
                vaultIdsToAmountsMap.set(ibtcPrimitivesVaultId, amount);
            }
            return vaultIdsToAmountsMap;
        });
    }
    isVaultEligibleForRedeem(vault, activeBlockNumber) {
        const bannedUntilBlockNumber = vault.bannedUntil || 0;
        return ((vault.status === types_1.VaultStatusExt.Active || vault.status === types_1.VaultStatusExt.Inactive) &&
            vault.issuedTokens.gt(vault.toBeRedeemedTokens) &&
            bannedUntilBlockNumber < activeBlockNumber);
    }
    getVaultsEligibleForRedeeming() {
        return __awaiter(this, void 0, void 0, function* () {
            const [vaults, activeBlockNumber] = yield Promise.all([
                this.list(),
                this.systemAPI.getCurrentActiveBlockNumber(),
            ]);
            // only non-banned, non-liquidated vaults with liquidity are eligible for redeems
            const redeemVaults = vaults.filter((vault) => {
                return this.isVaultEligibleForRedeem(vault, activeBlockNumber);
            });
            return redeemVaults;
        });
    }
    getVaultsWithRedeemableTokens() {
        return __awaiter(this, void 0, void 0, function* () {
            const map = new Map();
            const vaults = yield this.getVaultsEligibleForRedeeming();
            vaults
                .sort((vault1, vault2) => {
                // Descending order
                const vault1Redeemable = vault1.getRedeemableTokens().toBig();
                const vault2Redeemable = vault2.getRedeemableTokens().toBig();
                return vault2Redeemable.sub(vault1Redeemable).toNumber();
            })
                .forEach((vault) => map.set(vault.id, vault.getRedeemableTokens()));
            return map;
        });
    }
    isVaultFlaggedForTheft(vaultId, btcTxId) {
        return __awaiter(this, void 0, void 0, function* () {
            const theftReports = yield this.api.query.relay.theftReports(vaultId, {
                content: utils_1.addHexPrefix(btcTxId),
            });
            return theftReports.isEmpty;
        });
    }
    getLiquidationCollateralThreshold(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultCurrencyPair = utils_1.newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency);
            const threshold = yield this.api.query.vaultRegistry.liquidationCollateralThreshold(vaultCurrencyPair);
            if (!threshold.isSome) {
                return Promise.reject(`No liquidation threshold for currency ${collateralCurrency.ticker}`);
            }
            return utils_1.decodeFixedPointType(threshold.value);
        });
    }
    getPremiumRedeemThreshold(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultCurrencyPair = utils_1.newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency);
            const threshold = yield this.api.query.vaultRegistry.premiumRedeemThreshold(vaultCurrencyPair);
            if (!threshold.isSome) {
                return Promise.reject(`No premium redeem threshold for currency ${collateralCurrency.ticker}`);
            }
            return utils_1.decodeFixedPointType(threshold.value);
        });
    }
    getSecureCollateralThreshold(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultCurrencyPair = utils_1.newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency);
            const threshold = yield this.api.query.vaultRegistry.secureCollateralThreshold(vaultCurrencyPair);
            return utils_1.decodeFixedPointType(threshold.value);
        });
    }
    getAPY(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const [feesWrapped, lockedCollateral, blockRewardsAPY] = yield Promise.all([
                this.getWrappedReward(vaultAccountId, collateralCurrency),
                this.getLockedCollateral(vaultAccountId, collateralCurrency),
                this.getBlockRewardAPY(vaultAccountId, vaultAccountId, collateralCurrency, this.governanceCurrency),
            ]);
            return (yield this.feeAPI.calculateAPY(feesWrapped, lockedCollateral)).add(blockRewardsAPY);
        });
    }
    getPunishmentFee() {
        return __awaiter(this, void 0, void 0, function* () {
            const fee = yield this.api.query.fee.punishmentFee();
            return utils_1.decodeFixedPointType(fee);
        });
    }
    parseVaultStatus(status) {
        if (status.isActive) {
            return status.asActive.isTrue ? types_1.VaultStatusExt.Active : types_1.VaultStatusExt.Inactive;
        }
        else if (status.isLiquidated) {
            return types_1.VaultStatusExt.Liquidated;
        }
        else {
            throw new Error("Unknown vault status");
        }
    }
    parseVault(vault) {
        return __awaiter(this, void 0, void 0, function* () {
            const collateralCurrency = yield utils_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vault.id.currencies.collateral);
            const replaceCollateral = utils_1.newMonetaryAmount(vault.replaceCollateral.toString(), collateralCurrency);
            const liquidatedCollateral = utils_1.newMonetaryAmount(vault.liquidatedCollateral.toString(), collateralCurrency);
            const backingCollateral = yield this.computeBackingCollateral(vault.id);
            const secureThreshold = vault.secureCollateralThreshold.isSome
                ? utils_1.decodeFixedPointType(vault.secureCollateralThreshold.unwrap())
                : yield this.getSecureCollateralThreshold(collateralCurrency);
            return new types_1.VaultExt(this.api, this.oracleAPI, this.systemAPI, this.assetRegistryAPI, this.loansAPI, backingCollateral, vault.id, this.parseVaultStatus(vault.status), vault.bannedUntil.isSome ? vault.bannedUntil.value.toNumber() : undefined, utils_1.newMonetaryAmount(vault.toBeIssuedTokens.toString(), this.wrappedCurrency), utils_1.newMonetaryAmount(vault.issuedTokens.toString(), this.wrappedCurrency), utils_1.newMonetaryAmount(vault.toBeRedeemedTokens.toString(), this.wrappedCurrency), utils_1.newMonetaryAmount(vault.toBeReplacedTokens.toString(), this.wrappedCurrency), replaceCollateral, liquidatedCollateral, secureThreshold);
        });
    }
    reportVaultTheft(vaultId, btcTxId) {
        return __awaiter(this, void 0, void 0, function* () {
            const txInclusionDetails = yield utils_1.getTxProof(this.electrsAPI, btcTxId);
            const tx = this.api.tx.relay.reportVaultTheft(vaultId, txInclusionDetails.merkleProof, txInclusionDetails.rawTx);
            yield this.transactionAPI.sendLogged(tx, this.api.events.relay.VaultTheft, true);
        });
    }
    buildAcceptNewIssuesExtrinsic(collateralCurrency, acceptNewIssues) {
        const vaultCurrencyPair = utils_1.newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency);
        return this.api.tx.vaultRegistry.acceptNewIssues(vaultCurrencyPair, acceptNewIssues);
    }
    toggleIssueRequests(vaultId, acceptNewIssues) {
        return __awaiter(this, void 0, void 0, function* () {
            const collateralCurrency = yield utils_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vaultId.currencies.collateral);
            const tx = this.buildAcceptNewIssuesExtrinsic(collateralCurrency, acceptNewIssues);
            yield this.transactionAPI.sendLogged(tx, this.api.events.system.ExtrinsicSuccess, true);
        });
    }
    getExchangeRateForLiquidation(vaultAccountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const [vaultExt, liquidationRateThreshold, lockedCollateral] = yield Promise.all([
                this.get(vaultAccountId, collateralCurrency),
                this.getLiquidationCollateralThreshold(collateralCurrency),
                this.getLockedCollateral(vaultAccountId, collateralCurrency),
            ]);
            if (liquidationRateThreshold.eq(0)) {
                return undefined;
            }
            const issuedTokens = vaultExt.getBackedTokens();
            if (issuedTokens.toBig().eq(0)) {
                return undefined;
            }
            // calculate the theoretical exchange rate at which the vault would be (close to) liquidated
            const liquidationRateCollateralPerWrapped = lockedCollateral
                .toBig()
                .div(issuedTokens.toBig().mul(liquidationRateThreshold));
            return liquidationRateCollateralPerWrapped;
        });
    }
}
exports.DefaultVaultsAPI = DefaultVaultsAPI;
//# sourceMappingURL=vaults.js.map