"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 });
exports.DefaultNominationAPI = exports.NominationAmountType = void 0;
const utils_1 = require("../utils");
const types_1 = require("../types");
const currency_1 = require("../utils/currency");
var NominationAmountType;
(function (NominationAmountType) {
    NominationAmountType["Raw"] = "raw";
    NominationAmountType["Parsed"] = "parsed";
})(NominationAmountType = exports.NominationAmountType || (exports.NominationAmountType = {}));
class DefaultNominationAPI {
    constructor(api, wrappedCurrency, vaultsAPI, rewardsAPI, transactionAPI, assetRegistryAPI, loansAPI) {
        this.api = api;
        this.wrappedCurrency = wrappedCurrency;
        this.vaultsAPI = vaultsAPI;
        this.rewardsAPI = rewardsAPI;
        this.transactionAPI = transactionAPI;
        this.assetRegistryAPI = assetRegistryAPI;
        this.loansAPI = loansAPI;
    }
    static buildDepositCollateralExtrinsic(api, vaultAccountId, amount, wrappedCurrency) {
        const vaultId = utils_1.newVaultId(api, vaultAccountId.toString(), amount.currency, wrappedCurrency);
        const amountAsPlanck = api.createType("Balance", amount.toString(true));
        return api.tx.nomination.depositCollateral(vaultId, amountAsPlanck);
    }
    depositCollateral(vaultAccountId, amount) {
        return __awaiter(this, void 0, void 0, function* () {
            const tx = DefaultNominationAPI.buildDepositCollateralExtrinsic(this.api, vaultAccountId, amount, this.wrappedCurrency);
            yield this.transactionAPI.sendLogged(tx, this.api.events.nomination.DepositCollateral, true);
        });
    }
    static buildWithdrawCollateralExtrinsic(api, rewardsAPI, vaultAccountId, amount, wrappedCurrency, nonce) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultId = utils_1.newVaultId(api, vaultAccountId.toString(), amount.currency, wrappedCurrency);
            const definedNonce = nonce ? nonce : yield rewardsAPI.getStakingPoolNonce(amount.currency, vaultAccountId);
            const amountAsPlanck = api.createType("Balance", amount.toString(true));
            const parsedNonce = api.createType("Index", definedNonce);
            return api.tx.nomination.withdrawCollateral(vaultId, amountAsPlanck, parsedNonce);
        });
    }
    withdrawCollateral(vaultAccountId, amount, nonce) {
        return __awaiter(this, void 0, void 0, function* () {
            const tx = yield DefaultNominationAPI.buildWithdrawCollateralExtrinsic(this.api, this.rewardsAPI, vaultAccountId, amount, this.wrappedCurrency, nonce);
            yield this.transactionAPI.sendLogged(tx, this.api.events.nomination.WithdrawCollateral, true);
        });
    }
    optIn(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultCurrencyPair = utils_1.newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency);
            const tx = this.api.tx.nomination.optInToNomination(vaultCurrencyPair);
            yield this.transactionAPI.sendLogged(tx, this.api.events.nomination.NominationOptIn, true);
        });
    }
    optOut(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultCurrencyPair = utils_1.newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency);
            const tx = this.api.tx.nomination.optOutOfNomination(vaultCurrencyPair);
            yield this.transactionAPI.sendLogged(tx, this.api.events.nomination.NominationOptOut, true);
        });
    }
    setNominationEnabled(enabled) {
        return __awaiter(this, void 0, void 0, function* () {
            const tx = this.api.tx.sudo.sudo(this.api.tx.nomination.setNominationEnabled(enabled));
            yield this.transactionAPI.sendLogged(tx, undefined, true);
        });
    }
    isNominationEnabled() {
        return __awaiter(this, void 0, void 0, function* () {
            const isNominationEnabled = yield this.api.query.nomination.nominationEnabled();
            return isNominationEnabled.isTrue;
        });
    }
    getNonces() {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultIds = yield this.listVaults();
            const nonceMap = new Map();
            for (const vaultId of vaultIds) {
                const nonce = yield this.rewardsAPI.getStakingPoolNonce(yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vaultId.currencies.collateral), vaultId.accountId);
                nonceMap.set(vaultId, nonce);
            }
            return nonceMap;
        });
    }
    listAllNominations() {
        return __awaiter(this, void 0, void 0, function* () {
            const nonces = yield this.getNonces();
            const stakesMap = yield this.api.query.vaultStaking.stake.entries();
            const nominations = [];
            for (const v of stakesMap) {
                const nonce = utils_1.storageKeyToNthInner(v[0], 0);
                const [vaultId, nominatorId] = utils_1.storageKeyToNthInner(v[0], 1);
                const nomination = utils_1.decodeFixedPointType(v[1]);
                const collateralCurrency = yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, vaultId.currencies.collateral);
                const monetaryNomination = utils_1.newMonetaryAmount(nomination, collateralCurrency, true);
                const rawNomination = {
                    nonce: nonce.toNumber(),
                    vaultId,
                    nominatorId,
                    amount: monetaryNomination,
                    type: NominationAmountType.Raw,
                };
                // Cannot just do `nonces.get(rawNomination.vaultId)` because vaultId objects differ
                // ever so slightly even if they have identical properties
                const vaultIdHighestNonce = yield utils_1.queryNominationsMap(this.assetRegistryAPI, this.loansAPI, nonces, rawNomination.vaultId);
                // Only consider active nominations, i.e. with the latest nonce
                if (rawNomination.nonce === vaultIdHighestNonce && rawNomination.amount.toBig().gt(0)) {
                    nominations.push(rawNomination);
                }
            }
            return nominations;
        });
    }
    listNominatorRewards() {
        return __awaiter(this, void 0, void 0, function* () {
            const rawList = yield this.listAllNominations();
            return yield Promise.all(rawList.map((rawNomination) => __awaiter(this, void 0, void 0, function* () {
                const reward = yield this.vaultsAPI.computeReward(rawNomination.vaultId.accountId, rawNomination.nominatorId, yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, rawNomination.vaultId.currencies.collateral), yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, rawNomination.vaultId.currencies.wrapped));
                return {
                    nonce: rawNomination.nonce,
                    vaultId: rawNomination.vaultId,
                    nominatorId: rawNomination.nominatorId,
                    amount: reward,
                };
            })));
        });
    }
    getActiveNominatorRewards(nominatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            const nominatorRewards = yield this.listNominatorRewards();
            return nominatorRewards.filter((nominationReward) => {
                return nominationReward.nominatorId.toString() === nominatorId.toString();
            });
        });
    }
    getNominatorReward(vaultId, collateralCurrency, rewardCurrency, nominatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.vaultsAPI.computeReward(vaultId, nominatorId, collateralCurrency, rewardCurrency);
        });
    }
    list() {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.getFilteredNominations();
        });
    }
    getFilteredNominations(vaultId, collateralCurrency, nominatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            const rawList = yield this.listAllNominations();
            // Filter by nominatorId and vaultId if each is defined respectively.
            const rawNominations = [];
            for (const rawNomination of rawList) {
                const nominationCurrency = yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, rawNomination.vaultId.currencies.collateral);
                const nominationWrappedCurrency = yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, rawNomination.vaultId.currencies.wrapped);
                const wrappedCurrency = this.wrappedCurrency;
                if ((!nominatorId || nominatorId === nominatorId) &&
                    (!vaultId || vaultId === vaultId) &&
                    (!collateralCurrency || utils_1.isCurrencyEqual(collateralCurrency, nominationCurrency)) &&
                    utils_1.isCurrencyEqual(nominationWrappedCurrency, wrappedCurrency)) {
                    rawNominations.push(rawNomination);
                }
            }
            return yield Promise.all(rawNominations.map((rawNomination) => __awaiter(this, void 0, void 0, function* () {
                return Object.assign(Object.assign({}, rawNomination), { amount: yield this.rewardsAPI.computeCollateralInStakingPool(rawNomination.vaultId, rawNomination.nominatorId), type: NominationAmountType.Parsed });
            })));
        });
    }
    getNominationStatus(vaultId, collateralCurrency, nominatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            // There is at most one entry determined by this pair
            const filteredNominations = yield this.getFilteredNominations(vaultId, collateralCurrency, nominatorId);
            if (filteredNominations.length === 0) {
                return Promise.reject(new Error("No nomination associated with this (nominator, vault) pair"));
            }
            if (filteredNominations[0].amount.isZero()) {
                return types_1.NominationStatus.Unstaked;
            }
            else {
                return types_1.NominationStatus.Staked;
            }
        });
    }
    getTotalNomination(vaultId, collateralCurrency, nominatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            const filteredNominations = yield this.getFilteredNominations(vaultId, collateralCurrency, nominatorId);
            if (!filteredNominations.length) {
                // Cannot return `zero` because the type of nominated collateral is unknown
                return Promise.reject("No nomination available");
            }
            const zero = utils_1.newMonetaryAmount(0, collateralCurrency);
            const ccyFilteredNominations = [];
            for (const nomination of filteredNominations) {
                const wrappedVaultCurrency = yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, nomination.vaultId.currencies.wrapped);
                const collateralVaultCurrency = yield currency_1.currencyIdToMonetaryCurrency(this.assetRegistryAPI, this.loansAPI, nomination.vaultId.currencies.collateral);
                if (wrappedVaultCurrency.ticker === this.wrappedCurrency.ticker &&
                    collateralCurrency &&
                    collateralVaultCurrency.ticker === collateralCurrency.ticker) {
                    ccyFilteredNominations.push(nomination);
                }
            }
            return ccyFilteredNominations
                .map((nomination) => nomination.amount)
                .reduce((previousValue, currentValue) => previousValue.add(currentValue), zero);
        });
    }
    listVaults() {
        return __awaiter(this, void 0, void 0, function* () {
            const nominatorMap = yield this.api.query.nomination.vaults.entries();
            return nominatorMap.filter((v) => v[1]).map((v) => utils_1.storageKeyToNthInner(v[0]));
        });
    }
    isVaultOptedIn(accountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const optedInVaults = yield this.listVaults();
            const vaultId = utils_1.newVaultId(this.api, accountId.toString(), collateralCurrency, this.wrappedCurrency);
            return optedInVaults.includes(vaultId);
        });
    }
}
exports.DefaultNominationAPI = DefaultNominationAPI;
//# sourceMappingURL=nomination.js.map