"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");
var NominationAmountType;
(function (NominationAmountType) {
    NominationAmountType["Raw"] = "raw";
    NominationAmountType["Parsed"] = "parsed";
})(NominationAmountType = exports.NominationAmountType || (exports.NominationAmountType = {}));
class DefaultNominationAPI {
    constructor(api, wrappedCurrency, vaultsAPI, rewardsAPI, transactionAPI) {
        this.api = api;
        this.wrappedCurrency = wrappedCurrency;
        this.vaultsAPI = vaultsAPI;
        this.rewardsAPI = rewardsAPI;
        this.transactionAPI = transactionAPI;
    }
    depositCollateral(vaultAccountId, amount) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultId = (0, utils_1.newVaultId)(this.api, vaultAccountId.toString(), amount.currency, this.wrappedCurrency);
            const amountAsPlanck = this.api.createType("Balance", amount.toString());
            const tx = this.api.tx.nomination.depositCollateral(vaultId, amountAsPlanck);
            yield this.transactionAPI.sendLogged(tx, this.api.events.nomination.DepositCollateral, true);
        });
    }
    withdrawCollateral(vaultAccountId, amount, nonce) {
        return __awaiter(this, void 0, void 0, function* () {
            const currencyIdLiteral = (0, types_1.tickerToCurrencyIdLiteral)(amount.currency.ticker);
            const vaultId = (0, utils_1.newVaultId)(this.api, vaultAccountId.toString(), amount.currency, this.wrappedCurrency);
            const definedNonce = nonce
                ? nonce
                : yield this.rewardsAPI.getStakingPoolNonce(currencyIdLiteral, vaultAccountId);
            const amountAsPlanck = this.api.createType("Balance", amount.toString());
            const parsedNonce = this.api.createType("Index", definedNonce);
            const tx = this.api.tx.nomination.withdrawCollateral(vaultId, amountAsPlanck, parsedNonce);
            yield this.transactionAPI.sendLogged(tx, this.api.events.nomination.WithdrawCollateral, true);
        });
    }
    optIn(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const vaultCurrencyPair = (0, 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 = (0, 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((0, types_1.currencyIdToLiteral)(vaultId.currencies.collateral), vaultId.accountId);
                types_1.currencyIdToLiteral;
                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();
            return stakesMap
                .map((v) => {
                const nonce = (0, utils_1.storageKeyToNthInner)(v[0], 0);
                const [vaultId, nominatorId] = (0, utils_1.storageKeyToNthInner)(v[0], 1);
                const nomination = (0, utils_1.decodeFixedPointType)(v[1]);
                const collateralCurrency = (0, types_1.currencyIdToMonetaryCurrency)(vaultId.currencies.collateral);
                const monetaryNomination = (0, utils_1.newMonetaryAmount)(nomination, collateralCurrency, true);
                return {
                    nonce: nonce.toNumber(),
                    vaultId,
                    nominatorId,
                    amount: monetaryNomination,
                    type: NominationAmountType.Raw,
                };
            })
                .filter((rawNomination) => {
                // Cannot just do `nonces.get(rawNomination.vaultId)` because vaultId objects differ
                // ever so slightly even if they have identical properties
                const vaultIdHighestNonce = (0, utils_1.queryNominationsMap)(nonces, rawNomination.vaultId);
                // Only consider active nominations, i.e. with the latest nonce
                return rawNomination.nonce === vaultIdHighestNonce && rawNomination.amount.toBig().gt(0);
            });
        });
    }
    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, (0, types_1.currencyIdToLiteral)(rawNomination.vaultId.currencies.collateral), (0, types_1.currencyIdToLiteral)(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, collateralCurrencyId, rewardCurrencyId, nominatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.vaultsAPI.computeReward(vaultId, nominatorId, collateralCurrencyId, rewardCurrencyId);
        });
    }
    list() {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.getFilteredNominations();
        });
    }
    getFilteredNominations(vaultId, collateralCurrencyId, 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 = rawList.filter((rawNomination) => {
                const nominationCurrencyId = (0, types_1.currencyIdToLiteral)(rawNomination.vaultId.currencies.collateral);
                const nominationWrappedCurrencyId = (0, types_1.currencyIdToLiteral)(rawNomination.vaultId.currencies.wrapped);
                const wrappedCurrencyId = (0, types_1.tickerToCurrencyIdLiteral)(this.wrappedCurrency.ticker);
                return ((!nominatorId || nominatorId === nominatorId) &&
                    (!vaultId || vaultId === vaultId) &&
                    (!collateralCurrencyId || collateralCurrencyId === nominationCurrencyId) &&
                    nominationWrappedCurrencyId === wrappedCurrencyId);
            });
            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, collateralCurrencyId, 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, collateralCurrencyId, 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 collateralCurrencyId = collateralCurrency
                ? (0, types_1.tickerToCurrencyIdLiteral)(collateralCurrency.ticker)
                : undefined;
            const filteredNominations = yield this.getFilteredNominations(vaultId, collateralCurrencyId, nominatorId);
            if (!filteredNominations.length) {
                // Cannot return `zero` because the type of nominated collateral is unknown
                return Promise.reject("No nomination available");
            }
            const zero = (0, utils_1.newMonetaryAmount)(0, collateralCurrency);
            return filteredNominations
                .filter((nomination) => {
                const wrappedVaultCurrency = (0, types_1.currencyIdToMonetaryCurrency)(nomination.vaultId.currencies.wrapped);
                const collateralVaultCurrency = (0, types_1.currencyIdToMonetaryCurrency)(nomination.vaultId.currencies.collateral);
                wrappedVaultCurrency.ticker === this.wrappedCurrency.ticker &&
                    collateralCurrency &&
                    collateralVaultCurrency.ticker === collateralCurrency.ticker;
            })
                .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) => (0, utils_1.storageKeyToNthInner)(v[0]));
        });
    }
    isVaultOptedIn(accountId, collateralCurrencyIdLiteral) {
        return __awaiter(this, void 0, void 0, function* () {
            const optedInVaults = yield this.listVaults();
            const collateralCurrency = (0, types_1.currencyIdToMonetaryCurrency)((0, utils_1.newCurrencyId)(this.api, collateralCurrencyIdLiteral));
            const vaultId = (0, utils_1.newVaultId)(this.api, accountId.toString(), collateralCurrency, this.wrappedCurrency);
            return optedInVaults.includes(vaultId);
        });
    }
}
exports.DefaultNominationAPI = DefaultNominationAPI;
//# sourceMappingURL=nomination.js.map