Skip to content

Commit 0e6fc47

Browse files
committed
fix: group unknown vault protocols
1 parent e511e6f commit 0e6fc47

8 files changed

Lines changed: 80 additions & 22 deletions

File tree

src/lib/top-vaults/VaultGroupTable.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import { createRender } from '$lib/components/datatable/utils';
1818
import DataTable from '$lib/components/datatable/DataTable.svelte';
1919
import TableRowTarget from '$lib/components/datatable/TableRowTarget.svelte';
20+
import { UNKNOWN_VAULT_PROTOCOL_SLUG } from '$lib/top-vaults/helpers';
2021
import VaultGroupNameCell from './VaultGroupNameCell.svelte';
2122
import RiskCell from './RiskCell.svelte';
2223
import Core3RiskCell from './Core3RiskCell.svelte';
@@ -99,7 +100,7 @@
99100
createRender(VaultGroupNameCell, {
100101
label: value.name,
101102
logoUrl: getLogoHref?.(value.slug),
102-
showPlaceholder: value.name === 'Unknown' && !getLogoHref?.(value.slug)
103+
showPlaceholder: value.slug === UNKNOWN_VAULT_PROTOCOL_SLUG && !getLogoHref?.(value.slug)
103104
}),
104105
plugins: { sort: { getSortValue: (v) => v.name, invert: true } }
105106
}),

src/lib/top-vaults/helpers.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
isEligibleFrontpageVault,
66
hasSupportedProtocol,
77
getProtocolDisplayName,
8+
isUnknownVaultProtocol,
89
isUnsupportedProtocolSlug,
910
meetsMinTvl,
1011
getFormattedLockup,
@@ -292,6 +293,20 @@ describe('isUnsupportedProtocolSlug', () => {
292293
});
293294
});
294295

