- 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
123 lines
5.8 KiB
Markdown
123 lines
5.8 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
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` | `WalletVerifier` — `window.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`:
|
|
|
|
```js
|
|
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:
|
|
```bash
|
|
docker build -t cbbtc-dashboard . && docker stop dione-dashboard && docker rm dione-dashboard && docker run -d --name dione-dashboard -p 8080:80 cbbtc-dashboard
|
|
```
|