Files
dione/AGENTS.md
Dione c573e58e0f feat: add neon color picker for wallet management
- Replace muted Tailwind palette with 16-color neon palette
- Add clickable color dot in sidebar that opens a popup swatch grid
- Allow color override for all wallets (including embedded)
- Fix getColorForWallet to respect localStorage override
- Fix duplicate renameWallet in wallets.js
- Update Dockerfile to serve ESM modules via nginx
2026-06-10 07:10:24 +00:00

5.8 KiB

cbBTC Treasury Dashboard

What it is: A single-page HTML dashboard tracking cbBTC acquisition across wallets. No build step, no JS framework, no tests — just one index.html served by nginx.

How to run

docker build -t cbBTC-dashboard . && docker run -p 8080:80 cbBTC-dashboard

Or just open index.html in a browser (without the /api/ proxy, only the Kraken BTC price chart will work).

Architecture

The codebase is split into three vanilla ESM modules plus the single-page dashboard:

Module Purpose
wallets.js WalletManager — localStorage state, chain-specific address validation, verification lifecycle
verifier.js WalletVerifier — EIP-712 typed-data signing via window.ethereum, user rejection handling
stream.js WalletStreamManager — single WebSocket with BroadcastChannel leader election, exponential backoff + jitter

File layout

File Purpose
index.html Dashboard UI. Embedded chart data, ApexCharts, TailwindCSS. Loads modules via <script type="module">.
wallets.js WalletManager — anonymous localStorage wallet state, add/remove/verify/revoke
verifier.js WalletVerifierwindow.ethereum connection + EIP-712 signature flow
stream.js WalletStreamManager — single WS, cross-tab leader election, event routing
Dockerfile 4 lines. Copies index.html and default.conf into nginx:alpine.
default.conf Reverse proxy: /api/http://192.168.1.102:8000/api/.
aave_portfolio.md / wallet_portfolio.md Reference data dumps. NOT consumed by the app.

Key facts for editing index.html

  • Data is embedded as JS constants near the top of the <script> block:
    • walletCumulData — time-series of cumulative BTC per wallet
    • dailySatsData — rolling sats/day with price info
    • walletsMetadata — wallet id → address, name, color, chain
    • walletCosts / walletBuys — cost basis per wallet
  • API endpoints (proxied through nginx):
    • GET /api/v1/prices/{symbol}/history?range=N — price history
    • GET /api/v1/portfolio/{address}/base/aave — Aave portfolio snapshots
  • BTC price fetched directly from Kraken: https://api.kraken.com/0/public/OHLC?pair=XBTUSD&interval=1440
  • Polling: setInterval every 30s with a 60s throttle on the Aave endpoint
  • Tracked wallet: 0x0c1a4a060e119f981412e323104d1c134d413dba ("penguin", Base)
  • Token decimals: cbBTC=8, WETH=18, USDC=6

Module API

Each module is loaded via <script type="module"> in index.html:

import { WalletManager } from './wallets.js';
import { WalletVerifier } from './verifier.js';
import { WalletStreamManager } from './stream.js';

const wm = new WalletManager();
wm.loadWallets();

const verifier = new WalletVerifier(wm);
const stream = new WalletStreamManager(wm);
stream.connect();

WalletManager

Method Description
loadWallets() Reads from localStorage, sanitizes (always isVerified: false)
addWallet(address, chain, nickname) Validates format, appends, persists
removeWallet(address, chain) Drops from state, persists
verifyWallet(address, chain, signature, messageData) Sets isVerified: true
revokeVerification(address, chain) Resets to unverified
findWallet(address, chain) Lookup helper
getWallets() Returns deep copy of state

WalletVerifier

Method Description
triggerWalletVerification(chain, nickname) Full flow: eth_requestAccounts → EIP-712 sign → add + verify
connect() Quick connect check (no signature)

EIP-712 domain: { name: "Anonymous Wallet Tracker", version: "1", chainId: 1 }

WalletStreamManager

Method Description
connect() Triggers BroadcastChannel leader election → opens single WS
disconnect() Closes WS + BC, cancels timers
on(event, callback) Register callback per event type
subscribeToNewWallet(wallet) Dynamic subscribe (leader → WS, follower → BC)
unsubscribeFromWallet(address, chain) Dynamic unsubscribe

Cross-tab leader election uses BroadcastChannel("dione_shared_stream") with a 200ms timeout. Leader-death broadcast triggers failover election. Reconnect uses exponential backoff (1s → 30s) with ±25% jitter.

Gotchas

  • apiBase = window.location.origin — the dashboard relies on nginx proxy to reach the backend at 192.168.1.102:8000. Running without Docker/proxy means the Aave table and price charts fail silently.
  • No linting, no type-checking, no CI. Validate changes by opening the HTML in a browser.
  • 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.

Coding Guidelines

You are an expert senior Web3 frontend engineer specializing in high-scale performance and stateless cryptographic security.

We are building the client-side code for an anonymous multi-wallet tracker. The application handles up to 100k concurrent connections, so code efficiency, memory management, and avoiding redundant network overhead are paramount.

  1. Modern TypeScript/JavaScript — clean, modular, and well-commented.
  2. No bloated third-party UI frameworks — stick to vanilla methods or standard state hooks.
  3. Production-ready error handling — bulletproof handling for wallet rejections and network drops.

After every index.html edit

Always rebuild and restart the Docker container so changes take effect:

docker build -t cbbtc-dashboard . && docker stop dione-dashboard && docker rm dione-dashboard && docker run -d --name dione-dashboard -p 8080:80 cbbtc-dashboard