diff --git a/packages/babylon-wallet-connector/tests/unit/deriveContextHash.test.ts b/packages/babylon-wallet-connector/tests/unit/deriveContextHash.test.ts index 8ef3aea52..6ac913b6a 100644 --- a/packages/babylon-wallet-connector/tests/unit/deriveContextHash.test.ts +++ b/packages/babylon-wallet-connector/tests/unit/deriveContextHash.test.ts @@ -2,10 +2,10 @@ * Unit tests for `deriveContextHash` adapter behavior. * * Tests the shared `unsupportedDeriveContextHash` helper used by every - * non-supporting BTC adapter (OKX, Ledger v1/v2, AppKit, Tomo, + * non-supporting BTC adapter (OKX, Ledger v1/v2, Keystone, AppKit, Tomo, * Injectable fallback) and the injectable wrapper that stubs the method - * when the underlying wallet doesn't implement it. UniSat, OneKey, and - * Keystone implement the method natively instead of using this helper. + * when the underlying wallet doesn't implement it. UniSat and OneKey + * forward to the wallet's native method instead of using this helper. * * The provider classes themselves are not imported here — their * modules transitively pull in SVG asset imports that the unit-test diff --git a/services/vault/src/applications/aave/utils/__tests__/healthFactorDisplay.test.ts b/services/vault/src/applications/aave/utils/__tests__/healthFactorDisplay.test.ts index 949204005..0afe8fc87 100644 --- a/services/vault/src/applications/aave/utils/__tests__/healthFactorDisplay.test.ts +++ b/services/vault/src/applications/aave/utils/__tests__/healthFactorDisplay.test.ts @@ -1,7 +1,10 @@ import { describe, expect, it } from "vitest"; import { HEALTH_FACTOR_DISPLAY_CAP } from "../../constants"; -import { formatHealthFactor } from "../healthFactorDisplay"; +import { + formatHealthFactor, + HEALTH_FACTOR_HEALTHY_THRESHOLD, +} from "../healthFactorDisplay"; describe("formatHealthFactor", () => { it("returns '-' when there is no debt (null)", () => { @@ -31,4 +34,16 @@ describe("formatHealthFactor", () => { `${HEALTH_FACTOR_DISPLAY_CAP}.00`, ); }); + + it("returns the raw number for high HF (delta callers depend on this)", () => { + // formatHealthFactor must never substitute a label for high values — + // callers that show deltas (e.g. action review) need the numeric string + // to compute before/after diffs. The label substitution lives at the + // call site, not here. + expect(formatHealthFactor(HEALTH_FACTOR_HEALTHY_THRESHOLD)).toBe("50.00"); + expect(formatHealthFactor(HEALTH_FACTOR_HEALTHY_THRESHOLD + 1)).toBe( + "51.00", + ); + expect(formatHealthFactor(100)).toBe("100.00"); + }); }); diff --git a/services/vault/src/applications/aave/utils/healthFactorDisplay.ts b/services/vault/src/applications/aave/utils/healthFactorDisplay.ts index 00a3b3faa..f4e37d332 100644 --- a/services/vault/src/applications/aave/utils/healthFactorDisplay.ts +++ b/services/vault/src/applications/aave/utils/healthFactorDisplay.ts @@ -27,6 +27,11 @@ export function getHealthFactorColor( } } +/** Above this value, the health factor is effectively unbounded. Callers that show + * a high-HF label (e.g. the Overview row) use this; numeric before/after deltas + * intentionally do not, to preserve the magnitude of the change. */ +export const HEALTH_FACTOR_HEALTHY_THRESHOLD = 50; + export function formatHealthFactor(healthFactor: number | null): string { // null = no debt; non-finite or absurdly high = negligible debt. All render // as "-" ("infinitely healthy") rather than "Infinity" or the scientific diff --git a/services/vault/src/applications/aave/utils/index.ts b/services/vault/src/applications/aave/utils/index.ts index dacc2bc44..b15ef0eee 100644 --- a/services/vault/src/applications/aave/utils/index.ts +++ b/services/vault/src/applications/aave/utils/index.ts @@ -19,6 +19,7 @@ export type { // Display utilities (frontend-only, not in SDK) export { HEALTH_FACTOR_COLORS, + HEALTH_FACTOR_HEALTHY_THRESHOLD, formatHealthFactor, getHealthFactorColor, } from "./healthFactorDisplay"; diff --git a/services/vault/src/components/shared/utils/healthFactorGauge.ts b/services/vault/src/components/shared/utils/healthFactorGauge.ts index 9214b3d4f..54c809a17 100644 --- a/services/vault/src/components/shared/utils/healthFactorGauge.ts +++ b/services/vault/src/components/shared/utils/healthFactorGauge.ts @@ -2,12 +2,13 @@ import { HEALTH_FACTOR_COLORS, type HealthFactorStatus, } from "@/applications/aave/utils"; +import { COPY } from "@/copy"; export const STATUS_LABELS: Record< Exclude, string > = { - safe: "Healthy", + safe: COPY.overview.healthFactorHealthy, warning: "At Risk", danger: "Liquidatable", } satisfies Record; diff --git a/services/vault/src/components/simple/OverviewSection.tsx b/services/vault/src/components/simple/OverviewSection.tsx index 615a0ad11..94663a8d5 100644 --- a/services/vault/src/components/simple/OverviewSection.tsx +++ b/services/vault/src/components/simple/OverviewSection.tsx @@ -8,6 +8,7 @@ import { formatHealthFactor, getHealthFactorColor, + HEALTH_FACTOR_HEALTHY_THRESHOLD, type HealthFactorStatus, } from "@/applications/aave/utils"; import { HealthFactorGauge, HeartIcon } from "@/components/shared"; @@ -37,7 +38,10 @@ export function OverviewSection({ return ; } - const healthFactorFormatted = formatHealthFactor(healthFactor); + const healthFactorFormatted = + healthFactor !== null && healthFactor > HEALTH_FACTOR_HEALTHY_THRESHOLD + ? COPY.overview.healthFactorHealthy + : formatHealthFactor(healthFactor); const healthFactorColor = getHealthFactorColor(healthFactorStatus); const showHealthFactor = healthFactor !== null; diff --git a/services/vault/src/context/wallet/VaultWalletConnectionProvider.tsx b/services/vault/src/context/wallet/VaultWalletConnectionProvider.tsx index 73bd966a2..1edae7132 100644 --- a/services/vault/src/context/wallet/VaultWalletConnectionProvider.tsx +++ b/services/vault/src/context/wallet/VaultWalletConnectionProvider.tsx @@ -21,10 +21,10 @@ import { getNetworkConfigETH } from "@/config/network"; import { logger } from "@/infrastructure"; // Vault deposits require the connected BTC wallet to implement the -// `deriveContextHash` API (see docs/specs/derive-context-hash.md). UniSat, -// OneKey, and Keystone expose a conformant implementation today, so every -// other BTC adapter is gated off here. Re-enable an entry as soon as its -// wallet vendor ships `deriveContextHash`. Each non-conforming adapter still +// `deriveContextHash` API (see docs/specs/derive-context-hash.md). UniSat +// and OneKey expose a conformant implementation today, so every other BTC +// adapter is gated off here. Re-enable an entry as soon as its wallet +// vendor ships `deriveContextHash`. Each non-conforming adapter still // throws `WALLET_METHOD_NOT_SUPPORTED` at the connector layer; this // list just keeps them out of the connection UI in the first place so // users don't pick something that can't complete a deposit. diff --git a/services/vault/src/copy.ts b/services/vault/src/copy.ts index a595f9320..0bb421b9b 100644 --- a/services/vault/src/copy.ts +++ b/services/vault/src/copy.ts @@ -701,6 +701,7 @@ export const COPY = { overview: { heading: "Overview", healthFactorLabel: "Health factor", + healthFactorHealthy: "Healthy", ltvLabel: "Current LTV", totalCollateralValueLabel: "Total collateral value", amountToRepayLabel: "Amount to repay",