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
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pnpm run test:integration # Run integration tests (requires build)
- Use runes (`$state`, `$derived`, `$effect`, etc.)
- Async components supported (can use `await` in markup)
- Update legacy Svelte 4 syntax to runes when modifying components
- For Svelte UI changes, check that tooltips remain readable and are not too wide.
- Run `pnpm run check` on modified files before committing (e.g., `pnpm run check src/lib/components/MyComponent.svelte`)

**TypeScript:**
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Tooltip.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ For more information see:
display: none;
position: absolute;
contain: content;
width: min(90vw, 32rem, auto);
width: max-content;
max-width: min(90vw, 300px);
padding: 0.25rem 0 0 0;
border: none;
background: transparent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Performance metrics table for a vault across multiple lookback periods.
import type { VaultInfo, PeriodMetrics } from '$lib/top-vaults/schemas';
import type { Chain } from '$lib/helpers/chain';
import MetricsBox from '$lib/components/MetricsBox.svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
import {
formatPercent,
formatPercentProfit,
Expand Down Expand Up @@ -50,6 +51,21 @@ Performance metrics table for a vault across multiple lookback periods.

// Check if net fee information is available
const hasNetFees = $derived(vault.net_fees?.fee_mode != null);
const netTransactionFees = $derived(
[
{ label: 'deposit', value: vault.net_fees?.deposit },
{ label: 'withdrawal', value: vault.net_fees?.withdraw }
].filter((fee): fee is { label: string; value: number } => typeof fee.value === 'number' && fee.value > 0)
);
const hasNetTransactionFees = $derived(netTransactionFees.length > 0);
const netReturnFeeTooltip = $derived.by(() => {
const fees = netTransactionFees.map((fee) => `${formatPercent(fee.value)} ${fee.label} fee`);
if (fees.length === 0) return '';

const feeDescription = fees.length === 2 ? `${fees[0]} and ${fees[1]}` : fees[0];

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.`;
});

type RowDefinition = {
label: string | (() => string);
Expand Down Expand Up @@ -174,6 +190,10 @@ Performance metrics table for a vault across multiple lookback periods.
function getValue(period: string, field: keyof PeriodMetrics): unknown {
return periodMap[period]?.[field] ?? null;
}

function isNetReturn(row: RowDefinition): boolean {
return row.field === 'cagr_net' || row.field === 'returns_net';
}
</script>

{#if vault.period_results?.length}
Expand Down Expand Up @@ -203,7 +223,18 @@ Performance metrics table for a vault across multiple lookback periods.
<tr>
<td class="label">{@html getLabel(row)}</td>
{#each periodOrder as period}
<td>{row.formatter(getValue(period, row.field))}</td>
<td>
{#if hasNetTransactionFees && isNetReturn(row)}
<Tooltip>
<span slot="trigger" class="net-return-with-fees">
{row.formatter(getValue(period, row.field))}
</span>
<svelte:fragment slot="popup">{netReturnFeeTooltip}</svelte:fragment>
</Tooltip>
{:else}
{row.formatter(getValue(period, row.field))}
{/if}
</td>
{/each}
</tr>
{/each}
Expand Down Expand Up @@ -272,6 +303,10 @@ Performance metrics table for a vault across multiple lookback periods.
}
}

.net-return-with-fees {
border-bottom: 1px dashed var(--c-text-light);
}

.error-row {
background: var(--c-warning-bg, rgba(255, 200, 0, 0.1));

Expand Down
Loading