From cfeb6527ec2346d8673b21af6f16000e80d079a8 Mon Sep 17 00:00:00 2001
From: starksama <257340800+starksama@users.noreply.github.com>
Date: Fri, 13 Feb 2026 00:05:11 +0800
Subject: [PATCH 1/5] fix: recognize vault-only oracles as standard from
scanner metadata
When an oracle has only vault conversions (no feeds), the Morpho API
returns null feeds, causing getOracleType to fallback to Custom.
Now we trust the scanner metadata type for standard oracles too,
not just meta oracles. This fixes markets like 0x72cc79... that use
srRoyUSDC vault conversion.
Fixes #381
---
src/utils/oracle.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/utils/oracle.ts b/src/utils/oracle.ts
index 619dd22c..bfcbd3ae 100644
--- a/src/utils/oracle.ts
+++ b/src/utils/oracle.ts
@@ -245,10 +245,11 @@ export function getOracleType(
chainId?: number,
metadataMap?: OracleMetadataRecord,
) {
- // Check scanner metadata for meta oracle type
+ // Check scanner metadata for oracle type (meta or standard with vault-only)
if (metadataMap && oracleAddress) {
const metadata = getOracleFromMetadata(metadataMap, oracleAddress);
if (metadata?.type === 'meta') return OracleType.Meta;
+ if (metadata?.type === 'standard') return OracleType.Standard;
}
// Morpho API only contains oracleData if it follows the standard MorphoOracle structure with feeds
From 14f0f4ba1fe6d2bdf249ce9fa0b5a32edbd23a48 Mon Sep 17 00:00:00 2001
From: starksama <257340800+starksama@users.noreply.github.com>
Date: Fri, 13 Feb 2026 00:15:05 +0800
Subject: [PATCH 2/5] fix: don't flag vault-only oracles as having unknown
feeds
When all feeds are null but scanner metadata shows a vault exists,
treat it as a valid vault-only oracle instead of marking it as
'hasCompletelyUnknown'. This prevents false warnings on markets
like srRoyUSDC that only use vault conversion.
---
src/utils/oracle.ts | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/src/utils/oracle.ts b/src/utils/oracle.ts
index bfcbd3ae..b8a962c8 100644
--- a/src/utils/oracle.ts
+++ b/src/utils/oracle.ts
@@ -290,6 +290,24 @@ export function parsePriceFeedVendors(
}
if (!oracleData.baseFeedOne && !oracleData.baseFeedTwo && !oracleData.quoteFeedOne && !oracleData.quoteFeedTwo) {
+ // Check if this is a vault-only oracle (no feeds but has vault conversion)
+ const oracleMetadata =
+ options?.metadataMap && options.oracleAddress ? getOracleFromMetadata(options.metadataMap, options.oracleAddress) : undefined;
+ const oracleMetadataData = oracleMetadata?.data && !isMetaOracleData(oracleMetadata.data) ? oracleMetadata.data : undefined;
+ const hasVault = oracleMetadataData?.baseVault || oracleMetadataData?.quoteVault;
+
+ // Vault-only oracles are valid — don't mark as unknown
+ if (hasVault) {
+ return {
+ coreVendors: [],
+ taggedVendors: [],
+ hasCompletelyUnknown: false,
+ hasTaggedUnknown: false,
+ vendors: [],
+ hasUnknown: false,
+ };
+ }
+
return {
coreVendors: [],
taggedVendors: [],
From 5f5768043211933c28b9812e18fd4394caaf6821 Mon Sep 17 00:00:00 2001
From: starksama <257340800+starksama@users.noreply.github.com>
Date: Fri, 13 Feb 2026 00:20:40 +0800
Subject: [PATCH 3/5] fix: filter out false 'unrecognized asset' warnings using
dynamic token list
The subgraph fetcher only checks the static token list (supportedTokens),
but we also have dynamic tokens from Pendle API via useTokensQuery.
Now useMarketWarnings filters out 'unrecognized_loan_asset' and
'unrecognized_collateral_asset' warnings if the token IS found in the
dynamic token list (which includes Pendle tokens).
---
src/hooks/useMarketWarnings.ts | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/src/hooks/useMarketWarnings.ts b/src/hooks/useMarketWarnings.ts
index 5ba02b62..fd83267d 100644
--- a/src/hooks/useMarketWarnings.ts
+++ b/src/hooks/useMarketWarnings.ts
@@ -1,23 +1,41 @@
import { useMemo } from 'react';
import { useOracleMetadata, type OracleMetadataRecord } from '@/hooks/useOracleMetadata';
+import { useTokensQuery } from '@/hooks/queries/useTokensQuery';
import type { Market, WarningWithDetail } from '@/utils/types';
import { getMarketWarningsWithDetail } from '@/utils/warnings';
/**
* Hook to compute market warnings with details on-demand
* Uses oracle metadata when available for accurate feed detection
+ * Uses dynamic token list to filter out false "unrecognized asset" warnings
*/
export const useMarketWarnings = (market: Market | null | undefined): WarningWithDetail[] => {
const chainId = market?.morphoBlue?.chain?.id;
const { data: oracleMetadataMap } = useOracleMetadata(chainId);
+ const { findToken } = useTokensQuery();
return useMemo(() => {
if (!market) return [];
- return getMarketWarningsWithDetail(market, {
+
+ const warnings = getMarketWarningsWithDetail(market, {
considerWhitelist: true,
oracleMetadataMap,
});
- }, [market, oracleMetadataMap]);
+
+ // Filter out false "unrecognized asset" warnings
+ // The subgraph fetcher only checks static token list, but we have dynamic tokens too (Pendle, etc.)
+ return warnings.filter((warning) => {
+ if (warning.code === 'unrecognized_loan_asset' && market.loanAsset?.address) {
+ const found = findToken(market.loanAsset.address, chainId ?? 0);
+ if (found) return false; // Token found in dynamic list, remove warning
+ }
+ if (warning.code === 'unrecognized_collateral_asset' && market.collateralAsset?.address) {
+ const found = findToken(market.collateralAsset.address, chainId ?? 0);
+ if (found) return false; // Token found in dynamic list, remove warning
+ }
+ return true;
+ });
+ }, [market, oracleMetadataMap, findToken, chainId]);
};
/**
From 8e4d5b118ee068ea2de2c0e506210627ec3d2d90 Mon Sep 17 00:00:00 2001
From: starksama <257340800+starksama@users.noreply.github.com>
Date: Fri, 13 Feb 2026 00:27:39 +0800
Subject: [PATCH 4/5] fix: show checkmark icon and proper tooltip for
vault-only oracles
For oracles that only use vault conversion (no price feeds):
- Show green checkmark icon instead of empty badge
- Tooltip explains: 'Uses onchain vault contract for price conversion'
Previously showed nothing with confusing 'Uses feeds from .' tooltip.
---
.../components/oracle-vendor-badge.tsx | 29 ++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/features/markets/components/oracle-vendor-badge.tsx b/src/features/markets/components/oracle-vendor-badge.tsx
index 2a98e833..ab2bfca8 100644
--- a/src/features/markets/components/oracle-vendor-badge.tsx
+++ b/src/features/markets/components/oracle-vendor-badge.tsx
@@ -3,7 +3,7 @@
import React from 'react';
import { Tooltip } from '@/components/ui/tooltip';
import Image from 'next/image';
-import { IoWarningOutline, IoHelpCircleOutline } from 'react-icons/io5';
+import { IoWarningOutline, IoHelpCircleOutline, IoCheckmarkCircleOutline } from 'react-icons/io5';
import {
OracleType,
OracleVendorIcons,
@@ -50,6 +50,17 @@ function OracleVendorBadge({ oracleData, chainId, oracleAddress, showText = fals
const isCustom = oracleType === OracleType.Custom;
const isMeta = oracleType === OracleType.Meta;
+ // Check if this is a vault-only oracle (no feeds, only vault conversion)
+ const oracleMetadata = oracleMetadataMap && oracleAddress ? getOracleFromMetadata(oracleMetadataMap, oracleAddress) : undefined;
+ const oracleMetadataData = oracleMetadata?.data && !isMetaOracleData(oracleMetadata.data) ? oracleMetadata.data : undefined;
+ const isVaultOnly =
+ oracleType === OracleType.Standard &&
+ !oracleMetadataData?.baseFeedOne &&
+ !oracleMetadataData?.baseFeedTwo &&
+ !oracleMetadataData?.quoteFeedOne &&
+ !oracleMetadataData?.quoteFeedTwo &&
+ (oracleMetadataData?.baseVault || oracleMetadataData?.quoteVault);
+
const vendorInfo = (() => {
if (isMeta && oracleMetadataMap && oracleAddress) {
const metadata = getOracleFromMetadata(oracleMetadataMap, oracleAddress);
@@ -72,6 +83,12 @@ function OracleVendorBadge({ oracleData, chainId, oracleAddress, showText = fals
className="text-secondary"
size={16}
/>
+ ) : isVaultOnly ? (
+ // Vault-only oracle - show checkmark icon
+
Standard Oracle
+Uses onchain vault contract for price conversion.
+