From a61e0b0457364404b99a5b3def33d58ee103d227 Mon Sep 17 00:00:00 2001 From: Dione Date: Sat, 6 Jun 2026 12:16:41 +0000 Subject: [PATCH] feat: multi-wallet architecture with localStorage state, EIP-712 verification, cross-tab WS leadership - wallets.js: WalletManager state management with chain validation - verifier.js: WalletVerifier EIP-712 signing via window.ethereum - stream.js: WalletStreamManager with BroadcastChannel leader election, backoff + jitter - AGENTS.md: updated with coding guidelines --- AGENTS.md | 58 +++ Dockerfile | 4 + aave_portfolio.md | 493 +++++++++++++++++++ default.conf | 12 + index.html | 1045 +++++++++++++++++++++++++++++++++++++++++ package.json | 5 + stream.js | 489 +++++++++++++++++++ test/README.md | 116 +++++ test/test_setup.mjs | 4 + test/test_stream.js | 151 ++++++ test/test_tabs.js | 122 +++++ test/test_verifier.js | 76 +++ test/test_wallets.js | 80 ++++ verifier.js | 292 ++++++++++++ wallet_portfolio.md | 724 ++++++++++++++++++++++++++++ wallet_test.html | 43 ++ wallets.js | 323 +++++++++++++ 17 files changed, 4037 insertions(+) create mode 100644 AGENTS.md create mode 100644 Dockerfile create mode 100644 aave_portfolio.md create mode 100644 default.conf create mode 100644 index.html create mode 100644 package.json create mode 100644 stream.js create mode 100644 test/README.md create mode 100644 test/test_setup.mjs create mode 100644 test/test_stream.js create mode 100644 test/test_tabs.js create mode 100644 test/test_verifier.js create mode 100644 test/test_wallets.js create mode 100644 verifier.js create mode 100644 wallet_portfolio.md create mode 100644 wallet_test.html create mode 100644 wallets.js diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3007da9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,58 @@ +# 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). + +## File layout + +| File | Purpose | +|---|---| +| `index.html` | Everything. Frontend, embedded chart data, inline JS logic. Edit here. | +| `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 ` + + + + + +
+
+
+

Treasury Intelligence

+

cbBTC Acquisition Core

+
+
+
Tracked Wallets
+
+ + + +
+
+
+ +
+
+
Current Holdings
+
0.677555 BTC
+
$Calculating...
+
+
+
Total PnL
+
$--.--
+
--.--%
+
+
+
Avg Buy Price
+
$93,444
+
Cost Basis
+
+
+
Total Invested
+
$88,887.34
+
340 Transactions
+
+
+ +
+ +
+
+
+
+ BTC Price +
+ + Real-Time +
+
+

$Loading...

+
+
+ --.--% +

Since --

+
+
+
+
+ + +
+
+
+ BTC Treasury Growth +

0.677555 BTC

+
+
+ NET HELD +

Cumulative Acquisition

+
+
+
+
+ + +
+
+
+ Avg BTC Acquired/Day +

-- sats

+
+
+ 30D AVG +

Daily Acquisition

+
+
+
+
+ + +
+
+
+ Holdings Breakdown +

0.677555 BTC

+
+
+ COMPOSITION +

Direct + Aave

+
+
+
+
+
+ + +
+
+
+
+

Transaction Ledger

