"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 monetary_js_1 = require("@interlay/monetary-js");
const chai_1 = require("chai");
const big_js_1 = __importDefault(require("big.js"));
const src_1 = require("../../../../../src");
const setup_1 = require("../../../../../src/utils/setup");
const config_1 = require("../../../../config");
const helpers_1 = require("../../../../utils/helpers");
const asset_registry_1 = require("../../../../../src/parachain/asset-registry");
const bn_js_1 = require("bn.js");
describe("Initialize parachain state", () => {
    // copied from interbtc testnet runtimes
    const vaultAnnuityGenericAddress = "5EYCAe5jvsMTc6NLhunLTPVjJg5cZNweWKjNXKqz9RUqQJDY";
    let api;
    let userInterBtcAPI;
    let sudoInterBtcAPI;
    let bitcoinCoreClient;
    let keyring;
    let sudoAccount;
    let userAccount;
    let vault_1;
    let vault_2;
    let vault_3;
    let vault_to_ban;
    let vault_to_liquidate;
    let collateralCurrency;
    let aUSD;
    function accountIdFromKeyring(keyPair) {
        return src_1.newAccountId(api, keyPair.address);
    }
    function waitForRegister(api, accountId, collateralCurrency) {
        return __awaiter(this, void 0, void 0, function* () {
            for (;;) {
                try {
                    yield api.get(accountId, collateralCurrency);
                    return;
                }
                catch (e) {
                    console.log(e);
                }
                yield helpers_1.sleep(helpers_1.SLEEP_TIME_MS);
            }
        });
    }
    before(function () {
        return __awaiter(this, void 0, void 0, function* () {
            api = yield src_1.createSubstrateAPI(config_1.PARACHAIN_ENDPOINT);
            const ss58Prefix = src_1.getSS58Prefix(api);
            keyring = new api_1.Keyring({ type: "sr25519", ss58Format: ss58Prefix });
            sudoAccount = keyring.addFromUri(config_1.SUDO_URI);
            userAccount = keyring.addFromUri(config_1.USER_1_URI);
            vault_1 = keyring.addFromUri(config_1.VAULT_1_URI);
            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);
            bitcoinCoreClient = new src_1.BitcoinCoreClient(config_1.BITCOIN_CORE_NETWORK, config_1.BITCOIN_CORE_HOST, config_1.BITCOIN_CORE_USERNAME, config_1.BITCOIN_CORE_PASSWORD, config_1.BITCOIN_CORE_PORT, config_1.BITCOIN_CORE_WALLET);
            userInterBtcAPI = new src_1.DefaultInterBtcApi(api, "regtest", userAccount, config_1.ESPLORA_BASE_PATH);
            sudoInterBtcAPI = new src_1.DefaultInterBtcApi(api, "regtest", sudoAccount, config_1.ESPLORA_BASE_PATH);
            collateralCurrency = helpers_1.getCorrespondingCollateralCurrenciesForTests(userInterBtcAPI.getGovernanceCurrency())[0];
            const vaultCollateralPairs = [
                [vault_1, collateralCurrency],
                [vault_2, collateralCurrency],
                [vault_3, collateralCurrency],
                [vault_to_ban, collateralCurrency],
                [vault_to_liquidate, collateralCurrency],
            ];
            // wait for all vaults to register
            yield Promise.all(vaultCollateralPairs
                .map(([keyring, collateralCurrency]) => [
                accountIdFromKeyring(keyring),
                collateralCurrency,
            ])
                .map(([accountId, collateral]) => waitForRegister(userInterBtcAPI.vaults, accountId, collateral)));
        });
    });
    after(() => __awaiter(void 0, void 0, void 0, function* () {
        api.disconnect();
    }));
    it("should set the stable confirmations and ready the BTC-Relay", () => __awaiter(void 0, void 0, void 0, function* () {
        // Speed up the process by only requiring 0 parachain and 0 bitcoin confirmations
        const stableBitcoinConfirmationsToSet = 0;
        const stableParachainConfirmationsToSet = 0;
        let [stableBitcoinConfirmations, stableParachainConfirmations] = yield Promise.all([
            userInterBtcAPI.btcRelay.getStableBitcoinConfirmations(),
            userInterBtcAPI.btcRelay.getStableParachainConfirmations(),
        ]);
        if (stableBitcoinConfirmations != 0 || stableParachainConfirmations != 0) {
            yield setup_1.initializeStableConfirmations(api, {
                bitcoinConfirmations: stableBitcoinConfirmationsToSet,
                parachainConfirmations: stableParachainConfirmationsToSet,
            }, sudoAccount, bitcoinCoreClient);
            [stableBitcoinConfirmations, stableParachainConfirmations] = yield Promise.all([
                userInterBtcAPI.btcRelay.getStableBitcoinConfirmations(),
                userInterBtcAPI.btcRelay.getStableParachainConfirmations(),
            ]);
        }
        chai_1.assert.equal(stableBitcoinConfirmationsToSet, stableBitcoinConfirmations, "Setting the Bitcoin confirmations failed");
        chai_1.assert.equal(stableParachainConfirmationsToSet, stableParachainConfirmations, "Setting the Parachain confirmations failed");
    }));
    it("should fund vault annuity account", () => __awaiter(void 0, void 0, void 0, function* () {
        // get address in with local prefix
        const vaultAnnuityAddress = keyring.addFromAddress(vaultAnnuityGenericAddress).address;
        // fund with 100 KINT/INTR
        const fundAmount = new monetary_js_1.MonetaryAmount(sudoInterBtcAPI.getGovernanceCurrency(), 100);
        const [foundEvent] = yield Promise.all([
            helpers_1.waitForEvent(sudoInterBtcAPI, api.events.tokens.Transfer, false, 6 * 12 * 1000),
            userInterBtcAPI.tokens.transfer(vaultAnnuityAddress, fundAmount),
        ]);
        chai_1.assert.isTrue(foundEvent, "Expected to find transfer event funding vault annuity account, but did not");
    }));
    it("should have or register aUSD as foreign asset", () => __awaiter(void 0, void 0, void 0, function* () {
        aUSD = yield helpers_1.getAUSDForeignAsset(sudoInterBtcAPI.assetRegistry);
        if (aUSD === undefined) {
            // register aUSD as foreign asset for tests
            const nextAssetId = (yield sudoInterBtcAPI.api.query.assetRegistry.lastAssetId()).toNumber() + 1;
            const callToRegister = sudoInterBtcAPI.api.tx.assetRegistry.registerAsset({
                name: api.createType("Bytes", "Acala USD"),
                symbol: api.createType("Bytes", helpers_1.AUSD_TICKER),
                decimals: api.createType("u32", 6),
                existentialDeposit: api.createType("u128", 1000),
                additional: api.createType("InterbtcPrimitivesCustomMetadata", {
                    feePerSecond: api.createType("u128", 10),
                    coingeckoId: api.createType("Bytes", "acala-dollar"),
                }),
            }, api.createType("u32", nextAssetId));
            const timeoutMs = 10 * helpers_1.APPROX_BLOCK_TIME_MS; // aproximately 10 blocks
            const [eventFound] = yield Promise.all([
                helpers_1.waitForEvent(sudoInterBtcAPI, api.events.assetRegistry.RegisteredAsset, false, timeoutMs),
                sudoInterBtcAPI.api.tx.sudo.sudo(callToRegister).signAndSend(sudoAccount),
            ]);
            chai_1.assert.isTrue(eventFound, `Sudo event to register new foreign asset not found - timeout of ${timeoutMs} ms exceeded`);
        }
        const currencies = yield sudoInterBtcAPI.assetRegistry.getForeignAssets();
        chai_1.assert.isAtLeast(currencies.length, 1, `Expected at least one foreign asset registered, but found ${currencies.length}`);
        aUSD = yield helpers_1.getAUSDForeignAsset(sudoInterBtcAPI.assetRegistry);
        chai_1.assert.isDefined(aUSD, "aUSD not found after registration");
    }));
    it("should set oracle value expiry to a longer period", () => __awaiter(void 0, void 0, void 0, function* () {
        // previous: values provided by Sander
        // const keyValue: [string, string][]
        //   = [["0x6ecdde33e5d791e9c15595f33fdaafd6274fa0ce974831e362c6e5afca42e154", "0x80eeff0000000000"]];
        // set the oracle value expiry to be approximately 4.5 hours
        const timeoutMs = 5 * helpers_1.APPROX_BLOCK_TIME_MS; // aproximately 5 blocks
        const [sudidEventFound] = yield Promise.all([
            helpers_1.waitForEvent(sudoInterBtcAPI, api.events.sudo.Sudid, false, timeoutMs),
            src_1.setNumericStorage(sudoInterBtcAPI.api, "Oracle", "MaxDelay", new bn_js_1.BN(helpers_1.ORACLE_MAX_DELAY), sudoAccount, 64),
        ]);
        chai_1.assert.isTrue(sudidEventFound, `Sudo event to set oracle values expiry not found - timeout of ${timeoutMs} ms exceeded`);
    }));
    it("should set the exchange rate for foreign assets", () => __awaiter(void 0, void 0, void 0, function* () {
        const assetRegistryApi = new asset_registry_1.DefaultAssetRegistryAPI(sudoInterBtcAPI.api);
        const foreignAssets = yield assetRegistryApi.getForeignAssets();
        for (const foreignAsset of foreignAssets) {
            const exchangeRate = new monetary_js_1.ExchangeRate(monetary_js_1.Bitcoin, foreignAsset, helpers_1.getExchangeRateValueToSetForTesting(foreignAsset));
            yield setup_1.initializeExchangeRate(exchangeRate, sudoInterBtcAPI.oracle);
        }
    }));
    it("should set the exchange rate for collateral tokens", () => __awaiter(void 0, void 0, void 0, function* () {
        function setCollateralExchangeRate(value, currency) {
            return __awaiter(this, void 0, void 0, function* () {
                const exchangeRate = new monetary_js_1.ExchangeRate(monetary_js_1.Bitcoin, currency, value);
                // result will be medianized
                return setup_1.initializeExchangeRate(exchangeRate, sudoInterBtcAPI.oracle);
            });
        }
        yield setCollateralExchangeRate(helpers_1.getExchangeRateValueToSetForTesting(monetary_js_1.Polkadot), monetary_js_1.Polkadot);
        yield setCollateralExchangeRate(helpers_1.getExchangeRateValueToSetForTesting(monetary_js_1.Kusama), monetary_js_1.Kusama);
        yield setCollateralExchangeRate(helpers_1.getExchangeRateValueToSetForTesting(monetary_js_1.Kintsugi), monetary_js_1.Kintsugi);
    }));
    it("should set BTC tx fees", () => __awaiter(void 0, void 0, void 0, function* () {
        const setFeeEstimate = new big_js_1.default(1);
        let getFeeEstimate = yield sudoInterBtcAPI.oracle.getBitcoinFees();
        if (!getFeeEstimate) {
            yield setup_1.initializeBtcTxFees(setFeeEstimate, sudoInterBtcAPI.oracle);
            // just check that this is set since we medianize results
            getFeeEstimate = yield sudoInterBtcAPI.oracle.getBitcoinFees();
        }
        chai_1.assert.isDefined(getFeeEstimate);
    }));
    it("should update vault annuity rewards", () => __awaiter(void 0, void 0, void 0, function* () {
        // update rewards (needs sudo), doing this a fair bit later than transfer
        // so the test doesn't need to wait for transfer to settle
        const updateRewardsExtrinsic = api.tx.vaultAnnuity.updateRewards();
        const hash = yield api.tx.sudo.sudo(updateRewardsExtrinsic).signAndSend(sudoAccount);
        chai_1.assert.isNotEmpty(hash);
    }));
    it("should enable vault nomination", () => __awaiter(void 0, void 0, void 0, function* () {
        let isNominationEnabled = yield sudoInterBtcAPI.nomination.isNominationEnabled();
        if (!isNominationEnabled) {
            yield setup_1.initializeVaultNomination(true, sudoInterBtcAPI.nomination);
            isNominationEnabled = yield sudoInterBtcAPI.nomination.isNominationEnabled();
        }
        chai_1.assert.isTrue(isNominationEnabled);
    }));
    it("should set collateral ceiling and thresholds for aUSD", function () {
        return __awaiter(this, void 0, void 0, function* () {
            // only get aUSD if it hasn't been set yet (e.g. when running this test in isolation)
            !aUSD ? (aUSD = yield helpers_1.getAUSDForeignAsset(sudoInterBtcAPI.assetRegistry)) : undefined;
            if (aUSD === undefined) {
                // no point in completing this if aUSD is not registered
                this.skip();
            }
            const sudoThis = (extrinsic) => __awaiter(this, void 0, void 0, function* () { return sudoInterBtcAPI.api.tx.sudo.sudo(extrinsic).signAndSend(sudoAccount); });
            // (unsafely) get first collateral currency's ceiling and thresholds
            const existingCollCcy = helpers_1.getCorrespondingCollateralCurrenciesForTests(userInterBtcAPI.getGovernanceCurrency())[0];
            const existingCcyPair = src_1.newVaultCurrencyPair(api, existingCollCcy, sudoInterBtcAPI.getWrappedCurrency());
            // borrow values from existing currency pair
            const [optionCeilValue, existingSecureThreshold, existingPremiumThreshold, existingLiquidationThreshold] = yield Promise.all([
                sudoInterBtcAPI.api.query.vaultRegistry.systemCollateralCeiling(existingCcyPair),
                sudoInterBtcAPI.vaults.getSecureCollateralThreshold(existingCollCcy),
                sudoInterBtcAPI.vaults.getPremiumRedeemThreshold(existingCollCcy),
                sudoInterBtcAPI.vaults.getLiquidationCollateralThreshold(existingCollCcy),
            ]);
            // boldly assume the value is set
            const existingCeiling = optionCeilValue.unwrap();
            // encode thresholds
            const encodedSecThresh = src_1.encodeUnsignedFixedPoint(sudoInterBtcAPI.api, new big_js_1.default(existingSecureThreshold));
            const encodedPremThresh = src_1.encodeUnsignedFixedPoint(sudoInterBtcAPI.api, new big_js_1.default(existingPremiumThreshold));
            const encodedLiqThresh = src_1.encodeUnsignedFixedPoint(sudoInterBtcAPI.api, new big_js_1.default(existingLiquidationThreshold));
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            const aUsdCcyPair = src_1.newVaultCurrencyPair(api, aUSD, sudoInterBtcAPI.getWrappedCurrency());
            // set the collateral ceiling
            const setCeilingExtrinsic = sudoInterBtcAPI.api.tx.vaultRegistry.setSystemCollateralCeiling(aUsdCcyPair, existingCeiling);
            // set normal threshold
            const setThresholdExtrinsic = sudoInterBtcAPI.api.tx.vaultRegistry.setSecureCollateralThreshold(aUsdCcyPair, encodedSecThresh);
            // set premium threshold
            const setPremiumThresholdExtrinsic = sudoInterBtcAPI.api.tx.vaultRegistry.setPremiumRedeemThreshold(aUsdCcyPair, encodedPremThresh);
            // set liquidation threshold
            const setLiquidationThresholdExtrinsic = sudoInterBtcAPI.api.tx.vaultRegistry.setLiquidationCollateralThreshold(aUsdCcyPair, encodedLiqThresh);
            // set minimum collateral required
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            const minimumCollateral = new monetary_js_1.MonetaryAmount(aUSD, 100);
            const minimumCollateralToSet = api.createType("u128", minimumCollateral.toString(true));
            const setMinimumCollateralExtrinsic = sudoInterBtcAPI.api.tx.vaultRegistry.setMinimumCollateral(aUsdCcyPair.collateral, minimumCollateralToSet);
            // batch all
            const batch = sudoInterBtcAPI.api.tx.utility.batchAll([
                setCeilingExtrinsic,
                setThresholdExtrinsic,
                setPremiumThresholdExtrinsic,
                setLiquidationThresholdExtrinsic,
                setMinimumCollateralExtrinsic,
            ]);
            const timeoutMs = 5 * helpers_1.APPROX_BLOCK_TIME_MS;
            const [foundEvent] = yield Promise.all([
                helpers_1.waitForEvent(sudoInterBtcAPI, api.events.sudo.Sudid, false, timeoutMs),
                sudoThis(batch),
            ]);
            chai_1.assert.isTrue(foundEvent, `Cannot find Sudid event for setting thresholds in batch - timeout after ${timeoutMs} ms`);
        });
    });
    it("should fund and register aUSD foreign asset vaults", function () {
        return __awaiter(this, void 0, void 0, function* () {
            // only get aUSD if it hasn't been set yet (e.g. when running this test in isolation)
            !aUSD ? (aUSD = yield helpers_1.getAUSDForeignAsset(sudoInterBtcAPI.assetRegistry)) : undefined;
            if (aUSD === undefined) {
                // no point in completing this if aUSD is not registered
                this.skip();
            }
            // assign locally to make TS understand it isn't undefined
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            const aUsd = aUSD;
            // free money to set with the help of sudo
            const freeBalanceToSet = new monetary_js_1.MonetaryAmount(aUsd, 1000000000);
            const collateralAmount = new monetary_js_1.MonetaryAmount(aUsd, 10000);
            const vaultAccountIdAndKeyrings = [vault_1, vault_2, vault_3].map((keyringPair) => {
                return [keyringPair, accountIdFromKeyring(keyringPair)];
            });
            // register the vaults with aUSD collateral
            for (const [vaultKeyringPair, vaultAccountId] of vaultAccountIdAndKeyrings) {
                const balanceTimeout = 5 * helpers_1.APPROX_BLOCK_TIME_MS;
                // set balance and wait for event
                const [balanceEventFound] = yield Promise.all([
                    helpers_1.waitForEvent(sudoInterBtcAPI, api.events.tokens.BalanceSet, false, balanceTimeout),
                    helpers_1.sudo(sudoInterBtcAPI, () => __awaiter(this, void 0, void 0, function* () { return sudoInterBtcAPI.tokens.setBalance(vaultAccountId, freeBalanceToSet); })),
                ]);
                chai_1.assert.isTrue(balanceEventFound, `Cannot find BalanceSet event for ${vaultAccountId} - timeout after ${balanceTimeout} ms`);
                // register the aUSD vault
                const vaultInterBtcApi = new src_1.DefaultInterBtcApi(api, "regtest", vaultKeyringPair, config_1.ESPLORA_BASE_PATH);
                const waitForEventTimeoutMs = 5 * helpers_1.APPROX_BLOCK_TIME_MS; // aproximately 5 blocks
                const [foundRegisterEvent] = yield Promise.all([
                    helpers_1.waitForEvent(vaultInterBtcApi, api.events.vaultRegistry.RegisterVault, false, waitForEventTimeoutMs),
                    vaultInterBtcApi.vaults.registerNewCollateralVault(collateralAmount),
                ]);
                chai_1.assert.isTrue(foundRegisterEvent, `Vault registration event not found for account id ${vaultAccountId} - timeout of ${waitForEventTimeoutMs} ms exceeded`);
            }
        });
    });
    it("should return aUSD among the collateral currencies", function () {
        return __awaiter(this, void 0, void 0, function* () {
            // run this check a few tests after it has been registered to avoid needing to wait for
            // block finalizations
            const collateralCurrencies = yield src_1.getCollateralCurrencies(userInterBtcAPI.api, userInterBtcAPI.assetRegistry);
            chai_1.assert.isDefined(
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            collateralCurrencies.find((currency) => currency.ticker === aUSD.ticker), "Expected to find aUSD within the array of collateral currencies, but did not");
        });
    });
});
//# sourceMappingURL=initialize.test.js.map