From 5ab9cb4b5c3986fb0f0e309fed077e7743e243d2 Mon Sep 17 00:00:00 2001 From: Dione Date: Wed, 10 Jun 2026 08:22:39 +0000 Subject: [PATCH] Enforce verified-only wallet monitoring - Restore persistent verification: wallets.js loadWallets() now preserves isVerified, signature, messageData from localStorage (was reset to false). Only restores verification when messageData is present (anti-tampering). - Add WalletManager.getVerifiedWallets() for EVM-only verification gating. - Remove all hardcoded embedded data (walletCumulData, walletsMetadata, walletCosts, walletBuys, dailySatsData, aaveCumulData, totalCumulData). - Derive all chart data dynamically from API snapshots. - Rewrite calculateAggregatedSeries, calculateCurrentHoldings, setupSatsCard, setupBreakdownCard to source from addressSnapshots. - Replace fetchAaveSnapshots with fetchAllWalletData (verified wallets only). - Replace pollAaveUpdate with pollVerifiedWallets polling loop. - Add inline EIP-712 verification (verifyOwnership, handleVerifyWallet, handleRevokeVerification) with MetaMask integration. - Sidebar shows verified/unverified badges per wallet with verify/revoke buttons. - Non-EVM wallets (btc, solana, bitcoin) auto-verify on add (trust-based). - Update fallback WalletManager class identically with persistent verification. --- index.html | 441 ++++++++++++++++++++++++++++++++++------------------- wallets.js | 40 +++-- 2 files changed, 313 insertions(+), 168 deletions(-) diff --git a/index.html b/index.html index 76001ab..c2ea2df 100644 --- a/index.html +++ b/index.html @@ -331,7 +331,8 @@ window.WalletManager = WalletManager; const raw = localStorage.getItem('cbbtc_tracked_wallets'); this._wallets = raw ? JSON.parse(raw) : []; if (!Array.isArray(this._wallets)) this._wallets = []; - this._wallets = this._wallets.filter(w => w && w.address && w.chain).map(w => ({ address: String(w.address), chain: String(w.chain).toLowerCase(), nickname: String(w.nickname||''), isVerified: false, signature: null, messageData: null })); + this._wallets = this._wallets.filter(w => w && w.address && w.chain).map(w => ({ address: String(w.address), chain: String(w.chain).toLowerCase(), nickname: String(w.nickname||''), isVerified: !!(w.isVerified && w.messageData), signature: w.signature ? String(w.signature) : null, messageData: w.messageData ? w.messageData : null })); + this.getVerifiedWallets = function() { return this._wallets.filter(w => ['btc','bitcoin','solana'].includes(w.chain) ? true : w.isVerified).map(w => ({...w})); }; } catch(e) { this._wallets = []; } return this._wallets; } @@ -356,13 +357,31 @@ window.WalletManager = WalletManager; return { success: true, removedAddress: address }; } findWallet(address, chain) { return this._wallets.find(w => w.address === address && w.chain === chain); } - renameWallet(address, chain, nickname) { + renameWallet(address, chain, nickname) { const w = this.findWallet(address, chain); if (!w) return; w.nickname = String(nickname).trim(); this._persist(); - } - }; + } + verifyWallet(address, chain, signature, messageData) { + const w = this.findWallet(address, chain); + if (!w) return { success: false, error: 'Wallet not found' }; + w.isVerified = true; + w.signature = String(signature); + w.messageData = messageData || null; + this._persist(); + return { success: true }; + } + revokeVerification(address, chain) { + const w = this.findWallet(address, chain); + if (!w) return { success: false, error: 'Wallet not found' }; + w.isVerified = false; + w.signature = null; + w.messageData = null; + this._persist(); + return { success: true }; + } + }; } })(); @@ -375,16 +394,6 @@ const API_BASE = window.location.origin; const WALLET_COLORS = ['#F7931A','#FF007F','#39FF14','#00FFFF','#CCFF00','#9D00FF','#FF0033','#00FFCC','#FF00FF','#007FFF','#DEFF0A','#FF5E00','#8A2BE2','#00FF66','#FF1493','#7B00FF']; const TOKENS = {"cbBTC": {"decimals": 8, "priceSymbol": "BTC"}, "WETH": {"decimals": 18, "priceSymbol": "WETH"}, "USDC": {"decimals": 6, "priceSymbol": "USDC"}}; -/* Embedded data for hardcoded wallets */ -const walletCumulData = {"penguin": [[1732057200000, 0.0], [1732143600000, 0.0], [1732230000000, 0.0], [1732316400000, 0.0], [1732402800000, 0.0], [1732489200000, 0.0], [1732575600000, 0.0], [1732662000000, 0.0], [1732748400000, 0.0], [1732834800000, 0.0], [1732921200000, 0.00045671], [1733007600000, 0.00068314], [1733094000000, 0.00103581], [1733180400000, 0.00154653], [1733266800000, 0.00203502], [1733353200000, 0.00245059], [1733439600000, 0.0027401], [1733526000000, 0.0027401], [1733612400000, 0.0027401], [1733698800000, 0.0027401], [1733785200000, 0.00331277], [1733871600000, 0.00331277], [1733958000000, 0.00352929], [1734044400000, 0.00352929], [1734130800000, 0.00437782], [1734217200000, 0.00437782], [1734303600000, 0.00437782], [1734390000000, 0.00437782], [1734476400000, 0.00437782], [1734562800000, 0.00437782], [1734649200000, 0.00437782], [1734735600000, 0.00437782], [1734822000000, 0.00483383], [1734908400000, 0.00483383], [1734994800000, 0.00483383], [1735081200000, 0.00483383], [1735167600000, 0.00483383], [1735254000000, 0.00483383], [1735340400000, 0.00688418], [1735426800000, 0.00688418], [1735513200000, 0.00688418], [1735599600000, 0.00688418], [1735686000000, 0.00688418], [1735772400000, 0.00688418], [1735858800000, 0.00688418], [1735945200000, 0.00688418], [1736031600000, 0.00688418], [1736118000000, 0.00688418], [1736204400000, 0.00688418], [1736290800000, 0.00753678], [1736377200000, 0.00753678], [1736463600000, 0.00825444], [1736550000000, 0.00825444], [1736636400000, 0.00825444], [1736722800000, 0.0084866], [1736809200000, 0.0084866], [1736895600000, 0.0084866], [1736982000000, 0.0084866], [1737068400000, 0.0084866], [1737154800000, 0.0084866], [1737241200000, 0.0084866], [1737327600000, 0.0084866], [1737414000000, 0.0084866], [1737500400000, 0.0084866], [1737586800000, 0.0088232], [1737673200000, 0.0088232], [1737759600000, 0.0088232], [1737846000000, 0.0088232], [1737932400000, 0.00947181], [1738018800000, 0.00947181], [1738105200000, 0.00987744]], "nano": [[1732057200000, 0.0], [1732143600000, 0.0], [1732230000000, 0.0], [1732316400000, 0.0], [1732402800000, 0.0], [1732489200000, 0.0], [1732575600000, 0.0], [1732662000000, 0.0], [1732748400000, 0.0], [1732834800000, 0.0], [1732921200000, 0.00045671], [1733007600000, 0.00068314]]}; -const walletsMetadata = {"nano": {"id": "nano", "address": "0x3837ea82a38daa985ee613e69f72adbe12d0aa50", "name": "Base Wallet", "color": "#58a6ff", "chain": "base"}, "penguin": {"id": "penguin", "address": "0x0c1a4a060e119f981412e323104d1c134d413dba", "name": "Base Wallet", "color": "#f7931a", "chain": "base"}, "cold": {"id": "cold", "address": "bc1qhwsm859uy7aec2h4rj3e00p2vg8nzy8je8duqz", "name": "BTC Cold Storage", "color": "#f7931a", "chain": "btc"}}; -const walletCosts = {"nano": 57143.88957364997, "penguin": 28214.47038528, "cold": 3528.97661289}; -const walletBuys = {"nano": 0.5830959900000001, "penguin": 0.29296042999999994, "cold": 0.07518186}; -const dailySatsData = [{"ts": 1732057200000, "rolling_sats": 0, "btc_val": 0, "price": 94287, "days_since": -10, "cumul_btc": 0}, {"ts": 1732143600000, "rolling_sats": 0, "btc_val": 0, "price": 98317, "days_since": -9, "cumul_btc": 0}, {"ts": 1732230000000, "rolling_sats": 0, "btc_val": 0, "price": 98892, "days_since": -8, "cumul_btc": 0}, {"ts": 1732316400000, "rolling_sats": 0, "btc_val": 0, "price": 97672, "days_since": -7, "cumul_btc": 0}, {"ts": 1732402800000, "rolling_sats": 0, "btc_val": 0, "price": 97900, "days_since": -6, "cumul_btc": 0}, {"ts": 1732489200000, "rolling_sats": 0, "btc_val": 0, "price": 93010, "days_since": -5, "cumul_btc": 0}, {"ts": 1732575600000, "rolling_sats": 0, "btc_val": 0, "price": 91965, "days_since": -4, "cumul_btc": 0}, {"ts": 1732662000000, "rolling_sats": 0, "btc_val": 0, "price": 95863, "days_since": -3, "cumul_btc": 0}, {"ts": 1732748400000, "rolling_sats": 0, "btc_val": 0, "price": 95644, "days_since": -2, "cumul_btc": 0}, {"ts": 1732834800000, "rolling_sats": 0, "btc_val": 0, "price": 97460, "days_since": -1, "cumul_btc": 0}, {"ts": 1732921200000, "rolling_sats": 4152, "btc_val": 0.00004152, "price": 96408, "days_since": 0, "cumul_btc": 0.00045671}, {"ts": 1733007600000, "rolling_sats": 5693, "btc_val": 0.00005693, "price": 97185, "days_since": 1, "cumul_btc": 0.00068314}, {"ts": 1733094000000, "rolling_sats": 7968, "btc_val": 0.00007968, "price": 95841, "days_since": 2, "cumul_btc": 0.00103581}, {"ts": 1733180400000, "rolling_sats": 11047, "btc_val": 0.00011047, "price": 95850, "days_since": 3, "cumul_btc": 0.00154653}, {"ts": 1733266800000, "rolling_sats": 13567, "btc_val": 0.00013567, "price": 98587, "days_since": 4, "cumul_btc": 0.00203502}, {"ts": 1733353200000, "rolling_sats": 15316, "btc_val": 0.00015316, "price": 96946, "days_since": 5, "cumul_btc": 0.00245059}, {"ts": 1733439600000, "rolling_sats": 16118, "btc_val": 0.00016118, "price": 96537, "days_since": 6, "cumul_btc": 0.0027401}, {"ts": 1733526000000, "rolling_sats": 16118, "btc_val": 0.00016118, "price": 96201, "days_since": 7, "cumul_btc": 0.0027401}, {"ts": 1733612400000, "rolling_sats": 16118, "btc_val": 0.00016118, "price": 95811, "days_since": 8, "cumul_btc": 0.0027401}, {"ts": 1733698800000, "rolling_sats": 16118, "btc_val": 0.00016118, "price": 97183, "days_since": 9, "cumul_btc": 0.0027401}, {"ts": 1733785200000, "rolling_sats": 19612, "btc_val": 0.00019612, "price": 96685, "days_since": 10, "cumul_btc": 0.00331277}, {"ts": 1733871600000, "rolling_sats": 19612, "btc_val": 0.00019612, "price": 97491, "days_since": 11, "cumul_btc": 0.00331277}, {"ts": 1733958000000, "rolling_sats": 20414, "btc_val": 0.00020414, "price": 96537, "days_since": 12, "cumul_btc": 0.00352929}, {"ts": 1734044400000, "rolling_sats": 20414, "btc_val": 0.00020414, "price": 96685, "days_since": 13, "cumul_btc": 0.00352929}, {"ts": 1734130800000, "rolling_sats": 22742, "btc_val": 0.00022742, "price": 97163, "days_since": 14, "cumul_btc": 0.00437782}, {"ts": 1734217200000, "rolling_sats": 22742, "btc_val": 0.00022742, "price": 97000, "days_since": 15, "cumul_btc": 0.00437782}]; -const aaveCumulData = [[1732057200000, 0], [1732143600000, 0], [1732230000000, 0], [1732316400000, 0], [1732402800000, 0], [1732489200000, 0], [1732575600000, 0], [1732662000000, 0], [1732748400000, 0], [1732834800000, 0], [1732921200000, 0], [1733007600000, 0], [1733094000000, 0], [1733180400000, 0], [1733266800000, 0], [1733353200000, 0]]; -const directCumulData = [[1732057200000, 0], [1732143600000, 0], [1732230000000, 0], [1732316400000, 0], [1732402800000, 0], [1732489200000, 0], [1732575600000, 0], [1732662000000, 0], [1732748400000, 0], [1732834800000, 0], [1732921200000, 0.00045671], [1733007600000, 0.00068314]]; -const totalCumulData = [[1732057200000, 0], [1732143600000, 0], [1732230000000, 0], [1732316400000, 0], [1732402800000, 0], [1732489200000, 0], [1732575600000, 0], [1732662000000, 0], [1732748400000, 0], [1732834800000, 0], [1732921200000, 0.00045671], [1733007600000, 0.00068314]]; - /* =================================================================== App State =================================================================== */ @@ -401,7 +410,6 @@ let tickIntervalId; /* Aave data */ let aavePriceMap = {}; -let allAaveSnapshots = []; let lastFetchMs = 0; let todayBtcPrice = null; @@ -411,21 +419,11 @@ const walletSyncState = {}; /* Per-address snapshot data, keyed by address */ const addressSnapshots = {}; -/* Which embedded wallet IDs each tracked address maps to */ -function findEmbeddedId(address) { - for (const [id, meta] of Object.entries(walletsMetadata)) { - if (meta.address.toLowerCase() === address.toLowerCase()) return id; - } - return null; -} - /* Assigned color index for dynamic wallets */ let _colorIdx = 0; function getColorForWallet(address) { const c = localStorage.getItem('cbbtc_color_' + address); if (c) return c; - const embedded = findEmbeddedId(address); - if (embedded && walletsMetadata[embedded].color) return walletsMetadata[embedded].color; const color = WALLET_COLORS[_colorIdx % WALLET_COLORS.length]; _colorIdx++; localStorage.setItem('cbbtc_color_' + address, color); @@ -582,16 +580,27 @@ function renderSidebar() { ? `⏳ Syncing` : ''; const nick = w.nickname || shortAddr; + const isEVM = !['btc', 'bitcoin', 'solana'].includes(w.chain); + const isVerified = w.isVerified; + const verifyBadge = isEVM + ? (isVerified ? '✓ Verified' : '⚠ Unverified') + : '🔒 No-Sig'; + const verifyBtn = isEVM && !isVerified + ? '' + : ''; + const revokeBtn = isVerified && isEVM + ? '' + : ''; html += '
' + '
' + '
' + '
' + '' + - '
' + shortAddr + '
' + + '
' + shortAddr + ' ' + w.chain + '' + verifyBadge + '
' + '
' + '
' + '
' + - syncBadge + + verifyBtn + revokeBtn + syncBadge + '