+
+
+
+
+ + + + +
+
+
+
+ + + +
+
+
+ Archive +
+
+
+ + + + + + + + + + + + + + +
TimestampWalletCold StorageCollateralBorrowsHash
Loading snapshots...
+
+
+
+ + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..1d98918 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "name": "dione", + "type": "module", + "private": true +} diff --git a/stream.js b/stream.js new file mode 100644 index 0000000..076b486 --- /dev/null +++ b/stream.js @@ -0,0 +1,489 @@ +/** + * WalletStreamManager — Single WebSocket, multiplexed wallet subscriptions. + * Cross-tab leader election via BroadcastChannel. + * + * One leader owns the physical WebSocket; followers relay + * subscriptions and receive broadcasts through BroadcastChannel. + * Automatic failover when the leader tab closes. + * + * Usage: + * import { WalletStreamManager } from './stream.js'; + * const stream = new WalletStreamManager(walletManagerInstance); + * stream.connect(); + */ + +import { WalletManager } from './wallets.js'; + +/* ------------------------------------------------------------------ */ +/* Connection Config */ +/* ------------------------------------------------------------------ */ + +const WS_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/ws/portfolio`; +const CHANNEL_NAME = 'cbBTC-portfolio-sync'; +const HEARTBEAT_INTERVAL = 200; +const HEARTBEAT_KEY = 'cbbtc_leadership'; + +const BASE_DELAY = 1_000; +const MAX_DELAY = 30_000; +const JITTER_RATIO = 0.25; + +const WS_CONNECTING = 0; +const WS_OPEN = 1; +const WS_CLOSING = 2; +const WS_CLOSED = 3; + +/* ------------------------------------------------------------------ */ +/* WalletStreamManager */ +/* ------------------------------------------------------------------ */ + +export class WalletStreamManager { + constructor(walletManager) { + if (!(walletManager instanceof WalletManager)) { + throw new TypeError('WalletStreamManager requires a WalletManager instance.'); + } + + /** @type {WalletManager} */ + this._wm = walletManager; + + /** @type {WebSocket|null} */ + this._ws = null; + this._state = WS_CLOSED; + this._retryCount = 0; + this._reconnectTimer = null; + this._listeners = new Map(); + this._messageBuffer = []; + + /* Cross-tab state */ + /** @type {boolean} */ + this._isLeader = false; + + /** @type {BroadcastChannel|null} */ + this._channel = null; + + /** @type {ReturnType|null} */ + this._heartbeatTimer = null; + + /** @type {boolean} */ + this._wasIntentionalClose = false; + + /** @type {boolean} */ + this._wasConnectedBefore = false; + } + + /* ---------------------------------------------------------------- */ + /* Connection Lifecycle */ + /* ---------------------------------------------------------------- */ + + connect() { + if (this._state === WS_OPEN || this._state === WS_CONNECTING) { + return; + } + + /* Cross-tab leadership — only run on initial boot, not reconnect */ + this._initBroadcastChannel(); + if (!this._wasConnectedBefore) { + this._checkLeadership(); + this._wasConnectedBefore = true; + } + + /* If leader, open WebSocket and start heartbeats */ + if (this._isLeader) { + this._state = WS_CONNECTING; + this._wasIntentionalClose = false; + + this._ws = new WebSocket(WS_URL); + this._ws.binaryType = 'arraybuffer'; + + this._ws.onopen = () => { + this._state = WS_OPEN; + this._retryCount = 0; + this._sendSubscription(); + this._notify('connected', { url: WS_URL }); + }; + + this._ws.onmessage = (event) => { + const data = this._parseSafe(event.data); + if (!data) return; + /* Only leader broadcasts to other tabs */ + if (this._isLeader && this._channel) { + this._channel.postMessage(JSON.stringify(data)); + } + this._route(data); + }; + + this._ws.onerror = () => { + /* handled by onclose */ + }; + + this._ws.onclose = (evt) => { + this._state = WS_CLOSED; + + if (!this._wasIntentionalClose) { + this._scheduleReconnect(); + } + }; + + this._startHeartbeats(); + } else { + /* Follower — no WebSocket */ + this._state = WS_CLOSED; + } + } + + disconnect() { + this._wasIntentionalClose = true; + + /* Broadcast LEADER_DEATH before channel close so followers can elect a new leader */ + if (this._isLeader && this._channel) { + try { + this._channel.postMessage(JSON.stringify({ type: 'LEADER_DEATH' })); + } catch { /* channel already closed */ } + } + + if (this._reconnectTimer) { + clearTimeout(this._reconnectTimer); + this._reconnectTimer = null; + } + + if (this._heartbeatTimer) { + clearInterval(this._heartbeatTimer); + this._heartbeatTimer = null; + } + + if (this._channel) { + this._channel.close(); + this._channel = null; + } + + if (this._ws) { + this._ws.onclose = null; + try { this._ws.close(); } catch { /* already closed */ } + this._ws = null; + } + + /* Release leadership */ + if (this._isLeader) { + try { localStorage.removeItem(HEARTBEAT_KEY); } catch {} + this._isLeader = false; + } + + this._state = WS_CLOSED; + } + + /** + * Broadcast LEADER_DEATH when this tab is closing and it holds leadership. + * Used by the test to simulate the unload event. + */ + _handleExit() { + if (this._isLeader && this._channel) { + try { + this._channel.postMessage(JSON.stringify({ type: 'LEADER_DEATH' })); + } catch { /* channel already closed */ } + } + } + + /* ---------------------------------------------------------------- */ + /* Subscriptions */ + /* ---------------------------------------------------------------- */ + + subscribeToNewWallet(wallet) { + /* If follower, proxy the request to the leader */ + if (!this._isLeader && this._channel) { + this._channel.postMessage(JSON.stringify({ + type: 'SUBSCRIBE_REQUEST', + wallet + })); + return; + } + + /* Leader handles directly */ + if (this._state !== WS_OPEN || !this._ws) return; + + if (!this._wm.findWallet(wallet.address, wallet.chain)) { + const result = this._wm.addWallet(wallet.address, wallet.chain); + if (result.success) { + this._subscribeWallet(wallet); + } + } + } + + unsubscribeFromWallet(address, chain) { + if (!this._isLeader && this._channel) { + this._channel.postMessage(JSON.stringify({ + type: 'UNSUBSCRIBE_REQUEST', + wallet: { address, chain } + })); + return; + } + + if (this._state !== WS_OPEN || !this._ws) return; + this._send({ + action: 'unsubscribe_wallet', + wallet: { address, chain }, + }); + } + + /* ---------------------------------------------------------------- */ + /* Callback Registration */ + /* ---------------------------------------------------------------- */ + + on(event, callback) { + if (!this._listeners.has(event)) { + this._listeners.set(event, new Set()); + } + const set = this._listeners.get(event); + set.add(callback); + return () => set.delete(callback); + } + + /* ---------------------------------------------------------------- */ + /* Internal helpers */ + /* ---------------------------------------------------------------- */ + + _sendSubscription() { + const wallets = this._wm.getWallets(); + this._send({ + action: 'subscribe', + subscriptions: wallets.map((w) => ({ + address: w.address, + chain: w.chain, + signature: w.signature ?? null, + messageData: w.messageData ?? null, + })), + }); + } + + _subscribeWallet(wallet) { + this._send({ + action: 'subscribe_wallet', + wallet: { + address: wallet.address, + chain: wallet.chain, + signature: wallet.signature ?? null, + messageData: wallet.messageData ?? null, + }, + }); + } + + _parseSafe(raw) { + if (typeof raw === 'string') { + try { return JSON.parse(raw); } catch { return null; } + } + if (typeof raw === 'object' && raw !== null) return raw; + return null; + } + + _route(data) { + const type = data?.event ?? data?.type ?? 'message'; + + const typeCallbacks = this._listeners.get(type); + if (typeCallbacks) { + typeCallbacks.forEach(fn => { + try { fn(data); } catch (err) { + console.error(`Callback error for event "${type}":`, err); + } + }); + } + + const allCallbacks = this._listeners.get('message'); + if (allCallbacks) { + allCallbacks.forEach(fn => { + try { fn(data); } catch (err) { + console.error('Callback error for "message":', err); + } + }); + } + } + + _notify(event, payload) { + this._route({ event, ...payload }); + } + + _send(payload) { + try { + this._ws.send(JSON.stringify(payload)); + } catch { + /* ws closing/closed */ + } + } + + /* ---------------------------------------------------------------- */ + /* Reconnect + Backoff */ + /* ---------------------------------------------------------------- */ + + _scheduleReconnect() { + if (this._reconnectTimer) { + clearTimeout(this._reconnectTimer); + this._reconnectTimer = null; + } + + const delay = this._jitteredBackoff(this._retryCount); + this._retryCount++; + + this._reconnectTimer = setTimeout(() => { + this._reconnectTimer = null; + this.connect(); + }, delay); + } + + _jitteredBackoff(attempt) { + const base = Math.min(BASE_DELAY * (1 << attempt), MAX_DELAY); + const jitter = base * JITTER_RATIO * (Math.random() * 2 - 1); + return Math.max(100, Math.round(base + jitter)); + } + + /* ---------------------------------------------------------------- */ + /* Leader Election & Heartbeat */ + /* ------------------------------------------------------------------ */ + + _initBroadcastChannel() { + if (this._channel) return; + + /* BroadcastChannel may not be available (Node.js / non-browser) */ + /* eslint-disable-next-line no-undef */ + if (typeof BroadcastChannel === 'undefined') return; + + this._channel = new BroadcastChannel(CHANNEL_NAME); + + this._channel.onmessage = (ev) => { + this._handleBroadcastMessage(ev.data); + }; + } + + _handleBroadcastMessage(raw) { + const data = this._parseSafe(raw); + if (!data) return; + + switch (data.type) { + case 'LEADER_ELECTION': + /* Someone else is leader — we step down */ + if (this._isLeader) { + this._isLeader = false; + if (this._ws) { + try { this._ws.close(); } catch {} + this._ws = null; + } + } + break; + + case 'LEADER_DEATH': + /* Leader tab closed — we hold emergency election */ + if (this._isLeader) { + this._notify('disconnected', { code: null, reason: 'leader death' }); + } else { + /* Become the new leader — claim leadership immediately */ + this._isLeader = true; + try { localStorage.removeItem(HEARTBEAT_KEY); } catch {} + try { this._setHeartbeat(); } catch {} + this._state = WS_CONNECTING; + this._wasIntentionalClose = false; + /* Start heartbeats before opening socket */ + this._startHeartbeats(); + this._ws = new WebSocket(WS_URL); + this._ws.binaryType = 'arraybuffer'; + this._ws.onopen = () => { + this._state = WS_OPEN; + this._sendSubscription(); + }; + this._ws.onclose = () => { + this._state = WS_CLOSED; + }; + } + break; + + case 'SUBSCRIBE_REQUEST': + if (this._isLeader) { + const { wallet } = data; + if (wallet) this._subscribeWallet(wallet); + } + break; + + case 'UNSUBSCRIBE_REQUEST': + if (this._isLeader) { + const { wallet: uw } = data; + if (uw) { + this._wm.removeWallet(uw.address, uw.chain); + this._send({ + action: 'unsubscribe_wallet', + wallet: { address: uw.address, chain: uw.chain }, + }); + } + } + break; + + default: + /* Raw server data broadcast from leader → relay locally */ + this._route(data); + break; + } + } + + _checkLeadership() { + const stored = localStorage.getItem(HEARTBEAT_KEY); + + if (stored) { + /* Heartbeat from another tab — we follow */ + this._isLeader = false; + } else { + /* No one alive — claim leadership */ + this._isLeader = true; + try { + this._setHeartbeat(); + } catch {} + if (this._channel) { + this._channel.postMessage(JSON.stringify({ type: 'LEADER_ELECTION', timestamp: Date.now() })); + } + } + + /* Start heartbeat to keep leadership alive */ + this._startHeartbeats(); + } + + _setHeartbeat() { + localStorage.setItem(HEARTBEAT_KEY, JSON.stringify({ + timestamp: Date.now(), + id: `${Date.now()}` + })); + } + + _startHeartbeats() { + if (this._heartbeatTimer) return; + + this._heartbeatTimer = setInterval(() => { + if (this._isLeader) { + this._setHeartbeat(); + } + }, HEARTBEAT_INTERVAL); + } + + _handleLeaderDeath() { + if (this._channel) { + this._channel.postMessage(JSON.stringify({ type: 'LEADER_DEATH' })); + } + } + + /* ---------------------------------------------------------------- */ + /* Public Accessors */ + /* ------------------------------------------------------------------ */ + + get connectionState() { + if (this._state === WS_CONNECTING) return 'CONNECTING'; + if (this._state === WS_OPEN) return 'OPEN'; + if (this._state === WS_CLOSING) return 'CLOSING'; + return 'CLOSED'; + } + + get isConnected() { + return this._state === WS_OPEN; + } + + get isLeader() { + return this._isLeader; + } + + get ws() { + return this._ws; + } +} + +export default WalletStreamManager; diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..8cba5af --- /dev/null +++ b/test/README.md @@ -0,0 +1,116 @@ +# cbBTC Dashboard Test Suite + +The test suite is organized under the `/test` folder. All tests run in Node.js with ES modules and mock browser APIs (`localStorage`, `WebSocket`, `BroadcastChannel`, `window.ethereum`). The setup file `test/test_setup.mjs` provides the global `location` object that `stream.js` requires at import time. + +## Files + +| File | Targets | Requires | How to run | +|---|---|---|---| +| `test/test_wallets.js` | **WalletManager** — CRUD, persistence, security | None | `node --experimental-modules test/test_wallets.js` | +| `test/test_stream.js` | **WalletStreamManager** — WebSocket, backoff, routing | `test/test_setup.mjs` | `node --experimental-modules --import ./test/test_setup.mjs ./test/test_stream.js` | +| `test/test_tabs.js` | **WalletStreamManager** — multi-tab leader election, failover | `test/test_setup.mjs` | `node --experimental-modules --import ./test/test_setup.mjs ./test/test_tabs.js` | +| `test/test_verifier.js` | **WalletVerifier** — Web3 provider error handling | None | `node --experimental-modules test/test_verifier.js` | +| `test/test_setup.mjs` | Provides `globalThis.location` for `stream.js` | — | Imported via `--import` | + +## What Can Be Tested — by Module + +### 1. WalletManager (`test_wallets.js`) + +The `WalletManager` class manages tracked wallets in `localStorage`. The test suite covers: + +**Address Validation** +- Rejection of invalid address formats per chain (EVM, Bitcoin, Solana). +- Acceptance of valid addresses for EVM (`0x...`), Bitcoin legacy (`1...`), and Solana base58 patterns. + +**CRUD (Create/Read/Update/Delete)** +- `addWallet()` — adds wallets, persists to storage, returns success/error. +- `getWallets()` — returns deep copies of tracked wallets. +- `findWallet()` — returns wallet object or `undefined`. +- `removeWallet()` — splices wallet from state and persists. + +**Duplicate Guard** +- Same `address` + `chain` pair is rejected on second `addWallet()` call. + +**Verification Lifecycle** +- Wallets start with `isVerified: false`. +- `verifyWallet()` sets the flag to `true` and stores the signature. +- `revokeVerification()` resets the flag and clears the signature. + +**Security: localStorage Tampering** +- `loadWallets()` always resets `isVerified` to `false` on restart, regardless of what was written to storage. This prevents a user from forging verification by editing `localStorage` directly. + +### 2. WalletVerifier (`test_verifier.js`) + +The `WalletVerifier` class handles Web3 provider interactions. The test suite covers error-path cases: + +**Missing Extension** +- When `window.ethereum` is undefined, verification fails with `"No Web3 wallet extension found"`. + +**User Rejection (Account Request)** +- When the user denies `eth_requestAccounts` (EIP-1193 error 4001), verification cancels with `"Verification cancelled"`. + +**Signature Rejection** +- When the user denies the typed-data signature request, the error is mapped to `"rejected"`. + +### 3. WalletStreamManager — Core (`test_stream.js`) + +The `WalletStreamManager` class manages the WebSocket connection, event routing, and reconnect logic with exponential backoff. The test suite covers: + +**Multiplexed Subscription** +- On connect, the manager sends a single `subscribe` frame containing all tracked wallets (from `WalletManager`). + +**Event Routing** +- Incoming server messages are parsed and dispatched to registered `on('balance_update', ...)` callbacks. +- Events route by `data.type` (or `data.event`, or fallback `'message'`). + +**Callback Error Isolation** +- If one registered callback throws, remaining callbacks for the same event still execute. Exceptions are caught and logged but never propagate. + +**Dynamic Subscription** +- `subscribeToNewWallet()` sends an immediate `subscribe_wallet` frame without cycling the connection. + +**Exponential Backoff + Jitter** +- On network drop (`simulateNetworkDrop()`), the manager retries connecting. The delay follows `BASE_DELAY * 2^attempt` with ±25% jitter, capped at `MAX_DELAY` (30s). +- Tests speed up time by overriding `setTimeout` to fire instantly. + +### 4. WalletStreamManager — Multi-Tab (`test_tabs.js`) + +The leader/follower election and cross-tab synchronization system relies on `BroadcastChannel` and `localStorage` heartbeats. The test suite covers: + +**Leader Election** +- First tab to connect finds no heartbeat in `localStorage`, claims leadership, opens the WebSocket, and starts heartbeats. +- Second tab finds the heartbeat, assigns itself as a follower, and avoids opening a second WebSocket. + +**Cross-Tab Event Broadcasting** +- Leader receives server data on the WebSocket and posts it to `BroadcastChannel`. +- Followers receive the broadcast and dispatch it to their local listeners. + +**Proxy Subscription Forwarding** +- When a follower calls `subscribeToNewWallet()`, the request is sent through `BroadcastChannel` to the leader, which opens the actual network subscription. + +**Automatic Failover** +- When the leader tab closes (`disconnect()`), it broadcasts `LEADER_DEATH` before closing the channel. +- Followers detect the signal, claim leadership, remove the old heartbeat, write a new one, and open their own WebSocket connection. + +## Mock Infrastructure + +All tests replace browser APIs with in-memory equivalents: + +- **localStorage** — plain object (`mockStorage`) with `getItem`, `setItem`, `removeItem`, `clear`. +- **setTimeout** — overridden to fire callbacks after 1ms instead of the specified delay, so backoff/retry logic runs instantly. +- **WebSocket** — `MockWebSocket` auto-opens after 2ms, exposes `simulateServerPush()` and `simulateNetworkDrop()` helpers. +- **BroadcastChannel** — `MockBroadcastChannel` delivers messages synchronously to all other registered channels, simulating cross-tab delivery. + +## Extending the Test Suite + +To add a new test: + +1. Add a `describe`/`it` block or inline `assert` inside the relevant `test_.js` file. +2. For stream-related tests, ensure the file is imported via `--import ./test/test_setup.mjs`. +3. Run the test from the project root with the appropriate command. + +To add a new module test: + +1. Create `test/test_.js` with imports using `../.js` (relative to the `/test` folder). +2. Add the required mock infrastructure at the top. +3. Register it in this README with its run command. diff --git a/test/test_setup.mjs b/test/test_setup.mjs new file mode 100644 index 0000000..0a3522b --- /dev/null +++ b/test/test_setup.mjs @@ -0,0 +1,4 @@ +globalThis.location = { + protocol: 'http:', + host: 'localhost:8080' +}; diff --git a/test/test_stream.js b/test/test_stream.js new file mode 100644 index 0000000..0de27dc --- /dev/null +++ b/test/test_stream.js @@ -0,0 +1,151 @@ +import { WalletManager } from '../wallets.js'; +import { WalletStreamManager } from '../stream.js'; + +// --- 1. Environment Mocks (Storage & Timers) --- +const mockStorage = {}; +globalThis.localStorage = { + getItem: (key) => mockStorage[key] || null, + setItem: (key, value) => { mockStorage[key] = String(value); }, + clear: () => { Object.keys(mockStorage).forEach(k => delete mockStorage[k]); } +}; + +// Speed up time so our backoff tests don't take 30 actual seconds to run +const originalSetTimeout = globalThis.setTimeout; +globalThis.setTimeout = (callback, delay) => { + return originalSetTimeout(callback, 1); // Force all retries to happen instantly +}; + +// Mock location for stream.js WS_URL evaluation +globalThis.location = { + protocol: 'http:', + host: 'localhost:3000' +}; + +// --- 2. The Mock WebSocket Server Mirror --- +const mockInstances = []; +let lastSentPayload = null; +let connectionCount = 0; + +class MockWebSocket { + constructor(url) { + this.url = url; + this.readyState = 0; // CONNECTING + connectionCount++; + mockInstances.push(this); + + originalSetTimeout(() => { + this.readyState = 1; // OPEN + if (this.onopen) this.onopen(); + }, 2); + } + + send(data) { + lastSentPayload = JSON.parse(data); + } + + close() { + this.readyState = 3; // CLOSED + if (this.onclose) this.onclose({ code: 1000, reason: "Normal closure" }); + } + + // Helper method for our test runner to mimic the server pushing data to client + simulateServerPush(msgObject) { + if (this.onmessage) { + this.onmessage({ data: JSON.stringify(msgObject) }); + } + } + + // Helper method to simulate a sudden dirty network disconnect + simulateNetworkDrop() { + this.readyState = 3; + if (this.onclose) this.onclose({ code: 1006, reason: "Abnormal Drop" }); + } +} +globalThis.WebSocket = MockWebSocket; + +// --- 3. Test Runner Execution --- +function assert(condition, message) { + if (!condition) throw new Error(`❌ FAIL: ${message}`); + console.log(`✅ PASS: ${message}`); +} + +async function runStreamTests() { + console.log("🚀 Starting WalletStreamManager Real-Time Test Suite...\n"); + localStorage.clear(); + + // Seed two dummy wallets to simulate an existing local state + const wm = new WalletManager(); + wm.addWallet("0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", "base", "Base Acc"); + wm.addWallet("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "bitcoin", "Satoshi Acc"); + + const stream = new WalletStreamManager(wm); + stream.connect(); + + // Wait a few milliseconds for our mocked async connection handshakes to settle + await new Promise(r => originalSetTimeout(r, 10)); + + // --- TEST 1: Multiplexed On-Open Subscription --- + assert(connectionCount === 1, "WebSocket established exactly one connection"); + assert(lastSentPayload !== null, "Server received subscription frame on startup"); + assert(lastSentPayload.action === "subscribe", "Payload correctly set action to 'subscribe'"); + assert(lastSentPayload.subscriptions.length === 2, "Multiplexed frame packed all stored wallets successfully"); + + // --- TEST 2: Real-time Message Callback Routing --- + let balanceUpdatedCalled = false; + stream.on('balance_update', (msg) => { + if (msg.address === "0x04f728C520C438A000f7A5E9d904F0e725FFAEFE") { + balanceUpdatedCalled = true; + } + }); + + // Access the underlying mock instance to simulate the server pushing data + const activeSocketInstance = stream._ws; + activeSocketInstance.simulateServerPush({ + type: "balance_update", + address: "0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", + chain: "base", + balance: "50000000000" + }); + + assert(balanceUpdatedCalled === true, "Incoming server events route seamlessly to their respective listeners"); + + // --- TEST 3: Callback Error Isolation Safeguard --- + let secondaryListenerExecuted = false; + stream.on('transfer', () => { + throw new Error("CRASHING INTRUDER: I am a broken UI component callback!"); + }); + stream.on('transfer', () => { + secondaryListenerExecuted = true; + }); + + // Push a transfer event. The first listener throws an exception, but shouldn't halt execution. + try { + activeSocketInstance.simulateServerPush({ type: "transfer", tx_hash: "0xabc" }); + assert(secondaryListenerExecuted === true, "ERROR ISOLATION: Second event handler fired successfully despite preceding listener throwing a hard error"); + } catch (e) { + assert(false, "The StreamManager leaked a callback exception and crashed the event loop"); + } + + // --- TEST 4: Dynamic Delta Streaming Tracker Updates --- + const dynamicWallet = { address: "H7vjR5vP6dfYuxUzoZ76yvBpx6Z3zM7p9qE1vRkWk3xp", chain: "solana" }; + stream.subscribeToNewWallet(dynamicWallet); + + assert(lastSentPayload.action === "subscribe_wallet", "Dynamic frame used accurate 'subscribe_wallet' verb"); + assert(lastSentPayload.wallet?.address === dynamicWallet.address, "Dynamic delta subscription dispatched for runtime updates without connection cycling"); + + // --- TEST 5: Resilient Exponential Backoff Retry Loop --- + console.log("\n🕵️‍♂️ Triggering a sudden network failure drop scenario..."); + const preDropCount = connectionCount; + + stream._ws.simulateNetworkDrop(); // Crash the current open pipe + + await new Promise(r => originalSetTimeout(r, 15)); // Wait for fast-forward timers to run the retry loops + + assert(connectionCount > preDropCount, "RESILIENCE: Stream Manager successfully caught connection drop and automatically invoked reconnection logic"); + + // Cleanup + stream.disconnect(); + console.log("\n🎉 All core real-time streaming transport client tests passed!"); +} + +runStreamTests().catch(console.error); diff --git a/test/test_tabs.js b/test/test_tabs.js new file mode 100644 index 0000000..1770e8d --- /dev/null +++ b/test/test_tabs.js @@ -0,0 +1,122 @@ +import { WalletManager } from '../wallets.js'; +import { WalletStreamManager } from '../stream.js'; + +// --- 1. Infrastructure Mocks --- +const mockStorage = {}; +globalThis.localStorage = { + getItem: (key) => mockStorage[key] || null, + setItem: (key, value) => { mockStorage[key] = String(value); }, + clear: () => { Object.keys(mockStorage).forEach(k => delete mockStorage[k]); } +}; + +// Accelerate timers so leader election (200ms) and backoff happen instantly +const originalSetTimeout = globalThis.setTimeout; +globalThis.setTimeout = (callback, delay) => originalSetTimeout(callback, 1); + +// Standard WebSocket Mock +class MockWebSocket { + constructor(url) { + this.readyState = 1; // Immediately OPEN + originalSetTimeout(() => { if (this.onopen) this.onopen(); }, 1); + } + send(data) { globalLastSentPayload = JSON.parse(data); } + close() { if (this.onclose) this.onclose({ code: 1000 }); } +} +globalThis.WebSocket = MockWebSocket; + +// --- 2. The Cross-Tab Broadcast Router Mock --- +const activeChannels = new Set(); +class MockBroadcastChannel { + constructor(name) { + this.name = name; + activeChannels.add(this); + } + postMessage(data) { + // Distribute the message to all OTHER open channel instances simulation offline cross-tab delivery + for (const channel of activeChannels) { + if (channel !== this && channel.onmessage) { + channel.onmessage({ data }); + } + } + } + close() { activeChannels.delete(this); } +} +globalThis.BroadcastChannel = MockBroadcastChannel; + +// Global tracking variables for assertions +let globalLastSentPayload = null; + +function assert(condition, message) { + if (!condition) throw new Error(`❌ FAIL: ${message}`); + console.log(`✅ PASS: ${message}`); +} + +// --- 3. The Multi-Tab Simulation Scenario --- +async function runMultiTabTest() { + console.log("🚀 Starting Multi-Tab Synchronization & Leader Election Test Suite...\n"); + localStorage.clear(); + + // Seed initial wallet configuration + const wm = new WalletManager(); + wm.addWallet("0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", "base", "Shared Wallet"); + + // --- STEP 1: Boot First Tab --- + console.log("▶️ Initializing Tab 1..."); + const tab1 = new WalletStreamManager(wm); + tab1.connect(); + + await new Promise(r => originalSetTimeout(r, 15)); // Await leader election window + assert(tab1.isLeader === true, "Tab 1 detected no existing leaders and claimed leadership role."); + assert(tab1.ws !== null, "Tab 1 successfully initialized a physical WebSocket network connection."); + + // --- STEP 2: Boot Second Tab --- + console.log("\n▶️ Initializing Tab 2 (Simulating a new browser window/tab)..."); + const tab2 = new WalletStreamManager(wm); + tab2.connect(); + + await new Promise(r => originalSetTimeout(r, 15)); + assert(tab2.isLeader === false, "Tab 2 discovered active Leader and assigned itself as a Follower."); + assert(tab2.ws === undefined || tab2.ws === null, "Tab 2 kept its local WebSocket offline to save system resources."); + + // --- STEP 3: Verify Event Cross-Broadcasting Flow --- + let tab2ReceivedData = false; + tab2.on('balance_update', (msg) => { + if (msg.balance === "9999") tab2ReceivedData = true; + }); + + // Simulate server pushing an update specifically to Tab 1's socket connection + tab1.ws.onmessage({ + data: JSON.stringify({ type: "balance_update", balance: "9999" }) + }); + + await new Promise(r => originalSetTimeout(r, 5)); + assert(tab2ReceivedData === true, "Tab 1 received WebSocket data and pushed it locally across the BroadcastChannel to Tab 2."); + + // --- STEP 4: Proxy Subscription Modification Check --- + console.log("\n▶️ Simulating Follower (Tab 2) subscribing to a new asset dynamically..."); + const newAsset = { address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", chain: "bitcoin" }; + + globalLastSentPayload = null; + tab2.subscribeToNewWallet(newAsset); + + await new Promise(r => originalSetTimeout(r, 5)); + assert(globalLastSentPayload !== null, "Tab 2's subscription was successfully intercepted and routed to the channel."); + assert(globalLastSentPayload.action === "subscribe_wallet", "Tab 1 successfully executed the proxy network subscription on behalf of Tab 2."); + + // --- STEP 5: Graceful Failover Handover (Leader Death Scenario) --- + console.log("\n🕵️‍♂️ Simulating user abruptly closing Tab 1 (Leader)..."); + + // Simulate browser exit/unload routine for Tab 1 + tab1.disconnect(); + // Dispatch the exit event if your module binds directly to window event listeners + if (tab1._handleExit) tab1._handleExit(); + + await new Promise(r => originalSetTimeout(r, 15)); // Await emergency election frame + + assert(tab2.isLeader === true, "Tab 2 caught the LEADER_DEATH signal and successfully promoted itself to Leader."); + assert(tab2.ws !== null, "Tab 2 gracefully opened a brand new physical WebSocket connection to pick up the stream."); + + console.log("\n🎉 Massive Success! Cross-tab synchronization, proxy forwarding, and leader failovers function perfectly."); +} + +runMultiTabTest().catch(console.error); diff --git a/test/test_verifier.js b/test/test_verifier.js new file mode 100644 index 0000000..f909a70 --- /dev/null +++ b/test/test_verifier.js @@ -0,0 +1,76 @@ +import { WalletManager } from '../wallets.js'; +import { WalletVerifier } from '../verifier.js'; + +// Setup basic environment mocks +const mockStorage = {}; +globalThis.localStorage = { + getItem: (key) => mockStorage[key] || null, + setItem: (key, value) => { mockStorage[key] = String(value); } +}; + +function assertError(result, expectedMsg, testName) { + if (result.success === false && result.error.includes(expectedMsg)) { + console.log(`✅ PASS: ${testName}`); + } else { + console.error(`❌ FAIL: ${testName} (Got: "${result.error ?? 'success'}", Expected: "${expectedMsg}")`); + } +} + +async function runVerifierTests() { + console.log("🚀 Starting WalletVerifier Error State Test Suite...\n"); + + // --- TEST 1: Missing Extension --- + globalThis.window = {}; // No ethereum provider defined + const wm1 = new WalletManager(); + let verifier = new WalletVerifier(wm1); + assertError( + await verifier.triggerWalletVerification('base', 'Test'), + "No Web3 wallet extension found", + "Missing Extension Detection" + ); + + // --- TEST 2: User Rejects Account Connection (EIP-1193 Error 4001) --- + globalThis.window = { + ethereum: { + request: async ({ method }) => { + if (method === 'eth_requestAccounts') { + const err = new Error("User denied connection"); + err.code = 4001; + throw err; + } + } + } + }; + const wm2 = new WalletManager(); + verifier = new WalletVerifier(wm2); + assertError( + await verifier.triggerWalletVerification('base', 'Test'), + "Verification cancelled", + "Account Rejection (Code 4001)" + ); + + // --- TEST 3: User Rejects Signature --- + globalThis.window = { + ethereum: { + request: async ({ method }) => { + if (method === 'eth_requestAccounts') return ['0x04f728C520C438A000f7A5E9d904F0e725FFAEFE']; + if (method === 'eth_signTypedData_v4') { + const err = new Error("User rejected the signature request"); + err.code = 4001; + throw err; + } + } + } + }; + const wm3 = new WalletManager(); + verifier = new WalletVerifier(wm3); + assertError( + await verifier.triggerWalletVerification('base', 'Test'), + "rejected", + "Signature Rejection Mapping" + ); + + console.log("\n🏁 Test Suite Complete."); +} + +runVerifierTests(); diff --git a/test/test_wallets.js b/test/test_wallets.js new file mode 100644 index 0000000..3b0acfe --- /dev/null +++ b/test/test_wallets.js @@ -0,0 +1,80 @@ +import { WalletManager } from '../wallets.js'; + +// 1. Mock localStorage for the Node.js environment +const mockStorage = {}; +globalThis.localStorage = { + getItem: (key) => mockStorage[key] || null, + setItem: (key, value) => { mockStorage[key] = String(value); }, + removeItem: (key) => { delete mockStorage[key]; }, + clear: () => { Object.keys(mockStorage).forEach(k => delete mockStorage[k]); } +}; + +// Simple test assertion helper +function assert(condition, message) { + if (!condition) throw new Error(`❌ FAIL: ${message}`); + console.log(`✅ PASS: ${message}`); +} + +async function runTestSuite() { + console.log("🚀 Starting WalletManager Test Suite...\n"); + localStorage.clear(); + + const manager = new WalletManager(); + + // --- TEST 1: Address Format Validations --- + const invalidResult = manager.addWallet("invalid-address", "base", "My EVM"); + assert(!invalidResult.success, "Rejected invalid EVM address format properly"); + + // Add valid addresses across different patterns + assert(manager.addWallet("0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", "base", "Main Base").success, "Added EVM wallet"); + assert(manager.addWallet("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "bitcoin", "Satoshi BTC").success, "Added Bitcoin wallet"); + assert(manager.addWallet("H7vjR5vP6dfYuxUzoZ76yvBpx6Z3zM7p9qE1vRkWk3xp", "solana", "Solana Trading").success, "Added Solana wallet"); + console.log("👉 ACTUAL WALLETS IN STATE:", manager.getWallets()); + console.log("👉 CURRENT LENGTH:", manager.getWallets().length); + assert(manager.getWallets().length === 3, "Successfully added 3 distinct multichain wallets"); + + // --- TEST 2: Duplicate Rejection --- + const dupResult = manager.addWallet("0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", "base", "Duplicate Base"); + assert(!dupResult.success, "Correctly prevented duplicate entry"); + + // --- TEST 3: Verification Flow --- + const initialWallet = manager.findWallet("0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", "base"); + assert(initialWallet.isVerified === false, "Wallet starts completely unverified"); + + manager.verifyWallet( + "0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", + "base", + "0xmockSignature123456", + { action: "Verify Ownership" } + ); + + const verifiedWallet = manager.findWallet("0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", "base"); + assert(verifiedWallet.isVerified === true, "verifyWallet() successfully flips flag to true"); + assert(verifiedWallet.signature === "0xmockSignature123456", "Signature string is retained"); + + // --- TEST 4: The Core Security Hack Simulation --- + console.log("\n🕵️‍♂️ Simulating a user manually tampering with localStorage file..."); + + // Fetch raw storage payload, hack it to true, and write it back + const rawData = JSON.parse(localStorage.getItem("cbbtc_tracked_wallets")); + rawData.forEach(w => w.isVerified = true); // Force-inject "true" directly to file + localStorage.setItem("cbbtc_tracked_wallets", JSON.stringify(rawData)); + + // Instantiate a brand new manager to simulate page reload/reboot + const maliciousManager = new WalletManager(); + maliciousManager.loadWallets(); // Boot up from hacked storage + + const postBootWallet = maliciousManager.findWallet("0x04f728C520C438A000f7A5E9d904F0e725FFAEFE", "base"); + assert(postBootWallet.isVerified === false, "SECURITY VERIFIED: Tampered localStorage flags were successfully wiped back to false on boot."); + + // --- TEST 5: Removal --- + maliciousManager.removeWallet("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "bitcoin"); + assert(maliciousManager.findWallet("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "bitcoin") === undefined, "Successfully spliced and removed wallet from state"); + + console.log("\n🎉 All core client-side state tests passed successfully!"); +} + +runTestSuite().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/verifier.js b/verifier.js new file mode 100644 index 0000000..b534f04 --- /dev/null +++ b/verifier.js @@ -0,0 +1,292 @@ +/** + * Wallet Verifier — EIP-712 signature verification via window.ethereum. + * + * Orchestrates connection, typed-data signing, and state persistence. + * Designed to work alongside WalletManager. + * + * Usage: + * import { WalletVerifier } from './verifier.js'; + * const verifier = new WalletVerifier(walletManagerInstance); + * await verifier.triggerWalletVerification('base', 'My Wallet'); + */ + +import { WalletManager } from './wallets.js'; + +/* ------------------------------------------------------------------ */ +/* EIP-712 Domain */ +/* ------------------------------------------------------------------ */ + +const EIP712_DOMAIN = { + name: 'Anonymous Wallet Tracker', + version: '1', + chainId: 1, +}; + +const EIP712_TYPES = { + VerifyTracking: [ + { name: 'action', type: 'string' }, + { name: 'walletAddress', type: 'address' }, + { name: 'timestamp', type: 'uint256' }, + ], +}; + +/* ------------------------------------------------------------------ */ +/* Error Codes (EIP-1193) */ +/* ------------------------------------------------------------------ */ + +/* User rejected request */ +const USER_REJECTED_CODE = 4001; +/* User switched accounts mid-flow */ +const CHAIN_DISCONNECTED_CODE = 4900; + +/* Human-friendly message map */ +const ERROR_MESSAGES = { + [USER_REJECTED_CODE]: 'Wallet request rejected. Verification cancelled.', + [CHAIN_DISCONNECTED_CODE]: 'Wallet disconnected unexpectedly.', + NO_EXTENSION: 'No Web3 wallet extension found. Install MetaMask or similar.', + NO_ACCOUNT: 'Could not retrieve an active wallet address.', + SIGN_FAILED: 'Signature request failed.', + WALLET_EXISTS: 'This wallet address is already being tracked.', +}; + +/* ------------------------------------------------------------------ */ +/* Helpers */ +/* ------------------------------------------------------------------ */ + +/** + * Check if window.ethereum is available and callable. + * + * @returns {boolean} + */ +function hasEthereumProvider() { + return ( + typeof window !== 'undefined' && + typeof window.ethereum !== 'undefined' && + typeof window.ethereum.request === 'function' + ); +} + +/** + * Parse an EIP-1193 provider error into a user-friendly string. + * + * @param {Error} err + * @returns {string} + */ +function providerErrorMessage(err) { + /* MetaMask and compatible providers attach a code property */ + const code = err?.code ?? err?.cause?.code; + if (code === USER_REJECTED_CODE || code === 4001) { + return 'Verification cancelled — you rejected the wallet request.'; + } + if (code === CHAIN_DISCONNECTED_CODE || code === 4900) { + return 'Wallet connection dropped unexpectedly.'; + } + return err?.message ?? 'An unexpected error occurred.'; +} + +/* ------------------------------------------------------------------ */ +/* WalletVerifier */ +/* ------------------------------------------------------------------ */ + +export class WalletVerifier { + /** + * @param {WalletManager} walletManager — shared state manager instance + */ + constructor(walletManager) { + if (!(walletManager instanceof WalletManager)) { + throw new TypeError('WalletVerifier requires a WalletManager instance.'); + } + + /** @type {WalletManager} */ + this._wm = walletManager; + } + + /* ---------------------------------------------------------------- */ + /* Public API */ + /* ---------------------------------------------------------------- */ + + /** + * Full verification flow: request account → sign EIP-712 → persist. + * + * @param {string} chain — chain name for storage (e.g. 'base') + * @param {string} [nickname=''] — display nickname + * @returns {Promise<{ success: true, wallet: object } | { success: false, error: string }>} + */ + async triggerWalletVerification(chain, nickname = '') { + /* ---- Step 0: Provider check ---- */ + if (!hasEthereumProvider()) { + return { success: false, error: ERROR_MESSAGES.NO_EXTENSION }; + } + + let address; + let signature; + + try { + /* ---- Step 1: Request active account ---- */ + address = await this._requestAccount(); + + /* ---- Step 2: Build EIP-712 payload ---- */ + const typedData = this._buildTypedData(address); + + /* ---- Step 3: Request signature ---- */ + signature = await this._requestSignature(address, typedData); + + } catch (err) { + return { success: false, error: providerErrorMessage(err) }; + } + + /* ---- Step 4: Store in WalletManager ---- */ + const normalChain = String(chain).toLowerCase(); + const existing = this._wm.findWallet(address, normalChain); + + if (existing) { + /* Already tracked — just verify if not yet verified */ + if (!existing.isVerified) { + const verifyResult = this._wm.verifyWallet( + address, + normalChain, + signature, + this._buildMessageData(address) + ); + if (!verifyResult.success) { + return { success: false, error: verifyResult.error }; + } + return { success: true, wallet: this._wm.findWallet(address, normalChain) }; + } + return { + success: false, + error: 'This wallet is already verified and being tracked.', + }; + } + + /* New wallet — add then verify */ + const addResult = this._wm.addWallet(address, normalChain, nickname); + if (!addResult.success) { + return { success: false, error: addResult.error }; + } + + const verifyResult = this._wm.verifyWallet( + address, + normalChain, + signature, + this._buildMessageData(address) + ); + if (!verifyResult.success) { + return { success: false, error: verifyResult.error }; + } + + return { + success: true, + wallet: this._wm.findWallet(address, normalChain), + }; + } + + /* ---------------------------------------------------------------- */ + /* Internal Steps */ + /* ---------------------------------------------------------------- */ + + /** + * Request `eth_requestAccounts`. Throws on rejection. + * @returns {Promise} The connected address. + */ + async _requestAccount() { + const accounts = await window.ethereum.request({ + method: 'eth_requestAccounts', + }); + + if (!accounts || accounts.length === 0) { + throw new Error(ERROR_MESSAGES.NO_ACCOUNT); + } + + return accounts[0].toLowerCase(); + } + + /** + * Build the EIP-712 typed data payload. + * + * @param {string} address + * @returns {object} Full typedData object (domain + types + message) + */ + _buildTypedData(address) { + const message = this._buildMessageData(address); + return { + domain: EIP712_DOMAIN, + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + ], + VerifyTracking: EIP712_TYPES.VerifyTracking, + }, + primaryType: 'VerifyTracking', + message, + }; + } + + /** + * Build the message portion only (for storage). + * + * @param {string} address + * @returns {object} + */ + _buildMessageData(address) { + return { + action: 'Verify Ownership for Live Tracking', + walletAddress: address, + timestamp: Math.floor(Date.now() / 1000), + }; + } + + /** + * Request `eth_signTypedData_v4`. Throws on rejection. + * + * @param {string} address + * @param {object} typedData + * @returns {Promise} The signature hex string. + */ + async _requestSignature(address, typedData) { + try { + const sig = await window.ethereum.request({ + method: 'eth_signTypedData_v4', + params: [address, JSON.stringify(typedData)], + }); + + if (!sig || typeof sig !== 'string') { + throw new Error(ERROR_MESSAGES.SIGN_FAILED); + } + + return sig; + } catch (err) { + /* Re-throw EIP-1193 code errors for upstream handling */ + if (err?.code === USER_REJECTED_CODE) { + throw new Error('User rejected the signature request.'); + } + throw err; + } + } + + /* ---------------------------------------------------------------- */ + /* Quick connect (no signature) */ + /* ---------------------------------------------------------------- */ + + /** + * Minimal connection check — only requests accounts, does NOT sign. + * Useful for pre-flight checks or auto-connect on page load. + * + * @returns {Promise} Connected address, or null on rejection. + */ + async connect() { + if (!hasEthereumProvider()) return null; + try { + const accounts = await window.ethereum.request({ + method: 'eth_requestAccounts', + }); + return accounts?.[0]?.toLowerCase() ?? null; + } catch { + return null; + } + } +} + +export default WalletVerifier; diff --git a/wallet_portfolio.md b/wallet_portfolio.md new file mode 100644 index 0000000..9dfbd53 --- /dev/null +++ b/wallet_portfolio.md @@ -0,0 +1,724 @@ +# Wallet Portfolio: 0x0C1A4A060E119F981412E323104D134D413Dba + +**Chain:** base + +**Total Transactions:** 596 + +## Summary by Token + +| Token Address | IN Count | OUT Count | +|---|---|---| +| `0x04953c…c2d1` | 1 | 0 | +| `0x0697a1…94b5` | 1 | 0 | +| `0x096bbc…4f25` | 1 | 0 | +| `0x0a538a…5d21` | 1 | 0 | +| `0x0dbeec…a14e` | 1 | 0 | +| `0x108f04…c466` | 1 | 0 | +| `0x110e56…bbdd` | 1 | 0 | +| `0x1575f7…db90` | 1 | 0 | +| `0x1619d9…4435` | 0 | 6 | +| `0x19bfd7…b3cc` | 1 | 0 | +| `0x1ac32a…1630` | 1 | 0 | +| `0x1d432a…c2cf` | 1 | 0 | +| `0x1e3585…5e21` | 1 | 0 | +| `0x21c01e…613e` | 1 | 0 | +| `0x21e2ff…9c26` | 1 | 0 | +| `0x21f4bb…b625` | 1 | 0 | +| `0x244120…3bc0` | 1 | 0 | +| `0x2abb46…63dc` | 1 | 0 | +| `0x2d137c…5fbf` | 1 | 0 | +| `0x2d2c99…8a3e` | 1 | 0 | +| `0x2fa0b2…10de` | 1 | 0 | +| `0x313bbc…f319` | 1 | 0 | +| `0x32153d…3b7c` | 1 | 0 | +| `0x3319e7…38a2` | 1 | 0 | +| `0x359b6c…261c` | 1 | 0 | +| `0x377694…28fc` | 1 | 0 | +| `0x3c3689…103e` | 1 | 0 | +| `0x420000…0006` | 29 | 31 | +| `0x4744b6…4e84` | 1 | 0 | +| `0x49b31d…b8b5` | 8 | 0 | +| `0x4a6272…d7f5` | 1 | 0 | +| `0x4ba9ac…7d31` | 2 | 0 | +| `0x4da55c…7fba` | 1 | 0 | +| `0x4e65fe…c0ab` | 15 | 9 | +| `0x52e588…b0a9` | 1 | 0 | +| `0x57b0d4…c3c9` | 1 | 0 | +| `0x589836…46ab` | 1 | 0 | +| `0x596923…8d2e` | 1 | 0 | +| `0x59dca0…4c28` | 15 | 10 | +| `0x5b1a31…3938` | 1 | 0 | +| `0x5e7432…215b` | 1 | 0 | +| `0x62dfcd…b070` | 1 | 0 | +| `0x64b3a3…b8d7` | 1 | 0 | +| `0x64df7b…5ea2` | 1 | 0 | +| `0x6a627a…83f0` | 1 | 0 | +| `0x6f01bd…361d` | 1 | 0 | +| `0x6ff512…1e9f` | 1 | 0 | +| `0x754202…520c` | 1 | 0 | +| `0x78cdbe…8cbd` | 1 | 0 | +| `0x7c1803…5783` | 3 | 0 | +| `0x7e2a4a…1f92` | 1 | 0 | +| `0x7f0b20…fab3` | 1 | 0 | +| `0x82ac04…e198` | 1 | 0 | +| `0x833589…2913` | 91 | 78 | +| `0x85ca69…6758` | 1 | 0 | +| `0x891113…4062` | 1 | 0 | +| `0x8c17bb…9630` | 1 | 0 | +| `0x8c6408…5744` | 1 | 0 | +| `0x9032ad…d5b4` | 1 | 0 | +| `0x949b0e…f521` | 1 | 0 | +| `0x976e65…acba` | 1 | 0 | +| `0x9a9754…2925` | 1 | 0 | +| `0x9ca08b…9a8f` | 2 | 0 | +| `0x9de652…3975` | 1 | 0 | +| `0x9e0235…a0f0` | 0 | 2 | +| `0xa13a3c…be7d` | 1 | 0 | +| `0xa28be1…4c48` | 1 | 0 | +| `0xa340fa…fc24` | 1 | 0 | +| `0xa4b694…c3ee` | 1 | 0 | +| `0xa5fd26…3c56` | 1 | 0 | +| `0xa8d3fb…5edd` | 1 | 0 | +| `0xaa75ea…05ba` | 1 | 0 | +| `0xad03e2…0c4e` | 1 | 0 | +| `0xb0b076…b1e3` | 1 | 0 | +| `0xb11462…5e75` | 1 | 0 | +| `0xb47107…cf66` | 1 | 0 | +| `0xb5e33b…459d` | 1 | 0 | +| `0xb66799…a099` | 2 | 0 | +| `0xb77ac4…60e5` | 1 | 0 | +| `0xb96a7f…c850` | 1 | 0 | +| `0xbb13ed…e14c` | 1 | 0 | +| `0xbbd2ad…3634` | 1 | 0 | +| `0xbbe052…5a5c` | 1 | 0 | +| `0xbcd73d…2a1b` | 1 | 0 | +| `0xbdb930…8ee6` | 44 | 2 | +| `0xc040b8…b9e0` | 1 | 0 | +| `0xc175be…9cfb` | 1 | 0 | +| `0xc20386…20bd` | 1 | 0 | +| `0xcb1e7c…3f11` | 1 | 0 | +| `0xcbb7c0…33bf` | 82 | 44 | +| `0xcd15cb…d40b` | 1 | 0 | +| `0xce7d99…4503` | 1 | 0 | +| `0xd1304e…74af` | 1 | 0 | +| `0xd16884…21ee` | 1 | 0 | +| `0xd184e1…c9fa` | 1 | 0 | +| `0xd19f71…1529` | 1 | 0 | +| `0xd23b84…9c3e` | 1 | 0 | +| `0xd4a0e0…8bb7` | 5 | 2 | +| `0xd93d02…3f4b` | 1 | 0 | +| `0xd9c5a6…d690` | 0 | 6 | +| `0xde37f6…f148` | 1 | 0 | +| `0xdfc6a7…4512` | 2 | 0 | +| `0xe248c0…4380` | 2 | 3 | +| `0xe66acc…f06c` | 0 | 1 | +| `0xeb9caa…b309` | 0 | 6 | +| `0xec7020…5098` | 1 | 0 | +| `0xed414a…a5f1` | 1 | 0 | +| `0xf0c9af…3c57` | 1 | 0 | +| `0xfaf639…623c` | 1 | 0 | +| `0xfaf87e…db84` | 1 | 0 | +| `0xfc842d…0b78` | 1 | 0 | +| `0xfd755c…87ff` | 1 | 0 | +| `0xffb18d…3e09` | 1 | 0 | + +## Transaction Ledger + +| TX Hash | Block | Timestamp | Token Address | Amount | Direction | Balance | +|---|---|---|---|---|---|---| +| `0x3000fa4a…0e6fcc` | 35866511 | 2025-09-22 06:26:09 | `0xcbb7c0…33bf` | 18837 | IN | 18837 | +| `0x71ae0c29…4329f7` | 35910993 | 2025-09-23 07:08:53 | `0x833589…2913` | 6082576 | IN | 6082576 | +| `0x42bde7e9…a75ea5` | 35911008 | 2025-09-23 07:09:23 | `0xcbb7c0…33bf` | 5295 | IN | 24132 | +| `0x2a9bd6ff…363fc4` | 36043780 | 2025-09-26 08:55:07 | `0xcbb7c0…33bf` | 48513 | IN | 72645 | +| `0xe16ecb6f…1cf2bf` | 36043824 | 2025-09-26 08:56:35 | `0xcbb7c0…33bf` | 5544 | IN | 78189 | +| `0xe16ecb6f…1cf2bf` | 36043824 | 2025-09-26 08:56:35 | `0x833589…2913` | 6082576 | OUT | 0 | +| `0x16e002f8…8762a6` | 36044145 | 2025-09-26 09:07:17 | `0xfd755c…87ff` | 1300 | IN | 1300 | +| `0x3416967b…12a0ab` | 36064803 | 2025-09-26 20:35:53 | `0xb77ac4…60e5` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x1b1f70df…8f35cf` | 36083012 | 2025-09-27 06:42:51 | `0xcbb7c0…33bf` | 9894 | IN | 88083 | +| `0x9a53add7…1486c9` | 36083599 | 2025-09-27 07:02:25 | `0xd4a0e0…8bb7` | 3233045561590221319 | IN | 3233045561590221319 | +| `0x1b7e7081…5896ce` | 36083753 | 2025-09-27 07:07:33 | `0xcbb7c0…33bf` | 88083 | OUT | 0 | +| `0x1b7e7081…5896ce` | 36083753 | 2025-09-27 07:07:33 | `0xbdb930…8ee6` | 88082 | IN | 88082 | +| `0xae6db8bf…5cc0ee` | 36083926 | 2025-09-27 07:13:19 | `0x833589…2913` | 6000000000 | IN | 6000000000 | +| `0xae6db8bf…5cc0ee` | 36083926 | 2025-09-27 07:13:19 | `0x59dca0…4c28` | 6000000001 | IN | 6000000001 | +| `0x7f25acd2…19ec91` | 36083951 | 2025-09-27 07:14:09 | `0x833589…2913` | 6000000000 | OUT | 0 | +| `0x1babe0a9…60b3d6` | 36083986 | 2025-09-27 07:15:19 | `0x1619d9…4435` | 6000000000 | OUT | -6000000000 | +| `0xad80df6c…30876c` | 36084065 | 2025-09-27 07:17:57 | `0xeb9caa…b309` | 6000000000 | OUT | -6000000000 | +| `0x0f93f98f…100750` | 36084149 | 2025-09-27 07:20:45 | `0x1619d9…4435` | 6000000000 | OUT | -12000000000 | +| `0x94da7ad9…fc9339` | 36084211 | 2025-09-27 07:22:49 | `0x59dca0…4c28` | 50008156 | IN | 6050008157 | +| `0x94da7ad9…fc9339` | 36084211 | 2025-09-27 07:22:49 | `0x833589…2913` | 50000000 | IN | 50000000 | +| `0x8af3bc30…8fe3ef` | 36084229 | 2025-09-27 07:23:25 | `0x833589…2913` | 50000000 | OUT | 0 | +| `0x99bc66ac…3289d6` | 36084274 | 2025-09-27 07:24:55 | `0x1619d9…4435` | 50000000 | OUT | -12050000000 | +| `0x99a22816…99ba76` | 36084451 | 2025-09-27 07:30:49 | `0x833589…2913` | 30349169 | IN | 30349169 | +| `0x237e19d4…dcf8d7` | 36084467 | 2025-09-27 07:31:21 | `0x833589…2913` | 10 | IN | 30349179 | +| `0xb1faa777…9071b7` | 36084519 | 2025-09-27 07:33:05 | `0x59dca0…4c28` | 30340267 | OUT | 6019667890 | +| `0xb1faa777…9071b7` | 36084519 | 2025-09-27 07:33:05 | `0x833589…2913` | 30349169 | OUT | 10 | +| `0x84f8dc01…b9c2a2` | 36084556 | 2025-09-27 07:34:19 | `0x833589…2913` | 60 | IN | 70 | +| `0x38cfac2c…c5f69f` | 36085883 | 2025-09-27 08:18:33 | `0xeb9caa…b309` | 6000000000 | OUT | -12000000000 | +| `0x9adedf52…5bf7d1` | 36085913 | 2025-09-27 08:19:33 | `0x1619d9…4435` | 6000000000 | OUT | -18050000000 | +| `0x02017b3c…22b9c8` | 36088933 | 2025-09-27 10:00:13 | `0xfaf87e…db84` | 100 | IN | 100 | +| `0x19785a19…24a2be` | 36144556 | 2025-09-28 16:54:19 | `0xd184e1…c9fa` | 10000000000000000000 | IN | 10000000000000000000 | +| `0x41d6b8d1…cdb778` | 36169416 | 2025-09-29 06:42:59 | `0xcbb7c0…33bf` | 7411 | IN | 7411 | +| `0x04ec8d6c…0e2478` | 36169821 | 2025-09-29 06:56:29 | `0x3319e7…38a2` | 1560 | IN | 1560 | +| `0x87fc86f0…bd7e41` | 36169868 | 2025-09-29 06:58:03 | `0x833589…2913` | 6952536 | IN | 6952606 | +| `0x2e101e28…b89783` | 36169880 | 2025-09-29 06:58:27 | `0x420000…0006` | 1102787284277687 | IN | 1102787284277687 | +| `0xc6bae961…28e149` | 36169906 | 2025-09-29 06:59:19 | `0xcbb7c0…33bf` | 7411 | OUT | 0 | +| `0xc6bae961…28e149` | 36169906 | 2025-09-29 06:59:19 | `0xbdb930…8ee6` | 7410 | IN | 95492 | +| `0x9ac4cf11…e84f30` | 36169958 | 2025-09-29 07:01:03 | `0x833589…2913` | 6952606 | OUT | 0 | +| `0x9ac4cf11…e84f30` | 36169958 | 2025-09-29 07:01:03 | `0x59dca0…4c28` | 4496461 | OUT | 6015171429 | +| `0x8958d2ed…bb83a5` | 36174286 | 2025-09-29 09:25:19 | `0xc20386…20bd` | 47000000000000000000 | IN | 47000000000000000000 | +| `0xab554b4b…ffb284` | 36194473 | 2025-09-29 20:38:13 | `0x52e588…b0a9` | 10000000000000000000 | IN | 10000000000000000000 | +| `0x6309b557…236508` | 36212634 | 2025-09-30 06:43:35 | `0xcbb7c0…33bf` | 9137 | IN | 9137 | +| `0xc9b80a0c…2e133d` | 36213173 | 2025-09-30 07:01:33 | `0xcbb7c0…33bf` | 11606 | IN | 20743 | +| `0x98e3a68b…ac9938` | 36256402 | 2025-10-01 07:02:31 | `0xcbb7c0…33bf` | 9901 | IN | 30644 | +| `0x55f2a565…13a72a` | 36256843 | 2025-10-01 07:17:13 | `0xcbb7c0…33bf` | 9353 | IN | 39997 | +| `0xa7d22c44…a74441` | 36256884 | 2025-10-01 07:18:35 | `0xbdb930…8ee6` | 39997 | IN | 135489 | +| `0xa7d22c44…a74441` | 36256884 | 2025-10-01 07:18:35 | `0xcbb7c0…33bf` | 39997 | OUT | 0 | +| `0x3581056f…fbb263` | 36256899 | 2025-10-01 07:19:05 | `0xd4a0e0…8bb7` | 1722966482988947 | IN | 3234768528073210266 | +| `0x3581056f…fbb263` | 36256899 | 2025-10-01 07:19:05 | `0x420000…0006` | 1102787284277687 | OUT | 0 | +| `0x9c61e9a2…f420bd` | 36260877 | 2025-10-01 09:31:41 | `0x833589…2913` | 121492498 | IN | 121492498 | +| `0x5290fe06…34fdb6` | 36260906 | 2025-10-01 09:32:39 | `0x59dca0…4c28` | 118879873 | OUT | 5896291556 | +| `0x5290fe06…34fdb6` | 36260906 | 2025-10-01 09:32:39 | `0x833589…2913` | 121492498 | OUT | 0 | +| `0x3e18cb5e…9e43fd` | 36261005 | 2025-10-01 09:35:57 | `0xd4a0e0…8bb7` | 13986800783937 | IN | 3234782514873994203 | +| `0x3e18cb5e…9e43fd` | 36261005 | 2025-10-01 09:35:57 | `0xbdb930…8ee6` | 11956981 | IN | 12092470 | +| `0x3e18cb5e…9e43fd` | 36261005 | 2025-10-01 09:35:57 | `0xd4a0e0…8bb7` | 3234782514873994203 | OUT | 0 | +| `0xd3d040ef…3569e9` | 36281223 | 2025-10-01 20:49:53 | `0xfaf639…623c` | 88000000000000000000 | IN | 88000000000000000000 | +| `0xc169e4a9…e703fa` | 36385942 | 2025-10-04 07:00:31 | `0x420000…0006` | 13254996724777810 | IN | 13254996724777810 | +| `0xc031a808…663d61` | 36385960 | 2025-10-04 07:01:07 | `0x833589…2913` | 93949458 | IN | 93949458 | +| `0x2a4dfb46…f4d8c5` | 36385971 | 2025-10-04 07:01:29 | `0xcbb7c0…33bf` | 261 | IN | 261 | +| `0x2c8d32b2…927dbd` | 36386336 | 2025-10-04 07:13:39 | `0x420000…0006` | 13254996724777810 | OUT | 0 | +| `0x2c8d32b2…927dbd` | 36386336 | 2025-10-04 07:13:39 | `0x833589…2913` | 59791455 | IN | 153740913 | +| `0x7a44e777…e21c9e` | 36386358 | 2025-10-04 07:14:23 | `0x59dca0…4c28` | 90416551 | OUT | 5805875005 | +| `0x7a44e777…e21c9e` | 36386358 | 2025-10-04 07:14:23 | `0x833589…2913` | 93949458 | OUT | 59791455 | +| `0x1b0eb636…a6cded` | 36430868 | 2025-10-05 07:58:03 | `0xe248c0…4380` | 17779582532688607113 | IN | 17779582532688607113 | +| `0x7764a18e…48bfa9` | 36430883 | 2025-10-05 07:58:33 | `0x833589…2913` | 4962536 | IN | 64753991 | +| `0x500f296d…aa6754` | 36431633 | 2025-10-05 08:23:33 | `0x833589…2913` | 6767927 | IN | 71521918 | +| `0xfda09658…86b0aa` | 36431644 | 2025-10-05 08:23:55 | `0x420000…0006` | 1394847451225735 | IN | 1394847451225735 | +| `0x508abc17…1a95ab` | 36432543 | 2025-10-05 08:53:53 | `0x420000…0006` | 1394847451225735 | OUT | 0 | +| `0x508abc17…1a95ab` | 36432543 | 2025-10-05 08:53:53 | `0x833589…2913` | 6377868 | IN | 77899786 | +| `0x57bf20ea…31601e` | 36432561 | 2025-10-05 08:54:29 | `0x59dca0…4c28` | 70240884 | OUT | 5735634121 | +| `0x57bf20ea…31601e` | 36432561 | 2025-10-05 08:54:29 | `0x833589…2913` | 71521918 | OUT | 6377868 | +| `0x165b7c5a…92b198` | 36472066 | 2025-10-06 06:51:19 | `0x833589…2913` | 76194917 | IN | 82572785 | +| `0x969e4e7e…26a282` | 36472118 | 2025-10-06 06:53:03 | `0x833589…2913` | 25475204 | IN | 108047989 | +| `0x8ffd6009…235b5e` | 36472148 | 2025-10-06 06:54:03 | `0x833589…2913` | 108047989 | OUT | 0 | +| `0x8ffd6009…235b5e` | 36472148 | 2025-10-06 06:54:03 | `0x59dca0…4c28` | 106963715 | OUT | 5628670406 | +| `0x628cd565…d4e5d0` | 36516723 | 2025-10-07 07:39:53 | `0x833589…2913` | 46038224 | IN | 46038224 | +| `0x40d8e6b8…3ee206` | 36516867 | 2025-10-07 07:44:41 | `0x833589…2913` | 23684005 | IN | 69722229 | +| `0xf90e90d7…04086f` | 36516917 | 2025-10-07 07:46:21 | `0x833589…2913` | 69722229 | OUT | 0 | +| `0xf90e90d7…04086f` | 36516917 | 2025-10-07 07:46:21 | `0x59dca0…4c28` | 68518931 | OUT | 5560151475 | +| `0x0d689e2a…1ec588` | 36707537 | 2025-10-11 17:40:21 | `0x833589…2913` | 820000000 | IN | 820000000 | +| `0x0d689e2a…1ec588` | 36707537 | 2025-10-11 17:40:21 | `0x59dca0…4c28` | 825061882 | IN | 6385213357 | +| `0xf25e2206…09702b` | 36707563 | 2025-10-11 17:41:13 | `0x59dca0…4c28` | 820000788 | IN | 7205214145 | +| `0xf25e2206…09702b` | 36707563 | 2025-10-11 17:41:13 | `0x833589…2913` | 820000000 | IN | 1640000000 | +| `0xcc943485…5cea0b` | 36707594 | 2025-10-11 17:42:15 | `0x833589…2913` | 820000000 | OUT | 820000000 | +| `0xf4fc3952…8ffab1` | 36707617 | 2025-10-11 17:43:01 | `0x59dca0…4c28` | 819998152 | OUT | 6385215993 | +| `0xf4fc3952…8ffab1` | 36707617 | 2025-10-11 17:43:01 | `0x833589…2913` | 820000000 | OUT | 0 | +| `0x4da21941…cb5f75` | 36707661 | 2025-10-11 17:44:29 | `0xd9c5a6…d690` | 820000000 | OUT | -820000000 | +| `0x244bc05f…7d9cb7` | 36707719 | 2025-10-11 17:46:25 | `0xeb9caa…b309` | 820000000 | OUT | -12820000000 | +| `0xefa47321…b8a103` | 36707849 | 2025-10-11 17:50:45 | `0xd9c5a6…d690` | 820000000 | OUT | -1640000000 | +| `0xe177f083…d734be` | 36708121 | 2025-10-11 17:59:49 | `0x833589…2913` | 10 | IN | 10 | +| `0x87eb355c…a1742b` | 36708158 | 2025-10-11 18:01:03 | `0xd9c5a6…d690` | 820000000 | OUT | -2460000000 | +| `0xdf6faf1b…dfd4e2` | 36708459 | 2025-10-11 18:11:05 | `0xd9c5a6…d690` | 820000000 | OUT | -3280000000 | +| `0x43518c8a…77c8c6` | 36708746 | 2025-10-11 18:20:39 | `0xd9c5a6…d690` | 820000000 | OUT | -4100000000 | +| `0xfd2467ad…21a106` | 36709033 | 2025-10-11 18:30:13 | `0xd9c5a6…d690` | 820000000 | OUT | -4920000000 | +| `0xcc00fa4a…c3743c` | 36709540 | 2025-10-11 18:47:07 | `0xeb9caa…b309` | 820000000 | OUT | -13640000000 | +| `0x26c3b9d9…4fe1a6` | 36774316 | 2025-10-13 06:46:19 | `0x833589…2913` | 20727275 | IN | 20727285 | +| `0x916db55a…c041d2` | 36774337 | 2025-10-13 06:47:01 | `0x420000…0006` | 4831202451151400 | IN | 4831202451151400 | +| `0xcc532b24…1306bd` | 36774590 | 2025-10-13 06:55:27 | `0x833589…2913` | 43019126 | IN | 63746411 | +| `0xc47af74e…fbec53` | 36774648 | 2025-10-13 06:57:23 | `0xcbb7c0…33bf` | 55117 | IN | 55378 | +| `0xc47af74e…fbec53` | 36774648 | 2025-10-13 06:57:23 | `0x833589…2913` | 63746411 | OUT | 0 | +| `0x1847ce01…cf04e0` | 36774669 | 2025-10-13 06:58:05 | `0xcbb7c0…33bf` | 38909 | IN | 94287 | +| `0x1847ce01…cf04e0` | 36774669 | 2025-10-13 06:58:05 | `0xe248c0…4380` | 17735133576356885596 | OUT | 44448956331721517 | +| `0x1847ce01…cf04e0` | 36774669 | 2025-10-13 06:58:05 | `0xe248c0…4380` | 44448956331721517 | OUT | 0 | +| `0x261943c4…aadbfe` | 36774692 | 2025-10-13 06:58:51 | `0xcbb7c0…33bf` | 17444 | IN | 111731 | +| `0x261943c4…aadbfe` | 36774692 | 2025-10-13 06:58:51 | `0x420000…0006` | 4831202451151400 | OUT | 0 | +| `0x2291dd8d…16d46c` | 36774711 | 2025-10-13 06:59:29 | `0xbdb930…8ee6` | 111854 | IN | 12204324 | +| `0x2291dd8d…16d46c` | 36774711 | 2025-10-13 06:59:29 | `0xcbb7c0…33bf` | 111731 | OUT | 0 | +| `0xc775a6ad…41aefa` | 36774931 | 2025-10-13 07:06:49 | `0xbcd73d…2a1b` | 930 | IN | 930 | +| `0x22c2b07a…5c7b83` | 36800492 | 2025-10-13 21:18:51 | `0x1619d9…4435` | 820000000 | OUT | -18870000000 | +| `0xaabc61eb…9d2fbb` | 36801990 | 2025-10-13 22:08:47 | `0xc040b8…b9e0` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x8faf6e28…1ab867` | 36803182 | 2025-10-13 22:48:31 | `0x0dbeec…a14e` | 15000000000000000000 | IN | 15000000000000000000 | +| `0x1205ca06…9d85df` | 36804099 | 2025-10-13 23:19:05 | `0x9a9754…2925` | 200000000000000000000 | IN | 200000000000000000000 | +| `0x46e573e4…d34428` | 36817746 | 2025-10-14 06:53:59 | `0x833589…2913` | 21028478 | IN | 21028478 | +| `0x95d89d93…616404` | 36817758 | 2025-10-14 06:54:23 | `0x420000…0006` | 5274283594741374 | IN | 5274283594741374 | +| `0xee5c7e92…954548` | 36817794 | 2025-10-14 06:55:35 | `0x833589…2913` | 21028478 | OUT | 0 | +| `0xee5c7e92…954548` | 36817794 | 2025-10-14 06:55:35 | `0xcbb7c0…33bf` | 18722 | IN | 18722 | +| `0x95aae565…80fda6` | 36817813 | 2025-10-14 06:56:13 | `0x420000…0006` | 5274283594741374 | OUT | 0 | +| `0x95aae565…80fda6` | 36817813 | 2025-10-14 06:56:13 | `0xcbb7c0…33bf` | 18818 | IN | 37540 | +| `0x59eb68a8…284e2f` | 36817834 | 2025-10-14 06:56:55 | `0xbdb930…8ee6` | 37549 | IN | 12241873 | +| `0x59eb68a8…284e2f` | 36817834 | 2025-10-14 06:56:55 | `0xcbb7c0…33bf` | 37540 | OUT | 0 | +| `0x525401cc…63a2d6` | 36861459 | 2025-10-15 07:11:05 | `0x833589…2913` | 19011280 | IN | 19011280 | +| `0xd6c37584…5e8e21` | 36861478 | 2025-10-15 07:11:43 | `0x420000…0006` | 4464474181793630 | IN | 4464474181793630 | +| `0xbad28f5f…2bec15` | 36861846 | 2025-10-15 07:23:59 | `0x420000…0006` | 4464474181793630 | OUT | 0 | +| `0xbad28f5f…2bec15` | 36861846 | 2025-10-15 07:23:59 | `0xcbb7c0…33bf` | 16297 | IN | 16297 | +| `0xc5c53cea…f9cc47` | 36861914 | 2025-10-15 07:26:15 | `0x833589…2913` | 19011280 | OUT | 0 | +| `0xc5c53cea…f9cc47` | 36861914 | 2025-10-15 07:26:15 | `0xcbb7c0…33bf` | 16850 | IN | 33147 | +| `0xe5a8f2fd…28c2d4` | 36861944 | 2025-10-15 07:27:15 | `0xbdb930…8ee6` | 33158 | IN | 12275031 | +| `0xe5a8f2fd…28c2d4` | 36861944 | 2025-10-15 07:27:15 | `0xcbb7c0…33bf` | 33147 | OUT | 0 | +| `0x223cf3f4…620b92` | 36886435 | 2025-10-15 21:03:37 | `0x9ca08b…9a8f` | 200000000000000000000 | IN | 200000000000000000000 | +| `0x0fd31fc3…74df1f` | 36890084 | 2025-10-15 23:05:15 | `0x6a627a…83f0` | 25000000000000000000 | IN | 25000000000000000000 | +| `0xc4dfb164…aeaab7` | 36890266 | 2025-10-15 23:11:19 | `0x9ca08b…9a8f` | 300000000000000000000 | IN | 500000000000000000000 | +| `0x1d374865…45d4a6` | 36901981 | 2025-10-16 05:41:49 | `0xa28be1…4c48` | 10000000000000000000 | IN | 10000000000000000000 | +| `0x54db8012…2cd4a0` | 36903495 | 2025-10-16 06:32:17 | `0x420000…0006` | 2848723441998648 | IN | 2848723441998648 | +| `0xd82c15df…12c7a1` | 36903508 | 2025-10-16 06:32:43 | `0x833589…2913` | 11115934 | IN | 11115934 | +| `0x04dbde68…fd32d5` | 36903664 | 2025-10-16 06:37:55 | `0x4a6272…d7f5` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x4152a1e3…9dc325` | 36903995 | 2025-10-16 06:48:57 | `0x420000…0006` | 2848723441998648 | OUT | 0 | +| `0x4152a1e3…9dc325` | 36903995 | 2025-10-16 06:48:57 | `0xcbb7c0…33bf` | 10262 | IN | 10262 | +| `0xbe8a31df…51733b` | 36904018 | 2025-10-16 06:49:43 | `0xcbb7c0…33bf` | 9944 | IN | 20206 | +| `0xbe8a31df…51733b` | 36904018 | 2025-10-16 06:49:43 | `0x833589…2913` | 11115934 | OUT | 0 | +| `0x2628b7e4…30ee66` | 36904040 | 2025-10-16 06:50:27 | `0xbdb930…8ee6` | 20217 | IN | 12295248 | +| `0x2628b7e4…30ee66` | 36904040 | 2025-10-16 06:50:27 | `0xcbb7c0…33bf` | 20206 | OUT | 0 | +| `0xb2922fd0…cbdfc0` | 36941430 | 2025-10-17 03:36:47 | `0x1ac32a…1630` | 25000000000000000000 | IN | 25000000000000000000 | +| `0xe0b989a4…57e8b8` | 36946245 | 2025-10-17 06:17:17 | `0x833589…2913` | 6649741 | IN | 6649741 | +| `0xb2c97155…bebd5c` | 36946261 | 2025-10-17 06:17:49 | `0x420000…0006` | 1660600639101492 | IN | 1660600639101492 | +| `0xb7cb088b…acec2a` | 36977851 | 2025-10-17 23:50:49 | `0x32153d…3b7c` | 10000000000000000000 | IN | 10000000000000000000 | +| `0xedb76258…91b20d` | 36990285 | 2025-10-18 06:45:17 | `0x833589…2913` | 9962853 | IN | 16612594 | +| `0xcf7aa379…64b62f` | 36990299 | 2025-10-18 06:45:45 | `0x420000…0006` | 2461774316862416 | IN | 4122374955963908 | +| `0x5fc52807…b58a1e` | 37010627 | 2025-10-18 18:03:21 | `0x096bbc…4f25` | 20000000000000000000 | IN | 20000000000000000000 | +| `0xc263e2aa…f401c4` | 37036660 | 2025-10-19 08:31:07 | `0x833589…2913` | 4266040 | IN | 20878634 | +| `0xc3ae7f2c…e905cf` | 37036672 | 2025-10-19 08:31:31 | `0x420000…0006` | 1089060992834058 | IN | 5211435948797966 | +| `0xc05ad3ac…8bde97` | 37036874 | 2025-10-19 08:38:15 | `0x420000…0006` | 13028589871994 | OUT | 5198407358925972 | +| `0xc05ad3ac…8bde97` | 37036874 | 2025-10-19 08:38:15 | `0x420000…0006` | 5198407358925972 | OUT | 0 | +| `0xc05ad3ac…8bde97` | 37036874 | 2025-10-19 08:38:15 | `0xcbb7c0…33bf` | 18833 | IN | 18833 | +| `0x46ac1eb2…605bc5` | 37036895 | 2025-10-19 08:38:57 | `0xcbb7c0…33bf` | 19528 | IN | 38361 | +| `0x46ac1eb2…605bc5` | 37036895 | 2025-10-19 08:38:57 | `0x833589…2913` | 20826438 | OUT | 52196 | +| `0x46ac1eb2…605bc5` | 37036895 | 2025-10-19 08:38:57 | `0x833589…2913` | 52196 | OUT | 0 | +| `0x6b4cd4e6…c29cbe` | 37036919 | 2025-10-19 08:39:45 | `0xbdb930…8ee6` | 38405 | IN | 12333653 | +| `0x6b4cd4e6…c29cbe` | 37036919 | 2025-10-19 08:39:45 | `0xcbb7c0…33bf` | 38361 | OUT | 0 | +| `0x783dc483…eecfc7` | 37037415 | 2025-10-19 08:56:17 | `0x7e2a4a…1f92` | 950 | IN | 950 | +| `0x3641da6a…f82b05` | 37054780 | 2025-10-19 18:35:07 | `0xb47107…cf66` | 3000000000000000000 | IN | 3000000000000000000 | +| `0x6784534b…bc99db` | 37055421 | 2025-10-19 18:56:29 | `0x04953c…c2d1` | 199999999999999983222784 | IN | 200000000000000000000000 | +| `0x6a2d6e51…ce3e1e` | 37058183 | 2025-10-19 20:28:33 | `0x2d2c99…8a3e` | 20000000000000000000 | IN | 20000000000000000000 | +| `0x2824621b…4f315a` | 37120215 | 2025-10-21 06:56:17 | `0x833589…2913` | 12833585 | IN | 12833585 | +| `0x3eb2db86…2a2c4f` | 37120228 | 2025-10-21 06:56:43 | `0x420000…0006` | 3212318843511769 | IN | 3212318843511769 | +| `0x06c452c9…73a788` | 37121094 | 2025-10-21 07:25:35 | `0x420000…0006` | 3212318843511769 | OUT | 0 | +| `0x06c452c9…73a788` | 37121094 | 2025-10-21 07:25:35 | `0xcbb7c0…33bf` | 11551 | IN | 11551 | +| `0x1ba17b8c…eae452` | 37121115 | 2025-10-21 07:26:17 | `0x833589…2913` | 12833585 | OUT | 0 | +| `0x1ba17b8c…eae452` | 37121115 | 2025-10-21 07:26:17 | `0xcbb7c0…33bf` | 11849 | IN | 23400 | +| `0x0a479825…535a4f` | 37121134 | 2025-10-21 07:26:55 | `0xbdb930…8ee6` | 23424 | IN | 12357077 | +| `0x0a479825…535a4f` | 37121134 | 2025-10-21 07:26:55 | `0xcbb7c0…33bf` | 23400 | OUT | 0 | +| `0x40f13488…c9a7d0` | 37162000 | 2025-10-22 06:09:07 | `0x833589…2913` | 10539721 | IN | 10539721 | +| `0x65c0a208…672066` | 37162014 | 2025-10-22 06:09:35 | `0x420000…0006` | 2715399303509136 | IN | 2715399303509136 | +| `0xba570218…95b79a` | 37162198 | 2025-10-22 06:15:43 | `0x833589…2913` | 10539721 | OUT | 0 | +| `0xba570218…95b79a` | 37162198 | 2025-10-22 06:15:43 | `0xcbb7c0…33bf` | 9717 | IN | 9717 | +| `0x413fd0a0…4a84b3` | 37162220 | 2025-10-22 06:16:27 | `0x420000…0006` | 2715399303509136 | OUT | 0 | +| `0x413fd0a0…4a84b3` | 37162220 | 2025-10-22 06:16:27 | `0xcbb7c0…33bf` | 9669 | IN | 19386 | +| `0xda8984a2…3230f5` | 37162240 | 2025-10-22 06:17:07 | `0xbdb930…8ee6` | 19394 | IN | 12376471 | +| `0xda8984a2…3230f5` | 37162240 | 2025-10-22 06:17:07 | `0xcbb7c0…33bf` | 19386 | OUT | 0 | +| `0x9cae154b…7dc73e` | 37164698 | 2025-10-22 07:39:03 | `0xa5fd26…3c56` | 20000000000000000000 | IN | 20000000000000000000 | +| `0x732f8d43…180c8e` | 37168220 | 2025-10-22 09:36:27 | `0x7f0b20…fab3` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x3da19f2e…524535` | 37170021 | 2025-10-22 10:36:29 | `0x110e56…bbdd` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x02a6262d…d3aef1` | 37193478 | 2025-10-22 23:38:23 | `0xc175be…9cfb` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x23a4a08c…101d04` | 37209449 | 2025-10-23 08:30:45 | `0x833589…2913` | 11707460 | IN | 11707460 | +| `0xc1493792…c64421` | 37209461 | 2025-10-23 08:31:09 | `0x420000…0006` | 3038619259828122 | IN | 3038619259828122 | +| `0x87caf2ca…3d0b6a` | 37209640 | 2025-10-23 08:37:07 | `0xcbb7c0…33bf` | 10762 | IN | 10762 | +| `0x87caf2ca…3d0b6a` | 37209640 | 2025-10-23 08:37:07 | `0x420000…0006` | 3038619259828122 | OUT | 0 | +| `0x5bcd5aaa…e1fc99` | 37209660 | 2025-10-23 08:37:47 | `0x833589…2913` | 11707460 | OUT | 0 | +| `0x5bcd5aaa…e1fc99` | 37209660 | 2025-10-23 08:37:47 | `0xcbb7c0…33bf` | 10649 | IN | 21411 | +| `0xf0d72960…ba9a48` | 37209681 | 2025-10-23 08:38:29 | `0xbdb930…8ee6` | 21422 | IN | 12397893 | +| `0xf0d72960…ba9a48` | 37209681 | 2025-10-23 08:38:29 | `0xcbb7c0…33bf` | 21411 | OUT | 0 | +| `0xf770629e…dc089c` | 37210656 | 2025-10-23 09:10:59 | `0xb96a7f…c850` | 800 | IN | 800 | +| `0x98d80ab5…e653a1` | 37213316 | 2025-10-23 10:39:39 | `0x949b0e…f521` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x87b424f3…baa85b` | 37213734 | 2025-10-23 10:53:35 | `0xce7d99…4503` | 1000000000000000000 | IN | 1000000000000000000 | +| `0xf7a89f15…38c098` | 37230423 | 2025-10-23 20:09:53 | `0xd1304e…74af` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x789e8d55…3475f6` | 37249399 | 2025-10-24 06:42:25 | `0x833589…2913` | 3728724144 | IN | 3728724144 | +| `0xee700161…20f85e` | 37249476 | 2025-10-24 06:44:59 | `0x420000…0006` | 1563694951801848 | IN | 1563694951801848 | +| `0xa67b7639…209ebc` | 37250721 | 2025-10-24 07:26:29 | `0x420000…0006` | 1563694951801848 | OUT | 0 | +| `0xa67b7639…209ebc` | 37250721 | 2025-10-24 07:26:29 | `0xcbb7c0…33bf` | 5571 | IN | 5571 | +| `0xc0075249…325ccb` | 37250745 | 2025-10-24 07:27:17 | `0xcbb7c0…33bf` | 5711 | IN | 11282 | +| `0xc0075249…325ccb` | 37250745 | 2025-10-24 07:27:17 | `0x833589…2913` | 6371475 | OUT | 3722352669 | +| `0x65aa9bee…389541` | 37250761 | 2025-10-24 07:27:49 | `0xcbb7c0…33bf` | 11282 | OUT | 0 | +| `0x65aa9bee…389541` | 37250761 | 2025-10-24 07:27:49 | `0xbdb930…8ee6` | 11291 | IN | 12409184 | +| `0xdfcdaf76…a585ba` | 37292535 | 2025-10-25 06:40:17 | `0x420000…0006` | 1399430415647153 | IN | 1399430415647153 | +| `0xb2358d40…9320d3` | 37292547 | 2025-10-25 06:40:41 | `0x833589…2913` | 5342684 | IN | 3727695353 | +| `0xdeae8e71…1f61e5` | 37292854 | 2025-10-25 06:50:55 | `0xcbb7c0…33bf` | 8894 | IN | 8894 | +| `0xdeae8e71…1f61e5` | 37292854 | 2025-10-25 06:50:55 | `0x833589…2913` | 25000 | OUT | 3727670353 | +| `0xdeae8e71…1f61e5` | 37292854 | 2025-10-25 06:50:55 | `0x833589…2913` | 9975000 | OUT | 3717695353 | +| `0xc6fee136…a2cce5` | 37292879 | 2025-10-25 06:51:45 | `0xcbb7c0…33bf` | 4925 | IN | 13819 | +| `0xc6fee136…a2cce5` | 37292879 | 2025-10-25 06:51:45 | `0x420000…0006` | 1399430415647153 | OUT | 0 | +| `0x765efa8b…170d32` | 37292907 | 2025-10-25 06:52:41 | `0xbdb930…8ee6` | 13829 | IN | 12423013 | +| `0x765efa8b…170d32` | 37292907 | 2025-10-25 06:52:41 | `0xcbb7c0…33bf` | 13819 | OUT | 0 | +| `0xd316ed5c…1b0b92` | 37292953 | 2025-10-25 06:54:13 | `0x833589…2913` | 3717695353 | OUT | 0 | +| `0xd316ed5c…1b0b92` | 37292953 | 2025-10-25 06:54:13 | `0x4e65fe…c0ab` | 3717695352 | IN | 3717695352 | +| `0xe0c90a57…b951f0` | 37384194 | 2025-10-27 09:35:35 | `0x833589…2913` | 35627125 | IN | 35627125 | +| `0x441fef96…c36f45` | 37384208 | 2025-10-27 09:36:03 | `0x420000…0006` | 2645313342942932 | IN | 2645313342942932 | +| `0xdd6e4135…3fe24b` | 37384297 | 2025-10-27 09:39:01 | `0x420000…0006` | 6613283357357 | OUT | 2638700059585575 | +| `0xdd6e4135…3fe24b` | 37384297 | 2025-10-27 09:39:01 | `0xcbb7c0…33bf` | 9543 | IN | 9543 | +| `0xdd6e4135…3fe24b` | 37384297 | 2025-10-27 09:39:01 | `0x420000…0006` | 2638700059585575 | OUT | 0 | +| `0xd700528d…163b83` | 37384312 | 2025-10-27 09:39:31 | `0x833589…2913` | 35627125 | OUT | 0 | +| `0xd700528d…163b83` | 37384312 | 2025-10-27 09:39:31 | `0x4e65fe…c0ab` | 36644368 | IN | 3754339720 | +| `0x9fd77753…c132af` | 37384325 | 2025-10-27 09:39:57 | `0xcbb7c0…33bf` | 9543 | OUT | 0 | +| `0x9fd77753…c132af` | 37384325 | 2025-10-27 09:39:57 | `0xbdb930…8ee6` | 9565 | IN | 12432578 | +| `0xa64c5204…75ffd6` | 37443834 | 2025-10-28 18:43:35 | `0x108f04…c466` | 1000000000000000000 | IN | 1000000000000000000 | +| `0xb27680e6…f1239f` | 37465775 | 2025-10-29 06:54:57 | `0x420000…0006` | 2810527913295505 | IN | 2810527913295505 | +| `0xc2ed4ae8…4f850a` | 37465785 | 2025-10-29 06:55:17 | `0x833589…2913` | 10972046 | IN | 10972046 | +| `0x8bc365aa…75b4b9` | 37465992 | 2025-10-29 07:02:11 | `0xcbb7c0…33bf` | 9961 | IN | 9961 | +| `0x8bc365aa…75b4b9` | 37465992 | 2025-10-29 07:02:11 | `0x420000…0006` | 2810527913295505 | OUT | 0 | +| `0x19ef468f…d1d191` | 37466012 | 2025-10-29 07:02:51 | `0xcbb7c0…33bf` | 9665 | IN | 19626 | +| `0x19ef468f…d1d191` | 37466012 | 2025-10-29 07:02:51 | `0x833589…2913` | 10944616 | OUT | 27430 | +| `0x19ef468f…d1d191` | 37466012 | 2025-10-29 07:02:51 | `0x833589…2913` | 27430 | OUT | 0 | +| `0xc9011be6…6aad27` | 37466029 | 2025-10-29 07:03:25 | `0xcbb7c0…33bf` | 19626 | OUT | 0 | +| `0xc9011be6…6aad27` | 37466029 | 2025-10-29 07:03:25 | `0xbdb930…8ee6` | 19645 | IN | 12452223 | +| `0x90ee66e8…4369e6` | 37510521 | 2025-10-30 07:46:29 | `0x420000…0006` | 2798295800569228 | IN | 2798295800569228 | +| `0x7b01319f…cea1fe` | 37510532 | 2025-10-30 07:46:51 | `0x833589…2913` | 10677924 | IN | 10677924 | +| `0x555db7bc…31e7ed` | 37510687 | 2025-10-30 07:52:01 | `0xcbb7c0…33bf` | 9566 | IN | 9566 | +| `0x555db7bc…31e7ed` | 37510687 | 2025-10-30 07:52:01 | `0x833589…2913` | 10677924 | OUT | 0 | +| `0x0c925c72…8dbc83` | 37510708 | 2025-10-30 07:52:43 | `0x420000…0006` | 2798295800569228 | OUT | 0 | +| `0x0c925c72…8dbc83` | 37510708 | 2025-10-30 07:52:43 | `0xcbb7c0…33bf` | 9866 | IN | 19432 | +| `0xa53c0e18…e8ab1a` | 37510729 | 2025-10-30 07:53:25 | `0xcbb7c0…33bf` | 19432 | OUT | 0 | +| `0xa53c0e18…e8ab1a` | 37510729 | 2025-10-30 07:53:25 | `0xbdb930…8ee6` | 19442 | IN | 12471665 | +| `0x72bc7c29…916536` | 37511321 | 2025-10-30 08:13:09 | `0x2fa0b2…10de` | 1777 | IN | 1777 | +| `0x536a6506…85cd62` | 37552219 | 2025-10-31 06:56:25 | `0x420000…0006` | 2030543601814043 | IN | 2030543601814043 | +| `0xf1fac66a…91be72` | 37552231 | 2025-10-31 06:56:49 | `0x833589…2913` | 7381854 | IN | 7381854 | +| `0xaf73b364…9d689f` | 37552745 | 2025-10-31 07:13:57 | `0x833589…2913` | 42427976 | IN | 49809830 | +| `0xf2235dab…b5cadd` | 37552805 | 2025-10-31 07:15:57 | `0x4e65fe…c0ab` | 1913088 | IN | 3756252808 | +| `0xf2235dab…b5cadd` | 37552805 | 2025-10-31 07:15:57 | `0x4e65fe…c0ab` | 7666740 | IN | 3763919548 | +| `0xc8b4acef…99369c` | 37552835 | 2025-10-31 07:16:57 | `0xcbb7c0…33bf` | 45302 | IN | 45302 | +| `0xc8b4acef…99369c` | 37552835 | 2025-10-31 07:16:57 | `0x833589…2913` | 49809830 | OUT | 0 | +| `0x7e729f2b…ede704` | 37552857 | 2025-10-31 07:17:41 | `0x420000…0006` | 5076359004535 | OUT | 2025467242809508 | +| `0x7e729f2b…ede704` | 37552857 | 2025-10-31 07:17:41 | `0x420000…0006` | 2025467242809508 | OUT | 0 | +| `0x7e729f2b…ede704` | 37552857 | 2025-10-31 07:17:41 | `0xcbb7c0…33bf` | 7078 | IN | 52380 | +| `0xc817ac58…5dcb3e` | 37552881 | 2025-10-31 07:18:29 | `0xcbb7c0…33bf` | 50305 | IN | 102685 | +| `0x02a7dddc…aed00f` | 37552903 | 2025-10-31 07:19:13 | `0xbdb930…8ee6` | 102695 | IN | 12574360 | +| `0x02a7dddc…aed00f` | 37552903 | 2025-10-31 07:19:13 | `0xcbb7c0…33bf` | 102685 | OUT | 0 | +| `0x5d1772b8…2351a9` | 37560394 | 2025-10-31 11:28:55 | `0x0a538a…5d21` | 1000000000000000000 | IN | 1000000000000000000 | +| `0xbd990d8f…07e927` | 37593275 | 2025-11-01 05:44:57 | `0xffb18d…3e09` | 1000000000000000000 | IN | 1000000000000000000 | +| `0xc622a999…751f79` | 37598890 | 2025-11-01 08:52:07 | `0x833589…2913` | 6597514 | IN | 6597514 | +| `0x183c0609…4dbf8d` | 37598901 | 2025-11-01 08:52:29 | `0x420000…0006` | 1284811283789103 | IN | 1284811283789103 | +| `0xd8fb9f08…ff00f9` | 37599143 | 2025-11-01 09:00:33 | `0x833589…2913` | 14526676 | IN | 21124190 | +| `0x5c1a9c68…af2dc9` | 37599191 | 2025-11-01 09:02:09 | `0x833589…2913` | 21071380 | OUT | 52810 | +| `0x5c1a9c68…af2dc9` | 37599191 | 2025-11-01 09:02:09 | `0xcbb7c0…33bf` | 19135 | IN | 19135 | +| `0x5c1a9c68…af2dc9` | 37599191 | 2025-11-01 09:02:09 | `0x833589…2913` | 52810 | OUT | 0 | +| `0xf255bbaa…d36139` | 37599210 | 2025-11-01 09:02:47 | `0xcbb7c0…33bf` | 4509 | IN | 23644 | +| `0xf255bbaa…d36139` | 37599210 | 2025-11-01 09:02:47 | `0x420000…0006` | 1284811283789103 | OUT | 0 | +| `0xe00627f8…447708` | 37599228 | 2025-11-01 09:03:23 | `0xbdb930…8ee6` | 23657 | IN | 12598017 | +| `0xe00627f8…447708` | 37599228 | 2025-11-01 09:03:23 | `0xcbb7c0…33bf` | 23644 | OUT | 0 | +| `0x5aba5c04…fc5791` | 37599272 | 2025-11-01 09:04:51 | `0xa340fa…fc24` | 2300 | IN | 2300 | +| `0x3675e908…de3938` | 37619597 | 2025-11-01 20:22:21 | `0x78cdbe…8cbd` | 100000000000000000000 | IN | 100000000000000000000 | +| `0x18e181ab…5ca3f3` | 37682491 | 2025-11-03 07:18:49 | `0x833589…2913` | 17636128 | IN | 17636128 | +| `0xfa7870f4…369200` | 37682504 | 2025-11-03 07:19:15 | `0x420000…0006` | 1597945793587525 | IN | 1597945793587525 | +| `0x2bb939d8…0222e9` | 37682555 | 2025-11-03 07:20:57 | `0x833589…2913` | 17636128 | OUT | 0 | +| `0x2bb939d8…0222e9` | 37682555 | 2025-11-03 07:20:57 | `0xcbb7c0…33bf` | 16380 | IN | 16380 | +| `0xc8773036…594ed1` | 37682578 | 2025-11-03 07:21:43 | `0xcbb7c0…33bf` | 5515 | IN | 21895 | +| `0xc8773036…594ed1` | 37682578 | 2025-11-03 07:21:43 | `0x420000…0006` | 1597945793587525 | OUT | 0 | +| `0x5f931306…ef0653` | 37682604 | 2025-11-03 07:22:35 | `0xcbb7c0…33bf` | 21895 | OUT | 0 | +| `0x5f931306…ef0653` | 37682604 | 2025-11-03 07:22:35 | `0xbdb930…8ee6` | 21918 | IN | 12619935 | +| `0xfa182b9d…f26fbd` | 37704527 | 2025-11-03 19:33:21 | `0xb66799…a099` | 100000000000000000000 | IN | 100000000000000000000 | +| `0x1237edb9…fde99a` | 37704552 | 2025-11-03 19:34:11 | `0xb66799…a099` | 100000000000000000000 | IN | 200000000000000000000 | +| `0x51541d2a…c4be1e` | 37710302 | 2025-11-03 22:45:51 | `0x4ba9ac…7d31` | 100000000000000000000 | IN | 100000000000000000000 | +| `0x96925ac3…404fd2` | 37711493 | 2025-11-03 23:25:33 | `0x4ba9ac…7d31` | 100000000000000000000 | IN | 200000000000000000000 | +| `0xf296363e…337a48` | 37731943 | 2025-11-04 10:47:13 | `0x4744b6…4e84` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x67b0a86d…15e589` | 37746632 | 2025-11-04 18:56:51 | `0x833589…2913` | 3766143577 | IN | 3766143577 | +| `0x67b0a86d…15e589` | 37746632 | 2025-11-04 18:56:51 | `0x4e65fe…c0ab` | 3763919549 | OUT | -1 | +| `0xf8fc9457…54f92a` | 37746688 | 2025-11-04 18:58:43 | `0xcbb7c0…33bf` | 3713729 | IN | 3713729 | +| `0xf8fc9457…54f92a` | 37746688 | 2025-11-04 18:58:43 | `0x833589…2913` | 3766143577 | OUT | 0 | +| `0x7c628198…41ee7a` | 37746715 | 2025-11-04 18:59:37 | `0xbdb930…8ee6` | 3713747 | IN | 16333682 | +| `0x7c628198…41ee7a` | 37746715 | 2025-11-04 18:59:37 | `0xcbb7c0…33bf` | 3713729 | OUT | 0 | +| `0x51623f91…90f088` | 37836612 | 2025-11-06 20:56:11 | `0x82ac04…e198` | 200000000000000000000 | IN | 200000000000000000000 | +| `0xde40db9b…495c7c` | 37902197 | 2025-11-08 09:22:21 | `0x833589…2913` | 36006042 | IN | 36006042 | +| `0x421ce677…44e86b` | 37902210 | 2025-11-08 09:22:47 | `0x420000…0006` | 5117740967835488 | IN | 5117740967835488 | +| `0x3b5a5cfb…5ce4c6` | 37902386 | 2025-11-08 09:28:39 | `0x833589…2913` | 36006042 | OUT | 0 | +| `0x9a3347d9…8fb068` | 37902398 | 2025-11-08 09:29:03 | `0x420000…0006` | 5117740967835488 | OUT | 0 | +| `0x2a469889…adfdbd` | 37902523 | 2025-11-08 09:33:13 | `0x1619d9…4435` | 36006042 | OUT | -18906006042 | +| `0xd9dd89ba…e2118a` | 37924798 | 2025-11-08 21:55:43 | `0xd93d02…3f4b` | 1000000000000000000 | IN | 1000000000000000000 | +| `0xafeb344e…636ba9` | 37944178 | 2025-11-09 08:41:43 | `0x420000…0006` | 3112259982337328 | IN | 3112259982337328 | +| `0x23a237dd…af33af` | 37944192 | 2025-11-09 08:42:11 | `0x833589…2913` | 10111098 | IN | 10111098 | +| `0x03b16bf8…6f12eb` | 37944246 | 2025-11-09 08:43:59 | `0x420000…0006` | 7780649955843 | OUT | 3104479332381485 | +| `0x03b16bf8…6f12eb` | 37944246 | 2025-11-09 08:43:59 | `0xcbb7c0…33bf` | 10402 | IN | 10402 | +| `0x03b16bf8…6f12eb` | 37944246 | 2025-11-09 08:43:59 | `0x420000…0006` | 3104479332381485 | OUT | 0 | +| `0x090b5d2b…998de4` | 37944261 | 2025-11-09 08:44:29 | `0x1e3585…5e21` | 8888 | IN | 8888 | +| `0x68792bf9…3245df` | 37944266 | 2025-11-09 08:44:39 | `0x833589…2913` | 25277 | OUT | 10085821 | +| `0x68792bf9…3245df` | 37944266 | 2025-11-09 08:44:39 | `0xcbb7c0…33bf` | 9939 | IN | 20341 | +| `0x68792bf9…3245df` | 37944266 | 2025-11-09 08:44:39 | `0x833589…2913` | 10085821 | OUT | 0 | +| `0x2ce58002…084fc6` | 37944287 | 2025-11-09 08:45:21 | `0xcbb7c0…33bf` | 20341 | OUT | 0 | +| `0x2ce58002…084fc6` | 37944287 | 2025-11-09 08:45:21 | `0xbdb930…8ee6` | 20436 | IN | 16354118 | +| `0xca18913c…552b5c` | 37944783 | 2025-11-09 09:01:53 | `0xf0c9af…3c57` | 880 | IN | 880 | +| `0x979e2702…6b8c39` | 38024071 | 2025-11-11 05:04:49 | `0x9032ad…d5b4` | 9000000000000000000000 | IN | 9000000000000000000000 | +| `0x1e776bb4…aec3b3` | 38029775 | 2025-11-11 08:14:57 | `0x833589…2913` | 26669847 | IN | 26669847 | +| `0xb5861243…4f3835` | 38029790 | 2025-11-11 08:15:27 | `0x420000…0006` | 7223109901530816 | IN | 7223109901530816 | +| `0x068b4fa7…0b091a` | 38030008 | 2025-11-11 08:22:43 | `0x833589…2913` | 66675 | OUT | 26603172 | +| `0x068b4fa7…0b091a` | 38030008 | 2025-11-11 08:22:43 | `0xcbb7c0…33bf` | 25261 | IN | 25261 | +| `0x068b4fa7…0b091a` | 38030008 | 2025-11-11 08:22:43 | `0x833589…2913` | 26603172 | OUT | 0 | +| `0x2e0f8e1d…b41485` | 38030031 | 2025-11-11 08:23:29 | `0xcbb7c0…33bf` | 24370 | IN | 49631 | +| `0x2e0f8e1d…b41485` | 38030031 | 2025-11-11 08:23:29 | `0x420000…0006` | 7223109901530816 | OUT | 0 | +| `0xae95502f…201220` | 38030048 | 2025-11-11 08:24:03 | `0xcbb7c0…33bf` | 49631 | OUT | 0 | +| `0xae95502f…201220` | 38030048 | 2025-11-11 08:24:03 | `0xbdb930…8ee6` | 49670 | IN | 16403788 | +| `0xb3341a02…96af40` | 38035758 | 2025-11-11 11:34:23 | `0x85ca69…6758` | 10000000000000000000 | IN | 10000000000000000000 | +| `0xde9fbd0d…4c6f1d` | 38072017 | 2025-11-12 07:43:01 | `0x833589…2913` | 12340777 | IN | 12340777 | +| `0x8d1f27e9…fe2285` | 38072029 | 2025-11-12 07:43:25 | `0x420000…0006` | 3744089812086374 | IN | 3744089812086374 | +| `0x93a7d2ad…e768ea` | 38072071 | 2025-11-12 07:44:49 | `0xcbb7c0…33bf` | 12447 | IN | 12447 | +| `0x93a7d2ad…e768ea` | 38072071 | 2025-11-12 07:44:49 | `0x420000…0006` | 3734729587556158 | OUT | 9360224530216 | +| `0x93a7d2ad…e768ea` | 38072071 | 2025-11-12 07:44:49 | `0x420000…0006` | 9360224530216 | OUT | 0 | +| `0xcd59b793…45c6ee` | 38072092 | 2025-11-12 07:45:31 | `0xcbb7c0…33bf` | 11903 | IN | 24350 | +| `0xcd59b793…45c6ee` | 38072092 | 2025-11-12 07:45:31 | `0x833589…2913` | 12340777 | OUT | 0 | +| `0x5a7b4687…7a6853` | 38072107 | 2025-11-12 07:46:01 | `0xbdb930…8ee6` | 24369 | IN | 16428157 | +| `0x5a7b4687…7a6853` | 38072107 | 2025-11-12 07:46:01 | `0xcbb7c0…33bf` | 24350 | OUT | 0 | +| `0x45786602…ca0c0b` | 38078867 | 2025-11-12 11:31:21 | `0xd23b84…9c3e` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x999f041d…42973f` | 38109576 | 2025-11-13 04:34:59 | `0x2d137c…5fbf` | 10000000000000000000 | IN | 10000000000000000000 | +| `0x02d0545f…13e938` | 38118767 | 2025-11-13 09:41:21 | `0x833589…2913` | 22434157 | IN | 22434157 | +| `0x52446e4b…44dee1` | 38118781 | 2025-11-13 09:41:49 | `0x420000…0006` | 6379367469939676 | IN | 6379367469939676 | +| `0xe6fd5968…985919` | 38118916 | 2025-11-13 09:46:19 | `0x833589…2913` | 56085 | OUT | 22378072 | +| `0xe6fd5968…985919` | 38118916 | 2025-11-13 09:46:19 | `0x833589…2913` | 22378072 | OUT | 0 | +| `0xe6fd5968…985919` | 38118916 | 2025-11-13 09:46:19 | `0xcbb7c0…33bf` | 21636 | IN | 21636 | +| `0xb29ae846…a4091a` | 38118938 | 2025-11-13 09:47:03 | `0x420000…0006` | 6363419051264827 | OUT | 15948418674849 | +| `0xb29ae846…a4091a` | 38118938 | 2025-11-13 09:47:03 | `0x420000…0006` | 15948418674849 | OUT | 0 | +| `0xb29ae846…a4091a` | 38118938 | 2025-11-13 09:47:03 | `0xcbb7c0…33bf` | 21456 | IN | 43092 | +| `0xee76cfcf…603442` | 38118961 | 2025-11-13 09:47:49 | `0xbdb930…8ee6` | 43113 | IN | 16471270 | +| `0xee76cfcf…603442` | 38118961 | 2025-11-13 09:47:49 | `0xcbb7c0…33bf` | 43092 | OUT | 0 | +| `0xa56471be…d1e7c4` | 38158632 | 2025-11-14 07:50:11 | `0x420000…0006` | 2534300917923168 | IN | 2534300917923168 | +| `0x72cba5f1…b936b9` | 38158644 | 2025-11-14 07:50:35 | `0x833589…2913` | 7659773 | IN | 7659773 | +| `0xed73e0e2…ddd1db` | 38158701 | 2025-11-14 07:52:29 | `0xcbb7c0…33bf` | 8386 | IN | 8386 | +| `0xed73e0e2…ddd1db` | 38158701 | 2025-11-14 07:52:29 | `0x420000…0006` | 2534300917923168 | OUT | 0 | +| `0xc74cddbb…18fd69` | 38158719 | 2025-11-14 07:53:05 | `0x833589…2913` | 7640624 | OUT | 19149 | +| `0xc74cddbb…18fd69` | 38158719 | 2025-11-14 07:53:05 | `0xcbb7c0…33bf` | 7841 | IN | 16227 | +| `0xc74cddbb…18fd69` | 38158719 | 2025-11-14 07:53:05 | `0x833589…2913` | 19149 | OUT | 0 | +| `0x69f159d1…5a3979` | 38158737 | 2025-11-14 07:53:41 | `0xcbb7c0…33bf` | 16227 | OUT | 0 | +| `0x69f159d1…5a3979` | 38158737 | 2025-11-14 07:53:41 | `0xbdb930…8ee6` | 16246 | IN | 16487516 | +| `0xdb05c4ce…bd2880` | 38165597 | 2025-11-14 11:42:21 | `0xcb1e7c…3f11` | 2000000000000000000 | IN | 2000000000000000000 | +| `0x017a8b11…650519` | 38183883 | 2025-11-14 21:51:53 | `0x8c17bb…9630` | 10000000000000000000 | IN | 10000000000000000000 | +| `0x055c9239…043281` | 38192013 | 2025-11-15 02:22:53 | `0x21e2ff…9c26` | 2394000000000000000 | IN | 2394000000000000000 | +| `0x101d5765…6b9763` | 38204634 | 2025-11-15 09:23:35 | `0xbbd2ad…3634` | 2000000000000000000 | IN | 2000000000000000000 | +| `0x2514eac1…681c2d` | 38281994 | 2025-11-17 04:22:15 | `0x19bfd7…b3cc` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x75f49834…9901de` | 38288135 | 2025-11-17 07:46:57 | `0x833589…2913` | 21925082 | IN | 21925082 | +| `0xdd348786…5ec951` | 38288180 | 2025-11-17 07:48:27 | `0xcbb7c0…33bf` | 22805 | IN | 22805 | +| `0xdd348786…5ec951` | 38288180 | 2025-11-17 07:48:27 | `0x833589…2913` | 21925082 | OUT | 0 | +| `0xf37116c4…dd6b6e` | 38288196 | 2025-11-17 07:48:59 | `0xbdb930…8ee6` | 22882 | IN | 16510398 | +| `0xf37116c4…dd6b6e` | 38288196 | 2025-11-17 07:48:59 | `0xcbb7c0…33bf` | 22805 | OUT | 0 | +| `0xbae7cbdb…4d1656` | 38289909 | 2025-11-17 08:46:05 | `0xaa75ea…05ba` | 1000000000000000000 | IN | 1000000000000000000 | +| `0xe3e5f874…4d5e8f` | 38293280 | 2025-11-17 10:38:27 | `0xde37f6…f148` | 189909384000000000000 | IN | 189909384000000000000 | +| `0x850a9ae0…63e3ad` | 38296435 | 2025-11-17 12:23:37 | `0xbbe052…5a5c` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x5ff16646…aaa9c4` | 38305202 | 2025-11-17 17:15:51 | `0x976e65…acba` | 25000000000000000000 | IN | 25000000000000000000 | +| `0xd56df8db…749332` | 38317857 | 2025-11-18 00:17:41 | `0xcd15cb…d40b` | 95000000000000000000 | IN | 95000000000000000000 | +| `0x1b828b90…76e85b` | 38324734 | 2025-11-18 04:06:55 | `0xfc842d…0b78` | 1000000000000000000 | IN | 1000000000000000000 | +| `0xfd927396…5004e9` | 38375205 | 2025-11-19 08:09:17 | `0x833589…2913` | 4494323022 | IN | 4494323022 | +| `0x03c8c951…30113f` | 38375331 | 2025-11-19 08:13:29 | `0x833589…2913` | 4494323022 | OUT | 0 | +| `0x03c8c951…30113f` | 38375331 | 2025-11-19 08:13:29 | `0x4e65fe…c0ab` | 4494323021 | IN | 4494323020 | +| `0xe8262709…5740c0` | 38403530 | 2025-11-19 23:53:27 | `0xdfc6a7…4512` | 25000000000000000000 | IN | 25000000000000000000 | +| `0xc18c9097…3de135` | 38403636 | 2025-11-19 23:56:59 | `0xdfc6a7…4512` | 1000000000000000000 | IN | 26000000000000000000 | +| `0x26225b2e…e3a2d3` | 38599104 | 2025-11-24 12:32:35 | `0x833589…2913` | 4496583247 | IN | 4496583247 | +| `0x26225b2e…e3a2d3` | 38599104 | 2025-11-24 12:32:35 | `0x4e65fe…c0ab` | 4494323021 | OUT | -1 | +| `0xa15e33f2…128a5d` | 38599184 | 2025-11-24 12:35:15 | `0x833589…2913` | 4496583247 | OUT | 0 | +| `0x17f89fbb…733d3a` | 38635325 | 2025-11-25 08:39:57 | `0x21c01e…613e` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x17f89fbb…733d3a` | 38635325 | 2025-11-25 08:39:57 | `0x420000…0006` | 1 | IN | 1 | +| `0x2ef2fb15…c58e43` | 38646133 | 2025-11-25 14:40:13 | `0x57b0d4…c3c9` | 1000000000000000000 | IN | 1000000000000000000 | +| `0x2ef2fb15…c58e43` | 38646133 | 2025-11-25 14:40:13 | `0x420000…0006` | 1 | IN | 2 | +| `0x1d12498a…ee345f` | 38766160 | 2025-11-28 09:21:07 | `0x9de652…3975` | 20000000000000000000 | IN | 20000000000000000000 | +| `0x03853811…44b96c` | 38853246 | 2025-11-30 09:43:59 | `0xe248c0…4380` | 35559165065377214226 | IN | 35559165065377214226 | +| `0xb08327b8…9290ad` | 38893039 | 2025-12-01 07:50:25 | `0xcbb7c0…33bf` | 55399 | IN | 55399 | +| `0xb08327b8…9290ad` | 38893039 | 2025-12-01 07:50:25 | `0xe248c0…4380` | 35559165065377214226 | OUT | 0 | +| `0xb00e48bf…d1d56c` | 38893062 | 2025-12-01 07:51:11 | `0xbdb930…8ee6` | 55973 | IN | 16566371 | +| `0xb00e48bf…d1d56c` | 38893062 | 2025-12-01 07:51:11 | `0xcbb7c0…33bf` | 55399 | OUT | 0 | +| `0xc81354be…74ed46` | 38893509 | 2025-12-01 08:06:05 | `0x1575f7…db90` | 777 | IN | 777 | +| `0x01e8a634…978318` | 38909578 | 2025-12-01 17:01:43 | `0xd16884…21ee` | 10000000000000000000 | IN | 10000000000000000000 | +| `0x65bbb098…30e8c2` | 38913593 | 2025-12-01 19:15:33 | `0x596923…8d2e` | 999999999999999983222784 | IN | 1000000000000000000000000 | +| `0x6ae67c0c…3655fc` | 38931057 | 2025-12-02 04:57:41 | `0x8c6408…5744` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x2f71d78c…0f4a85` | 39023177 | 2025-12-04 08:08:21 | `0x49b31d…b8b5` | 500000000000000000 | IN | 500000000000000000 | +| `0x2ade167d…22d634` | 39040325 | 2025-12-04 17:39:57 | `0xb11462…5e75` | 50000000000000000000 | IN | 50000000000000000000 | +| `0x5cfda145…e53150` | 39073902 | 2025-12-05 12:19:11 | `0x49b31d…b8b5` | 500000000000000000 | IN | 1000000000000000000 | +| `0xe202284a…13c9fa` | 39109335 | 2025-12-06 08:00:17 | `0x49b31d…b8b5` | 500000000000000000 | IN | 1500000000000000000 | +| `0xcb2f1536…17b9cd` | 39155228 | 2025-12-07 09:30:03 | `0x49b31d…b8b5` | 500000000000000000 | IN | 2000000000000000000 | +| `0x9bf483a5…30d159` | 39175914 | 2025-12-07 20:59:35 | `0x62dfcd…b070` | 100000000000000000000 | IN | 100000000000000000000 | +| `0x8a2a83d3…d0f4b6` | 39195670 | 2025-12-08 07:58:07 | `0x49b31d…b8b5` | 500000000000000000 | IN | 2500000000000000000 | +| `0x80783d57…325828` | 39238678 | 2025-12-09 07:51:43 | `0x49b31d…b8b5` | 500000000000000000 | IN | 3000000000000000000 | +| `0xe6c986e2…107275` | 39280096 | 2025-12-10 06:52:19 | `0x49b31d…b8b5` | 500000000000000000 | IN | 3500000000000000000 | +| `0xb474e13e…a766ed` | 39367713 | 2025-12-12 07:32:53 | `0x49b31d…b8b5` | 500000000000000000 | IN | 4000000000000000000 | +| `0x9f1be49c…747f14` | 39367767 | 2025-12-12 07:34:41 | `0x4e65fe…c0ab` | 5794552 | IN | 5794551 | +| `0x4766c869…d6c481` | 41229119 | 2026-01-24 09:39:45 | `0x4e65fe…c0ab` | 10272181 | IN | 16066732 | +| `0x4766c869…d6c481` | 41229119 | 2026-01-24 09:39:45 | `0x4e65fe…c0ab` | 24489 | IN | 16091221 | +| `0xe855a375…aa812b` | 41229226 | 2026-01-24 09:43:19 | `0x833589…2913` | 300000000 | IN | 300000000 | +| `0xe855a375…aa812b` | 41229226 | 2026-01-24 09:43:19 | `0x59dca0…4c28` | 411734329 | IN | 6796950322 | +| `0x9181e78c…a3f3ed` | 41229352 | 2026-01-24 09:47:31 | `0x833589…2913` | 300000000 | OUT | 0 | +| `0x9181e78c…a3f3ed` | 41229352 | 2026-01-24 09:47:31 | `0xcbb7c0…33bf` | 335208 | IN | 335208 | +| `0x1b097af6…3df5a2` | 41229377 | 2026-01-24 09:48:21 | `0xcbb7c0…33bf` | 335208 | OUT | 0 | +| `0x1b097af6…3df5a2` | 41229377 | 2026-01-24 09:48:21 | `0xbdb930…8ee6` | 336773 | IN | 16903144 | +| `0xedf02c9d…29032d` | 41574320 | 2026-02-01 09:26:27 | `0x833589…2913` | 500000000 | IN | 500000000 | +| `0x8873c980…766ce5` | 41574404 | 2026-02-01 09:29:15 | `0x833589…2913` | 500000000 | OUT | 0 | +| `0x8873c980…766ce5` | 41574404 | 2026-02-01 09:29:15 | `0xcbb7c0…33bf` | 635883 | IN | 635883 | +| `0x61204937…550312` | 41574428 | 2026-02-01 09:30:03 | `0xcbb7c0…33bf` | 635883 | OUT | 0 | +| `0x61204937…550312` | 41574428 | 2026-02-01 09:30:03 | `0xbdb930…8ee6` | 636106 | IN | 17539250 | +| `0x6ebe914d…3a993a` | 41574587 | 2026-02-01 09:35:21 | `0x833589…2913` | 2815542619 | IN | 2815542619 | +| `0xa3a6efd2…ba426a` | 41574859 | 2026-02-01 09:44:25 | `0xad03e2…0c4e` | 500 | IN | 500 | +| `0xe2a30a38…764b41` | 41576034 | 2026-02-01 10:23:35 | `0x833589…2913` | 2815542619 | OUT | 0 | +| `0xe2a30a38…764b41` | 41576034 | 2026-02-01 10:23:35 | `0x4e65fe…c0ab` | 2815556148 | IN | 2831647369 | +| `0x81bfba94…63b1e7` | 41584183 | 2026-02-01 14:55:13 | `0x6f01bd…361d` | 22000000000000000000 | IN | 22000000000000000000 | +| `0x9e6dbc23…7b475e` | 41830159 | 2026-02-07 07:34:25 | `0x833589…2913` | 94331106 | IN | 94331106 | +| `0x63ac9766…91c201` | 41831708 | 2026-02-07 08:26:03 | `0x833589…2913` | 5504749619 | IN | 5599080725 | +| `0x970f5442…e4aa43` | 41831727 | 2026-02-07 08:26:41 | `0x833589…2913` | 5599080725 | OUT | 0 | +| `0x970f5442…e4aa43` | 41831727 | 2026-02-07 08:26:41 | `0x4e65fe…c0ab` | 5601149651 | IN | 8432797020 | +| `0x9b3a74c1…43a41d` | 41831742 | 2026-02-07 08:27:11 | `0x4e65fe…c0ab` | 10698667 | IN | 8443495687 | +| `0x9b3a74c1…43a41d` | 41831742 | 2026-02-07 08:27:11 | `0x4e65fe…c0ab` | 311 | IN | 8443495998 | +| `0xa07b9728…75a269` | 41831784 | 2026-02-07 08:28:35 | `0xd4a0e0…8bb7` | 999999999999999999 | IN | 999999999999999999 | +| `0x279c0089…7f6a78` | 41831809 | 2026-02-07 08:29:25 | `0xb5e33b…459d` | 9000000000 | IN | 9000000000 | +| `0xb676584c…4c0ec7` | 41854278 | 2026-02-07 20:58:23 | `0xec7020…5098` | 25000000000000000000 | IN | 25000000000000000000 | +| `0x8bbe1de2…332278` | 41916753 | 2026-02-09 07:40:53 | `0xa4b694…c3ee` | 25000000000000000000 | IN | 25000000000000000000 | +| `0xe7cb18d3…febcbd` | 42134483 | 2026-02-14 08:38:33 | `0x833589…2913` | 500000000 | IN | 500000000 | +| `0xe7cb18d3…febcbd` | 42134483 | 2026-02-14 08:38:33 | `0x4e65fe…c0ab` | 494200356 | OUT | 7949295642 | +| `0x4aa0c8df…a515f5` | 42134515 | 2026-02-14 08:39:37 | `0xcbb7c0…33bf` | 717276 | IN | 717276 | +| `0x4aa0c8df…a515f5` | 42134515 | 2026-02-14 08:39:37 | `0x833589…2913` | 500000000 | OUT | 0 | +| `0x644255e7…3cbd9d` | 42134530 | 2026-02-14 08:40:07 | `0xbdb930…8ee6` | 717639 | IN | 18256889 | +| `0x644255e7…3cbd9d` | 42134530 | 2026-02-14 08:40:07 | `0xcbb7c0…33bf` | 717276 | OUT | 0 | +| `0x89386965…ead2ee` | 42134541 | 2026-02-14 08:40:29 | `0xed414a…a5f1` | 9000000000 | IN | 9000000000 | +| `0xa078a999…e36c88` | 42134597 | 2026-02-14 08:42:21 | `0xbb13ed…e14c` | 9000000000 | IN | 9000000000 | +| `0xa76b09c4…c246a3` | 42134891 | 2026-02-14 08:52:09 | `0x377694…28fc` | 9000000000 | IN | 9000000000 | +| `0xde5109dd…f47173` | 42436295 | 2026-02-21 08:18:57 | `0x4e65fe…c0ab` | 994818216 | OUT | 6954477426 | +| `0xde5109dd…f47173` | 42436295 | 2026-02-21 08:18:57 | `0x833589…2913` | 1000000000 | IN | 1000000000 | +| `0xdecf5db5…d1f593` | 42436330 | 2026-02-21 08:20:07 | `0x833589…2913` | 1000000000 | OUT | 0 | +| `0xdecf5db5…d1f593` | 42436330 | 2026-02-21 08:20:07 | `0xcbb7c0…33bf` | 1472042 | IN | 1472042 | +| `0xca02b7c2…263cf3` | 42436348 | 2026-02-21 08:20:43 | `0xcbb7c0…33bf` | 1472042 | OUT | 0 | +| `0xca02b7c2…263cf3` | 42436348 | 2026-02-21 08:20:43 | `0xbdb930…8ee6` | 1472180 | IN | 19729069 | +| `0x21071d45…35154d` | 42436488 | 2026-02-21 08:25:23 | `0x2abb46…63dc` | 9000000000 | IN | 9000000000 | +| `0xf1d14b91…da7f27` | 42737712 | 2026-02-28 07:46:11 | `0x4e65fe…c0ab` | 995664253 | OUT | 5958813173 | +| `0xf1d14b91…da7f27` | 42737712 | 2026-02-28 07:46:11 | `0x833589…2913` | 1000000000 | IN | 1000000000 | +| `0x1beefe6b…b26f3c` | 42737737 | 2026-02-28 07:47:01 | `0x833589…2913` | 1000000000 | OUT | 0 | +| `0x1beefe6b…b26f3c` | 42737737 | 2026-02-28 07:47:01 | `0xcbb7c0…33bf` | 1562497 | IN | 1562497 | +| `0x93021f1a…bfffc6` | 42737754 | 2026-02-28 07:47:35 | `0xcbb7c0…33bf` | 1562497 | OUT | 0 | +| `0x93021f1a…bfffc6` | 42737754 | 2026-02-28 07:47:35 | `0xbdb930…8ee6` | 1562581 | IN | 21291650 | +| `0xe522e4b2…17e6dc` | 42737946 | 2026-02-28 07:53:59 | `0x754202…520c` | 9000000000 | IN | 9000000000 | +| `0xbccbaff7…e5825f` | 43342573 | 2026-03-14 07:48:13 | `0x4e65fe…c0ab` | 5958813176 | OUT | -3 | +| `0xbccbaff7…e5825f` | 43342573 | 2026-03-14 07:48:13 | `0x833589…2913` | 5965138685 | IN | 5965138685 | +| `0x94371dc7…6a9cbf` | 43342621 | 2026-03-14 07:49:49 | `0x59dca0…4c28` | 542715125 | IN | 7339665447 | +| `0x94371dc7…6a9cbf` | 43342621 | 2026-03-14 07:49:49 | `0x833589…2913` | 500000000 | IN | 6465138685 | +| `0x1a5b85dc…8bfc49` | 43342702 | 2026-03-14 07:52:31 | `0x833589…2913` | 1000000000 | OUT | 5465138685 | +| `0x1a5b85dc…8bfc49` | 43342702 | 2026-03-14 07:52:31 | `0xcbb7c0…33bf` | 1416561 | IN | 1416561 | +| `0x1fc00c34…345c17` | 43342725 | 2026-03-14 07:53:17 | `0xbdb930…8ee6` | 1416790 | IN | 22708440 | +| `0x1fc00c34…345c17` | 43342725 | 2026-03-14 07:53:17 | `0xcbb7c0…33bf` | 1416561 | OUT | 0 | +| `0x41203b8a…281fea` | 43342761 | 2026-03-14 07:54:29 | `0xb0b076…b1e3` | 9000000000 | IN | 9000000000 | +| `0xcddb7628…b4e23c` | 43342813 | 2026-03-14 07:56:13 | `0x359b6c…261c` | 9000000000 | IN | 9000000000 | +| `0x91259c4b…ef042d` | 43343130 | 2026-03-14 08:06:47 | `0x833589…2913` | 5465138685 | OUT | 0 | +| `0x5733d48e…3051a0` | 43343134 | 2026-03-14 08:06:55 | `0x833589…2913` | 546 | IN | 546 | +| `0x35e3002c…f1f956` | 43343219 | 2026-03-14 08:09:45 | `0xeb9caa…b309` | 5465138685 | OUT | -19105138685 | +| `0x271dd80d…bdd3d6` | 43343627 | 2026-03-14 08:23:21 | `0x833589…2913` | 10 | IN | 556 | +| `0x87dd8bcf…83c9cf` | 43343697 | 2026-03-14 08:25:41 | `0x9e0235…a0f0` | 5465138685 | OUT | -5465138685 | +| `0x8d8771b0…f3e7e4` | 43343735 | 2026-03-14 08:26:57 | `0x833589…2913` | 36 | IN | 592 | +| `0x34a19027…7e1e24` | 43345060 | 2026-03-14 09:11:07 | `0xeb9caa…b309` | 5465138685 | OUT | -24570277370 | +| `0x2cd73e2f…692f47` | 43345687 | 2026-03-14 09:32:01 | `0x9e0235…a0f0` | 5465138685 | OUT | -10930277370 | +| `0x0a125ef3…9a98fe` | 43687295 | 2026-03-22 07:18:57 | `0x59dca0…4c28` | 406003069 | IN | 7745668516 | +| `0x0a125ef3…9a98fe` | 43687295 | 2026-03-22 07:18:57 | `0x833589…2913` | 400000000 | IN | 400000592 | +| `0x8e431121…63141c` | 43687366 | 2026-03-22 07:21:19 | `0x833589…2913` | 598948063 | IN | 998948655 | +| `0xf5c76366…829436` | 43687371 | 2026-03-22 07:21:29 | `0x833589…2913` | 598 | IN | 998949253 | +| `0x4f8e3f1b…2f5e0d` | 43687403 | 2026-03-22 07:22:33 | `0x833589…2913` | 998949253 | OUT | 0 | +| `0x4f8e3f1b…2f5e0d` | 43687403 | 2026-03-22 07:22:33 | `0xcbb7c0…33bf` | 1444091 | IN | 1444091 | +| `0xed77f261…2c9707` | 43687419 | 2026-03-22 07:23:05 | `0xcbb7c0…33bf` | 1444091 | OUT | 0 | +| `0xed77f261…2c9707` | 43687419 | 2026-03-22 07:23:05 | `0xbdb930…8ee6` | 1444231 | IN | 24152671 | +| `0xf25f5151…edb9f0` | 43687440 | 2026-03-22 07:23:47 | `0x0697a1…94b5` | 9000000000 | IN | 9000000000 | +| `0x67764d3d…aa7a28` | 43736373 | 2026-03-23 10:34:53 | `0x833589…2913` | 36 | IN | 36 | +| `0xc6436faf…33c654` | 43925845 | 2026-03-27 19:50:37 | `0x833589…2913` | 250000000 | IN | 250000036 | +| `0xc6436faf…33c654` | 43925845 | 2026-03-27 19:50:37 | `0x59dca0…4c28` | 254340496 | IN | 8000009012 | +| `0xcf343667…d5a608` | 43925901 | 2026-03-27 19:52:29 | `0x244120…3bc0` | 9000000000 | IN | 9000000000 | +| `0xda518f89…6a7a97` | 43926303 | 2026-03-27 20:05:53 | `0x5b1a31…3938` | 9000000000 | IN | 9000000000 | +| `0x75df40f0…134360` | 44291216 | 2026-04-05 06:49:39 | `0x833589…2913` | 3972528936 | IN | 4222528972 | +| `0xe3156ee7…a97645` | 44291218 | 2026-04-05 06:49:43 | `0x833589…2913` | 39 | IN | 4222529011 | +| `0xb9213e7e…83fc96` | 44291224 | 2026-04-05 06:49:55 | `0x833589…2913` | 397 | IN | 4222529408 | +| `0xf6f0ebda…906758` | 44291237 | 2026-04-05 06:50:21 | `0x833589…2913` | 39 | IN | 4222529447 | +| `0x89b993cb…ea3a04` | 44291264 | 2026-04-05 06:51:15 | `0x7c1803…5783` | 397 | IN | 397 | +| `0x89b993cb…ea3a04` | 44291264 | 2026-04-05 06:51:15 | `0x7c1803…5783` | 3972528936 | IN | 3972529333 | +| `0x8e1941b7…a54675` | 44291289 | 2026-04-05 06:52:05 | `0x833589…2913` | 500000000 | OUT | 3722529447 | +| `0x8e1941b7…a54675` | 44291289 | 2026-04-05 06:52:05 | `0xcbb7c0…33bf` | 748014 | IN | 748014 | +| `0x81424692…82692a` | 44291307 | 2026-04-05 06:52:41 | `0x4e65fe…c0ab` | 3722529446 | IN | 3722529443 | +| `0x81424692…82692a` | 44291307 | 2026-04-05 06:52:41 | `0x833589…2913` | 3722529447 | OUT | 0 | +| `0xb50f4e8f…c9051d` | 44291320 | 2026-04-05 06:53:07 | `0xbdb930…8ee6` | 748257 | IN | 24900928 | +| `0xb50f4e8f…c9051d` | 44291320 | 2026-04-05 06:53:07 | `0xcbb7c0…33bf` | 748014 | OUT | 0 | +| `0x707d7ff4…a8c3e5` | 44291382 | 2026-04-05 06:55:11 | `0x5e7432…215b` | 9000000000 | IN | 9000000000 | +| `0xb694ecfd…27b669` | 44291385 | 2026-04-05 06:55:17 | `0x833589…2913` | 36 | IN | 36 | +| `0x3a9be710…00ebf4` | 44291389 | 2026-04-05 06:55:25 | `0x833589…2913` | 39 | IN | 75 | +| `0x7f9f3ad3…ecbf7b` | 44291445 | 2026-04-05 06:57:17 | `0x7c1803…5783` | 36 | IN | 3972529369 | +| `0xa1131f0f…140e46` | 44551399 | 2026-04-11 07:22:25 | `0x833589…2913` | 350000000 | IN | 350000075 | +| `0xa1131f0f…140e46` | 44551399 | 2026-04-11 07:22:25 | `0x4e65fe…c0ab` | 348339849 | OUT | 3374189594 | +| `0x94f4da5f…932aa7` | 44551471 | 2026-04-11 07:24:49 | `0x833589…2913` | 160311492 | OUT | 189688583 | +| `0x94f4da5f…932aa7` | 44551471 | 2026-04-11 07:24:49 | `0x833589…2913` | 116590674 | OUT | 73097909 | +| `0x94f4da5f…932aa7` | 44551471 | 2026-04-11 07:24:49 | `0x833589…2913` | 14569445 | OUT | 58528464 | +| `0x94f4da5f…932aa7` | 44551471 | 2026-04-11 07:24:49 | `0xcbb7c0…33bf` | 20000 | IN | 20000 | +| `0x94f4da5f…932aa7` | 44551471 | 2026-04-11 07:24:49 | `0xcbb7c0…33bf` | 160000 | IN | 180000 | +| `0x94f4da5f…932aa7` | 44551471 | 2026-04-11 07:24:49 | `0xcbb7c0…33bf` | 220000 | IN | 400000 | +| `0xe12b3bc4…853bd3` | 44551498 | 2026-04-11 07:25:43 | `0xbdb930…8ee6` | 400114 | IN | 25301042 | +| `0xe12b3bc4…853bd3` | 44551498 | 2026-04-11 07:25:43 | `0xcbb7c0…33bf` | 400000 | OUT | 0 | +| `0xdf51b5cb…90af3c` | 44551510 | 2026-04-11 07:26:07 | `0x4e65fe…c0ab` | 58529107 | IN | 3432718701 | +| `0xdf51b5cb…90af3c` | 44551510 | 2026-04-11 07:26:07 | `0x833589…2913` | 58528464 | OUT | 0 | +| `0xb136f2df…3d84da` | 44551638 | 2026-04-11 07:30:23 | `0x891113…4062` | 99000000000 | IN | 99000000000 | +| `0x587d054c…cabec6` | 44551700 | 2026-04-11 07:32:27 | `0x1d432a…c2cf` | 9000000000 | IN | 9000000000 | +| `0xe0fdc66b…fd4a8e` | 44552517 | 2026-04-11 07:59:41 | `0xd19f71…1529` | 20000000000000000000 | IN | 20000000000000000000 | +| `0x7c3f2af7…73d11f` | 44855129 | 2026-04-18 08:06:45 | `0x4e65fe…c0ab` | 298181848 | OUT | 3134536853 | +| `0x7c3f2af7…73d11f` | 44855129 | 2026-04-18 08:06:45 | `0x833589…2913` | 300000000 | IN | 300000000 | +| `0x0c3e9e2c…e794ee` | 44855294 | 2026-04-18 08:12:15 | `0xcbb7c0…33bf` | 370831 | IN | 370831 | +| `0x0c3e9e2c…e794ee` | 44855294 | 2026-04-18 08:12:15 | `0x833589…2913` | 286000000 | OUT | 14000000 | +| `0x13b95c17…c97d7d` | 44855312 | 2026-04-18 08:12:51 | `0xcbb7c0…33bf` | 370831 | OUT | 0 | +| `0x13b95c17…c97d7d` | 44855312 | 2026-04-18 08:12:51 | `0xbdb930…8ee6` | 370951 | IN | 25671993 | +| `0xadec2267…c92608` | 44855326 | 2026-04-18 08:13:19 | `0x833589…2913` | 14000000 | OUT | 0 | +| `0xadec2267…c92608` | 44855326 | 2026-04-18 08:13:19 | `0x4e65fe…c0ab` | 14001092 | IN | 3148537945 | +| `0xcf01c6d8…6dd604` | 44910613 | 2026-04-19 14:56:13 | `0xd4a0e0…8bb7` | 3413610246749556 | IN | 1003413610246749555 | +| `0xcf01c6d8…6dd604` | 44910613 | 2026-04-19 14:56:13 | `0xd4a0e0…8bb7` | 1003413610246749555 | OUT | 0 | +| `0x323ce31e…1a04b0` | 44910728 | 2026-04-19 15:00:03 | `0xa13a3c…be7d` | 99000000000 | IN | 99000000000 | +| `0xb98a961a…9104c5` | 44948380 | 2026-04-20 11:55:07 | `0x4e65fe…c0ab` | 3148537948 | OUT | -3 | +| `0xb98a961a…9104c5` | 44948380 | 2026-04-20 11:55:07 | `0x833589…2913` | 3149326572 | IN | 3149326572 | +| `0x90d51a68…cddad2` | 44948407 | 2026-04-20 11:56:01 | `0x589836…46ab` | 99000000000 | IN | 99000000000 | +| `0xf33da011…4eca75` | 44948534 | 2026-04-20 12:00:15 | `0x59dca0…4c28` | 3128620743 | OUT | 4871388269 | +| `0xf33da011…4eca75` | 44948534 | 2026-04-20 12:00:15 | `0x833589…2913` | 3149326572 | OUT | 0 | +| `0xb4c587de…ef6cd6` | 44948553 | 2026-04-20 12:00:53 | `0xbdb930…8ee6` | 9999973 | OUT | 15672020 | +| `0xb4c587de…ef6cd6` | 44948553 | 2026-04-20 12:00:53 | `0xcbb7c0…33bf` | 10000000 | IN | 10000000 | +| `0x5ed54cce…52f91c` | 44948607 | 2026-04-20 12:02:41 | `0x833589…2913` | 4884421264 | IN | 4884421264 | +| `0x5ed54cce…52f91c` | 44948607 | 2026-04-20 12:02:41 | `0xcbb7c0…33bf` | 6500000 | OUT | 3500000 | +| `0x2cd053ff…242f3e` | 44948610 | 2026-04-20 12:02:47 | `0x4da55c…7fba` | 99000000000 | IN | 99000000000 | +| `0x69e86dab…4d6c66` | 44948625 | 2026-04-20 12:03:17 | `0x59dca0…4c28` | 4871388269 | OUT | 0 | +| `0x69e86dab…4d6c66` | 44948625 | 2026-04-20 12:03:17 | `0x833589…2913` | 4871389506 | OUT | 13031758 | +| `0x3931b81d…fa6b00` | 44948640 | 2026-04-20 12:03:47 | `0xbdb930…8ee6` | 15672020 | OUT | 0 | +| `0x3931b81d…fa6b00` | 44948640 | 2026-04-20 12:03:47 | `0xcbb7c0…33bf` | 15672020 | IN | 19172020 | +| `0x0cc0465d…6624a7` | 45164477 | 2026-04-25 11:58:21 | `0xbdb930…8ee6` | 1546509 | IN | 1546509 | +| `0x0cc0465d…6624a7` | 45164477 | 2026-04-25 11:58:21 | `0xcbb7c0…33bf` | 1546510 | OUT | 17625510 | +| `0x2474d563…43ff69` | 45164496 | 2026-04-25 11:58:59 | `0x833589…2913` | 500000000 | IN | 513031758 | +| `0x2474d563…43ff69` | 45164496 | 2026-04-25 11:58:59 | `0x59dca0…4c28` | 500000001 | IN | 500000001 | +| `0xa3ba4136…e1baa3` | 45164511 | 2026-04-25 11:59:29 | `0x313bbc…f319` | 99000000000 | IN | 99000000000 | +| `0x90715781…ae3d21` | 45164540 | 2026-04-25 12:00:27 | `0xcbb7c0…33bf` | 482444 | IN | 18107954 | +| `0x90715781…ae3d21` | 45164540 | 2026-04-25 12:00:27 | `0x833589…2913` | 375000000 | OUT | 138031758 | +| `0x90715781…ae3d21` | 45164540 | 2026-04-25 12:00:27 | `0xcbb7c0…33bf` | 160829 | IN | 18268783 | +| `0x90715781…ae3d21` | 45164540 | 2026-04-25 12:00:27 | `0x833589…2913` | 125000000 | OUT | 13031758 | +| `0x4f5b6531…35ced9` | 45164562 | 2026-04-25 12:01:11 | `0x833589…2913` | 13031758 | OUT | 0 | +| `0x34985365…dbb9d9` | 45164579 | 2026-04-25 12:01:45 | `0x3c3689…103e` | 99000000000 | IN | 99000000000 | +| `0x3ed03b24…334d84` | 45503930 | 2026-05-03 08:33:27 | `0xcbb7c0…33bf` | 769230 | OUT | 17499553 | +| `0x3ed03b24…334d84` | 45503930 | 2026-05-03 08:33:27 | `0xbdb930…8ee6` | 769236 | IN | 2315745 | +| `0x7ea01d19…fda386` | 45503955 | 2026-05-03 08:34:17 | `0x833589…2913` | 250000000 | IN | 250000000 | +| `0x7ea01d19…fda386` | 45503955 | 2026-05-03 08:34:17 | `0x59dca0…4c28` | 250470479 | IN | 750470480 | +| `0xb2ec79b0…c6832d` | 45503991 | 2026-05-03 08:35:29 | `0x833589…2913` | 250000000 | OUT | 0 | +| `0xb2ec79b0…c6832d` | 45503991 | 2026-05-03 08:35:29 | `0xcbb7c0…33bf` | 318858 | IN | 17818411 | +| `0xeff68999…e6f812` | 45821131 | 2026-05-10 16:46:49 | `0xbdb930…8ee6` | 1371275 | IN | 3687020 | +| `0xeff68999…e6f812` | 45821131 | 2026-05-10 16:46:49 | `0xcbb7c0…33bf` | 1371267 | OUT | 16447144 | +| `0xa0fab26c…5c4820` | 45821162 | 2026-05-10 16:47:51 | `0x833589…2913` | 458000000 | IN | 458000000 | +| `0xa0fab26c…5c4820` | 45821162 | 2026-05-10 16:47:51 | `0x59dca0…4c28` | 458654499 | IN | 1209124979 | +| `0x5d06de02…2dbb54` | 45821192 | 2026-05-10 16:48:51 | `0xcbb7c0…33bf` | 563279 | IN | 17010423 | +| `0x5d06de02…2dbb54` | 45821192 | 2026-05-10 16:48:51 | `0x833589…2913` | 458000000 | OUT | 0 | +| `0xf8a4c312…f2a59a` | 45822283 | 2026-05-10 17:25:13 | `0xcbb7c0…33bf` | 661478 | IN | 17671901 | +| `0x64190536…1a2d9e` | 46065676 | 2026-05-16 08:38:19 | `0x833589…2913` | 571509994 | IN | 571509994 | +| `0x6080a9e2…bd5933` | 46065830 | 2026-05-16 08:43:27 | `0x833589…2913` | 1000 | IN | 571510994 | +| `0x9a3590b8…faa5e4` | 46066210 | 2026-05-16 08:56:07 | `0xcbb7c0…33bf` | 1363703 | OUT | 16308198 | +| `0x9a3590b8…faa5e4` | 46066210 | 2026-05-16 08:56:07 | `0xbdb930…8ee6` | 1363714 | IN | 5050734 | +| `0x72f91ef7…ca6b64` | 46066262 | 2026-05-16 08:57:51 | `0x59dca0…4c28` | 444789930 | IN | 1653914909 | +| `0x72f91ef7…ca6b64` | 46066262 | 2026-05-16 08:57:51 | `0x833589…2913` | 444000000 | IN | 1015510994 | +| `0xc1085bb0…7d591a` | 46066333 | 2026-05-16 09:00:13 | `0x833589…2913` | 1015510994 | OUT | 0 | +| `0xc1085bb0…7d591a` | 46066333 | 2026-05-16 09:00:13 | `0xcbb7c0…33bf` | 1295088 | IN | 17603286 | +| `0x540a07ec…01c206` | 46099060 | 2026-05-17 03:11:07 | `0x833589…2913` | 57 | IN | 57 | +| `0xf3cb6190…ae203d` | 46374621 | 2026-05-23 12:16:29 | `0x64df7b…5ea2` | 1890 | IN | 1890 | +| `0x1538f328…4eab18` | 46374900 | 2026-05-23 12:25:47 | `0x833589…2913` | 547962299 | IN | 547962356 | +| `0xddaaec96…a60137` | 46375074 | 2026-05-23 12:31:35 | `0x833589…2913` | 420000000 | IN | 967962356 | +| `0xddaaec96…a60137` | 46375074 | 2026-05-23 12:31:35 | `0x59dca0…4c28` | 421313539 | IN | 2075228448 | +| `0xe9354a3c…507658` | 46375087 | 2026-05-23 12:32:01 | `0x6ff512…1e9f` | 69000000000 | IN | 69000000000 | +| `0x42c5e21b…afeda4` | 46375139 | 2026-05-23 12:33:45 | `0x833589…2913` | 60000000 | IN | 1027962356 | +| `0x42c5e21b…afeda4` | 46375139 | 2026-05-23 12:33:45 | `0x59dca0…4c28` | 60000357 | IN | 2135228805 | +| `0x0f148fdc…ccc4f5` | 46375165 | 2026-05-23 12:34:37 | `0x833589…2913` | 1027962356 | OUT | 0 | +| `0x0f148fdc…ccc4f5` | 46375165 | 2026-05-23 12:34:37 | `0xcbb7c0…33bf` | 1376394 | IN | 18979680 | +| `0x1090f563…a90404` | 46375212 | 2026-05-23 12:36:11 | `0xcbb7c0…33bf` | 1376394 | OUT | 17603286 | +| `0x1090f563…a90404` | 46375212 | 2026-05-23 12:36:11 | `0xbdb930…8ee6` | 1376423 | IN | 6427157 | +| `0xa9188d47…8b487f` | 46375227 | 2026-05-23 12:36:41 | `0x64b3a3…b8d7` | 9000000000 | IN | 9000000000 | +| `0x22cdc02b…54537d` | 46756786 | 2026-06-01 08:35:19 | `0x833589…2913` | 547380003 | IN | 547380003 | +| `0x36cd7249…d121d1` | 46756821 | 2026-06-01 08:36:29 | `0x833589…2913` | 136845000 | OUT | 410535003 | +| `0x36cd7249…d121d1` | 46756821 | 2026-06-01 08:36:29 | `0x833589…2913` | 410535003 | OUT | 0 | +| `0x36cd7249…d121d1` | 46756821 | 2026-06-01 08:36:29 | `0xcbb7c0…33bf` | 188008 | IN | 17791294 | +| `0x36cd7249…d121d1` | 46756821 | 2026-06-01 08:36:29 | `0xcbb7c0…33bf` | 563986 | IN | 18355280 | +| `0x81ebbc5b…5b77fd` | 46756836 | 2026-06-01 08:36:59 | `0x21f4bb…b625` | 99 | IN | 99 | +| `0x2a0abac6…2283a4` | 46756948 | 2026-06-01 08:40:43 | `0x833589…2913` | 444000000 | IN | 444000000 | +| `0x2a0abac6…2283a4` | 46756948 | 2026-06-01 08:40:43 | `0x59dca0…4c28` | 446168463 | IN | 2581397268 | +| `0x7011824c…88dd97` | 46756966 | 2026-06-01 08:41:19 | `0x833589…2913` | 199800000 | OUT | 244200000 | +| `0x7011824c…88dd97` | 46756966 | 2026-06-01 08:41:19 | `0x833589…2913` | 244200000 | OUT | 0 | +| `0x7011824c…88dd97` | 46756966 | 2026-06-01 08:41:19 | `0xcbb7c0…33bf` | 274477 | IN | 18629757 | +| `0x7011824c…88dd97` | 46756966 | 2026-06-01 08:41:19 | `0xcbb7c0…33bf` | 335589 | IN | 18965346 | +| `0xf85a8071…bc525e` | 46756999 | 2026-06-01 08:42:25 | `0xcbb7c0…33bf` | 1363250 | OUT | 17602096 | +| `0xf85a8071…bc525e` | 46756999 | 2026-06-01 08:42:25 | `0xbdb930…8ee6` | 1363292 | IN | 7790449 | +| `0x1b75923e…ac5ccc` | 46757027 | 2026-06-01 08:43:21 | `0xa8d3fb…5edd` | 9000000000 | IN | 9000000000 | +| `0x76d2fe60…b72fac` | 46758099 | 2026-06-01 09:19:05 | `0xe66acc…f06c` | 1004000000000000000 | OUT | -1004000000000000000 | diff --git a/wallet_test.html b/wallet_test.html new file mode 100644 index 0000000..37e55c8 --- /dev/null +++ b/wallet_test.html @@ -0,0 +1,43 @@ + + + + + Wallet Verifier Test Harness + + + +

Dione Multi-Wallet Verifier Test

+ +
Click the button to start initialization...
+ + + + diff --git a/wallets.js b/wallets.js new file mode 100644 index 0000000..2ad6d84 --- /dev/null +++ b/wallets.js @@ -0,0 +1,323 @@ +/** + * Wallet Management Module — Anonymous localStorage-backed state. + * + * Handles lifecycle of tracked wallets: load, add, remove, verify. + * Addresses are validated per-chain. Unverified wallets can never + * slip through as verified unless a valid signature is explicitly set. + * + * Usage: + * import { WalletManager } from './wallets.js'; + * const wm = new WalletManager(); + * wm.loadWallets(); + */ + +/* ------------------------------------------------------------------ */ +/* Types (JSDoc for editor support; runtime is plain JS) */ +/* ------------------------------------------------------------------ */ + +/** + * @typedef {'base'|'ethereum'|'bitcoin'|'arbitrum'|'optimism'|'polygon'|'solana'|string} ChainId + */ + +/** + * @typedef {Object} TrackedWallet + * @property {string} address + * @property {ChainId} chain + * @property {string} nickname + * @property {boolean} isVerified + * @property {string|null} signature + * @property {object|null} messageData + */ + +/* ------------------------------------------------------------------ */ +/* Constants */ +/* ------------------------------------------------------------------ */ + +const LS_KEY = 'cbbtc_tracked_wallets'; + +/* Accepted chain identifiers */ +const VALID_CHAINS = new Set([ + 'base', 'ethereum', 'bitcoin', 'arbitrum', 'optimism', + 'polygon', 'solana', 'avalanche', 'bsc', 'fantom', 'btc', +]); + +/* Regex patterns for address validation per chain */ +const ADDRESS_PATTERNS = { + /* EVM-compatible — 0x-prefixed 40-hex */ + base: /^(0x)?[0-9a-fA-F]{40}$/i, + ethereum: /^(0x)?[0-9a-fA-F]{40}$/i, + arbitrum: /^(0x)?[0-9a-fA-F]{40}$/i, + optimism: /^(0x)?[0-9a-fA-F]{40}$/i, + polygon: /^(0x)?[0-9a-fA-F]{40}$/i, + avalanche: /^(0x)?[0-9a-fA-F]{40}$/i, + bsc: /^(0x)?[0-9a-fA-F]{40}$/i, + fantom: /^(0x)?[0-9a-fA-F]{40}$/i, + + /* Bitcoin — bech32 or legacy pubkey hash */ + bitcoin: /^(bc1[a-z0-9]{25,39}|1[a-km-zA-HJ-NP-Z1-9]{25,34})$/, + btc: /^(bc1[a-z0-9]{25,39}|1[a-km-zA-HJ-NP-Z1-9]{25,34})$/, + + /* Solana — base58, 32-44 chars */ + solana: /^[1-9A-HJ-NP-Za-km-z]{32,44}$/, +}; + +/* ------------------------------------------------------------------ */ +/* Validators */ +/* ------------------------------------------------------------------ */ + +/** + * Validate an address against the chain's known format. + * Falls back to a looser check for chains without a pattern. + * + * @param {string} address + * @param {ChainId} chain + * @returns {boolean} + */ +function validateAddress(address, chain) { + if (!address || typeof address !== 'string') return false; + + const pattern = ADDRESS_PATTERNS[chain]; + if (pattern) { + return pattern.test(address.trim()); + } + + /* Fallback: non-empty, printable chars, reasonable length */ + return /^[A-Za-z0-9_.+/=-]{6,100}$/.test(address.trim()); +} + +/* ------------------------------------------------------------------ */ +/* WalletManager */ +/* ------------------------------------------------------------------ */ + +export class WalletManager { + constructor() { + /** @type {TrackedWallet[]} */ + this._wallets = []; + } + + /* ---------------------------------------------------------------- */ + /* Persist / Load */ + /* ---------------------------------------------------------------- */ + + /** + * Serialize state to localStorage. + * Silently ignores write errors (private-browsing mode). + * + * @private + */ + _persist() { + try { + localStorage.setItem(LS_KEY, JSON.stringify(this._wallets)); + } catch (_ignored) { + /* quotaExceeded or private-browsing — degrade gracefully */ + } + } + + /** + * Load wallets from localStorage. + * + * On first run returns an empty array. Restores any stored + * entries — even if the browser was closed between sessions. + * + * Invariant: every wallet read from storage has `isVerified: false` + * because a signature cannot survive an empty localStorage round-trip + * without an accompanying non-empty `signature` field, and we + * deliberately do NOT trust persisted signatures. + * + * @returns {TrackedWallet[]} loaded wallets + */ + loadWallets() { + try { + const raw = localStorage.getItem(LS_KEY); + if (!raw) { + this._wallets = []; + return []; + } + + const parsed = JSON.parse(raw); + + if (!Array.isArray(parsed)) { + this._wallets = []; + return []; + } + + /* Sanitize: enforce isVerified = false for all persisted entries. + A wallet can only be verified by calling verifyWallet(), not + by tampering with localStorage. */ + this._wallets = parsed + .filter((w) => w && typeof w === 'object' && w.address && w.chain) + .map((w) => ({ + address: String(w.address), + chain: String(w.chain).toLowerCase(), + nickname: w.nickname ? String(w.nickname) : '', + isVerified: false, /* SECURITY: never trust persisted flag */ + signature: null, /* SECURITY: discard persisted signatures */ + messageData: null, + })); + + return this._wallets; + + } catch (_ignored) { + this._wallets = []; + return []; + } + } + + /* ---------------------------------------------------------------- */ + /* CRUD */ + /* ---------------------------------------------------------------- */ + + /** + * @returns {TrackedWallet[]} deep copy of state + */ + getWallets() { + return this._wallets.map((w) => ({ ...w })); + } + + /** + * Add a new wallet. Rejects duplicates (same address+chain). + * + * @param {string} address + * @param {ChainId} chain + * @param {string} [nickname=''] + * @returns {{ success: true, wallet: TrackedWallet } | { success: false, error: string }} + */ + addWallet(address, chain, nickname = '') { + const normalAddress = String(address).trim(); + const normalChain = String(chain).toLowerCase(); + const normalNickname = String(nickname).trim(); + + /* Validate chain */ + if (!VALID_CHAINS.has(normalChain)) { + return { success: false, error: `Unsupported chain: ${normalChain}` }; + } + + /* Validate address format */ + if (!validateAddress(normalAddress, normalChain)) { + return { + success: false, + error: `Invalid ${normalChain} address: ${normalAddress}`, + }; + } + + /* Duplicate check */ + if (this._wallets.some((w) => + w.address === normalAddress && w.chain === normalChain + )) { + return { + success: false, + error: `Wallet already tracked: ${normalAddress} (${normalChain})`, + }; + } + + const wallet = { + address: normalAddress, + chain: normalChain, + nickname: normalNickname, + isVerified: false, + signature: null, + messageData: null, + }; + + this._wallets.push(wallet); + this._persist(); + return { success: true, wallet }; + } + + /** + * Remove a wallet by address + chain. + * + * @param {string} address + * @param {ChainId} chain + * @returns {{ success: true, removedAddress: string } | { success: false, error: string }} + */ + removeWallet(address, chain) { + const normalAddress = String(address).trim(); + const normalChain = String(chain).toLowerCase(); + + const idx = this._wallets.findIndex( + (w) => w.address === normalAddress && w.chain === normalChain + ); + + if (idx === -1) { + return { + success: false, + error: `Wallet not found: ${normalAddress} (${normalChain})`, + }; + } + + this._wallets.splice(idx, 1); + this._persist(); + return { success: true, removedAddress: normalAddress }; + } + + /** + * Find a wallet by address + chain. + * + * @param {string} address + * @param {ChainId} chain + * @returns {TrackedWallet | undefined} + */ + findWallet(address, chain) { + const normalAddress = String(address).trim(); + const normalChain = String(chain).toLowerCase(); + return this._wallets.find( + (w) => w.address === normalAddress && w.chain === normalChain + ); + } + + /* ---------------------------------------------------------------- */ + /* Verification */ + /* ---------------------------------------------------------------- */ + + /** + * Mark a wallet as verified after a successful signature check. + * Only works if the wallet exists in state. + * + * @param {string} address + * @param {ChainId} chain + * @param {string} signature + * @param {object} messageData + * @returns {{ success: true } | { success: false, error: string }} + */ + verifyWallet(address, chain, signature, messageData) { + const w = this.findWallet(address, chain); + if (!w) { + return { + success: false, + error: `Wallet not found: ${address} (${chain})`, + }; + } + + w.isVerified = true; + w.signature = String(signature); + w.messageData = messageData || null; + this._persist(); + return { success: true }; + } + + /** + * Revoke verification — resets back to unverified. + * + * @param {string} address + * @param {ChainId} chain + * @returns {{ success: true } | { success: false, error: string }} + */ + revokeVerification(address, chain) { + const w = this.findWallet(address, chain); + if (!w) { + return { + success: false, + error: `Wallet not found: ${address} (${chain})`, + }; + } + + w.isVerified = false; + w.signature = null; + w.messageData = null; + this._persist(); + return { success: true }; + } +} + +export default WalletManager;