Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/data-sources/subgraph/market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,6 @@ const transformSubgraphMarketToMarket = (
const inputTokenPriceUSD = subgraphMarket.inputTokenPriceUSD ?? '0';
const oracleAddress = (subgraphMarket.oracle?.oracleAddress ?? '0x') as Address;

if (
marketId.toLowerCase() === '0x9103c3b4e834476c9a62ea009ba2c884ee42e94e6e314a26f04d312434191836'
) {
console.log('subgraphMarket', subgraphMarket);
}

const totalSupplyShares = subgraphMarket.totalSupplyShares ?? '0';
const totalBorrowShares = subgraphMarket.totalBorrowShares ?? '0';
const fee = subgraphMarket.fee ?? '0';
Expand Down Expand Up @@ -271,6 +265,11 @@ const transformSubgraphMarketToMarket = (
hasUSDPrice: hasUSDPrice,
isProtectedByLiquidationBots: false, // Not available from subgraph
isMonarchWhitelisted: false,

// todo: not able to parse bad debt now
realizedBadDebt: {
underlying: '0'
}
};

return marketDetail;
Expand Down
17 changes: 17 additions & 0 deletions src/graphql/morpho-api-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ warnings {
__typename
}

realizedBadDebt {
underlying
usd
}
badDebt {
underlying
usd
}

oracle {
data {
... on MorphoChainlinkOracleData {
Expand Down Expand Up @@ -171,6 +180,14 @@ export const marketsQuery = `
name
decimals
}
badDebt {
underlying
usd
}
realizedBadDebt {
underlying
usd
}
state {
borrowAssets
supplyAssets
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/useMarketWarnings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getMarketWarningsWithDetail } from '@/utils/warnings';
* This separates data fetching concerns from presentation logic
*/
export const useMarketWarnings = (
market: Pick<Market, 'warnings' | 'uniqueKey' | 'oracle' | 'oracleAddress' | 'morphoBlue'>,
market: Market,
considerWhitelist = false,
): WarningWithDetail[] => {
return useMemo(() => {
Expand All @@ -18,6 +18,7 @@ export const useMarketWarnings = (
market.oracle,
market.oracleAddress,
market.morphoBlue?.chain?.id,
market.realizedBadDebt.underlying,
considerWhitelist,
]);
};
Expand All @@ -27,7 +28,7 @@ export const useMarketWarnings = (
* Use this in contexts where you can't use hooks
*/
export const computeMarketWarnings = (
market: Pick<Market, 'warnings' | 'uniqueKey' | 'oracle' | 'oracleAddress' | 'morphoBlue'>,
market: Market,
considerWhitelist = false,
): WarningWithDetail[] => {
return getMarketWarningsWithDetail(market, considerWhitelist);
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useUserPositionsSummaryData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const useUserPositionsSummaryData = (user: string | undefined) => {
queryKey: blockKeys.all,
queryFn: fetchBlockNumbers,
staleTime: 5 * 60 * 1000, // Consider block numbers fresh for 5 minutes
gcTime: 30 * 60 * 1000, // Keep in cache for 30 minutes
gcTime: 3 * 60 * 1000, // Keep in cache for 3 minutes
});

// Query for earnings calculations with progressive updates
Expand Down
2 changes: 2 additions & 0 deletions src/store/createWagmiConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
injectedWallet,
trustWallet,
ledgerWallet,
walletConnectWallet
} from '@rainbow-me/rainbowkit/wallets';
import { createConfig, http } from 'wagmi';
import { base, mainnet, polygon, unichain, arbitrum } from 'wagmi/chains';
Expand All @@ -26,6 +27,7 @@ const wallets =
injectedWallet,
trustWallet,
ledgerWallet,
walletConnectWallet
]
: [injectedWallet];

Expand Down
4 changes: 3 additions & 1 deletion src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ export type Market = {
timestamp: number;
rateAtUTarget: number;
};

realizedBadDebt: {
underlying: string
}
// whether we have USD price such has supplyUSD, borrowUSD, collateralUSD, etc. If not, use estimationP
hasUSDPrice: boolean;
warnings: MarketWarning[];
Expand Down
58 changes: 28 additions & 30 deletions src/utils/warnings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MarketWarning, MorphoChainlinkOracleData } from '@/utils/types';
import { Market, MarketWarning } from '@/utils/types';
import { monarchWhitelistedMarkets } from './markets';
import { getOracleType, OracleType, parsePriceFeedVendors, checkFeedsPath } from './oracle';
import { WarningCategory, WarningWithDetail } from './types';
Expand Down Expand Up @@ -68,12 +68,7 @@ const morphoOfficialWarnings: WarningWithDetail[] = [
description: 'This market has some unrealized bad debt',
category: WarningCategory.debt,
},
{
code: 'bad_debt_realized',
level: 'warning',
description: 'This market has some realized bad debt (>10 BPS of total supply)',
category: WarningCategory.debt,
},

{
code: 'not_whitelisted',
level: 'alert',
Expand Down Expand Up @@ -104,6 +99,13 @@ const subgraphWarnings: WarningWithDetail[] = [
},
];

const BAD_DEBT: WarningWithDetail = {
code: 'bad_debt_realized',
level: 'warning',
description: 'This market has some realized bad debt (>10 BPS of total supply)',
category: WarningCategory.debt,
}

const UNRECOGNIZED_ORACLE: WarningWithDetail = {
code: 'unrecognized_oracle',
level: 'alert',
Expand Down Expand Up @@ -142,30 +144,9 @@ const UNRECOGNIZED_FEEDS_TAGGED: WarningWithDetail = {
category: WarningCategory.oracle,
};

// {
// code: 'incorrect_loan_exchange_rate',
// level: 'warning',
// description: 'The market is using the exchange rate from a token different from the loan one. ',
// category: WarningCategory.oracle,
// },
// {
// code: 'incorrect_collateral_exchange_rate',
// level: 'warning',
// description:
// 'The market is using the exchange rate from a token different from the collateral one.',
// category: WarningCategory.oracle,
// },

export const getMarketWarningsWithDetail = (
market: {
warnings: MarketWarning[];
uniqueKey: string;
oracle?: { data: MorphoChainlinkOracleData };
oracleAddress?: string;
morphoBlue: { chain: { id: number } };
loanAsset?: { symbol: string };
collateralAsset?: { symbol: string };
},
market: Market,
considerWhitelist = false,
) => {
const result = [];
Expand Down Expand Up @@ -200,7 +181,24 @@ export const getMarketWarningsWithDetail = (
}
}

// append our own oracle warnings
// Append bad debt warnings
try {
const badDebtUnderlying = market.realizedBadDebt.underlying;
if (badDebtUnderlying != null) {
const badDebt = BigInt(badDebtUnderlying);
if (badDebt > 0n) {
// only push the bad debt error is it's > 10BPS
const supplyAssets = BigInt(market.state.supplyAssets);
if (badDebt * 1000n > supplyAssets) {
result.push(BAD_DEBT);
}
}
}
} catch {
// ignore invalid BigInt values (e.g., decimal strings like "0.00")
}
Comment on lines +184 to +199
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix bad-debt parsing

realizedBadDebt.underlying shows up as decimal text (e.g. "0.05"). BigInt throws, we hit the catch, and the warning never fires even though debt exists. Parse as a float (or other decimal helper) and guard before comparing; same for supplyAssets.

-    const badDebtUnderlying = market.realizedBadDebt.underlying;
-    if (badDebtUnderlying != null) {
-      const badDebt = BigInt(badDebtUnderlying);
-      if (badDebt > 0n) {
-        // only push the bad debt error is it's > 10BPS
-        const supplyAssets = BigInt(market.state.supplyAssets);
-        if (badDebt * 1000n > supplyAssets) {
-          result.push(BAD_DEBT);
-        }
-      }
-    }
+    const badDebtValue = Number(market.realizedBadDebt?.underlying ?? '0');
+    const supplyValue = Number(market.state?.supplyAssets ?? '0');
+    if (
+      Number.isFinite(badDebtValue) &&
+      Number.isFinite(supplyValue) &&
+      badDebtValue > 0 &&
+      badDebtValue * 1000 > supplyValue
+    ) {
+      result.push(BAD_DEBT);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Append bad debt warnings
try {
const badDebtUnderlying = market.realizedBadDebt.underlying;
if (badDebtUnderlying != null) {
const badDebt = BigInt(badDebtUnderlying);
if (badDebt > 0n) {
// only push the bad debt error is it's > 10BPS
const supplyAssets = BigInt(market.state.supplyAssets);
if (badDebt * 1000n > supplyAssets) {
result.push(BAD_DEBT);
}
}
}
} catch {
// ignore invalid BigInt values (e.g., decimal strings like "0.00")
}
// Append bad debt warnings
try {
const badDebtValue = Number(market.realizedBadDebt?.underlying ?? '0');
const supplyValue = Number(market.state?.supplyAssets ?? '0');
if (
Number.isFinite(badDebtValue) &&
Number.isFinite(supplyValue) &&
badDebtValue > 0 &&
badDebtValue * 1000 > supplyValue
) {
result.push(BAD_DEBT);
}
} catch {
// ignore invalid BigInt values (e.g., decimal strings like "0.00")
}
🤖 Prompt for AI Agents
In src/utils/warnings.ts around lines 184 to 199, replace the current BigInt
parsing/try-catch with numeric/decimal-safe parsing: parse
realizedBadDebt.underlying and market.state.supplyAssets as decimals (e.g.,
parseFloat or a Decimal/Big library) only proceed if both parse to finite
numbers, compute the ratio (badDebt / supplyAssets) and push BAD_DEBT when the
ratio > 0.001 (10 bps); if you need integer precision use a scaled integer
approach (multiply decimals to an agreed scale before comparing) and remove the
silent BigInt swallow so numeric parsing errors are handled by the guard checks
instead.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field should return string, expect to be integer.
Does your comment still hold?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antoncoding, you're right—if realizedBadDebt.underlying is an integer string (like "1234567890"), then BigInt() is the correct parser. My original comment assumed decimal text, which was incorrect.

The current implementation looks fine. The try-catch guards against malformed data, but if the API always returns valid integer strings, it should work as intended.

If you want to verify the catch block isn't hiding real issues, you could temporarily log any errors during development, but otherwise the code is sound.

🐰


// Append our own oracle warnings
const oracleType = getOracleType(
market.oracle?.data,
market.oracleAddress,
Expand Down