diff --git a/src/modules/reserve-overview/ReserveTopDetails.tsx b/src/modules/reserve-overview/ReserveTopDetails.tsx index ed6182c029..c4c3e517a0 100644 --- a/src/modules/reserve-overview/ReserveTopDetails.tsx +++ b/src/modules/reserve-overview/ReserveTopDetails.tsx @@ -6,6 +6,7 @@ import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Link } from 'src/components/primitives/Link'; import { useRootStore } from 'src/store/root'; import { GENERAL } from 'src/utils/events'; +import { assetIsBorrowableOnMarket } from 'src/utils/getMaxAmountAvailableToBorrow'; import { useShallow } from 'zustand/shallow'; import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem'; @@ -57,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); + }); + + 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, { + isInEmode: true, + userEmodeCategoryId: 1, + } as unknown as ExtendedFormattedUser) + ).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