Skip to content

Commit 9062ab9

Browse files
authored
fix: clarify fee-adjusted vault returns
## Why Net return figures can appear unexpectedly negative when a vault charges an entry or withdrawal fee. The return cells should make that adjustment visible and explain it. ## Lessons learnt Transaction fees are represented in the net fee schedule, so the UI should only flag net returns when a non-zero deposit or withdrawal fee is reported. Tooltip width needs a shared limit to remain readable. ## Summary - add dashed, explanatory fee tooltips to affected net return and net CAGR cells - cap shared tooltips at 300px - document tooltip-width review guidance for Svelte UI changes
1 parent 983dbc2 commit 9062ab9

3 files changed

Lines changed: 39 additions & 2 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pnpm run test:integration # Run integration tests (requires build)
5858
- Use runes (`$state`, `$derived`, `$effect`, etc.)
5959
- Async components supported (can use `await` in markup)
6060
- Update legacy Svelte 4 syntax to runes when modifying components
61+
- For Svelte UI changes, check that tooltips remain readable and are not too wide.
6162
- Run `pnpm run check` on modified files before committing (e.g., `pnpm run check src/lib/components/MyComponent.svelte`)
6263

6364
**TypeScript:**

src/lib/components/Tooltip.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ For more information see:
5353
display: none;
5454
position: absolute;
5555
contain: content;
56-
width: min(90vw, 32rem, auto);
56+
width: max-content;
57+
max-width: min(90vw, 300px);
5758
padding: 0.25rem 0 0 0;
5859
border: none;
5960
background: transparent;

src/routes/trading-view/vaults/[vault=slug]/VaultPeriodicMetrics.svelte

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Performance metrics table for a vault across multiple lookback periods.
55
import type { VaultInfo, PeriodMetrics } from '$lib/top-vaults/schemas';
66
import type { Chain } from '$lib/helpers/chain';
77
import MetricsBox from '$lib/components/MetricsBox.svelte';
8+
import Tooltip from '$lib/components/Tooltip.svelte';
89
import {
910
formatPercent,
1011
formatPercentProfit,
@@ -50,6 +51,21 @@ Performance metrics table for a vault across multiple lookback periods.
5051
5152
// Check if net fee information is available
5253
const hasNetFees = $derived(vault.net_fees?.fee_mode != null);
54+
const netTransactionFees = $derived(
55+
[
56+
{ label: 'deposit', value: vault.net_fees?.deposit },
57+
{ label: 'withdrawal', value: vault.net_fees?.withdraw }
58+
].filter((fee): fee is { label: string; value: number } => typeof fee.value === 'number' && fee.value > 0)
59+
);
60+
const hasNetTransactionFees = $derived(netTransactionFees.length > 0);
61+
const netReturnFeeTooltip = $derived.by(() => {
62+
const fees = netTransactionFees.map((fee) => `${formatPercent(fee.value)} ${fee.label} fee`);
63+
if (fees.length === 0) return '';
64+
65+
const feeDescription = fees.length === 2 ? `${fees[0]} and ${fees[1]}` : fees[0];
66+
67+
return `Net returns include the ${feeDescription}. These one-time fees are applied when you enter or exit the vault, so they can make a short-period net return negative even when gross returns are positive.`;
68+
});
5369
5470
type RowDefinition = {
5571
label: string | (() => string);
@@ -174,6 +190,10 @@ Performance metrics table for a vault across multiple lookback periods.
174190
function getValue(period: string, field: keyof PeriodMetrics): unknown {
175191
return periodMap[period]?.[field] ?? null;
176192
}
193+
194+
function isNetReturn(row: RowDefinition): boolean {
195+
return row.field === 'cagr_net' || row.field === 'returns_net';
196+
}
177197
</script>
178198

179199
{#if vault.period_results?.length}
@@ -203,7 +223,18 @@ Performance metrics table for a vault across multiple lookback periods.
203223
<tr>
204224
<td class="label">{@html getLabel(row)}</td>
205225
{#each periodOrder as period}
206-
<td>{row.formatter(getValue(period, row.field))}</td>
226+
<td>
227+
{#if hasNetTransactionFees && isNetReturn(row)}
228+
<Tooltip>
229+
<span slot="trigger" class="net-return-with-fees">
230+
{row.formatter(getValue(period, row.field))}
231+
</span>
232+
<svelte:fragment slot="popup">{netReturnFeeTooltip}</svelte:fragment>
233+
</Tooltip>
234+
{:else}
235+
{row.formatter(getValue(period, row.field))}
236+
{/if}
237+
</td>
207238
{/each}
208239
</tr>
209240
{/each}
@@ -272,6 +303,10 @@ Performance metrics table for a vault across multiple lookback periods.
272303
}
273304
}
274305
306+
.net-return-with-fees {
307+
border-bottom: 1px dashed var(--c-text-light);
308+
}
309+
275310
.error-row {
276311
background: var(--c-warning-bg, rgba(255, 200, 0, 0.1));
277312

0 commit comments

Comments
 (0)