Skip to content

prevent double keys in local storage - #677

Merged
bordalix merged 3 commits into
masterfrom
fix_local_storage
Jun 17, 2026
Merged

bordalix merged 3 commits into
masterfrom
fix_local_storage

Conversation

@bordalix

@bordalix bordalix commented Jun 16, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Refactor
    • Consolidated encrypted credential storage management by centralizing storage key definitions, improving consistency and maintainability of how mnemonics and private keys are persisted and accessed throughout the application.

@bordalix
bordalix requested a review from pietro909 June 16, 2026 18:04
@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b0d7b60-40b0-4b1c-8ce2-69f5ec88fc7e

📥 Commits

Reviewing files that changed from the base of the PR and between 8da7062 and 255f1bb.

📒 Files selected for processing (3)
  • src/lib/mnemonic.ts
  • src/lib/privateKey.ts
  • src/lib/storageKeys.ts

Walkthrough

Two exported string constants (MNEMONIC_STORAGE_KEY and NSEC_STORAGE_KEY) are added to a new src/lib/storageKeys.ts file. mnemonic.ts and privateKey.ts replace their previous local or hardcoded localStorage key strings with these shared constants, with no changes to encryption logic or exported function signatures.

Changes

Storage Key Centralization

Layer / File(s) Summary
storageKeys constants definition
src/lib/storageKeys.ts
Adds and exports MNEMONIC_STORAGE_KEY ('encrypted_mnemonic') and NSEC_STORAGE_KEY ('encrypted_private_key') as the canonical localStorage key strings.
Mnemonic and private key modules updated to use shared constants
src/lib/mnemonic.ts, src/lib/privateKey.ts
Replaces all local/hardcoded storage key usages with the imported constants. setMnemonic now removes NSEC_STORAGE_KEY on write; setPrivateKey removes MNEMONIC_STORAGE_KEY on write, ensuring consistent cross-module cleanup.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'prevent double keys in local storage' directly summarizes the main objective of consolidating storage key constants and removing duplicate mnemonic entries, which is the core change across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix_local_storage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: fc31333
Status: ✅  Deploy successful!
Preview URL: https://d9aeccbc.wallet-bitcoin.pages.dev
Branch Preview URL: https://fix-local-storage.wallet-bitcoin.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: fc31333
Status: ✅  Deploy successful!
Preview URL: https://2323354e.arkade-wallet.pages.dev
Branch Preview URL: https://fix-local-storage.arkade-wallet.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Deploying tmp-boltz-upstream-mainnet-arkade-wallet with  Cloudflare Pages  Cloudflare Pages

Latest commit: fc31333
Status: ✅  Deploy successful!
Preview URL: https://eb0373d9.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev
Branch Preview URL: https://fix-local-storage.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev

View logs

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — PR #677: prevent double keys in local storage

Summary

The PR does two things:

  1. Refactors hardcoded 'encrypted_mnemonic' / 'encrypted_private_key' strings into exported constants (MNEMONIC_STORAGE_KEY, NSEC_STORAGE_KEY)
  2. Adds mutual exclusion: setMnemonic() removes any stored private key, and setPrivateKey() removes any stored mnemonic

The mutual-exclusion logic is correct in intent — a wallet should have one identity source, not both. But there's one real issue and one gap.


🔴 Issue: Circular dependency introduced

mnemonic.ts:4 — import { NSEC_STORAGE_KEY } from './privateKey'
privateKey.ts:3 — import { MNEMONIC_STORAGE_KEY } from './mnemonic'

This creates a static circular import. On master, privateKey.ts was deliberately careful to use a dynamic import for mnemonic (await import('./mnemonic') at privateKey.ts:59) to avoid this exact cycle.

While ES module semantics mean the const string literals should resolve before any function runs, circular dependencies are fragile and a known source of subtle bugs — especially across bundler versions. If module evaluation order ever changes (e.g. a new import is added to either file), you could get undefined constants silently.

Fix: Extract both constants into a shared file (e.g. src/lib/storageKeys.ts):

// src/lib/storageKeys.ts
export const MNEMONIC_STORAGE_KEY = 'encrypted_mnemonic'
export const NSEC_STORAGE_KEY = 'encrypted_private_key'

Then both mnemonic.ts and privateKey.ts import from storageKeys.ts. No cycle.


🟡 Missing test coverage

The new mutual-exclusion behavior (the actual point of this PR) has no tests. The existing mnemonic.test.ts and privatekey.test.ts don't cover:

  • Setting a mnemonic clears any stored private key
  • Setting a private key clears any stored mnemonic
  • isValidPassword works correctly after a key-type switch

These are easy to add to the existing test files and would catch regressions in the exclusion logic.


✅ What's good

  • The constant refactoring is a clear improvement — eliminates the raw string 'encrypted_mnemonic' in isValidPassword (privateKey.ts:60 on master)
  • The mutual-exclusion semantics are correct: only one identity source at a time
  • No protocol-critical code affected (no VTXO/signing/forfeit/round changes)

Requesting changes for the circular dependency. The fix is straightforward — extract constants to a shared module. Tests are a nice-to-have but the circular dep should be resolved before merge.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — PR #677: prevent double keys in local storage

Summary

The PR does two things:

  1. Refactors hardcoded 'encrypted_mnemonic' / 'encrypted_private_key' strings into exported constants (MNEMONIC_STORAGE_KEY, NSEC_STORAGE_KEY)
  2. Adds mutual exclusion: setMnemonic() removes any stored private key, and setPrivateKey() removes any stored mnemonic

The mutual-exclusion logic is correct in intent — a wallet should have one identity source, not both. But there's one real issue and one gap.


🔴 Issue: Circular dependency introduced

mnemonic.ts:4 — import { NSEC_STORAGE_KEY } from './privateKey'
privateKey.ts:3 — import { MNEMONIC_STORAGE_KEY } from './mnemonic'

This creates a static circular import. On master, privateKey.ts was deliberately careful to use a dynamic import for mnemonic (await import('./mnemonic') at line 59) to avoid this exact cycle.

While ES module semantics mean the const string literals should resolve before any function runs, circular dependencies are fragile and a known source of subtle bugs — especially across bundler versions. If module evaluation order ever changes (e.g. a new import is added to either file), you could get undefined constants silently.

Fix: Extract both constants into a shared file (e.g. src/lib/storageKeys.ts):

// src/lib/storageKeys.ts
export const MNEMONIC_STORAGE_KEY = 'encrypted_mnemonic'
export const NSEC_STORAGE_KEY = 'encrypted_private_key'

Then both mnemonic.ts and privateKey.ts import from storageKeys.ts. No cycle.


🟡 Missing test coverage

The new mutual-exclusion behavior (the actual point of this PR) has no tests. The existing mnemonic.test.ts and privatekey.test.ts don't cover:

  • Setting a mnemonic clears any stored private key
  • Setting a private key clears any stored mnemonic
  • isValidPassword works correctly after a key-type switch

These are easy to add to the existing test files and would catch regressions in the exclusion logic.


✅ What's good

  • The constant refactoring is a clear improvement — eliminates the raw string 'encrypted_mnemonic' in isValidPassword (master privateKey.ts:58)
  • The mutual-exclusion semantics are correct: only one identity source at a time
  • No protocol-critical code affected (no VTXO/signing/forfeit/round changes)

Requesting changes for the circular dependency. The fix is straightforward — extract constants to a shared module. Tests are a nice-to-have but the circular dep should be resolved before merge.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Review — PR #677 (commit 255f1bb)

The circular dependency issue from my previous review is resolved. ✅

What changed: New src/lib/storageKeys.ts extracts both constants (MNEMONIC_STORAGE_KEY, NSEC_STORAGE_KEY). Both mnemonic.ts and privateKey.ts now import from storageKeys.ts — no cycle. The dynamic await import('./mnemonic') in isValidPassword is correctly preserved.

Verified:

  • All localStorage calls use the correct constant
  • Mutual exclusion logic intact: setMnemonic removes nsec, setPrivateKey removes mnemonic
  • No protocol-critical code touched

LGTM. The test coverage gap (🟡) from my earlier review still stands as a nice-to-have, but not blocking.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Review — PR #677 (commit 255f1bb)

The circular dependency issue from my previous review is resolved. ✅

What changed: New src/lib/storageKeys.ts extracts both constants (MNEMONIC_STORAGE_KEY, NSEC_STORAGE_KEY). Both mnemonic.ts and privateKey.ts now import from storageKeys.ts — no cycle. The dynamic await import('./mnemonic') in isValidPassword is correctly preserved.

Verified:

  • All localStorage calls use the correct constant
  • Mutual exclusion logic intact: setMnemonic removes nsec, setPrivateKey removes mnemonic
  • No protocol-critical code touched

LGTM. The test coverage gap (🟡) from my earlier review still stands as a nice-to-have, but not blocking.

@pietro909 pietro909 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mnemonic.test.ts and privatekey.test.ts already exist: worth adding a couple of dedicated tests asserting the new behavior?

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Review — commit fc31333 (tests added)

New commit adds exactly what pietro909 requested. No issues.

New tests in mnemonic.test.ts:

  • Verifies setMnemonic removes NSEC_STORAGE_KEY from localStorage ✅

New tests in privatekey.test.ts:

  • setPrivateKey / getPrivateKey roundtrip ✅
  • Wrong password throws ✅
  • Missing key throws ✅
  • setPrivateKey removes MNEMONIC_STORAGE_KEY from localStorage ✅

All four new test cases directly assert the mutual-exclusion invariant that is the whole point of this PR. Tests use beforeEach(() => localStorage.clear()) for proper isolation. LGTM.

No protocol-critical concerns — this is client-side credential storage refactoring, not VTXO/signing/forfeit logic.

@bordalix
bordalix merged commit 331f6fc into master Jun 17, 2026
5 checks passed
@pietro909
pietro909 deleted the fix_local_storage branch June 17, 2026 12:50
bordalix added a commit that referenced this pull request Jun 17, 2026
* remove 'complete boarding' button

* Upgrade ts-sdk 0.4.36 - boltz-swap 0.3.41 (#676)

* prevent double keys in local storage (#677)

* prevent double keys in local storage

* fix circular dependency

* add tests

* fix SVGs

* remove "Complete boarding button"

---------

Co-authored-by: pietro909 <2094604+pietro909@users.noreply.github.com>
bordalix added a commit that referenced this pull request Jul 7, 2026
* feat(wallet): redesign bitcoin home

Port the scoped home redesign foundation and bitcoin-only wallet home from the draft branch. Adds shared motion and haptics helpers, redesigned home header/hero/actions/assets/upsells/activity preview, hides bottom navigation for home-launched wallet flows, and wires scan/swap interactions without pulling in passkeys or prototype asset accounts.

* fix(wallet): use real assets on redesigned home

* fix(wallet): polish settings and activity follow-ups

Remove the bottom nav from the redesigned wallet flow and align the settings screens with the draft redesign, including the Arkade Mint entry under General.

Refresh the full Activity page before showing an empty state, keep the home activity preview hidden when empty, and use the shared secondary/drawer styling for the swap coming-soon sheet.

* chore(deps): update pnpm lockfile

* test(e2e): follow redesigned navigation

Update Playwright helpers and specs to navigate through the redesigned home header, settings entry points, and Boltz deep link instead of the removed bottom nav.

* test(e2e): align home and settings assertions

Use the settings header text rendered by the shared Header component and assert the redesigned zero-balance home through stable test ids.

* test(e2e): navigate app screens without reload

* tests fixes

* Guard BigInt coercion to prevent render-time crashes

* fix polling leak

* lint

* Add a fallback for --pill-navbar-footer-clearance in settings padding

* Remove overriding aria-label on settings rows.

* fix(settings): address Arkade Mint review feedback

* fix tests

* fix test

* feat(wallet): add bitcoin detail page (#630)

* feat(wallet): add bitcoin detail page

Add a bitcoin-only wallet detail screen opened from the home asset row, with the Liveline price chart, current price, percentage badge, holdings, wallet actions, and bitcoin-filtered recent activity.

Share the existing swap coming-soon drawer between home and the detail page, and cover the bitcoin row navigation with a wallet screen regression test.

* fix(wallet): harden bitcoin chart loading

Remove the invalid fiat conversion path from the bitcoin detail chart, use CoinGecko max data for non-USD all-time charts, cache chart responses per fiat/range, fetch Coinbase all-time chunks sequentially, and guard the holdings balance before BigInt conversion.

* fix(wallet): pause bitcoin chart while hovering

* fix tests

* fix test

* use graph worker

* default chart period one week

* remove console.log

* new marketData file

* fixes

---------

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* feat(wallet): align recent activity with prototype (#628)

* chore(dev): support mnemonic wallet auto-init

Allow local dev auto-init to use VITE_DEV_MNEMONIC in addition to VITE_DEV_NSEC, so demo wallets can be restored from the current seed phrase without using the broken legacy default key.

* feat(wallet): align recent activity with prototype

Update the home recent activity list, full activity view, and transaction receipt details to match the prototype visuals while keeping the shared Activity header from PR 627. Add swap/account asset rendering, receipt-style detail rows, and measured virtual rows for taller activity entries.

* fix(wallet): address activity review feedback

Guard the round waiting screen when no info logs exist, cancel delayed polling on unmount, preserve the safer settlement status flow, remove incomplete BRL fiat support, and share token ticker mapping helpers across activity and asset views.

* fix send max with fiat ' + tests

---------

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* feat(send): align send form with redesign prototype (#629)

Port the draft send-flow field shell, asset selector, amount max control, and button safety polish on top of PR 627.

Add dev mnemonic auto-init support so local wallets can boot from mnemonic-based seeds without falling back to stale nsec state.

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* feat(wallet): align recent activity with prototype (#628)

* chore(dev): support mnemonic wallet auto-init

Allow local dev auto-init to use VITE_DEV_MNEMONIC in addition to VITE_DEV_NSEC, so demo wallets can be restored from the current seed phrase without using the broken legacy default key.

* feat(wallet): align recent activity with prototype

Update the home recent activity list, full activity view, and transaction receipt details to match the prototype visuals while keeping the shared Activity header from PR 627. Add swap/account asset rendering, receipt-style detail rows, and measured virtual rows for taller activity entries.

* fix(wallet): address activity review feedback

Guard the round waiting screen when no info logs exist, cancel delayed polling on unmount, preserve the safer settlement status flow, remove incomplete BRL fiat support, and share token ticker mapping helpers across activity and asset views.

* fix send max with fiat ' + tests

---------

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* fix tests

* fix navigation

* remove border from video container in scan

* change toggle bg color when selected to purple

* fix(wallet): address home redesign review feedback

* fix flacky test

* fix flacky test

* contracts view: copy encoded params, empty state, active/inactive sections (#645)

* show encoded contract params and allow copying

* show encoded contract params and allow copying

* improve contracts view: empty state, active/inactive sections, label and createdAt

* address review: guard encodeArkContract, createdAt fallback, unique card key

Wrap encodeArkContract in try/catch via useMemo and render the parameters row only when encoding succeeds, so a contract with missing/malformed params can no longer white-screen the Contracts screen. Show 'Unknown' instead of an empty string when createdAt is absent. Use contract.address as the React key. Update tests with createdAt/params and add an empty-state case.

* fix buttons overflow in input

* fix other value when bip21 has amount

* remove excessive bottom padding on content-shell

* less sensible pull-to-refresh

* fix typescript build errors (tsc -b) (#646)

* fix typescript build errors (tsc -b)

* arkana fixes

* build fixes

* remove console.log

* validate asset mint amount is not too large

* validate asset mint amount

* validate mint values

* fixes

* refactor mint form

* remove console.log

* fix e2e tests

* feat(settings): move advanced tools into settings (#652)

* feat(settings): move advanced tools into settings

Remove the root Apps surface and keep developer-focused tools under Settings > Advanced. Add Boltz and Arkade Mint entries there, rename the main settings group to Display, and update navigation tab ownership so these flows return through settings instead of an apps tab.

Also refresh bun.lock to match the dependency versions declared on the PR 627 base and update affected settings/e2e tests.

* re-order menu options

* fix e2e tests

* fix test

---------

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* feat(settings): support bitcoin currency units (#653)

* feat(settings): support bitcoin currency units

Add BTC as a selectable currency and replace display preferences with bitcoin unit options for BTC, sats, and BIP-177 notation.

Format wallet totals, transactions, send/receive flows, portfolio rows, and the Bitcoin detail view with the selected bitcoin unit while keeping the detail market price in fiat.

Convert fiat-like asset balances into selected-currency totals and add focused coverage for CHF asset conversion, settings options, formatting, and Bitcoin detail display.

* fix e2e test

* Several fixes

---------

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* fallback to btc currency if price feed fails

* add useEffect missing dependencies to InputAmount

* fix e2e test

* arkana fixes

* Fix Settings / Display

* change Fiats type to Currencies
change Settings menu option names

* fix(wallet): address arkana wallet review

* fix other value when clicking send max

* update regtest link

* update price chart proxy url

* fix build errors

* style(settings): polish contracts screen (#664)

Redesign the dev-mode contracts screen with a full-width summary, clearer contract cards, copyable code fields, and subtle motion feedback.

Update the contracts screen tests to cover the new summary and metadata layout.

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>

* feat(settings): add currency icons (#663)

Add reusable leading content support to the settings Select component and use it on the currency picker so each option shows a circular bitcoin or country currency mark.

Extend TokenLogo with the remaining fiat currency marks used by settings, add selector icon styling, and update the fiat settings tests to distinguish row icons from the selected-state checkmark.

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>

* Fixes

* Remove onboarding button (#678)

* remove 'complete boarding' button

* Upgrade ts-sdk 0.4.36 - boltz-swap 0.3.41 (#676)

* prevent double keys in local storage (#677)

* prevent double keys in local storage

* fix circular dependency

* add tests

* fix SVGs

* remove "Complete boarding button"

---------

Co-authored-by: pietro909 <2094604+pietro909@users.noreply.github.com>

* Clean qrcode (#681)

* remove 'complete boarding' button

* fix SVGs

* remove "Complete boarding button"

* clean qr code ui

* remove console.log

* clean Reminder (#683)

* clean Reminder

* fix color

* fix duplicate init with devMnemonic

* fix test

* update regtest commit

* mini fixes

* fix test

* fix test

* persist value; fix toggle currency (#705)

* fix bitcoin balance in btc page

* fix server url input

* add error messages inline

* fix tests

* fix import

* fix secondary value when not useFiat

* fix flash of unlock screen (#710)

* bug fix in privacyAmount

* remove dead code

* click on asset card to view asset

* add test for clicking asset card in homepage

* fixes in input amount

* lnurl fix

* add unit tests

* bug fix

* fix test

* remove currencyDisplay from config (#713)

* remove currencyDisplay from config

* rabbit fixes

* fix test

* remove option from upsellsSection

* only show btc and registered assets on homepage

* fix import path

* rename config.fiat to config.currency

* fix test

---------

Co-authored-by: Sahil Chaturvedi <sahilc0@users.noreply.github.com>
Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>
Co-authored-by: pietro909 <2094604+pietro909@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants