From e28e66b29fa6063b4a5df59a90a9b4781ef24f67 Mon Sep 17 00:00:00 2001 From: Dione Date: Wed, 10 Jun 2026 19:52:11 +0000 Subject: [PATCH] fix: scan all snapshots for oldest timestamp in fetchAllWalletData snapshotsToDaily returns results sorted newest-first, so allSnaps[0] was the newest timestamp. This caused fetchPrices to compute a near-zero date range, resulting in $0 values for all but the most recent rows. Also removed redundant refreshPrices() call from init flow (fetchAllWalletData already fetches full-range prices). Added table data flow doc to AGENTS.md. --- AGENTS.md | 16 ++++++++++++++++ index.html | 10 +++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c4e2f34..818ca03 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -104,6 +104,22 @@ Cross-tab leader election uses `BroadcastChannel("dione_shared_stream")` with a - Adding chart data? Update the embedded arrays (`walletCumulData`, etc.) — they are plain JS arrays of `[timestampMs, value]` pairs. - `wallets.js` always forces `isVerified: false` on `loadWallets()` — persisted signatures are discarded. Verification must go through `WalletVerifier`. +## Transaction Ledger Table — Data Flow + +How the table gets its rows and values: + +1. **Fetch:** `fetchAllWalletData()` → `fetchWalletAaveData(address)` hits `/api/v1/portfolio/{address}/base/aave`, returns array of snapshot events. +2. **Deduplicate:** `snapshotsToDaily(events)` collapses events to one per day (keeps latest `block_timestamp` per day). **Result is sorted newest-first.** +3. **Store:** Deduplicated snapshots land in `addressSnapshots[address]`. +4. **Prices:** `fetchPrices(symbols, oldestDateMs)` fetches `/api/v1/prices/{symbol}/history?range=N` where N = days between now and oldest snapshot. **Critical:** you must scan all snapshots for the *lowest* `block_timestamp` to compute the correct range. `snapshots[0]` is the *newest* — never assume the array is oldest-first. The `aavePriceMap` is keyed `[priceSymbol][dateStr] = closePrice`. +5. **Render:** `renderCombinedTable()` iterates `addressSnapshots`, computes row values: + - `getTokenAmount(raw, symbol)` divides by token decimals (cbBTC /1e8, WETH /1e18, USDC /1e6) + - `priceForToken(symbol, dateStr)` looks up `aavePriceMap` for the matching date (falls back to nearest prior date) + - Each row shows USD = `amount × price` for wallet (cold), collateral, and debt columns +6. **Filter:** `updateDashboard()` hides/shows rows based on wallet-type and ledger-wallet checkbox filters. + +If table rows show `$0` for non-recent dates, check that `aavePriceMap` covers the full historical range — the `fetchPrices()` call must use the *oldest* snapshot timestamp, not the newest. + ## Coding Guidelines You are an expert senior Web3 frontend engineer specializing in high-scale performance and stateless cryptographic security. diff --git a/index.html b/index.html index b4e790b..a268ea0 100644 --- a/index.html +++ b/index.html @@ -1490,7 +1490,12 @@ async function fetchAllWalletData() { await Promise.all(promises); const allSnaps = Object.values(addressSnapshots).flat(); if (allSnaps.length > 0) { - const oldestTs = new Date(allSnaps[0].block_timestamp).getTime(); + /* snapshots are sorted newest-first; find the oldest */ + let oldestTs = Infinity; + allSnaps.forEach(s => { + const t = new Date(s.block_timestamp).getTime(); + if (t < oldestTs) oldestTs = t; + }); await fetchPrices(Object.keys(TOKENS), oldestTs); } } @@ -1801,10 +1806,9 @@ async function initDashboardGrid() { const rawPrices = result.result.XXBTZUSD; btcPriceData = rawPrices.map(item => [item[0] * 1000, parseFloat(item[4])]); - /* Fetch data for all verified wallets */ + /* Fetch data for all verified wallets (already fetches full-range prices) */ await fetchAllWalletData(); - await refreshPrices(); renderCombinedTable(); const cutoff = getOldestTransactionDate();