296+
describe('isUnknownVaultProtocol', () => {
297+
test.each([
298+
{ protocol: 'ERC-4626', protocol_slug: 'erc-4626' },
299+
{ protocol: '<protocol not yet identified>', protocol_slug: 'protocol-not-yet-identified' },
300+
{ protocol: 'Unknown', protocol_slug: 'unknown' }
301+
])('groups $protocol as unknown', (vault) => {
302+
expect(isUnknownVaultProtocol(vault)).toBe(true);
303+
});
304+
305+
test('does not group a recognised protocol as unknown', () => {
306+
expect(isUnknownVaultProtocol({ protocol: 'Yearn', protocol_slug: 'yearn' })).toBe(false);
307+
});
308+
});
309+
295310
describe('meetsMinTvl', () => {
296311
test('returns true when current_nav meets threshold', () => {
297312
const vault = createTestVault('Test vault', { current_nav: 50_000 });

src/lib/top-vaults/helpers.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const KINEXYS_PROTOCOL_SLUG = 'kinexys';
2222
const KINEXYS_DENOMINATION = 'USD (offchain)';
2323
const KINEXYS_DENOMINATION_SLUG = 'usd-offchain';
2424
export const UNKNOWN_VAULT_PROTOCOL_DISPLAY_NAME = 'Unknown vault protocol';
25+
/** Canonical group used for vaults whose underlying protocol is unidentified or unsupported. */
26+
export const UNKNOWN_VAULT_PROTOCOL_SLUG = 'unknown';
2527
const UNKNOWN_VAULT_PROTOCOL_SLUGS = new Set(['erc-4626']);
2628

2729
/**
@@ -211,6 +213,18 @@ export function isManuallyMappedUnknownProtocolSlug(protocolSlug: string | null
211213
return protocolSlug != null && UNKNOWN_VAULT_PROTOCOL_SLUGS.has(protocolSlug);
212214
}
213215

216+
/**
217+
* Whether a vault belongs in the combined unknown-protocol group.
218+
*
219+
* This includes protocol placeholders from the data source, the generic ERC-4626
220+
* classification, and records explicitly labelled "Unknown".
221+
*/
222+
export function isUnknownVaultProtocol(vault: Pick<VaultInfo, 'protocol'> & Partial<Pick<VaultInfo, 'protocol_slug'>>) {
223+
if (isManuallyMappedUnknownProtocolSlug(vault.protocol_slug)) return true;
224+
if (isUnsupportedProtocolName(vault.protocol) || isUnsupportedProtocolSlug(vault.protocol_slug)) return true;
225+
return vault.protocol?.trim().toLowerCase() === 'unknown';
226+
}
227+
214228
const FRONTPAGE_RISK_FILTER = riskFilterOptions.find((option) => option.label === 'Severe');
215229

216230
/**

src/lib/vault-protocol/helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { vaultProtocolMetadataUrl } from '$lib/config';
22
import { buildMetadataLogoProxyPath, type MetadataLogoOptions } from '$lib/metadata-logo/proxy';
3-
import { isUnsupportedProtocolSlug } from '$lib/top-vaults/helpers';
3+
import { isUnsupportedProtocolSlug, UNKNOWN_VAULT_PROTOCOL_SLUG } from '$lib/top-vaults/helpers';
44

55
/**
66
* Return the "light" version of the vault protocol logo URL for a given
@@ -14,7 +14,7 @@ import { isUnsupportedProtocolSlug } from '$lib/top-vaults/helpers';
1414
*/
1515
export function getVaultProtocolLogoUrl(slug: string, options: MetadataLogoOptions = {}): string | undefined {
1616
if (!vaultProtocolMetadataUrl) return undefined;
17-
if (isUnsupportedProtocolSlug(slug)) return undefined;
17+
if (slug === UNKNOWN_VAULT_PROTOCOL_SLUG || isUnsupportedProtocolSlug(slug)) return undefined;
1818
return buildMetadataLogoProxyPath('protocol', slug, {
1919
format: 'webp',
2020
...options

src/routes/trading-view/vaults/protocols/+page.server.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import {
55
getCore3PolForVault,
66
getProtocolDisplayName,
77
isBlacklisted,
8-
meetsMinTvl
8+
isUnknownVaultProtocol,
9+
meetsMinTvl,
10+
UNKNOWN_VAULT_PROTOCOL_DISPLAY_NAME,
11+
UNKNOWN_VAULT_PROTOCOL_SLUG
912
} from '$lib/top-vaults/helpers.js';
1013
import { sortOptions } from '$lib/top-vaults/VaultGroupTable.svelte';
1114
import { getNumberParam, getStringParam } from '$lib/helpers/url-params';
@@ -18,12 +21,15 @@ export async function load({ fetch, url: { searchParams } }) {
1821
const eligibleVaults = vaults.filter((v) => !isBlacklisted(v) && meetsMinTvl(v));
1922

2023
const protocols = eligibleVaults.reduce<Record<string, VaultGroup>>((acc, vault) => {
21-
const slug = vault.protocol_slug;
24+
const isUnknown = isUnknownVaultProtocol(vault);
25+
const slug = isUnknown ? UNKNOWN_VAULT_PROTOCOL_SLUG : vault.protocol_slug;
2226
const core3Pol = getCore3PolForVault(vault, core3_protocols);
2327

2428
acc[slug] ??= {
2529
slug,
26-
name: getProtocolDisplayName(vault.protocol, vault.protocol_slug),
30+
name: isUnknown
31+
? UNKNOWN_VAULT_PROTOCOL_DISPLAY_NAME
32+
: getProtocolDisplayName(vault.protocol, vault.protocol_slug),
2733
vault_count: 0,
2834
tvl: 0,
2935
avg_apy: null,
@@ -43,7 +49,11 @@ export async function load({ fetch, url: { searchParams } }) {
4349
// Calculate TVL-weighted average APY for each protocol
4450
const protocolGroups: VaultGroup[] = Object.values(protocols).map((group) => ({
4551
...group,
46-
avg_apy: calculateTvlWeightedApy(eligibleVaults.filter((v) => v.protocol_slug === group.slug))
52+
avg_apy: calculateTvlWeightedApy(
53+
eligibleVaults.filter((vault) =>
54+
group.slug === UNKNOWN_VAULT_PROTOCOL_SLUG ? isUnknownVaultProtocol(vault) : vault.protocol_slug === group.slug
55+
)
56+
)
4757
}));
4858

4959
const chartProtocols: MarketShareChartItem[] = protocolGroups.map((group) => ({

src/routes/trading-view/vaults/protocols/[protocol=slug]/+page.server.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,36 @@
11
import { error } from '@sveltejs/kit';
22
import { getCachedTopVaults } from '$lib/top-vaults/cache';
33
import { fetchVaultProtocolMetadata } from '$lib/vault-protocol/client';
4-
import { getCore3ProtocolForVault, getProtocolDisplayName } from '$lib/top-vaults/helpers.js';
4+
import {
5+
getCore3ProtocolForVault,
6+
getProtocolDisplayName,
7+
isUnknownVaultProtocol,
8+
UNKNOWN_VAULT_PROTOCOL_DISPLAY_NAME,
9+
UNKNOWN_VAULT_PROTOCOL_SLUG
10+
} from '$lib/top-vaults/helpers.js';
511

612
export async function load({ params, fetch }) {
713
const { protocol } = params;
814
const { vaults, core3_protocols } = await getCachedTopVaults(fetch);
915

10-
const protocolVault = vaults.find((v) => v.protocol_slug === protocol);
16+
const isUnknownGroup = protocol === UNKNOWN_VAULT_PROTOCOL_SLUG;
17+
const protocolVault = vaults.find((v) => (isUnknownGroup ? isUnknownVaultProtocol(v) : v.protocol_slug === protocol));
1118
if (!protocolVault) error(404, 'Vault protocol not found');
1219

13-
const protocolMetadata = await fetchVaultProtocolMetadata(fetch, protocol, protocolVault.protocol);
20+
const protocolMetadata = isUnknownGroup
21+
? undefined
22+
: await fetchVaultProtocolMetadata(fetch, protocol, protocolVault.protocol);
1423
const core3 =
1524
vaults
16-
.filter((v) => v.protocol_slug === protocol)
25+
.filter((v) => (isUnknownGroup ? isUnknownVaultProtocol(v) : v.protocol_slug === protocol))
1726
.map((vault) => getCore3ProtocolForVault(vault, core3_protocols))
1827
.find((rating) => rating !== null) ?? null;
1928

2029
return {
2130
protocolSlug: protocol,
22-
protocolName: getProtocolDisplayName(protocolVault.protocol, protocolVault.protocol_slug),
31+
protocolName: isUnknownGroup
32+
? UNKNOWN_VAULT_PROTOCOL_DISPLAY_NAME
33+
: getProtocolDisplayName(protocolVault.protocol, protocolVault.protocol_slug),
2334
protocolMetadata,
2435
core3
2536
};

src/routes/trading-view/vaults/protocols/[protocol=slug]/+page.svelte

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import type { TopVaults } from '$lib/top-vaults/schemas';
33
import { fetchAllVaultData, hasVaultCache } from '$lib/top-vaults/client-cache';
4-
import { isManuallyMappedUnknownProtocolSlug, isUnsupportedProtocolSlug } from '$lib/top-vaults/helpers';
4+
import { isUnknownVaultProtocol, UNKNOWN_VAULT_PROTOCOL_SLUG } from '$lib/top-vaults/helpers';
55
import { getVaultProtocolLogoUrl } from '$lib/vault-protocol/helpers.js';
66
import { resolve } from '$app/paths';
77
import { page } from '$app/state';
@@ -12,7 +12,7 @@
1212
1313
let { data } = $props();
1414
let { protocolSlug, protocolName, protocolMetadata, core3 } = $derived(data);
15-
let isUnknownVaultProtocol = $derived(isManuallyMappedUnknownProtocolSlug(protocolSlug));
15+
let isUnknownVaultProtocolGroup = $derived(protocolSlug === UNKNOWN_VAULT_PROTOCOL_SLUG);
1616
1717
let topVaults = $state<TopVaults>();
1818
let loading = $state(!hasVaultCache(page.data.generatedAt));
@@ -22,7 +22,9 @@
2222
.then((allData) => {
2323
topVaults = {
2424
...allData,
25-
vaults: allData.vaults.filter((v) => v.protocol_slug === protocolSlug)
25+
vaults: allData.vaults.filter((vault) =>
26+
isUnknownVaultProtocolGroup ? isUnknownVaultProtocol(vault) : vault.protocol_slug === protocolSlug
27+
)
2628
};
2729
})
2830
.catch((e) => console.error('Failed to load vault data:', e))
@@ -31,10 +33,12 @@
3133
3234
const unknownVaultDescription = 'These vaults are not yet mapped out. Contact us to have your vaults listed.';
3335
34-
let title = $derived(isUnknownVaultProtocol ? 'Unknown vaults' : `${protocolName} vaults and yields`);
35-
let heroTitle = $derived(isUnknownVaultProtocol ? 'Unknown vaults' : `${protocolName} powered stablecoin vaults`);
36+
let title = $derived(isUnknownVaultProtocolGroup ? 'Unknown vaults' : `${protocolName} vaults and yields`);
37+
let heroTitle = $derived(
38+
isUnknownVaultProtocolGroup ? 'Unknown vaults' : `${protocolName} powered stablecoin vaults`
39+
);
3640
let description = $derived(
37-
isUnknownVaultProtocol
41+
isUnknownVaultProtocolGroup
3842
? unknownVaultDescription
3943
: (protocolMetadata?.short_description ?? `Top stablecoin vaults on ${protocolName}`)
4044
);
@@ -92,11 +96,11 @@
9296
{loading}
9397
{protocolMetadata}
9498
title={heroTitle}
95-
subtitle={isUnknownVaultProtocol ? unknownVaultSubtitle : undefined}
99+
subtitle={isUnknownVaultProtocolGroup ? unknownVaultSubtitle : undefined}
96100
showFilters
97101
showUnknownFilter={false}
98102
defaultTvlKey="10k"
99-
defaultHideUnknown={isUnsupportedProtocolSlug(protocolSlug) ? 0 : 1}
103+
defaultHideUnknown={isUnknownVaultProtocolGroup ? 0 : 1}
100104
>
101105
{#snippet detailAside()}
102106
<VaultGroupMiniChart

src/routes/trading-view/vaults/protocols/[protocol=slug]/chart-data/+server.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import {
99
VAULT_GROUP_MINI_CHART_THREE_MONTH_LOOKBACK_DAYS
1010
} from '$lib/echarts/vault-group-mini-chart-server';
1111
import { getCachedTopVaults } from '$lib/top-vaults/cache';
12+
import { isUnknownVaultProtocol, UNKNOWN_VAULT_PROTOCOL_SLUG } from '$lib/top-vaults/helpers';
1213

13-
const CACHE_VERSION = 'protocol-mini-chart-v6';
14+
const CACHE_VERSION = 'protocol-mini-chart-v7';
1415
const cache = new Map<string, { payload: ProtocolMiniChartPayload; expires: number; version: string }>();
1516

1617
async function getCachedChartData(protocolSlug: string, fetch: Fetch) {
@@ -20,7 +21,9 @@ async function getCachedChartData(protocolSlug: string, fetch: Fetch) {
2021
if (cached && cached.version === CACHE_VERSION && now < cached.expires) return cached.payload;
2122

2223
const { vaults } = await getCachedTopVaults(fetch);
23-
const protocolVaults = vaults.filter((vault) => vault.protocol_slug === protocolSlug);
24+
const protocolVaults = vaults.filter((vault) =>
25+
protocolSlug === UNKNOWN_VAULT_PROTOCOL_SLUG ? isUnknownVaultProtocol(vault) : vault.protocol_slug === protocolSlug
26+
);
2427
if (protocolVaults.length === 0) error(404, 'Vault protocol not found');
2528

2629
const eligibleVaults = protocolVaults.filter(isEligibleVaultGroupMiniChartVault);

0 commit comments

Comments
 (0)