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
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"Bash(identify:*)",
"Bash(sips:*)",
"Bash(awk:*)",
"Bash(pnpm dlx:*)"
"Bash(pnpm dlx:*)",
"Bash(oracle ask:*)"
],
"deny": []
}
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@ next-env.d.ts

.cursor

CLAUDE.md
CLAUDE.md
FULLAUTO_CONTEXT.md
.claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { MarketIdentity, MarketIdentityFocus } from '@/components/MarketIdentity';
import { useMarkets } from '@/hooks/useMarkets';
import { useRateLabel } from '@/hooks/useRateLabel';
import { MarketAllocation } from '@/types/vaultAllocations';
import { formatBalance, formatReadable } from '@/utils/balance';
import { SupportedNetworks } from '@/utils/networks';
import { convertApyToApr } from '@/utils/rateMath';
import { formatAllocationAmount, calculateAllocationPercent } from '@/utils/vaultAllocation';
import { AllocationPieChart } from './AllocationPieChart';

Expand All @@ -20,6 +23,9 @@ export function MarketView({
vaultAssetDecimals,
chainId,
}: MarketViewProps) {
const { isAprDisplay } = useMarkets();
const { short: rateLabel } = useRateLabel();

// Sort by allocation amount (most to least)
const sortedItems = [...allocations].sort((a, b) => {
if (a.allocation > b.allocation) return -1;
Expand All @@ -33,7 +39,7 @@ export function MarketView({
<thead>
<tr className="text-xs text-secondary">
<th className="pb-3 text-left font-normal">Market</th>
<th className="pb-3 text-right font-normal">APY</th>
<th className="pb-3 text-right font-normal">{rateLabel}</th>
<th className="pb-3 text-right font-normal">Total Supply</th>
<th className="pb-3 text-right font-normal">Liquidity</th>
<th className="pb-3 text-right font-normal">Amount</th>
Expand All @@ -46,7 +52,8 @@ export function MarketView({
const { market, allocation } = item;
const percentage =
totalAllocation > 0n ? parseFloat(calculateAllocationPercent(allocation, totalAllocation)) : 0;
const supplyApy = (market.state.supplyApy * 100).toFixed(2);
const displayRate = (isAprDisplay ? convertApyToApr(market.state.supplyApy) : market.state.supplyApy);
const supplyRate = (displayRate * 100).toFixed(2);
const hasAllocation = allocation > 0n;
const totalSupply = formatReadable(
formatBalance(BigInt(market.state.supplyAssets || 0), market.loanAsset.decimals).toString()
Expand All @@ -70,9 +77,9 @@ export function MarketView({
/>
</td>

{/* APY */}
{/* APY/APR */}
<td className="p-3 text-right text-xs text-secondary whitespace-nowrap">
{supplyApy}%
{supplyRate}%
</td>

{/* Total Supply */}
Expand Down
15 changes: 11 additions & 4 deletions app/autovault/components/VaultListV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import { Spinner } from '@/components/common/Spinner';
import { useTokens } from '@/components/providers/TokenProvider';
import { TokenIcon } from '@/components/TokenIcon';
import { UserVaultV2 } from '@/data-sources/subgraph/v2-vaults';
import { useMarkets } from '@/hooks/useMarkets';
import { useRateLabel } from '@/hooks/useRateLabel';
import { formatReadable } from '@/utils/balance';
import { parseCapIdParams } from '@/utils/morpho';
import { SupportedNetworks, getNetworkImg } from '@/utils/networks';
import { convertApyToApr } from '@/utils/rateMath';

type VaultListV2Props = {
vaults: UserVaultV2[];
Expand All @@ -17,6 +20,8 @@ type VaultListV2Props = {

export function VaultListV2({ vaults, loading }: VaultListV2Props) {
const { findToken } = useTokens();
const { isAprDisplay } = useMarkets();
const { short: rateLabel } = useRateLabel();

Comment thread
antoncoding marked this conversation as resolved.
if (loading) {
return (
Expand Down Expand Up @@ -53,7 +58,7 @@ export function VaultListV2({ vaults, loading }: VaultListV2Props) {
<tr>
<th className="font-normal">ID</th>
<th className="font-normal">Asset</th>
<th className="font-normal">APY</th>
<th className="font-normal">{rateLabel}</th>
<th className="font-normal">Collaterals</th>
<th className="font-normal">Action</th>
</tr>
Expand Down Expand Up @@ -95,10 +100,12 @@ export function VaultListV2({ vaults, loading }: VaultListV2Props) {
</div>
</td>

{/* APY */}
<td data-label="APY">
{/* APY/APR */}
<td data-label={rateLabel}>
<span className="font-zen text-sm">
{vault.avgApy && (vault.avgApy * 100).toFixed(2) + '%'}
{vault.avgApy != null
? ((isAprDisplay ? convertApyToApr(vault.avgApy) : vault.avgApy) * 100).toFixed(2) + '%'
: '—'}
</span>
</td>

Expand Down
49 changes: 33 additions & 16 deletions app/market/[chainId]/[marketid]/RateChart.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable react/no-unstable-nested-components */

import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import { Card, CardHeader, CardBody } from '@heroui/react';
import { Progress } from '@heroui/react';
import {
Expand All @@ -16,6 +16,9 @@ import {
import ButtonGroup from '@/components/ButtonGroup';
import { Spinner } from '@/components/common/Spinner';
import { CHART_COLORS } from '@/constants/chartColors';
import { useMarkets } from '@/hooks/useMarkets';
import { useRateLabel } from '@/hooks/useRateLabel';
import { convertApyToApr } from '@/utils/rateMath';
import { MarketRates } from '@/utils/types';
import { TimeseriesDataPoint, Market, TimeseriesOptions } from '@/utils/types';

Expand All @@ -36,50 +39,64 @@ function RateChart({
selectedTimeRange,
handleTimeframeChange,
}: RateChartProps) {
const { isAprDisplay } = useMarkets();
const { short: rateLabel } = useRateLabel();

const [visibleLines, setVisibleLines] = useState({
supplyApy: true,
borrowApy: true,
apyAtTarget: true,
});

const getChartData = () => {
const getChartData = useMemo(() => {
if (!historicalData) return [];
const { supplyApy, borrowApy, apyAtTarget } = historicalData;

return supplyApy.map((point: TimeseriesDataPoint, index: number) => ({
x: point.x,
supplyApy: point.y,
borrowApy: borrowApy[index]?.y || 0,
apyAtTarget: apyAtTarget[index]?.y || 0,
}));
};
return supplyApy.map((point: TimeseriesDataPoint, index: number) => {
// Convert values to APR if display mode is enabled
const supplyVal = isAprDisplay ? convertApyToApr(point.y) : point.y;
const borrowVal = isAprDisplay ? convertApyToApr(borrowApy[index]?.y || 0) : (borrowApy[index]?.y || 0);
const targetVal = isAprDisplay ? convertApyToApr(apyAtTarget[index]?.y || 0) : (apyAtTarget[index]?.y || 0);

return {
x: point.x,
supplyApy: supplyVal,
borrowApy: borrowVal,
apyAtTarget: targetVal,
};
});
}, [historicalData, isAprDisplay]);

const formatPercentage = (value: number) => `${(value * 100).toFixed(2)}%`;

const getCurrentApyValue = (type: 'supply' | 'borrow') => {
return type === 'supply' ? market.state.supplyApy : market.state.borrowApy;
const apy = type === 'supply' ? market.state.supplyApy : market.state.borrowApy;
return isAprDisplay ? convertApyToApr(apy) : apy;
};

const getAverageApyValue = (type: 'supply' | 'borrow') => {
if (!historicalData) return 0;
const data = type === 'supply' ? historicalData.supplyApy : historicalData.borrowApy;
return data.length > 0
const avgApy = data.length > 0
? data.reduce((sum: number, point: TimeseriesDataPoint) => sum + point.y, 0) / data.length
: 0;
return isAprDisplay ? convertApyToApr(avgApy) : avgApy;
};

const getCurrentapyAtTargetValue = () => {
return market.state.apyAtTarget;
const apy = market.state.apyAtTarget;
return isAprDisplay ? convertApyToApr(apy) : apy;
};

const getAverageapyAtTargetValue = () => {
if (!historicalData?.apyAtTarget || historicalData.apyAtTarget.length === 0) return 0;
return (
const avgApy = (
historicalData.apyAtTarget.reduce(
(sum: number, point: TimeseriesDataPoint) => sum + point.y,
0,
) / historicalData.apyAtTarget.length
);
return isAprDisplay ? convertApyToApr(avgApy) : avgApy;
};

const getCurrentUtilizationRate = () => {
Expand Down Expand Up @@ -131,7 +148,7 @@ function RateChart({
</div>
) : (
<ResponsiveContainer width="100%" height={400} id="rate-chart">
<AreaChart data={getChartData()}>
<AreaChart data={getChartData}>
<defs>
<linearGradient id="rateChart-supplyGradient" x1="0" y1="0" x2="0" y2="1">
<stop
Expand Down Expand Up @@ -203,7 +220,7 @@ function RateChart({
<Area
type="monotone"
dataKey="supplyApy"
name="Supply APY"
name={`Supply ${rateLabel}`}
stroke={CHART_COLORS.supply.stroke}
strokeWidth={2}
fill="url(#rateChart-supplyGradient)"
Expand All @@ -213,7 +230,7 @@ function RateChart({
<Area
type="monotone"
dataKey="borrowApy"
name="Borrow APY"
name={`Borrow ${rateLabel}`}
stroke={CHART_COLORS.borrow.stroke}
strokeWidth={2}
fill="url(#rateChart-borrowGradient)"
Expand Down
31 changes: 19 additions & 12 deletions app/market/[chainId]/[marketid]/components/PositionStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { Spinner } from '@/components/common/Spinner';
import { TokenIcon } from '@/components/TokenIcon';
import { useMarketCampaigns } from '@/hooks/useMarketCampaigns';
import { useMarkets } from '@/hooks/useMarkets';
import { useRateLabel } from '@/hooks/useRateLabel';
import { formatBalance, formatReadable } from '@/utils/balance';
import { getTruncatedAssetName } from '@/utils/oracle';
import { convertApyToApr } from '@/utils/rateMath';
import { Market, MarketPosition } from '@/utils/types';
import { APYBreakdownTooltip } from 'app/markets/components/APYBreakdownTooltip';

Expand Down Expand Up @@ -51,7 +53,9 @@ export function PositionStats({
userPosition && hasPosition(userPosition) ? 'user' : 'global',
);

const { showFullRewardAPY } = useMarkets();
const { showFullRewardAPY, isAprDisplay } = useMarkets();
const { label: rateLabel } = useRateLabel({ prefix: 'Supply' });
const { label: borrowRateLabel } = useRateLabel({ prefix: 'Borrow' });
const { activeCampaigns, hasActiveRewards } = useMarketCampaigns({
marketId: market.uniqueKey,
loanTokenAddress: market.loanAsset.address,
Expand Down Expand Up @@ -140,15 +144,19 @@ export function PositionStats({
);
}

// Global stats - calculate APYs
// Global stats - calculate rates
const baseSupplyAPY = market.state.supplyApy * 100;
const baseBorrowAPY = market.state.borrowApy * 100;

// Convert to APR if display mode is enabled
const baseSupplyRate = isAprDisplay ? convertApyToApr(market.state.supplyApy) * 100 : baseSupplyAPY;
const baseBorrowRate = isAprDisplay ? convertApyToApr(market.state.borrowApy) * 100 : baseBorrowAPY;

const extraRewards = hasActiveRewards
? activeCampaigns.reduce((sum, campaign) => sum + campaign.apr, 0)
: 0;
const fullSupplyAPY = baseSupplyAPY + extraRewards;
const displaySupplyAPY = showFullRewardAPY && hasActiveRewards ? fullSupplyAPY : baseSupplyAPY;

const borrowAPY = market.state.borrowApy * 100;
const fullSupplyRate = baseSupplyRate + extraRewards;
const displaySupplyRate = showFullRewardAPY && hasActiveRewards ? fullSupplyRate : baseSupplyRate;

return (
<div className="space-y-2">
Expand Down Expand Up @@ -195,31 +203,30 @@ export function PositionStats({
</div>
</div>
<div className="flex items-center justify-between">
<span>Supply APY:</span>
<span>{rateLabel}:</span>
<div className="flex items-center gap-2">
{hasActiveRewards ? (
<APYBreakdownTooltip
baseAPY={baseSupplyAPY}
activeCampaigns={activeCampaigns}
fullAPY={fullSupplyAPY}
>
<span className="cursor-help">
{baseSupplyAPY.toFixed(2)}%
{baseSupplyRate.toFixed(2)}%
<span className="text-green-600 dark:text-green-400">
{' '}
(+{extraRewards.toFixed(2)}%)
</span>
</span>
</APYBreakdownTooltip>
) : (
<span>{displaySupplyAPY.toFixed(2)}%</span>
<span>{displaySupplyRate.toFixed(2)}%</span>
)}
</div>
</div>
<div className="flex items-center justify-between">
<span>Borrow APY:</span>
<span>{borrowRateLabel}:</span>
<div className="flex items-center gap-2">
<span>{borrowAPY.toFixed(2)}%</span>
<span>{baseBorrowRate.toFixed(2)}%</span>
</div>
</div>
</div>
Expand Down
Loading