"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.DefaultOracleAPI = exports.DEFAULT_INCLUSION_TIME = exports.DEFAULT_FEED_NAME = void 0;
const monetary_js_1 = require("@interlay/monetary-js");
const utils_1 = require("../utils");
exports.DEFAULT_FEED_NAME = "DOT/BTC";
exports.DEFAULT_INCLUSION_TIME = "Fast";
class DefaultOracleAPI {
    constructor(api, wrappedCurrency, transactionAPI) {
        this.api = api;
        this.wrappedCurrency = wrappedCurrency;
        this.transactionAPI = transactionAPI;
    }
    getExchangeRate(collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const oracleKey = (0, utils_1.createExchangeRateOracleKey)(this.api, collateralCurrency);
            const encodedRawRate = (0, utils_1.unwrapRawExchangeRate)(yield this.api.query.oracle.aggregate(oracleKey));
            if (encodedRawRate === undefined) {
                return Promise.reject(new Error(`No exchange rate for given currency: ${collateralCurrency.ticker}`));
            }
            const decodedRawRate = (0, utils_1.decodeFixedPointType)(encodedRawRate);
            return new monetary_js_1.ExchangeRate(this.wrappedCurrency, collateralCurrency, decodedRawRate, this.wrappedCurrency.rawBase, collateralCurrency.rawBase);
        });
    }
    convertWrappedToCurrency(amount, currency) {
        return __awaiter(this, void 0, void 0, function* () {
            const rate = yield this.getExchangeRate(currency);
            return rate.toCounter(amount);
        });
    }
    convertCollateralToWrapped(amount) {
        return __awaiter(this, void 0, void 0, function* () {
            const rate = yield this.getExchangeRate(amount.currency);
            return rate.toBase(amount);
        });
    }
    getOnlineTimeout() {
        return __awaiter(this, void 0, void 0, function* () {
            const moment = yield this.api.query.oracle.maxDelay();
            return moment.toNumber();
        });
    }
    setExchangeRate(exchangeRate) {
        return __awaiter(this, void 0, void 0, function* () {
            const encodedExchangeRate = (0, utils_1.encodeUnsignedFixedPoint)(this.api, exchangeRate.toBig({
                baseUnit: exchangeRate.base.rawBase,
                counterUnit: exchangeRate.counter.rawBase,
            }));
            const oracleKey = (0, utils_1.createExchangeRateOracleKey)(this.api, exchangeRate.counter);
            const tx = this.api.tx.oracle.feedValues([[oracleKey, encodedExchangeRate]]);
            yield this.transactionAPI.sendLogged(tx, this.api.events.oracle.FeedValues, true);
        });
    }
    getBitcoinFees() {
        return __awaiter(this, void 0, void 0, function* () {
            const fast = (0, utils_1.createInclusionOracleKey)(this.api, exports.DEFAULT_INCLUSION_TIME);
            const fees = yield this.api.query.oracle.aggregate(fast);
            const parseFees = (fee) => {
                const inner = (0, utils_1.unwrapRawExchangeRate)(fee);
                if (inner !== undefined) {
                    return (0, utils_1.decodeFixedPointType)(inner);
                }
                throw new Error("Bitcoin fee estimate not set");
            };
            return parseFees(fees);
        });
    }
    setBitcoinFees(fees) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!fees.round().eq(fees)) {
                throw new Error("tx fees must be an integer amount of satoshi");
            }
            else if (fees.lt(0)) {
                throw new Error("tx fees must be a positive amount of satoshi");
            }
            const oracleKey = (0, utils_1.createInclusionOracleKey)(this.api, exports.DEFAULT_INCLUSION_TIME);
            const encodedFee = (0, utils_1.encodeUnsignedFixedPoint)(this.api, fees);
            const tx = this.api.tx.oracle.feedValues([[oracleKey, encodedFee]]);
            yield this.transactionAPI.sendLogged(tx, this.api.events.oracle.FeedValues, true);
        });
    }
    getSourcesById() {
        return __awaiter(this, void 0, void 0, function* () {
            const oracles = yield this.api.query.oracle.authorizedOracles.entries();
            const nameMap = new Map();
            oracles.forEach((oracle) => nameMap.set((0, utils_1.storageKeyToNthInner)(oracle[0]).toString(), oracle[1].toUtf8()));
            return nameMap;
        });
    }
    getValidUntil(counterCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            const oracleKey = (0, utils_1.createExchangeRateOracleKey)(this.api, counterCurrency);
            const validUntil = yield this.api.query.oracle.validUntil(oracleKey);
            return validUntil.isSome ? (0, utils_1.convertMoment)(validUntil.value) : Promise.reject("No such oracle key");
        });
    }
    isOnline() {
        return __awaiter(this, void 0, void 0, function* () {
            const errors = yield this.api.query.security.errors();
            return !this.hasOracleError(errors.toArray());
        });
    }
    getRawValuesUpdated(key) {
        return __awaiter(this, void 0, void 0, function* () {
            const isSet = yield this.api.query.oracle.rawValuesUpdated(key);
            return isSet.unwrap().isTrue;
        });
    }
    waitForFeeEstimateUpdate(type = exports.DEFAULT_INCLUSION_TIME) {
        return __awaiter(this, void 0, void 0, function* () {
            const key = (0, utils_1.createInclusionOracleKey)(this.api, type);
            while (yield this.getRawValuesUpdated(key)) {
                (0, utils_1.sleep)(utils_1.SLEEP_TIME_MS);
            }
        });
    }
    waitForExchangeRateUpdate(exchangeRate) {
        return __awaiter(this, void 0, void 0, function* () {
            const key = (0, utils_1.createExchangeRateOracleKey)(this.api, exchangeRate.counter);
            while (yield this.getRawValuesUpdated(key)) {
                (0, utils_1.sleep)(utils_1.SLEEP_TIME_MS);
            }
        });
    }
    hasOracleError(errors) {
        for (const error of errors.values()) {
            if (error.isOracleOffline) {
                return true;
            }
        }
        return false;
    }
}
exports.DefaultOracleAPI = DefaultOracleAPI;
//# sourceMappingURL=oracle.js.map