DChain single-node blockchain + React Native messenger client. Core: - PBFT consensus with multi-sig validator admission + equivocation slashing - BadgerDB + schema migration scaffold (CurrentSchemaVersion=0) - libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1) - Native Go contracts (username_registry) alongside WASM (wazero) - WebSocket gateway with topic-based fanout + Ed25519-nonce auth - Relay mailbox with NaCl envelope encryption (X25519 + Ed25519) - Prometheus /metrics, per-IP rate limit, body-size cap Deployment: - Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus - 3-node dev compose (docker-compose.yml) with mocked internet topology - 3-validator prod compose (deploy/prod/) for federation - Auto-update from Gitea via /api/update-check + systemd timer - Build-time version injection (ldflags → node --version) - UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER) Client (client-app/): - Expo / React Native / NativeWind - E2E NaCl encryption, typing indicator, contact requests - Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch Documentation: - README.md, CHANGELOG.md, CONTEXT.md - deploy/single/README.md with 6 operator scenarios - deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design - docs/contracts/*.md per contract
225 lines
9.1 KiB
HTML
225 lines
9.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Wallet | DChain Explorer</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="/assets/explorer/style.css">
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
<script defer src="/assets/explorer/common.js"></script>
|
|
<script defer src="/assets/explorer/address.js"></script>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- top nav -->
|
|
<header class="topnav">
|
|
<div class="topnav-inner">
|
|
<a class="topnav-brand" href="/">
|
|
<span class="brand-gem">◆</span>
|
|
<span class="brand-name">DChain</span>
|
|
</a>
|
|
<nav class="topnav-links">
|
|
<a href="/contract">Contracts</a>
|
|
<a href="/tokens">Tokens</a>
|
|
<a href="/validators">Validators</a>
|
|
<a href="/relays">Relay Nodes</a>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- search bar (collapsed under nav) -->
|
|
<div class="addr-searchbar">
|
|
<div class="addr-searchbar-inner">
|
|
<div class="hero-search" style="max-width:640px">
|
|
<i data-lucide="search" class="hero-search-icon"></i>
|
|
<input id="addressInput" type="text" placeholder="Enter address (DC…) or public key…">
|
|
<button id="addressBtn" class="btn-hero">Load</button>
|
|
</div>
|
|
<div id="status" class="hero-status"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- empty state (shown when no address loaded, toggled by JS) -->
|
|
<div id="emptyState" class="page-body" style="display:none">
|
|
<div class="addr-empty-state">
|
|
<div class="addr-empty-icon"><i data-lucide="wallet"></i></div>
|
|
<div class="addr-empty-title">Search for a wallet</div>
|
|
<div class="addr-empty-sub">Enter a DC address (e.g. <span class="mono" style="color:var(--accent)">DC1abc…</span>) or a 64-character hex public key above to view balance and transaction history.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<main class="page-body" id="mainContent" style="display:none">
|
|
|
|
<!-- ── Error banner (hidden unless load failed) ──────────────────────── -->
|
|
<div id="errorBanner" class="addr-error-banner" style="display:none">
|
|
<div class="addr-error-icon"><i data-lucide="alert-circle"></i></div>
|
|
<div class="addr-error-body">
|
|
<div class="addr-error-title">Could not load wallet</div>
|
|
<div class="addr-error-msg" id="errorMsg">Unknown error</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Profile card (hidden on error) ───────────────────────────────── -->
|
|
<div id="profileSection">
|
|
<div class="addr-profile-card">
|
|
|
|
<!-- left: avatar + name + badges -->
|
|
<div class="addr-profile-left">
|
|
<div class="addr-avatar" id="addrAvatar">◆</div>
|
|
<div class="addr-profile-info">
|
|
<div class="addr-name" id="addrNickname">Unknown Wallet</div>
|
|
<div class="addr-badges" id="addrBadges"><!-- filled by JS --></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- right: balance -->
|
|
<div class="addr-profile-right">
|
|
<div class="addr-bal-label">Balance</div>
|
|
<div class="addr-bal-val" id="walletBalance">—</div>
|
|
<div class="addr-bal-sub" id="walletBalanceSub">—</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- ── Identity details grid ─────────────────────────────────────────── -->
|
|
<div class="panel addr-detail-panel">
|
|
<div class="addr-kv-list">
|
|
|
|
<div class="addr-kv-row">
|
|
<div class="addr-kv-key"><i data-lucide="at-sign"></i> Address</div>
|
|
<div class="addr-kv-val">
|
|
<span class="mono" id="walletAddress">—</span>
|
|
<button class="copy-btn" data-copy-id="walletAddress" title="Copy"><i data-lucide="copy"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="addr-kv-row">
|
|
<div class="addr-kv-key"><i data-lucide="key-round"></i> Public Key</div>
|
|
<div class="addr-kv-val">
|
|
<span class="mono addr-pubkey-text" id="walletPubKey">—</span>
|
|
<button class="copy-btn" data-copy-id="walletPubKey" title="Copy"><i data-lucide="copy"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="addr-kv-row" id="x25519Row" style="display:none">
|
|
<div class="addr-kv-key"><i data-lucide="message-square-lock"></i> Messaging Key (X25519)</div>
|
|
<div class="addr-kv-val">
|
|
<span class="mono addr-pubkey-text" id="walletX25519">—</span>
|
|
<button class="copy-btn" data-copy-id="walletX25519" title="Copy"><i data-lucide="copy"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="addr-kv-row" id="nodeLinkRow" style="display:none">
|
|
<div class="addr-kv-key"><i data-lucide="server"></i> Node Page</div>
|
|
<div class="addr-kv-val"><a id="nodePageLink" href="#">View node stats →</a></div>
|
|
</div>
|
|
|
|
<div class="addr-kv-row" id="walletBindingRow" style="display:none">
|
|
<div class="addr-kv-key"><i data-lucide="wallet"></i> Bound Wallet</div>
|
|
<div class="addr-kv-val"><a id="walletBindingLink" href="#">—</a></div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Node stats (shown only for validators / relay nodes) ─────────── -->
|
|
<div class="panel addr-node-panel" id="nodeStatsPanel" style="display:none">
|
|
<h2><i data-lucide="server"></i> Node Stats</h2>
|
|
<div class="addr-node-grid">
|
|
<div class="addr-node-cell">
|
|
<div class="addr-node-label">Blocks Produced</div>
|
|
<div class="addr-node-val" id="nodeBlocks">—</div>
|
|
</div>
|
|
<div class="addr-node-cell">
|
|
<div class="addr-node-label">Relay Proofs</div>
|
|
<div class="addr-node-val" id="nodeRelayProofs">—</div>
|
|
</div>
|
|
<div class="addr-node-cell">
|
|
<div class="addr-node-label">Reputation</div>
|
|
<div class="addr-node-val" id="nodeRepScore">—</div>
|
|
<div class="addr-node-sub" id="nodeRepRank">—</div>
|
|
</div>
|
|
<div class="addr-node-cell">
|
|
<div class="addr-node-label">Heartbeats</div>
|
|
<div class="addr-node-val" id="nodeHeartbeats">—</div>
|
|
</div>
|
|
<div class="addr-node-cell">
|
|
<div class="addr-node-label">Slashes</div>
|
|
<div class="addr-node-val" id="nodeSlashes">—</div>
|
|
</div>
|
|
<div class="addr-node-cell">
|
|
<div class="addr-node-label">Recent Rewards</div>
|
|
<div class="addr-node-val" id="nodeRecentRewards">—</div>
|
|
<div class="addr-node-sub" id="nodeRecentWindow">—</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Token Balances ───────────────────────────────────────────────── -->
|
|
<div class="panel" id="tokenBalPanel" style="display:none">
|
|
<div class="addr-hist-head">
|
|
<h2><i data-lucide="coins"></i> Token Balances</h2>
|
|
</div>
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Symbol</th>
|
|
<th>Name</th>
|
|
<th style="text-align:right">Balance</th>
|
|
<th style="width:90px"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tokenBalBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── NFT Collection ────────────────────────────────────────────────── -->
|
|
<div class="panel" id="nftPanel" style="display:none">
|
|
<div class="addr-hist-head">
|
|
<h2><i data-lucide="image"></i> NFTs</h2>
|
|
<span class="addr-hist-count" id="nftOwnedCount"></span>
|
|
</div>
|
|
<div id="addrNFTGrid" class="nft-grid" style="padding:1rem"></div>
|
|
</div>
|
|
|
|
<!-- ── Transaction History ───────────────────────────────────────────── -->
|
|
<div class="panel">
|
|
<div class="addr-hist-head">
|
|
<h2><i data-lucide="history"></i> History</h2>
|
|
<span class="addr-hist-count" id="walletTxCount"></span>
|
|
</div>
|
|
<div class="table-wrap">
|
|
<table id="walletTxTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Time</th>
|
|
<th>Type</th>
|
|
<th>From → To</th>
|
|
<th>Memo / Block</th>
|
|
<th style="text-align:right">Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="walletTxBody">
|
|
<tr><td colspan="5" class="tbl-empty">No transactions yet.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="addr-load-more">
|
|
<button id="loadMoreBtn" class="btn" style="display:none">
|
|
<i data-lucide="chevron-down"></i> Load more
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /#profileSection -->
|
|
|
|
</main>
|
|
|
|
</body>
|
|
</html>
|