<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.coingecko.com">
<title>Hyper Wallet Desktop - P2P Cryptocurrency Wallet</title>
<link rel="stylesheet" href="wallet.css">
</head>
<body>
<!-- Setup Wizard (shown when wallet not initialized) -->
<div id="setup-wizard" class="setup-wizard" style="display: none;">
<div class="setup-container">
<!-- Welcome Screen -->
<div id="setup-welcome" class="setup-screen">
<h1>💰 Welcome to Hyper Wallet</h1>
<p class="setup-subtitle">P2P Multi-Chain Cryptocurrency Wallet</p>
<div class="setup-content">
<p>To get started, you can create a new wallet, restore an existing one, or join an existing wallet from another device.</p>
<div class="setup-actions">
<button class="btn-primary btn-large" onclick="showCreateWallet()">🆕 Create New Wallet</button>
<button class="btn-secondary btn-large" onclick="showRestoreWallet()">🔄 Restore Existing Wallet</button>
<button class="btn-secondary btn-large" onclick="showJoinWallet()">🔗 Join Existing Wallet</button>
</div>
</div>
</div>
<!-- Create Wallet Screen -->
<div id="setup-create" class="setup-screen" style="display: none;">
<h2>Create New Wallet</h2>
<div class="setup-content">
<div class="setup-warning">
<h3>⚠️ CRITICAL: Backup Your Recovery Phrases</h3>
<p>You will be shown TWO sets of recovery words:</p>
<ul>
<li><strong>Identity (24 words)</strong> - For device attestation and P2P sync</li>
<li><strong>Wallet (12 words)</strong> - For cryptocurrency keys</li>
</ul>
<p><strong>Both are required to fully restore your wallet!</strong></p>
<ul>
<li>Write them down on paper (separately)</li>
<li>Store them in a safe place</li>
<li>Never share them with anyone</li>
<li>Never store them digitally (no screenshots, no cloud storage)</li>
</ul>
</div>
<div id="mnemonic-display" class="mnemonic-display" style="display: none;">
<div class="mnemonic-section">
<h3>🔐 Identity Mnemonic (24 words)</h3>
<p class="mnemonic-subtitle">For device attestation and multi-device sync</p>
<div id="identity-mnemonic-words" class="mnemonic-words mnemonic-words-24"></div>
</div>
<div class="mnemonic-section">
<h3>💰 Wallet Mnemonic (12 words)</h3>
<p class="mnemonic-subtitle">For cryptocurrency private keys</p>
<div id="wallet-mnemonic-words" class="mnemonic-words"></div>
</div>
<div class="mnemonic-confirm">
<label>
<input type="checkbox" id="mnemonic-confirm-check">
I have written down BOTH recovery phrases and stored them safely
</label>
</div>
</div>
<div id="password-section" class="password-section">
<h3>🔒 Set Encryption Password</h3>
<p class="password-hint">Your recovery phrases will be encrypted with this password. You'll need it to access your wallet.</p>
<div class="form-group">
<label for="create-password">Password (min 8 characters)</label>
<input type="password" id="create-password" placeholder="Enter strong password" minlength="8">
</div>
<div class="form-group">
<label for="create-password-confirm">Confirm Password</label>
<input type="password" id="create-password-confirm" placeholder="Re-enter password" minlength="8">
</div>
<div id="create-password-error" class="form-error" style="display: none;"></div>
<div class="password-warning">
<p>⚠️ <strong>IMPORTANT</strong>: If you forget this password, you can only restore using your recovery phrases!</p>
</div>
</div>
<div class="setup-actions">
<button class="btn-secondary" onclick="showSetupWelcome()">← Back</button>
<button id="btn-generate-mnemonic" class="btn-primary" onclick="generateNewWallet()">Generate Recovery Phrases</button>
<button id="btn-confirm-mnemonic" class="btn-primary" onclick="confirmMnemonicBackup()" style="display: none;" disabled>Continue →</button>
</div>
</div>
</div>
<!-- Restore Wallet Screen -->
<div id="setup-restore" class="setup-screen" style="display: none;">
<h2>Restore Existing Wallet</h2>
<div class="setup-content">
<p>Enter both your identity and wallet recovery phrases to restore your system.</p>
<div class="mnemonic-input-container">
<label for="restore-identity-mnemonic"><strong>Identity Mnemonic (24 words)</strong></label>
<p class="input-help">For device attestation and P2P sync</p>
<textarea id="restore-identity-mnemonic" class="mnemonic-input" placeholder="Enter your 24-word identity mnemonic (separated by spaces)" rows="4"></textarea>
</div>
<div class="mnemonic-input-container">
<label for="restore-wallet-mnemonic"><strong>Wallet Mnemonic (12 words)</strong></label>
<p class="input-help">For cryptocurrency private keys</p>
<textarea id="restore-wallet-mnemonic" class="mnemonic-input" placeholder="Enter your 12-word wallet mnemonic (separated by spaces)" rows="3"></textarea>
</div>
<div class="password-section">
<h3>🔒 Set Encryption Password</h3>
<p class="password-hint">Set a password to encrypt your restored mnemonics.</p>
<div class="form-group">
<label for="restore-password">Password (min 8 characters)</label>
<input type="password" id="restore-password" placeholder="Enter strong password" minlength="8">
</div>
<div class="form-group">
<label for="restore-password-confirm">Confirm Password</label>
<input type="password" id="restore-password-confirm" placeholder="Re-enter password" minlength="8">
</div>
</div>
<div id="restore-error" class="form-error"></div>
<div class="setup-actions">
<button class="btn-secondary" onclick="showSetupWelcome()">← Back</button>
<button class="btn-primary" onclick="restoreWallet()">Restore System</button>
</div>
</div>
</div>
<!-- Join Existing Wallet Screen -->
<div id="setup-join" class="setup-screen" style="display: none;">
<h2>Join Existing Wallet</h2>
<div class="setup-content">
<p>Join an existing wallet from another device using an invite code.</p>
<div class="setup-info">
<h3>ℹ️ How it Works</h3>
<ol>
<li>On your primary device (e.g., tabertron), click "Add Device" in Settings</li>
<li>Copy the invite code or scan the QR code</li>
<li>Paste the invite code below</li>
<li>Both identity and wallet keys will sync automatically via P2P</li>
</ol>
</div>
<div class="mnemonic-input-container">
<label for="join-invite-code"><strong>Invite Code</strong></label>
<p class="input-help">Paste the long invite string from your primary device</p>
<textarea id="join-invite-code" class="mnemonic-input" placeholder="Paste invite code here..." rows="3"></textarea>
</div>
<div class="mnemonic-input-container">
<label for="join-device-name"><strong>Device Name</strong></label>
<p class="input-help">Give this device a name (e.g., "ashatron", "laptop", "phone")</p>
<input type="text" id="join-device-name" class="text-input" placeholder="Enter device name">
</div>
<div id="join-status" class="sync-status" style="display: none;">
<div class="spinner"></div>
<p id="join-status-text">Connecting to primary device...</p>
</div>
<div id="join-error" class="form-error"></div>
<div class="setup-actions">
<button class="btn-secondary" onclick="showSetupWelcome()">← Back</button>
<button id="btn-join-wallet" class="btn-primary" onclick="joinExistingWallet()">Join Wallet</button>
</div>
</div>
</div>
</div>
</div>
<!-- Main App (shown when wallet initialized) -->
<div class="container" id="main-app">
<!-- Header -->
<header>
<h1>💰 Hyper Wallet</h1>
<div class="header-actions">
<div class="network-badge" id="network-badge">
<span class="status-dot"></span>
<span id="network-name">Loading...</span>
</div>
<button class="btn-icon" onclick="showContacts()" title="Contacts">📇</button>
<button class="btn-icon" onclick="showSettings()" title="Settings">⚙️</button>
</div>
</header>
<!-- Status Bar -->
<div class="status-bar" id="status-bar">
<div class="status-item">
<span class="status-label">Status:</span>
<span id="wallet-status">Checking...</span>
</div>
<div class="status-item">
<span class="status-label">Network:</span>
<div class="network-selector">
<button class="network-btn active" data-network="testnet" onclick="setNetwork('testnet')">Testnet</button>
<button class="network-btn" data-network="mainnet" onclick="setNetwork('mainnet')">Mainnet</button>
</div>
</div>
<div class="status-item">
<button class="btn-small" onclick="refreshAll()">🔄 Refresh</button>
</div>
</div>
<!-- Currency Selector -->
<section class="currency-selector-section">
<div class="selector-header">
<h2>Select Currency</h2>
</div>
<select id="currency-selector" class="currency-select" onchange="switchCurrency()">
<!-- Options populated dynamically from SUPPORTED_CHAINS -->
</select>
</section>
<!-- Wallet View -->
<section class="wallet-view">
<div class="wallet-container">
<div class="wallet-info-box">
<div class="wallet-header">
<div class="wallet-icon" id="wallet-icon">₿</div>
<div class="wallet-info">
<h2 id="wallet-name">Bitcoin</h2>
<div class="wallet-network" id="wallet-network">Testnet</div>
</div>
<div class="wallet-address-selector">
<label for="wallet-address-select">Active Address:</label>
<select id="wallet-address-select" onchange="onWalletAddressChange()">
<option value="">Loading...</option>
</select>
</div>
</div>
<div class="wallet-balance">
<div id="wallet-balance-display">
<div class="spinner"></div>
</div>
</div>
<div class="wallet-actions">
<button class="btn-secondary" onclick="showReceiveForCurrent()">📥 Receive</button>
<button class="btn-secondary" onclick="showSendForCurrent()">📤 Send</button>
<button class="btn-secondary" onclick="showManageAddresses()">💼 Manage Addresses</button>
</div>
</div>
<div class="wallet-qr-box">
<h3>Receive Address</h3>
<div class="qr-container" id="wallet-qr-container">
<div class="spinner"></div>
</div>
<div class="wallet-address" id="wallet-address-display">
Loading address...
</div>
</div>
</div>
</section>
<!-- Transactions -->
<section class="transactions">
<div class="section-header">
<h2>Recent Transactions</h2>
<div>
<button class="btn-small" onclick="syncTransactions()" id="sync-btn">🔄 Sync</button>
<button class="btn-small" onclick="exportTransactions()">💾 Export</button>
<button class="btn-small" onclick="loadTransactions()">↻ Refresh</button>
</div>
</div>
<div id="tx-list" class="tx-list">
<div class="spinner"></div>
</div>
</section>
<!-- Footer -->
<footer>
<p>
Hyper Wallet v7.0-beta |
<a href="https://github.com/tetherto/wdk-core" target="_blank">WDK</a> |
P2P Multi-Device Sync
</p>
</footer>
</div>
<!-- Receive Modal -->
<dialog id="receive-modal">
<div class="modal-header">
<h3>📥 Receive <span id="receive-chain"></span></h3>
<button class="modal-close" onclick="closeReceive()">×</button>
</div>
<div class="modal-body">
<div class="qr-placeholder">
<div id="qr-code">QR Code Here</div>
</div>
<div class="address-display">
<input type="text" id="receive-address" readonly onclick="this.select()">
<button class="btn-primary" onclick="copyAddress()">📋 Copy</button>
</div>
<p class="help-text" id="receive-address-label"></p>
<!-- Wallet Address Management -->
<div class="wallet-addresses-section">
<div class="section-header">
<h4>💼 My Wallet Addresses</h4>
<button class="btn-secondary btn-sm" onclick="deriveNewAddress()">+ New Address</button>
</div>
<div id="wallet-addresses-list" class="addresses-list">
<p class="loading-text">Loading addresses...</p>
</div>
</div>
</div>
</dialog>
<!-- Send Modal -->
<dialog id="send-modal">
<div class="modal-header">
<h3>📤 Send <span id="send-chain"></span></h3>
<button class="modal-close" onclick="closeSend()">×</button>
</div>
<div class="modal-body">
<form id="send-form" onsubmit="return false;">
<div class="form-group">
<label for="send-to">Recipient Address</label>
<input type="text" id="send-to" placeholder="Enter address" required>
<span class="form-error" id="send-to-error"></span>
</div>
<div class="form-group">
<label for="send-amount">Amount</label>
<input type="text" id="send-amount" placeholder="0.00" required>
<div class="input-helper">
<span id="send-amount-helper"></span>
<button type="button" class="btn-link" onclick="setMaxAmount()">Max</button>
</div>
<span class="form-error" id="send-amount-error"></span>
</div>
<div class="form-group">
<label>Fee Estimate</label>
<div class="fee-display" id="fee-display">
<div class="spinner"></div>
</div>
</div>
<div class="form-actions">
<button type="button" class="btn-secondary" onclick="closeSend()">Cancel</button>
<button type="submit" class="btn-primary" onclick="confirmSend()">Review Transaction</button>
</div>
</form>
</div>
</dialog>
<!-- Confirmation Modal -->
<dialog id="confirm-modal">
<div class="modal-header">
<h3>⚠️ Confirm Transaction</h3>
<button class="modal-close" onclick="closeConfirm()">×</button>
</div>
<div class="modal-body">
<div id="confirm-content"></div>
<div class="form-actions">
<button class="btn-secondary" onclick="closeConfirm()">Cancel</button>
<button class="btn-primary" onclick="executeSend()">Send Transaction</button>
</div>
</div>
</dialog>
<!-- Transaction Details Modal -->
<dialog id="tx-details-modal">
<div class="modal-header">
<h3>📋 Transaction Details</h3>
<button class="modal-close" onclick="closeTxDetails()">×</button>
</div>
<div class="modal-body">
<div id="tx-details-content"></div>
</div>
</dialog>
<!-- Manage Addresses Modal -->
<dialog id="manage-addresses-modal" class="large-modal">
<div class="modal-header">
<h3>💼 Manage Addresses <span id="manage-addresses-chain"></span></h3>
<button class="modal-close" onclick="closeManageAddresses()">×</button>
</div>
<div class="modal-body">
<div class="section-header">
<p class="help-text">Manage your wallet addresses. You can derive new addresses, give them labels (pet-names), and select which one to use for receiving.</p>
<button class="btn-primary btn-sm" onclick="deriveNewAddressFromManage()">+ Derive New Address</button>
</div>
<div id="manage-addresses-list" class="addresses-list">
<p class="loading-text">Loading addresses...</p>
</div>
</div>
</dialog>
<!-- Settings Modal -->
<dialog id="settings-modal" class="settings-modal">
<div class="modal-header">
<h3>⚙️ Settings</h3>
<button class="modal-close" onclick="closeSettings()">×</button>
</div>
<div class="modal-body settings-body">
<!-- Identity Section -->
<section class="settings-section">
<h4>🔐 Device Identity</h4>
<div class="settings-card">
<div class="settings-row">
<span class="settings-label">Public Key:</span>
<span class="settings-value" id="identity-public-key">Loading...</span>
</div>
<div class="settings-row">
<span class="settings-label">Device Name:</span>
<span class="settings-value" id="identity-device-name">Loading...</span>
</div>
<div class="settings-row">
<span class="settings-label">Status:</span>
<span class="settings-value" id="identity-status">
<span class="status-badge" id="identity-status-badge">Active</span>
</span>
</div>
</div>
<div class="settings-actions">
<button class="btn-secondary btn-small" onclick="exportIdentity()">📥 Export Identity Mnemonic</button>
<button class="btn-secondary btn-small" onclick="showWhoAmI()">👤 Show Details</button>
</div>
</section>
<!-- P2P Sync Section -->
<section class="settings-section">
<h4>🔄 P2P Multi-Device Sync</h4>
<div class="settings-card">
<div class="settings-row">
<span class="settings-label">Sync Status:</span>
<span class="settings-value">
<span class="status-badge status-syncing" id="sync-status-badge">Syncing...</span>
</span>
</div>
<div class="settings-row">
<span class="settings-label">Connected Devices:</span>
<span class="settings-value" id="sync-device-count">0</span>
</div>
<div class="settings-row">
<span class="settings-label">Last Sync:</span>
<span class="settings-value" id="sync-last-time">Never</span>
</div>
</div>
<div class="settings-actions">
<button class="btn-secondary btn-small" onclick="addDevice()">➕ Add Device</button>
<button class="btn-secondary btn-small" onclick="joinDevice()">🔗 Join Device</button>
<button class="btn-secondary btn-small" onclick="showDevices()">📱 Manage Devices</button>
<button class="btn-secondary btn-small" onclick="forceSyncNow()">🔄 Sync Now</button>
</div>
</section>
<!-- Security Section -->
<section class="settings-section">
<h4>🔒 Security</h4>
<div class="settings-card">
<div class="settings-info">
<p>Your wallet mnemonics are encrypted with a password.</p>
<p>You can change your password below. You'll need your current password to make changes.</p>
</div>
<div class="settings-row">
<span class="settings-label">Encryption:</span>
<span class="settings-value">
<span class="status-badge status-active">Enabled</span>
</span>
</div>
<div class="settings-row">
<span class="settings-label">Password:</span>
<span class="settings-value monospace">••••••••</span>
</div>
</div>
<div class="settings-actions">
<button class="btn-secondary btn-small" onclick="showChangePassword()">🔑 Change Password</button>
</div>
</section>
<!-- Wallet Backup Section -->
<section class="settings-section">
<h4>💾 Backup & Recovery</h4>
<div class="settings-card">
<div class="settings-info">
<p>Your wallet uses two recovery phrases:</p>
<ul>
<li><strong>Identity (24 words)</strong> - For device attestation and sync</li>
<li><strong>Wallet (12 words)</strong> - For cryptocurrency keys</li>
</ul>
<p class="warning-text">⚠️ Never share these with anyone!</p>
</div>
</div>
<div class="settings-actions">
<button class="btn-secondary btn-small" onclick="exportWalletMnemonic()">📥 Export Wallet Mnemonic</button>
<button class="btn-secondary btn-small" onclick="verifyBackup()">✓ Verify Backup</button>
</div>
</section>
<!-- Network Settings -->
<section class="settings-section">
<h4>🌐 Network Settings</h4>
<div class="settings-card">
<div class="settings-row">
<span class="settings-label">Default Network:</span>
<span class="settings-value">
<select id="default-network-select" class="settings-select" onchange="changeDefaultNetwork()">
<option value="testnet">Testnet (Recommended)</option>
<option value="mainnet">Mainnet (Real Funds)</option>
</select>
</span>
</div>
<div class="settings-row">
<span class="settings-label">Server URL:</span>
<span class="settings-value monospace">http://localhost:4004</span>
</div>
</div>
</section>
<!-- Blockchain Providers -->
<section class="settings-section">
<h4>🔗 Blockchain Providers</h4>
<div class="settings-card">
<div class="settings-info">
<p>Configure which API providers to use for blockchain synchronization.</p>
</div>
<div class="settings-row">
<span class="settings-label">Bitcoin Provider:</span>
<span class="settings-value">
<select id="bitcoin-provider-select" class="settings-select" onchange="changeBitcoinProvider()">
<option value="electrum">Electrum (Native WDK)</option>
<option value="blockstream">Blockstream API</option>
<option value="mempool">Mempool.space</option>
</select>
</span>
</div>
<div class="settings-row">
<span class="settings-label">Ethereum Provider:</span>
<span class="settings-value">
<select id="ethereum-provider-select" class="settings-select" onchange="changeEthereumProvider()">
<option value="blockscout">Blockscout (No API Key)</option>
<option value="etherscan">Etherscan (Requires Key)</option>
<option value="alchemy">Alchemy (Requires Key)</option>
</select>
</span>
</div>
<div id="ethereum-api-key-row" class="settings-row" style="display: none;">
<span class="settings-label">Ethereum API Key:</span>
<span class="settings-value">
<input type="text" id="ethereum-api-key-input" class="settings-input" placeholder="Enter API key">
<button class="btn-small" onclick="saveEthereumApiKey()">Save</button>
</span>
</div>
</div>
</section>
<!-- System Info -->
<section class="settings-section">
<h4>ℹ️ System Information</h4>
<div class="settings-card">
<div class="settings-row">
<span class="settings-label">Version:</span>
<span class="settings-value" id="system-version">7.0.0-beta</span>
</div>
<div class="settings-row">
<span class="settings-label">Data Directory:</span>
<span class="settings-value monospace">~/.hyper-wallet</span>
</div>
<div class="settings-row">
<span class="settings-label">Storage:</span>
<span class="settings-value" id="storage-info">Autobase + Hyperbee</span>
</div>
</div>
</section>
<!-- Danger Zone -->
<section class="settings-section danger-section">
<h4>⚠️ Danger Zone</h4>
<div class="settings-card danger-card">
<div class="settings-info">
<p><strong>Reset Wallet:</strong> This will delete all local data. Make sure you have backed up your recovery phrases!</p>
</div>
</div>
<div class="settings-actions">
<button class="btn-danger btn-small" onclick="resetWallet()">🗑️ Reset Wallet</button>
</div>
</section>
</div>
</dialog>
<!-- Label Input Modal -->
<dialog id="label-input-modal">
<div class="modal-header">
<h3>🏷️ Address Label</h3>
<button class="modal-close" onclick="closeLabelInput()">×</button>
</div>
<div class="modal-body">
<p class="help-text">Enter a label for this address (optional):</p>
<p class="help-text" style="font-size: 0.85em; color: #888;">Examples: "Hardware wallet", "Hot wallet", "MetaMask", "Savings"</p>
<input type="text" id="label-input-field" class="form-control" placeholder="Enter label" style="margin: 1rem 0;">
<div class="form-actions">
<button class="btn-secondary" onclick="closeLabelInput()">Cancel</button>
<button class="btn-primary" onclick="confirmLabelInput()">Confirm</button>
</div>
</div>
</dialog>
<!-- Device Add Modal -->
<dialog id="device-add-modal" class="modal-dialog">
<div class="modal-header">
<h3>➕ Add Device</h3>
<button class="modal-close" onclick="closeDeviceAdd()">×</button>
</div>
<div class="modal-body">
<div class="device-add-content">
<div class="pairing-status waiting" id="device-pairing-status">
⏳ Waiting for device to connect...
</div>
<div class="invite-section">
<label><strong>Invite Code</strong></label>
<p class="help-text">Share this code with your other device:</p>
<div class="invite-code-display">
<textarea id="device-invite-code" readonly onclick="this.select()"></textarea>
<button class="btn-primary" onclick="copyInviteCode()">📋 Copy</button>
</div>
</div>
<div class="pairing-instructions">
<h4>On the other device:</h4>
<ol>
<li>Open the wallet web UI</li>
<li>Go to Settings → P2P Multi-Device Sync</li>
<li>Click "Join Device"</li>
<li>Paste the invite code</li>
</ol>
<p class="help-text">The pairing will happen automatically over the P2P network. Both mnemonics (identity + wallet) will be securely transmitted.</p>
</div>
<div id="device-pairing-result" style="display: none;" class="pairing-result">
<!-- Populated when pairing completes -->
</div>
</div>
</div>
</dialog>
<!-- Join Device Modal -->
<dialog id="device-join-modal" class="modal-dialog">
<div class="modal-header">
<h3>🔗 Join Device</h3>
<button class="modal-close" onclick="closeDeviceJoin()">×</button>
</div>
<div class="modal-body">
<div class="device-join-content">
<p class="section-description">
Enter the invite code from your primary device to join this device to the wallet.
You will receive both mnemonics (identity + wallet) securely over the P2P network.
</p>
<div class="form-group">
<label for="join-invite-code"><strong>Invite Code</strong></label>
<textarea id="join-invite-code" class="mnemonic-input" placeholder="Paste invite code here..." rows="6"></textarea>
</div>
<div class="form-group">
<label for="join-device-name"><strong>Device Name</strong></label>
<input type="text" id="join-device-name" placeholder="My Phone" />
</div>
<div id="join-error" class="form-error"></div>
<div class="form-actions">
<button class="btn-secondary" onclick="closeDeviceJoin()">Cancel</button>
<button class="btn-primary" onclick="executeDeviceJoin()">Join Wallet</button>
</div>
</div>
</div>
</dialog>
<!-- Devices List Modal -->
<dialog id="devices-modal" class="modal-dialog">
<div class="modal-header">
<h3>📱 Manage Devices</h3>
<button class="modal-close" onclick="closeDevices()">×</button>
</div>
<div class="modal-body">
<div class="devices-content">
<div class="section-header-row">
<p class="section-description">
Devices with access to this wallet. Click keys to copy full value.
</p>
<button class="btn-secondary btn-small" onclick="deduplicateDevices()">🧹 Remove Duplicates</button>
</div>
<div class="device-keys-legend">
<div><strong>Identity Key:</strong> Shared across all your devices (from 24-word mnemonic)</div>
<div><strong>Writer Key:</strong> Unique per device (Autobase writer)</div>
</div>
<div id="devices-list" class="devices-list">
<div class="spinner"></div>
</div>
</div>
</div>
</dialog>
<!-- Contacts Modal -->
<dialog id="contacts-modal" class="modal-dialog">
<div class="modal-header">
<h3>📇 Contact Book</h3>
<button class="modal-close" onclick="closeContacts()">×</button>
</div>
<div class="modal-body">
<div class="contacts-content">
<div class="section-header-row">
<p class="section-description">
Manage your blockchain contacts. Send transactions by name instead of typing addresses.
</p>
<button class="btn-primary btn-small" onclick="showAddContact()">➕ Add Contact</button>
</div>
<div id="contacts-list" class="contacts-list">
<div class="spinner"></div>
</div>
</div>
</div>
</dialog>
<!-- Add Contact Modal -->
<dialog id="add-contact-modal" class="modal-dialog">
<div class="modal-header">
<h3>➕ Add Contact</h3>
<button class="modal-close" onclick="closeAddContact()">×</button>
</div>
<div class="modal-body">
<form id="add-contact-form" onsubmit="submitAddContact(event)">
<div class="form-group">
<label for="contact-name">Name *</label>
<input type="text" id="contact-name" required placeholder="Alice">
</div>
<div class="form-group">
<label for="contact-chain">Chain *</label>
<select id="contact-chain" required>
<option value="bitcoin">Bitcoin</option>
<option value="ethereum">Ethereum</option>
</select>
</div>
<div class="form-group">
<label for="contact-address">Address *</label>
<input type="text" id="contact-address" required placeholder="bc1q... or 0x...">
<small class="form-hint">Enter the blockchain address</small>
</div>
<div class="form-group">
<label for="contact-notes">Notes (optional)</label>
<textarea id="contact-notes" rows="3" placeholder="Friend from college"></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn-primary">Add Contact</button>
<button type="button" class="btn-secondary" onclick="closeAddContact()">Cancel</button>
</div>
<div id="add-contact-error" class="form-error" style="display: none;"></div>
</form>
</div>
</dialog>
<!-- Edit Contact Modal -->
<dialog id="edit-contact-modal" class="modal-dialog">
<div class="modal-header">
<h3>✏️ Edit Contact</h3>
<button class="modal-close" onclick="closeEditContact()">×</button>
</div>
<div class="modal-body">
<form id="edit-contact-form" onsubmit="submitEditContact(event)">
<input type="hidden" id="edit-contact-id">
<div class="form-group">
<label for="edit-contact-name">Name *</label>
<input type="text" id="edit-contact-name" required>
</div>
<div class="form-group">
<label>Chain</label>
<input type="text" id="edit-contact-chain" disabled>
</div>
<div class="form-group">
<label>Address</label>
<input type="text" id="edit-contact-address" disabled>
<small class="form-hint">Address cannot be changed</small>
</div>
<div class="form-group">
<label for="edit-contact-notes">Notes</label>
<textarea id="edit-contact-notes" rows="3"></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn-primary">Save Changes</button>
<button type="button" class="btn-secondary" onclick="closeEditContact()">Cancel</button>
</div>
<div id="edit-contact-error" class="form-error" style="display: none;"></div>
</form>
</div>
</dialog>
<!-- Add Wallet to Contact Modal -->
<dialog id="add-wallet-modal" class="modal-dialog">
<div class="modal-header">
<h3>➕ Add Wallet to Contact</h3>
<button class="modal-close" onclick="closeAddWallet()">×</button>
</div>
<div class="modal-body">
<p class="modal-info">
Adding wallet for: <strong id="add-wallet-contact-name"></strong>
</p>
<form id="add-wallet-form" onsubmit="submitAddWallet(event)">
<div class="form-group">
<label for="add-wallet-chain">Blockchain *</label>
<select id="add-wallet-chain" required>
<option value="bitcoin">₿ Bitcoin</option>
<option value="ethereum">Ξ Ethereum</option>
<option value="ethereum-usdt">₮ Ethereum - USDT</option>
</select>
</div>
<div class="form-group">
<label for="add-wallet-address">Wallet Address *</label>
<input type="text" id="add-wallet-address" required placeholder="bc1q... or 0x...">
<small class="form-hint">Enter the blockchain address for this wallet</small>
</div>
<div class="form-group">
<label for="add-wallet-label">Label (optional)</label>
<input type="text" id="add-wallet-label" placeholder="e.g., Hardware wallet, Hot wallet, MetaMask">
<small class="form-hint">Optional label to identify this wallet (helpful when you have multiple addresses for the same chain)</small>
</div>
<div class="form-actions">
<button type="submit" class="btn-primary">Add Wallet</button>
<button type="button" class="btn-secondary" onclick="closeAddWallet()">Cancel</button>
</div>
<div id="add-wallet-error" class="form-error" style="display: none;"></div>
</form>
</div>
</dialog>
<!-- Password Setup/Unlock Dialog -->
<dialog id="password-dialog">
<div class="modal-header">
<h3 id="password-dialog-title">🔐 Unlock Wallet</h3>
</div>
<div class="modal-body">
<div id="password-dialog-warning" class="warning-box" style="display: none; background: #fff3cd; border: 2px solid #ffc107; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
<strong style="color: #856404;">⚠️ IMPORTANT - SET ENCRYPTION PASSWORD:</strong>
<ul style="margin: 10px 0; padding-left: 20px; text-align: left; color: #856404;">
<li>This password will <strong>ENCRYPT</strong> your wallet mnemonics</li>
<li>You <strong>MUST remember</strong> this password</li>
<li>Required <strong>every time</strong> you access the wallet</li>
<li><strong>Cannot be recovered</strong> if forgotten</li>
</ul>
</div>
<div class="form-group">
<label for="password-input">Password (min 8 characters):</label>
<input
type="password"
id="password-input"
class="input-field"
placeholder="Enter password"
autocomplete="new-password"
style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
/>
</div>
<div class="form-group" id="password-confirm-group">
<label for="password-confirm-input">Confirm Password:</label>
<input
type="password"
id="password-confirm-input"
class="input-field"
placeholder="Re-enter password"
autocomplete="new-password"
style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
/>
</div>
<div id="password-match-error" style="color: #d32f2f; font-weight: bold; display: none; margin-top: 10px;">
❌ Passwords do not match
</div>
<div id="password-strength" style="margin-top: 15px; display: none;">
<div style="display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 14px;">
<span>Strength:</span>
<span id="password-strength-text" style="font-weight: bold;">Weak</span>
</div>
<div style="height: 6px; background: #e0e0e0; border-radius: 3px; overflow: hidden;">
<div id="password-strength-bar" style="height: 100%; width: 0%; transition: width 0.3s, background 0.3s;"></div>
</div>
</div>
<div class="button-row" style="margin-top: 25px; display: flex; gap: 10px; justify-content: flex-end;">
<button id="password-cancel-btn" class="secondary-btn" style="padding: 10px 20px; font-size: 16px;">Cancel</button>
<button id="password-submit-btn" class="primary-btn" disabled style="padding: 10px 20px; font-size: 16px;">Submit</button>
</div>
<div id="password-attempts" style="margin-top: 15px; color: #666; font-size: 0.9em; text-align: center;">
</div>
</div>
</dialog>
<!-- Change Password Dialog -->
<dialog id="change-password-dialog">
<div class="modal-header">
<h3>🔑 Change Password</h3>
</div>
<div class="modal-body">
<div class="warning-box" style="background: #fff3cd; border: 2px solid #ffc107; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
<strong style="color: #856404;">⚠️ IMPORTANT:</strong>
<ul style="margin: 10px 0; padding-left: 20px; text-align: left; color: #856404;">
<li>You'll need your <strong>current password</strong> to change it</li>
<li><strong>Remember your new password</strong> - it cannot be recovered</li>
<li>All devices will need the new password on next access</li>
</ul>
</div>
<div class="form-group">
<label for="current-password-input">Current Password:</label>
<input
type="password"
id="current-password-input"
class="input-field"
placeholder="Enter current password"
autocomplete="current-password"
style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
/>
</div>
<div class="form-group">
<label for="new-password-input">New Password (min 8 characters):</label>
<input
type="password"
id="new-password-input"
class="input-field"
placeholder="Enter new password"
autocomplete="new-password"
style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
/>
</div>
<div class="form-group">
<label for="new-password-confirm-input">Confirm New Password:</label>
<input
type="password"
id="new-password-confirm-input"
class="input-field"
placeholder="Re-enter new password"
autocomplete="new-password"
style="width: 100%; padding: 10px; font-size: 16px; border: 2px solid #ddd; border-radius: 6px;"
/>
</div>
<div id="change-password-match-error" style="color: #d32f2f; font-weight: bold; display: none; margin-top: 10px;">
❌ New passwords do not match
</div>
<div id="change-password-strength" style="margin-top: 15px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 14px;">
<span>Strength:</span>
<span id="change-password-strength-text" style="font-weight: bold;">Weak</span>
</div>
<div style="height: 6px; background: #e0e0e0; border-radius: 3px; overflow: hidden;">
<div id="change-password-strength-bar" style="height: 100%; width: 0%; transition: width 0.3s, background 0.3s;"></div>
</div>
</div>
<div class="button-row" style="margin-top: 25px; display: flex; gap: 10px; justify-content: flex-end;">
<button id="change-password-cancel-btn" class="secondary-btn" style="padding: 10px 20px; font-size: 16px;">Cancel</button>
<button id="change-password-submit-btn" class="primary-btn" disabled style="padding: 10px 20px; font-size: 16px;">Change Password</button>
</div>
<div id="change-password-error" style="margin-top: 15px; color: #d32f2f; font-weight: bold; text-align: center; display: none;">
</div>
</div>
</dialog>
<!-- Password Prompt Modal -->
<dialog id="password-prompt-modal" class="modal-dialog">
<div class="modal-header">
<h3>🔒 Enter Password</h3>
<button class="modal-close" onclick="closePasswordPrompt()">×</button>
</div>
<div class="modal-body">
<p id="password-prompt-message">Enter your encryption password:</p>
<div class="form-group">
<input type="password" id="password-prompt-input" class="form-control" placeholder="Password" autocomplete="current-password" style="width: 100%; padding: 10px; font-size: 16px;">
</div>
<div class="form-actions">
<button class="btn-secondary" onclick="closePasswordPrompt()">Cancel</button>
<button class="btn-primary" onclick="submitPasswordPrompt()">Submit</button>
</div>
</div>
</dialog>
<!-- Mnemonic Display Modal -->
<dialog id="mnemonic-display-modal" class="modal-dialog">
<div class="modal-header">
<h3>🔐 Wallet Mnemonic</h3>
<button class="modal-close" onclick="closeMnemonicDisplay()">×</button>
</div>
<div class="modal-body">
<div class="warning-box" style="background: #fff3cd; border: 2px solid #ffc107; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
<strong style="color: #856404;">⚠️ CRITICAL: KEEP THIS SAFE!</strong>
<ul style="margin: 10px 0; padding-left: 20px; text-align: left; color: #856404;">
<li>This mnemonic controls your cryptocurrency funds</li>
<li>Anyone with these words can steal your funds</li>
<li>Write them down on paper and store securely</li>
<li>Never share with anyone, never store digitally</li>
</ul>
</div>
<div class="mnemonic-display" style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<pre id="mnemonic-display-text" style="font-size: 16px; font-weight: bold; line-height: 1.8; word-wrap: break-word; white-space: pre-wrap;"></pre>
</div>
<div class="form-actions">
<button class="btn-secondary" onclick="copyMnemonicToClipboard()">📋 Copy to Clipboard</button>
<button class="btn-primary" onclick="closeMnemonicDisplay()">Close</button>
</div>
</div>
</dialog>
<!-- Error Toast -->
<div id="error-toast" class="toast"></div>
<!-- IPC Communication Layer -->
<script>
// Define window.ipcFetch IMMEDIATELY (before wallet.js loads)
// Queue requests until Pear.ipc is ready
(function() {
let requestId = 0
const pending = new Map()
const queue = []
let ipcReady = false
// Define window.ipcFetch synchronously (queues until ready)
window.ipcFetch = function(url, options = {}) {
return new Promise((resolve, reject) => {
const id = ++requestId
const request = {
id,
url,
method: options.method || 'GET',
headers: options.headers || {},
body: options.body
}
pending.set(id, { resolve, reject })
if (ipcReady && window._ipcPipe) {
// IPC ready - send immediately (newline-delimited)
console.log(`[IPC →] Request ${id}: ${request.method} ${request.url}`)
window._ipcPipe.write(JSON.stringify(request) + '\n')
} else {
// IPC not ready - queue request
console.log(`[IPC ⏳] Queued ${id}: ${request.method} ${request.url}`)
queue.push(request)
}
// Timeout after 30 seconds
setTimeout(() => {
if (pending.has(id)) {
pending.delete(id)
console.error(`[IPC] Timeout for request ${id}`)
reject(new Error('IPC timeout'))
}
}, 30000)
}).then(response => {
// Convert IPC response to fetch-like response
return {
ok: response.status >= 200 && response.status < 300,
status: response.status,
headers: response.headers,
json: async () => JSON.parse(response.body),
text: async () => response.body
}
})
}
console.log('✅ window.ipcFetch defined (queuing until IPC ready)')
// Initialize IPC when Pear is available
const initIPC = () => {
if (typeof Pear === 'undefined') {
console.log('⏳ Waiting for Pear...')
setTimeout(initIPC, 50)
return
}
console.log('🔧 Initializing IPC client...')
console.log(' Pear object:', Pear)
console.log(' Pear.constructor:', Pear.constructor)
console.log(' Pear.constructor.IPC:', Pear.constructor.IPC)
// Get the pipe from IPC (same way backend does)
const ipc = Pear[Pear.constructor.IPC]
const pipe = ipc.pipe()
console.log(' Got pipe from ipc.pipe()')
console.log(' Pipe object:', pipe)
// Buffer for incomplete messages
let buffer = ''
// Handle responses from main process via pipe
pipe.on('data', (data) => {
try {
// Convert Buffer/Uint8Array to string properly
let dataStr
if (typeof data === 'string') {
dataStr = data
} else if (data instanceof Uint8Array || data instanceof Buffer || ArrayBuffer.isView(data)) {
// Use TextDecoder for proper UTF-8 conversion
const decoder = new TextDecoder('utf-8')
dataStr = decoder.decode(data)
} else {
console.error('[IPC] Unexpected data type:', typeof data, data)
return
}
console.log('[IPC ← RAW] Received data chunk (string):', dataStr.substring(0, 200))
console.log('[IPC ← RAW] Data chunk length:', dataStr.length)
// Append to buffer
buffer += dataStr
console.log('[IPC] Buffer now:', buffer.length, 'chars')
// Process complete messages (newline-delimited)
const lines = buffer.split('\n')
console.log('[IPC] Split into', lines.length, 'lines')
// Keep the last incomplete line in buffer
buffer = lines.pop() || ''
console.log('[IPC] Buffer remainder:', buffer.length, 'chars')
// Process each complete line
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
console.log(`[IPC] Processing line ${i}:`, line.substring(0, 100))
if (!line.trim()) {
console.log(`[IPC] Line ${i} is empty, skipping`)
continue
}
try {
console.log(`[IPC] Attempting to parse line ${i}`)
const response = JSON.parse(line)
console.log(`[IPC ←] Response ${response.id}:`, response.status)
const resolver = pending.get(response.id)
if (resolver) {
pending.delete(response.id)
if (response.error) {
resolver.reject(new Error(response.error))
} else {
resolver.resolve(response)
}
} else {
console.warn('[IPC] No pending request for ID:', response.id)
}
} catch (err) {
console.error('[IPC] Parse error for line:', line.substring(0, 100), err)
}
}
} catch (err) {
console.error('[IPC] Fatal error:', err)
}
})
// Add error handlers
pipe.on('error', (err) => {
console.error('[IPC] Pipe error:', err)
})
pipe.on('end', () => {
console.log('[IPC] Pipe ended')
})
pipe.on('close', () => {
console.log('[IPC] Pipe closed')
})
// Mark IPC as ready
ipcReady = true
console.log('✅ IPC ready - processing queued requests')
console.log('✅ Pipe listening for data events')
// Process queued requests
while (queue.length > 0) {
const request = queue.shift()
console.log(`[IPC →] Sending queued ${request.id}: ${request.method} ${request.url}`)
pipe.write(JSON.stringify(request) + '\n')
}
// Store pipe for future use
window._ipcPipe = pipe
console.log('✅ IPC client initialized')
}
// Start initialization immediately
initIPC()
})()
</script>
<script type="module" src="wallet.js"></script>
</body>
</html>