From afe2066e5fa006bfb9e3dde624fed9b712c53f66 Mon Sep 17 00:00:00 2001 From: Brendon Votteler Date: Tue, 23 May 2023 11:10:54 +0200 Subject: [PATCH 1/4] fix: better handling of liquidation vault not found rejection inside getMaxBurnableTokens --- src/parachain/redeem.ts | 14 +++++++++++--- src/parachain/vaults.ts | 5 ++++- test/unit/parachain/redeem.test.ts | 25 ++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/parachain/redeem.ts b/src/parachain/redeem.ts index 7fff4a37e..fb624c796 100644 --- a/src/parachain/redeem.ts +++ b/src/parachain/redeem.ts @@ -12,7 +12,7 @@ import { InterbtcPrimitivesRedeemRedeemRequest, } from "@polkadot/types/lookup"; -import { VaultsAPI } from "./vaults"; +import { NO_LIQUIDATION_VAULT_FOUND_REJECTION, VaultsAPI } from "./vaults"; import { decodeBtcAddress, decodeFixedPointType, @@ -27,7 +27,7 @@ import { allocateAmountsToVaults } from "../utils/issueRedeem"; import { ElectrsAPI } from "../external"; import { TransactionAPI } from "./transaction"; import { OracleAPI } from "./oracle"; -import { CollateralCurrencyExt, ExtrinsicData, Redeem, WrappedCurrency } from "../types"; +import { CollateralCurrencyExt, ExtrinsicData, Redeem, SystemVaultExt, WrappedCurrency } from "../types"; import { SystemAPI } from "./system"; /** @@ -332,7 +332,15 @@ export class DefaultRedeemAPI implements RedeemAPI { } async getMaxBurnableTokens(collateralCurrency: CollateralCurrencyExt): Promise> { - const liquidationVault = await this.vaultsAPI.getLiquidationVault(collateralCurrency); + const liquidationVault: SystemVaultExt | null = await this.vaultsAPI.getLiquidationVault(collateralCurrency) + // method rejects if no vault was found, wrap as null + .catch((reason) => (reason === NO_LIQUIDATION_VAULT_FOUND_REJECTION) ? null : Promise.reject(reason)); + + if (liquidationVault === null) { + // no liquidation vault exists, therefore, no burnable tokens) + return BitcoinAmount.zero(); + } + // This should never be below 0, but still... const burnableTokens = liquidationVault.issuedTokens.sub(liquidationVault.toBeRedeemedTokens); if (burnableTokens.lte(BitcoinAmount.zero())) { diff --git a/src/parachain/vaults.ts b/src/parachain/vaults.ts index ed91e1ded..6505272df 100644 --- a/src/parachain/vaults.ts +++ b/src/parachain/vaults.ts @@ -410,6 +410,8 @@ export interface VaultsAPI { ): Promise; } +export const NO_LIQUIDATION_VAULT_FOUND_REJECTION = "No liquidation vault found"; + export class DefaultVaultsAPI implements VaultsAPI { constructor( private api: ApiPromise, @@ -657,8 +659,9 @@ export class DefaultVaultsAPI implements VaultsAPI { const vaultCurrencyPair = newVaultCurrencyPair(this.api, collateralCurrency, this.wrappedCurrency); const liquidationVault = await this.api.query.vaultRegistry.liquidationVault(vaultCurrencyPair); if (!liquidationVault.isSome) { - return Promise.reject("System vault could not be fetched"); + return Promise.reject(NO_LIQUIDATION_VAULT_FOUND_REJECTION); } + return await parseSystemVault( this.api, liquidationVault.value as VaultRegistrySystemVault, diff --git a/test/unit/parachain/redeem.test.ts b/test/unit/parachain/redeem.test.ts index b6d2927b7..b3ab981a7 100644 --- a/test/unit/parachain/redeem.test.ts +++ b/test/unit/parachain/redeem.test.ts @@ -2,8 +2,9 @@ import { expect } from "../../chai"; import sinon from "sinon"; import { DefaultRedeemAPI, DefaultVaultsAPI, VaultsAPI } from "../../../src"; import { newMonetaryAmount } from "../../../src/utils"; -import { ExchangeRate, KBtc, Kintsugi } from "@interlay/monetary-js"; +import { ExchangeRate, KBtc, Kintsugi} from "@interlay/monetary-js"; import Big from "big.js"; +import { NO_LIQUIDATION_VAULT_FOUND_REJECTION } from "../../../src/parachain/vaults"; describe("DefaultRedeemAPI", () => { let redeemApi: DefaultRedeemAPI; @@ -95,4 +96,26 @@ describe("DefaultRedeemAPI", () => { ); }); }); + + describe("getMaxBurnableTokens", () => { + afterEach(() => { + sinon.restore(); + sinon.reset(); + }); + + it("should return zero when no getLiquidationVault rejects with no liquidation vault message", async () => { + // stub internal call to return no liquidation vault + stubbedVaultsApi.getLiquidationVault.withArgs(sinon.match.any).returns(Promise.reject(NO_LIQUIDATION_VAULT_FOUND_REJECTION)); + + const actualValue = await redeemApi.getMaxBurnableTokens(Kintsugi); + expect(actualValue.toBig().toNumber()).to.be.eq(0); + }); + + it("should propagate rejection if getLiquidationVault rejects with other message", async () => { + // stub internal call to return no liquidation vault + stubbedVaultsApi.getLiquidationVault.withArgs(sinon.match.any).returns(Promise.reject("foobar happened here")); + + await expect(redeemApi.getMaxBurnableTokens(Kintsugi)).to.be.rejectedWith("foobar happened here"); + }); + }); }); From 034bd7a20fbdda46856dea4dded8ff1b50d07dad Mon Sep 17 00:00:00 2001 From: Brendon Votteler Date: Tue, 23 May 2023 11:11:27 +0200 Subject: [PATCH 2/4] chore: bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79f5a6753..91f1c9ea5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@interlay/interbtc-api", - "version": "2.2.3", + "version": "2.2.4", "description": "JavaScript library to interact with interBTC", "main": "build/src/index.js", "typings": "build/src/index.d.ts", From 5a1a7c997c77867396c9af9909c50fbf29b65021 Mon Sep 17 00:00:00 2001 From: Brendon Votteler Date: Tue, 23 May 2023 11:15:54 +0200 Subject: [PATCH 3/4] chore: formatting/linting --- test/unit/parachain/redeem.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/parachain/redeem.test.ts b/test/unit/parachain/redeem.test.ts index b3ab981a7..1eb2cd153 100644 --- a/test/unit/parachain/redeem.test.ts +++ b/test/unit/parachain/redeem.test.ts @@ -2,7 +2,7 @@ import { expect } from "../../chai"; import sinon from "sinon"; import { DefaultRedeemAPI, DefaultVaultsAPI, VaultsAPI } from "../../../src"; import { newMonetaryAmount } from "../../../src/utils"; -import { ExchangeRate, KBtc, Kintsugi} from "@interlay/monetary-js"; +import { ExchangeRate, KBtc, Kintsugi } from "@interlay/monetary-js"; import Big from "big.js"; import { NO_LIQUIDATION_VAULT_FOUND_REJECTION } from "../../../src/parachain/vaults"; From b05764932d7664419d2c02f8c2f478cae91d6ed9 Mon Sep 17 00:00:00 2001 From: Brendon Votteler Date: Tue, 23 May 2023 11:51:31 +0200 Subject: [PATCH 4/4] chore: improve test description --- test/unit/parachain/redeem.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/parachain/redeem.test.ts b/test/unit/parachain/redeem.test.ts index 1eb2cd153..70ad7ac84 100644 --- a/test/unit/parachain/redeem.test.ts +++ b/test/unit/parachain/redeem.test.ts @@ -103,7 +103,7 @@ describe("DefaultRedeemAPI", () => { sinon.reset(); }); - it("should return zero when no getLiquidationVault rejects with no liquidation vault message", async () => { + it("should return zero if getLiquidationVault rejects with no liquidation vault message", async () => { // stub internal call to return no liquidation vault stubbedVaultsApi.getLiquidationVault.withArgs(sinon.match.any).returns(Promise.reject(NO_LIQUIDATION_VAULT_FOUND_REJECTION));