fix(rwallet): populate yield APY stats, hide top tabs on yield view
- Fix DeFi Llama field mapping: use apyMean30d + apyPct7D (apyMean7d doesn't exist in their API) - Add apyBase, apy30d fields to YieldOpportunity type - Deduplicate rates table (best APY per protocol+chain+asset) - Hide "My Wallets / Wallet Visualizer" top tab bar on yield page - Color-code APY values, better TVL formatting (B/M) - Bump JS cache to v=11 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f9d4164f28
commit
fdf2a429f5
|
|
@ -90,7 +90,9 @@ interface YieldRate {
|
||||||
assetAddress: string;
|
assetAddress: string;
|
||||||
vaultAddress: string;
|
vaultAddress: string;
|
||||||
apy: number;
|
apy: number;
|
||||||
|
apyBase?: number;
|
||||||
apy7d?: number;
|
apy7d?: number;
|
||||||
|
apy30d?: number;
|
||||||
tvl?: number;
|
tvl?: number;
|
||||||
vaultName?: string;
|
vaultName?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -2043,6 +2045,15 @@ class FolkWalletViewer extends HTMLElement {
|
||||||
|
|
||||||
// ── Rates table ──
|
// ── Rates table ──
|
||||||
if (this.yieldRates.length > 0) {
|
if (this.yieldRates.length > 0) {
|
||||||
|
// Deduplicate: keep highest-APY entry per protocol+chain+asset
|
||||||
|
const deduped = new Map<string, YieldRate>();
|
||||||
|
const sorted = [...this.yieldRates].sort((a, b) => b.apy - a.apy);
|
||||||
|
for (const r of sorted) {
|
||||||
|
const key = `${r.protocol}:${r.chainId}:${r.asset}`;
|
||||||
|
if (!deduped.has(key)) deduped.set(key, r);
|
||||||
|
}
|
||||||
|
const rates = [...deduped.values()];
|
||||||
|
|
||||||
html += `<div class="yield-section">
|
html += `<div class="yield-section">
|
||||||
<h3 class="yield-section-title">Available Rates</h3>
|
<h3 class="yield-section-title">Available Rates</h3>
|
||||||
<table class="balance-table">
|
<table class="balance-table">
|
||||||
|
|
@ -2051,20 +2062,21 @@ class FolkWalletViewer extends HTMLElement {
|
||||||
<th>Chain</th>
|
<th>Chain</th>
|
||||||
<th>Asset</th>
|
<th>Asset</th>
|
||||||
<th class="amount-cell">APY</th>
|
<th class="amount-cell">APY</th>
|
||||||
<th class="amount-cell">7d Avg</th>
|
<th class="amount-cell">30d Avg</th>
|
||||||
<th class="amount-cell">TVL</th>
|
<th class="amount-cell">TVL</th>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody>`;
|
<tbody>`;
|
||||||
const sorted = [...this.yieldRates].sort((a, b) => b.apy - a.apy);
|
for (const r of rates) {
|
||||||
for (const r of sorted) {
|
|
||||||
const protocolLabel = r.protocol === "aave-v3" ? "Aave V3" : (r.vaultName || "Morpho");
|
const protocolLabel = r.protocol === "aave-v3" ? "Aave V3" : (r.vaultName || "Morpho");
|
||||||
|
const chainColor = r.chainId === "1" ? "#627eea" : "#0052ff";
|
||||||
|
const apyColor = r.apy >= 3 ? "var(--rs-success)" : r.apy >= 1.5 ? "#ffa726" : "var(--rs-text-secondary)";
|
||||||
html += `<tr>
|
html += `<tr>
|
||||||
<td><span class="yield-protocol-badge ${r.protocol}">${protocolLabel}</span></td>
|
<td><span class="yield-protocol-badge ${r.protocol}">${protocolLabel}</span></td>
|
||||||
<td>${chainNames[r.chainId] || r.chainId}</td>
|
<td><span class="yield-chain-badge" style="background:${chainColor}22;color:${chainColor}">${chainNames[r.chainId] || r.chainId}</span></td>
|
||||||
<td><span class="token-symbol">${this.esc(r.asset)}</span></td>
|
<td><span class="token-symbol">${this.esc(r.asset)}</span></td>
|
||||||
<td class="amount-cell" style="color:var(--rs-success);font-weight:600">${r.apy.toFixed(2)}%</td>
|
<td class="amount-cell" style="color:${apyColor};font-weight:700">${r.apy.toFixed(2)}%</td>
|
||||||
<td class="amount-cell">${r.apy7d ? r.apy7d.toFixed(2) + "%" : "-"}</td>
|
<td class="amount-cell">${r.apy30d != null ? r.apy30d.toFixed(2) + "%" : "-"}</td>
|
||||||
<td class="amount-cell">${r.tvl ? "$" + (r.tvl / 1e6).toFixed(1) + "M" : "-"}</td>
|
<td class="amount-cell">${r.tvl ? "$" + (r.tvl >= 1e9 ? (r.tvl / 1e9).toFixed(1) + "B" : (r.tvl / 1e6).toFixed(0) + "M") : "-"}</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
}
|
}
|
||||||
html += `</tbody></table></div>`;
|
html += `</tbody></table></div>`;
|
||||||
|
|
@ -2329,11 +2341,12 @@ class FolkWalletViewer extends HTMLElement {
|
||||||
|
|
||||||
private render() {
|
private render() {
|
||||||
const isMyWallets = this.topTab === "my-wallets" && this.isAuthenticated;
|
const isMyWallets = this.topTab === "my-wallets" && this.isAuthenticated;
|
||||||
|
const isYield = this.activeView === "yield";
|
||||||
|
|
||||||
this.shadow.innerHTML = `
|
this.shadow.innerHTML = `
|
||||||
${this.renderStyles()}
|
${this.renderStyles()}
|
||||||
${this.isAuthenticated ? this.renderTopTabBar() : ''}
|
${this.isAuthenticated && !isYield ? this.renderTopTabBar() : ''}
|
||||||
${isMyWallets ? this.renderMyWalletsTab() : this.renderVisualizerTab()}
|
${isMyWallets && !isYield ? this.renderMyWalletsTab() : this.renderVisualizerTab()}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Top tab listeners
|
// Top tab listeners
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ export interface YieldOpportunity {
|
||||||
vaultAddress: string; // aToken for Aave, vault for Morpho
|
vaultAddress: string; // aToken for Aave, vault for Morpho
|
||||||
apy: number;
|
apy: number;
|
||||||
apy7d?: number;
|
apy7d?: number;
|
||||||
|
apy30d?: number;
|
||||||
|
apyBase?: number;
|
||||||
tvl?: number;
|
tvl?: number;
|
||||||
poolId?: string; // DeFi Llama pool ID
|
poolId?: string; // DeFi Llama pool ID
|
||||||
vaultName?: string;
|
vaultName?: string;
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,10 @@ interface LlamaPool {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
tvlUsd: number;
|
tvlUsd: number;
|
||||||
apy: number;
|
apy: number;
|
||||||
apyMean7d?: number;
|
apyBase?: number;
|
||||||
|
apyReward?: number | null;
|
||||||
|
apyPct7D?: number;
|
||||||
|
apyMean30d?: number;
|
||||||
underlyingTokens?: string[];
|
underlyingTokens?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,7 +122,9 @@ async function fetchDefiLlamaRates(): Promise<YieldOpportunity[]> {
|
||||||
assetAddress,
|
assetAddress,
|
||||||
vaultAddress,
|
vaultAddress,
|
||||||
apy: pool.apy || 0,
|
apy: pool.apy || 0,
|
||||||
apy7d: pool.apyMean7d,
|
apyBase: pool.apyBase ?? undefined,
|
||||||
|
apy7d: pool.apyPct7D != null ? pool.apy + pool.apyPct7D : undefined,
|
||||||
|
apy30d: pool.apyMean30d ?? undefined,
|
||||||
tvl: pool.tvlUsd,
|
tvl: pool.tvlUsd,
|
||||||
poolId: pool.pool,
|
poolId: pool.pool,
|
||||||
vaultName: protocol === "morpho-blue"
|
vaultName: protocol === "morpho-blue"
|
||||||
|
|
|
||||||
|
|
@ -1014,7 +1014,7 @@ function renderWallet(spaceSlug: string, initialView?: string) {
|
||||||
modules: getModuleInfoList(),
|
modules: getModuleInfoList(),
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
body: `<folk-wallet-viewer${viewAttr}></folk-wallet-viewer>`,
|
body: `<folk-wallet-viewer${viewAttr}></folk-wallet-viewer>`,
|
||||||
scripts: `<script type="module" src="/modules/rwallet/folk-wallet-viewer.js?v=10"></script>`,
|
scripts: `<script type="module" src="/modules/rwallet/folk-wallet-viewer.js?v=11"></script>`,
|
||||||
styles: `<link rel="stylesheet" href="/modules/rwallet/wallet.css">`,
|
styles: `<link rel="stylesheet" href="/modules/rwallet/wallet.css">`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue