| … | … | @@ -93,13 +93,33 @@ function showRestoreWallet() { |
| 93 | 93 | |
| 94 | 94 | async function generateNewWallet() { |
| 95 | 95 | try { |
| 96 | // Validate password | |
| 97 | const password = document.getElementById('create-password').value | |
| 98 | const passwordConfirm = document.getElementById('create-password-confirm').value | |
| 99 | const errorDiv = document.getElementById('create-password-error') | |
| 100 | ||
| 101 | errorDiv.style.display = 'none' | |
| 102 | ||
| 103 | if (!password || password.length < 8) { | |
| 104 | errorDiv.textContent = 'Password must be at least 8 characters' | |
| 105 | errorDiv.style.display = 'block' | |
| 106 | return | |
| 107 | } | |
| 108 | ||
| 109 | if (password !== passwordConfirm) { | |
| 110 | errorDiv.textContent = 'Passwords do not match' | |
| 111 | errorDiv.style.display = 'block' | |
| 112 | return | |
| 113 | } | |
| 114 | ||
| 96 | 115 | const btn = document.getElementById('btn-generate-mnemonic') |
| 97 | 116 | btn.disabled = true |
| 98 | 117 | btn.textContent = 'Generating...' |
| 99 | 118 | |
| 100 | 119 | const res = await authFetch(`${API_BASE}/init`, { |
| 101 | 120 | method: 'POST', |
| 102 | headers: { 'Content-Type': 'application/json' } | |
| 121 | headers: { 'Content-Type': 'application/json' }, | |
| 122 | body: JSON.stringify({ password }) | |
| 103 | 123 | }) |
| 104 | 124 | |
| 105 | 125 | const data = await res.json() |
| … | … | @@ -185,6 +205,8 @@ function confirmMnemonicBackup() { |
| 185 | 205 | async function restoreWallet() { |
| 186 | 206 | const identityInput = document.getElementById('restore-identity-mnemonic') |
| 187 | 207 | const walletInput = document.getElementById('restore-wallet-mnemonic') |
| 208 | const password = document.getElementById('restore-password').value | |
| 209 | const passwordConfirm = document.getElementById('restore-password-confirm').value | |
| 188 | 210 | const errorDiv = document.getElementById('restore-error') |
| 189 | 211 | |
| 190 | 212 | const identityMnemonic = identityInput.value.trim() |
| … | … | @@ -197,13 +219,24 @@ async function restoreWallet() { |
| 197 | 219 | return |
| 198 | 220 | } |
| 199 | 221 | |
| 222 | if (!password || password.length < 8) { | |
| 223 | errorDiv.textContent = 'Password must be at least 8 characters' | |
| 224 | return | |
| 225 | } | |
| 226 | ||
| 227 | if (password !== passwordConfirm) { | |
| 228 | errorDiv.textContent = 'Passwords do not match' | |
| 229 | return | |
| 230 | } | |
| 231 | ||
| 200 | 232 | try { |
| 201 | 233 | const res = await authFetch(`${API_BASE}/restore`, { |
| 202 | 234 | method: 'POST', |
| 203 | 235 | headers: { 'Content-Type': 'application/json' }, |
| 204 | 236 | body: JSON.stringify({ |
| 205 | 237 | identityMnemonic, |
| 206 | walletMnemonic | |
| 238 | walletMnemonic, | |
| 239 | password | |
| 207 | 240 | }) |
| 208 | 241 | }) |
| 209 | 242 | |
| … | … | @@ -547,8 +580,19 @@ async function exportIdentity() { |
| 547 | 580 | return |
| 548 | 581 | } |
| 549 | 582 | |
| 583 | // Prompt for password | |
| 584 | const password = prompt('🔒 Enter your encryption password:') | |
| 585 | if (!password) { | |
| 586 | showError('Password required to export mnemonic') | |
| 587 | return | |
| 588 | } | |
| 589 | ||
| 550 | 590 | try { |
| 551 | const res = await authFetch(`${API_BASE}/export-identity`) | |
| 591 | const res = await authFetch(`${API_BASE}/export-identity`, { | |
| 592 | method: 'POST', | |
| 593 | headers: { 'Content-Type': 'application/json' }, | |
| 594 | body: JSON.stringify({ password }) | |
| 595 | }) | |
| 552 | 596 | const data = await res.json() |
| 553 | 597 | |
| 554 | 598 | if (data.error) { |
| … | … | @@ -568,8 +612,19 @@ async function exportWalletMnemonic() { |
| 568 | 612 | return |
| 569 | 613 | } |
| 570 | 614 | |
| 615 | // Prompt for password | |
| 616 | const password = prompt('🔒 Enter your encryption password:') | |
| 617 | if (!password) { | |
| 618 | showError('Password required to export mnemonic') | |
| 619 | return | |
| 620 | } | |
| 621 | ||
| 571 | 622 | try { |
| 572 | const res = await authFetch(`${API_BASE}/export-wallet`) | |
| 623 | const res = await authFetch(`${API_BASE}/export-wallet`, { | |
| 624 | method: 'POST', | |
| 625 | headers: { 'Content-Type': 'application/json' }, | |
| 626 | body: JSON.stringify({ password }) | |
| 627 | }) | |
| 573 | 628 | const data = await res.json() |
| 574 | 629 | |
| 575 | 630 | if (data.error) { |
| … | … | @@ -63,6 +63,24 @@ |
| 63 | 63 | </label> |
| 64 | 64 | </div> |
| 65 | 65 | </div> |
| 66 | ||
| 67 | <div id="password-section" class="password-section"> | |
| 68 | <h3>🔒 Set Encryption Password</h3> | |
| 69 | <p class="password-hint">Your recovery phrases will be encrypted with this password. You'll need it to access your wallet.</p> | |
| 70 | <div class="form-group"> | |
| 71 | <label for="create-password">Password (min 8 characters)</label> | |
| 72 | <input type="password" id="create-password" placeholder="Enter strong password" minlength="8"> | |
| 73 | </div> | |
| 74 | <div class="form-group"> | |
| 75 | <label for="create-password-confirm">Confirm Password</label> | |
| 76 | <input type="password" id="create-password-confirm" placeholder="Re-enter password" minlength="8"> | |
| 77 | </div> | |
| 78 | <div id="create-password-error" class="form-error" style="display: none;"></div> | |
| 79 | <div class="password-warning"> | |
| 80 | <p>⚠️ <strong>IMPORTANT</strong>: If you forget this password, you can only restore using your recovery phrases!</p> | |
| 81 | </div> | |
| 82 | </div> | |
| 83 | ||
| 66 | 84 | <div class="setup-actions"> |
| 67 | 85 | <button class="btn-secondary" onclick="showSetupWelcome()">← Back</button> |
| 68 | 86 | <button id="btn-generate-mnemonic" class="btn-primary" onclick="generateNewWallet()">Generate Recovery Phrases</button> |
| … | … | @@ -89,6 +107,19 @@ |
| 89 | 107 | <textarea id="restore-wallet-mnemonic" class="mnemonic-input" placeholder="Enter your 12-word wallet mnemonic (separated by spaces)" rows="3"></textarea> |
| 90 | 108 | </div> |
| 91 | 109 | |
| 110 | <div class="password-section"> | |
| 111 | <h3>🔒 Set Encryption Password</h3> | |
| 112 | <p class="password-hint">Set a password to encrypt your restored mnemonics.</p> | |
| 113 | <div class="form-group"> | |
| 114 | <label for="restore-password">Password (min 8 characters)</label> | |
| 115 | <input type="password" id="restore-password" placeholder="Enter strong password" minlength="8"> | |
| 116 | </div> | |
| 117 | <div class="form-group"> | |
| 118 | <label for="restore-password-confirm">Confirm Password</label> | |
| 119 | <input type="password" id="restore-password-confirm" placeholder="Re-enter password" minlength="8"> | |
| 120 | </div> | |
| 121 | </div> | |
| 122 | ||
| 92 | 123 | <div id="restore-error" class="form-error"></div> |
| 93 | 124 | |
| 94 | 125 | <div class="setup-actions"> |
| … | … | @@ -189,7 +189,7 @@ function generateIdentityMnemonic() { |
| 189 | 189 | |
| 190 | 190 | // Load identity from mnemonic |
| 191 | 191 | async function loadIdentity() { |
| 192 | const mnemonic = loadIdentityMnemonic() | |
| 192 | const mnemonic = loadIdentityMnemonicCached() | |
| 193 | 193 | if (!mnemonic) { |
| 194 | 194 | throw new Error('Identity not initialized') |
| 195 | 195 | } |
| … | … | @@ -283,7 +283,7 @@ const routes = { |
| 283 | 283 | // GET /api/balance/:chain (protected) |
| 284 | 284 | 'GET /api/balance/': requireAuth(async (req, res, chain) => { |
| 285 | 285 | try { |
| 286 | const walletMnemonic = loadWalletMnemonic() | |
| 286 | const walletMnemonic = loadWalletMnemonicCached() | |
| 287 | 287 | if (!walletMnemonic) { |
| 288 | 288 | return sendError(res, 'Wallet not initialized', 400) |
| 289 | 289 | } |
| … | … | @@ -309,7 +309,7 @@ const routes = { |
| 309 | 309 | // GET /api/address/:chain |
| 310 | 310 | 'GET /api/address/': requireAuth(async (req, res, chain) => { |
| 311 | 311 | try { |
| 312 | const walletMnemonic = loadWalletMnemonic() | |
| 312 | const walletMnemonic = loadWalletMnemonicCached() | |
| 313 | 313 | if (!walletMnemonic) { |
| 314 | 314 | return sendError(res, 'Wallet not initialized', 400) |
| 315 | 315 | } |
| … | … | @@ -335,7 +335,7 @@ const routes = { |
| 335 | 335 | // GET /api/qrcode/:chain |
| 336 | 336 | 'GET /api/qrcode/': requireAuth(async (req, res, chain) => { |
| 337 | 337 | try { |
| 338 | const walletMnemonic = loadWalletMnemonic() | |
| 338 | const walletMnemonic = loadWalletMnemonicCached() | |
| 339 | 339 | if (!walletMnemonic) { |
| 340 | 340 | return sendError(res, 'Wallet not initialized', 400) |
| 341 | 341 | } |
| … | … | @@ -412,7 +412,7 @@ const routes = { |
| 412 | 412 | await initializeStorage(identity) |
| 413 | 413 | |
| 414 | 414 | // Initialize WDK |
| 415 | const walletMnemonic = loadWalletMnemonic() | |
| 415 | const walletMnemonic = loadWalletMnemonicCached() | |
| 416 | 416 | if (!walletMnemonic) { |
| 417 | 417 | return sendError(res, 'Wallet not initialized', 400) |
| 418 | 418 | } |
| … | … | @@ -442,7 +442,7 @@ const routes = { |
| 442 | 442 | // GET /api/whoami |
| 443 | 443 | 'GET /api/whoami': requireAuth(async (req, res) => { |
| 444 | 444 | try { |
| 445 | const identityMnemonic = loadIdentityMnemonic() | |
| 445 | const identityMnemonic = loadIdentityMnemonicCached() | |
| 446 | 446 | if (!identityMnemonic) { |
| 447 | 447 | return sendError(res, 'Identity not initialized', 400) |
| 448 | 448 | } |
| … | … | @@ -565,8 +565,8 @@ const routes = { |
| 565 | 565 | 'POST /api/device/add': requireAuth(async (req, res) => { |
| 566 | 566 | try { |
| 567 | 567 | const identity = await loadIdentity() |
| 568 | const identityMnemonic = loadIdentityMnemonic() | |
| 569 | const walletMnemonic = loadWalletMnemonic() | |
| 568 | const identityMnemonic = loadIdentityMnemonicCached() | |
| 569 | const walletMnemonic = loadWalletMnemonicCached() | |
| 570 | 570 | |
| 571 | 571 | if (!walletMnemonic) { |
| 572 | 572 | return sendError(res, 'Wallet not initialized', 400) |
| … | … | @@ -1009,7 +1009,7 @@ const routes = { |
| 1009 | 1009 | return sendError(res, 'Missing required fields', 400) |
| 1010 | 1010 | } |
| 1011 | 1011 | |
| 1012 | const walletMnemonic = loadWalletMnemonic() | |
| 1012 | const walletMnemonic = loadWalletMnemonicCached() | |
| 1013 | 1013 | if (!walletMnemonic) { |
| 1014 | 1014 | return sendError(res, 'Wallet not initialized', 400) |
| 1015 | 1015 | } |
| … | … | @@ -1043,7 +1043,7 @@ const routes = { |
| 1043 | 1043 | return sendError(res, 'Missing required fields', 400) |
| 1044 | 1044 | } |
| 1045 | 1045 | |
| 1046 | const walletMnemonic = loadWalletMnemonic() | |
| 1046 | const walletMnemonic = loadWalletMnemonicCached() | |
| 1047 | 1047 | if (!walletMnemonic) { |
| 1048 | 1048 | return sendError(res, 'Wallet not initialized', 400) |
| 1049 | 1049 | } |
| … | … | @@ -1070,7 +1070,7 @@ const routes = { |
| 1070 | 1070 | // POST /api/sync-transactions - Manual transaction sync from blockchain |
| 1071 | 1071 | 'POST /api/sync-transactions': requireAuth(async (req, res) => { |
| 1072 | 1072 | try { |
| 1073 | const walletMnemonic = loadWalletMnemonic() | |
| 1073 | const walletMnemonic = loadWalletMnemonicCached() | |
| 1074 | 1074 | if (!walletMnemonic) { |
| 1075 | 1075 | return sendError(res, 'Wallet not initialized', 400) |
| 1076 | 1076 | } |
| … | … | @@ -1237,7 +1237,7 @@ const routes = { |
| 1237 | 1237 | 'POST /api/wallet/init': async (req, res) => { |
| 1238 | 1238 | try { |
| 1239 | 1239 | // Check if wallet already exists |
| 1240 | const existing = loadWalletMnemonic() | |
| 1240 | const existing = loadWalletMnemonicCached() | |
| 1241 | 1241 | if (existing) { |
| 1242 | 1242 | return sendError(res, 'Wallet already initialized', 400) |
| 1243 | 1243 | } |
| … | … | @@ -1347,7 +1347,7 @@ const routes = { |
| 1347 | 1347 | } |
| 1348 | 1348 | |
| 1349 | 1349 | // Check if wallet already exists |
| 1350 | const existing = loadWalletMnemonic() | |
| 1350 | const existing = loadWalletMnemonicCached() | |
| 1351 | 1351 | if (existing) { |
| 1352 | 1352 | return sendError(res, 'Wallet already initialized. Delete ~/.hyper-wallet/.wallet-mnemonic to restore.', 400) |
| 1353 | 1353 | } |