From 36f009ed1ffb1423c08d60d5a64696c2d5d9c319 Mon Sep 17 00:00:00 2001 From: helpfulBro Date: Sun, 28 Jun 2026 19:26:48 +1000 Subject: [PATCH 1/2] fix: show available liquidity for e-mode-only borrowable assets (#3032) Co-authored-by: Sam Mason de Caires --- .../reserve-overview/ReserveTopDetails.tsx | 4 +- .../getMaxAmountAvailableToBorrow.spec.ts | 50 +++++++++++++++++++ src/utils/getMaxAmountAvailableToBorrow.ts | 11 ++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/utils/__tests__/getMaxAmountAvailableToBorrow.spec.ts diff --git a/src/modules/reserve-overview/ReserveTopDetails.tsx b/src/modules/reserve-overview/ReserveTopDetails.tsx index ed6182c029..99187e0b32 100644 --- a/src/modules/reserve-overview/ReserveTopDetails.tsx +++ b/src/modules/reserve-overview/ReserveTopDetails.tsx @@ -5,6 +5,8 @@ import { CircleIcon } from 'src/components/CircleIcon'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Link } from 'src/components/primitives/Link'; import { useRootStore } from 'src/store/root'; +import { assetIsBorrowableOnMarket } from 'src/utils/getMaxAmountAvailableToBorrow'; + import { GENERAL } from 'src/utils/events'; import { useShallow } from 'zustand/shallow'; @@ -57,7 +59,7 @@ export const ReserveTopDetails = ({ underlyingAsset }: ReserveTopDetailsProps) = Available liquidity} loading={loading} hideIcon> { + it('returns true when borrowingEnabled is true', () => { + expect( + assetIsBorrowableOnMarket({ borrowingEnabled: true, eModes: [] }) + ).toBe(true); + }); + + it('returns true when borrowable in any e-mode', () => { + expect( + assetIsBorrowableOnMarket({ + borrowingEnabled: false, + eModes: [{ id: 1, borrowingEnabled: true }], + }) + ).toBe(true); + }); + + it('returns false when not borrowable in normal mode or e-mode', () => { + expect( + assetIsBorrowableOnMarket({ + borrowingEnabled: false, + eModes: [{ id: 1, borrowingEnabled: false }], + }) + ).toBe(false); + }); +}); + +describe('assetCanBeBorrowedByUser', () => { + it('allows e-mode users to borrow when their category permits it', () => { + expect( + assetCanBeBorrowedByUser(baseReserve as any, { + isInEmode: true, + userEmodeCategoryId: 1, + } as any) + ).toBe(true); + }); +}); diff --git a/src/utils/getMaxAmountAvailableToBorrow.ts b/src/utils/getMaxAmountAvailableToBorrow.ts index a996276978..aa99ccc532 100644 --- a/src/utils/getMaxAmountAvailableToBorrow.ts +++ b/src/utils/getMaxAmountAvailableToBorrow.ts @@ -20,6 +20,17 @@ interface PoolReserveBorrowSubset { borrowCapUSD: string; } +type MarketBorrowabilityReserve = Pick; + +/** + * Whether a reserve has any borrow path on the market. + * Mirrors on-chain ValidationLogic: outside e-mode uses borrowingEnabled; + * inside e-mode uses the category borrowableBitmap (exposed as eMode.borrowingEnabled). + */ +export function assetIsBorrowableOnMarket(reserve: MarketBorrowabilityReserve): boolean { + return reserve.borrowingEnabled || reserve.eModes.some((eMode) => eMode.borrowingEnabled); +} + /** * Calculates the maximum amount a user can borrow. * @param poolReserve From e36f1ec11ef3ac88c60e876326ef5e3a26f15ae3 Mon Sep 17 00:00:00 2001 From: Sam Mason de Caires Date: Sun, 28 Jun 2026 10:32:39 +0100 Subject: [PATCH 2/2] fix: fix up PR tests --- src/modules/reserve-overview/ReserveTopDetails.tsx | 5 ++--- .../getMaxAmountAvailableToBorrow.spec.ts | 14 ++++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/modules/reserve-overview/ReserveTopDetails.tsx b/src/modules/reserve-overview/ReserveTopDetails.tsx index 99187e0b32..c4c3e517a0 100644 --- a/src/modules/reserve-overview/ReserveTopDetails.tsx +++ b/src/modules/reserve-overview/ReserveTopDetails.tsx @@ -5,9 +5,8 @@ import { CircleIcon } from 'src/components/CircleIcon'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Link } from 'src/components/primitives/Link'; import { useRootStore } from 'src/store/root'; -import { assetIsBorrowableOnMarket } from 'src/utils/getMaxAmountAvailableToBorrow'; - import { GENERAL } from 'src/utils/events'; +import { assetIsBorrowableOnMarket } from 'src/utils/getMaxAmountAvailableToBorrow'; import { useShallow } from 'zustand/shallow'; import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem'; @@ -59,7 +58,7 @@ export const ReserveTopDetails = ({ underlyingAsset }: ReserveTopDetailsProps) = Available liquidity} loading={loading} hideIcon> { it('returns true when borrowingEnabled is true', () => { - expect( - assetIsBorrowableOnMarket({ borrowingEnabled: true, eModes: [] }) - ).toBe(true); + expect(assetIsBorrowableOnMarket({ borrowingEnabled: true, eModes: [] })).toBe(true); }); it('returns true when borrowable in any e-mode', () => { @@ -41,10 +43,10 @@ describe('assetIsBorrowableOnMarket', () => { describe('assetCanBeBorrowedByUser', () => { it('allows e-mode users to borrow when their category permits it', () => { expect( - assetCanBeBorrowedByUser(baseReserve as any, { + assetCanBeBorrowedByUser(baseReserve, { isInEmode: true, userEmodeCategoryId: 1, - } as any) + } as unknown as ExtendedFormattedUser) ).toBe(true); }); });