diff --git a/index.html b/index.html index f599b2d..5d366a2 100644 --- a/index.html +++ b/index.html @@ -477,19 +477,20 @@ window.WalletManager = WalletManager; this._persist(); return { success: true }; } - setWalletOrder(addresses) { - const addrSet = this._wallets.map(w => w.address); - const ordered = []; - for (const addr of addresses) { - if (addrSet.includes(addr)) { - const w = this._wallets.find(x => x.address === addr); - if (w) ordered.push(w); - } - } - const remaining = this._wallets.filter(w => !ordered.includes(w)); - this._wallets = [...ordered, ...remaining]; - this._persist(); - } + setWalletOrder(keys) { + const keySet = this._wallets.map(w => w.address + ':' + w.chain); + const ordered = []; + for (const k of keys) { + if (keySet.includes(k)) { + const [addr, chain] = k.split(':'); + const w = this._wallets.find(x => x.address === addr && x.chain === chain); + if (w) ordered.push(w); + } + } + const remaining = this._wallets.filter(w => !ordered.includes(w)); + this._wallets = [...ordered, ...remaining]; + this._persist(); + } }; } })(); @@ -501,7 +502,12 @@ const orangeBrandColor = '#FF7A00'; const blueBrandColor = '#3b82f6'; 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"}, "WBTC": {"decimals": 8, "priceSymbol": "BTC"}, "WETH": {"decimals": 18, "priceSymbol": "WETH"}, "USDC": {"decimals": 6, "priceSymbol": "USDC"}}; +const TOKENS = {"cbBTC": {"decimals": 8, "priceSymbol": "BTC"}, "WBTC": {"decimals": 8, "priceSymbol": "BTC"}, "WETH": {"decimals": 18, "priceSymbol": "WETH"}, "USDC": {"decimals": 6, "priceSymbol": "USDC"}, "USDCn": {"decimals": 6, "priceSymbol": "USDC"}, "wHYPE": {"decimals": 18, "priceSymbol": "HYPE"}}; +function symbolDisplay(s) { return s === 'USDCn' ? 'USDC' : s; } + +/* Compound key for wallet identity: "{address}:{chain}" */ +function walletKey(address, chain) { return address + ':' + chain; } +function walletKeyFromAddress(address) { return walletKey(address, 'base'); } /* =================================================================== App State @@ -536,38 +542,39 @@ let _csvImportRecords = []; /* Assigned color index for dynamic wallets */ let _colorIdx = 0; -function getColorForWallet(address) { - const c = localStorage.getItem('cbbtc_color_' + address); +function getColorForWallet(address, chain) { + const key = walletKey(address, chain || 'base'); + const c = localStorage.getItem('cbbtc_color_' + key); if (c) return c; const color = WALLET_COLORS[_colorIdx % WALLET_COLORS.length]; _colorIdx++; - localStorage.setItem('cbbtc_color_' + address, color); + localStorage.setItem('cbbtc_color_' + key, color); return color; } -function setColorForWallet(address, color) { - localStorage.setItem('cbbtc_color_' + address, color); +function setColorForWallet(address, chain, color) { + localStorage.setItem('cbbtc_color_' + walletKey(address, chain || 'base'), color); renderAll(); } -function cycleWalletColor(address) { - const next = getNextColor(address); - setColorForWallet(address, next); +function cycleWalletColor(address, chain) { + const next = getNextColor(address, chain); + setColorForWallet(address, chain, next); } -function getNextColor(address) { - const current = getColorForWallet(address); +function getNextColor(address, chain) { + const current = getColorForWallet(address, chain); const idx = WALLET_COLORS.indexOf(current); const nextIdx = (idx + 1) % WALLET_COLORS.length; return WALLET_COLORS[nextIdx]; } /* Color Picker */ -let _colorPickerTargetAddr = null; +let _colorPickerTarget = null; /* {address, chain} */ -function openColorPicker(event, address) { +function openColorPicker(event, address, chain) { event.stopPropagation(); - _colorPickerTargetAddr = address; + _colorPickerTarget = { address, chain }; const overlay = document.getElementById('color-picker-overlay'); const popup = document.getElementById('color-picker-popup'); @@ -578,7 +585,7 @@ function openColorPicker(event, address) { newPopup.className = 'color-picker-popup'; newPopup.style.left = event.clientX + 'px'; newPopup.style.top = event.clientY + 'px'; - const currentColor = getColorForWallet(address); + const currentColor = getColorForWallet(address, chain); WALLET_COLORS.forEach((c, i) => { const swatch = document.createElement('div'); swatch.className = 'color-picker-swatch' + (c.toLowerCase() === currentColor.toLowerCase() ? ' selected' : ''); @@ -586,7 +593,7 @@ function openColorPicker(event, address) { swatch.style.background = c; swatch.addEventListener('click', (e) => { e.stopPropagation(); - selectColor(c); + if (_colorPickerTarget) setColorForWallet(_colorPickerTarget.address, _colorPickerTarget.chain, c); closeColorPicker(); }); newPopup.appendChild(swatch); @@ -611,21 +618,25 @@ function selectColor(color) { } } -/* Wallet filter state — stores UNCHECKED addresses. Empty = all checked. */ +/* Wallet filter state — stores UNCHECKED compound keys. Empty = all checked. */ function getWalletFilter() { - return JSON.parse(localStorage.getItem('cbbtc_ledger_wallets') || '[]'); + const raw = JSON.parse(localStorage.getItem('cbbtc_ledger_wallets') || '[]'); + /* Migrate old plain-address entries to compound keys */ + return raw.filter(k => k.includes(':')).concat( + raw.filter(k => !k.includes(':')).map(k => walletKeyFromAddress(k)) + ); } -function isWalletInLedger(address) { - return !getWalletFilter().includes(address); /* unchecked not in list => checked */ +function isWalletInLedger(address, chain) { + return !getWalletFilter().includes(walletKey(address, chain || 'base')); } -function setWalletChecked(address, checked) { +function setWalletChecked(key, checked) { let unchecked = getWalletFilter(); if (!checked) { - if (!unchecked.includes(address)) unchecked.push(address); + if (!unchecked.includes(key)) unchecked.push(key); } else { - const idx = unchecked.indexOf(address); + const idx = unchecked.indexOf(key); if (idx >= 0) unchecked.splice(idx, 1); } @@ -639,20 +650,21 @@ function setWalletChecked(address, checked) { updateDashboard(); } -function toggleWalletFilter(address) { - setWalletChecked(address, !isWalletInLedger(address)); +function toggleWalletFilter(key) { + setWalletChecked(key, !getWalletFilter().includes(key)); } -function syncWalletCheckboxes(address, checked) { - document.querySelectorAll(`.wallet-filter-toggle[data-address="${address}"]`).forEach(cb => { +function syncWalletCheckboxes(key, checked) { + document.querySelectorAll(`.wallet-filter-toggle[data-key="${key}"]`).forEach(cb => { cb.checked = checked; }); } function syncAllWalletCheckboxes() { wm.getWallets().forEach(w => { - const checked = isWalletInLedger(w.address); - document.querySelectorAll(`.wallet-filter-toggle[data-address="${w.address}"]`).forEach(cb => { + const key = walletKey(w.address, w.chain); + const checked = isWalletInLedger(w.address, w.chain); + document.querySelectorAll(`.wallet-filter-toggle[data-key="${key}"]`).forEach(cb => { cb.checked = checked; }); }); @@ -688,9 +700,9 @@ function renderSidebar() { } let html = ''; wallets.forEach((w, i) => { - const color = getColorForWallet(w.address); + const color = getColorForWallet(w.address, w.chain); const shortAddr = w.address.slice(0, 6) + '...' + w.address.slice(-4); - const syncState = walletSyncState[w.address]; + const syncState = walletSyncState[walletKey(w.address, w.chain)]; const syncBadge = syncState === 'syncing' || syncState === 'pending' ? `⏳ Syncing` : ''; @@ -737,7 +749,7 @@ function handleRenameNickname(input) { =================================================================== */ let _addWalletSelectedColor = null; let _addWalletSelectedPlatform = 'aave'; -const HYPERLIQUIDE_CHAINS = ['hyperliquide']; +const HYPEREVM_CHAINS = ['hyperevm']; function openAddWalletModal() { if (!window.ethereum) { @@ -776,7 +788,7 @@ function openAddWalletModal() { } function autoPickColor() { - const usedColors = wm.getWallets().map(w => getColorForWallet(w.address)); + const usedColors = wm.getWallets().map(w => getColorForWallet(w.address, w.chain)); for (const c of WALLET_COLORS) { if (!usedColors.includes(c)) return c; } @@ -804,9 +816,9 @@ function renderLendingPlatformSelector(chainName) { const container = document.getElementById('lending-platform-selector'); container.innerHTML = ''; - const isHyperliquide = HYPERLIQUIDE_CHAINS.includes(chainName); + const isHyperEVM = HYPEREVM_CHAINS.includes(chainName); - if (isHyperliquide) { + if (isHyperEVM) { const platform = 'hyperlend'; const logoUrl = 'https://app.hyperlend.finance/assets/header-logo-CiRKYBzy.svg'; const color = '#caeae5'; @@ -840,7 +852,7 @@ function renderLendingPlatformSelector(chainName) { } /* Reverse map: chainId (decimal) → chain name */ -const CHAIN_IDS_REVERSED = { '1': 'ethereum', '8453': 'base', '42161': 'arbitrum', '10': 'optimism', '137': 'polygon', '43114': 'avalanche', '56': 'bsc', '250': 'fantom', '999': 'hyperliquide' }; +const CHAIN_IDS_REVERSED = { '1': 'ethereum', '8453': 'base', '42161': 'arbitrum', '10': 'optimism', '137': 'polygon', '43114': 'avalanche', '56': 'bsc', '250': 'fantom', '999': 'hyperevm' }; function fetchWalletAddress() { if (!window.ethereum) { @@ -940,7 +952,7 @@ async function handleAddWallet(e) { const addResult = wm.addWallet(address, autoChain, nickname, lendingPlatform); if (!addResult.success) { errEl.textContent = addResult.error; errEl.classList.remove('hidden'); return false; } wm.verifyWallet(address, autoChain, '', { action: 'Auto-verified (non-verification chain)', walletAddress: address }); - walletSyncState[address] = 'synced'; + walletSyncState[walletKey(address, autoChain)] = 'synced'; closeAddWalletModal(); closeSidebar(); renderAll(); @@ -962,7 +974,7 @@ async function handleAddWallet(e) { closeAddWalletModal(); closeSidebar(); - walletSyncState[address] = 'syncing'; + walletSyncState[walletKey(address, chain)] = 'syncing'; /* Register the address with the backend for monitoring */ try { @@ -990,7 +1002,7 @@ async function handleAddWallet(e) { EIP-712 Verification =================================================================== */ /* Chain name → numeric chainId for EIP-712 domain */ -const CHAIN_IDS = { ethereum: 1, base: 8453, arbitrum: 42161, optimism: 10, polygon: 137, avalanche: 43114, bsc: 56, fantom: 250, hyperliquide: 999 }; +const CHAIN_IDS = { ethereum: 1, base: 8453, arbitrum: 42161, optimism: 10, polygon: 137, avalanche: 43114, bsc: 56, fantom: 250, hyperevm: 999 }; const VERIFY_TYPES = { VerifyTracking: [ { name: 'action', type: 'string' }, @@ -1064,12 +1076,13 @@ async function handleVerifyWallet(address, chain, nickname) { Delete Wallet =================================================================== */ function handleDeleteWallet(address, chain, idx) { + const key = walletKey(address, chain); wm.removeWallet(address, chain); - delete walletSyncState[address]; - delete addressSnapshots[address]; + delete walletSyncState[key]; + delete addressSnapshots[key]; /* Remove from unchecked list */ let unchecked = getWalletFilter(); - const ui = unchecked.indexOf(address); + const ui = unchecked.indexOf(key); if (ui >= 0) unchecked.splice(ui, 1); if (unchecked.length === 0) { localStorage.removeItem('cbbtc_ledger_wallets'); @@ -1100,17 +1113,18 @@ function renderWalletPills() { } let html = ''; wallets.forEach((w) => { - const color = getColorForWallet(w.address); - const syncState = walletSyncState[w.address]; + const color = getColorForWallet(w.address, w.chain); + const syncState = walletSyncState[walletKey(w.address, w.chain)]; const isSyncing = syncState === 'syncing' || syncState === 'pending'; const syncIndicator = isSyncing ? ' ' : ''; const nick = w.nickname || 'Wallet'; - const isActive = isWalletInLedger(w.address); + const isActive = isWalletInLedger(w.address, w.chain); const platformBadge = getPlatformBadge(w.lendingPlatform); - html += '