minibits-cashandClaude Opus 4.8 2f7a2763f2 Reference mints by stable id, not by url
Completes the mint-identity work. A mint url is a network locator and mints
move, but the url had become the de facto foreign key for nearly every
persisted row, so a url edit had to fan out across the schema and mostly did
not. Each table now references the mint by an identity that a move cannot
disturb, and setMintUrl shrinks to the two things that genuinely hold a url:
Mint.mintUrl and the proofs.mintUrl cache.

Which identity, per table

mintId (Mint.id) where the row needs the mint itself:

- onchain_mint_quotes. The critical one: a quote's address stays creditable for
  as long as the mint exists (rows are never deleted), so the reference has to
  outlive a move. It followed row.mintUrl, and the watcher swallows errors by
  design — a renamed mint stranded deposits permanently and silently.
- reservations. A url edit racing an open send did not merely misfile the
  proofs; commitReservation resolved the mint by url, so it threw "Mint not
  found" and aborted the commit of an operation the mint had already performed.
- transactions. `mint` was two things at once, switched by status: a historical
  record of where a finished payment happened, AND a live pointer dialled for an
  open one. That conflation is why a rename had to rewrite in-flight rows —
  rewriting the very column that records the past. mintId takes the identity
  job, so `mint` is frozen as history and the status-scoped rewrite is retired.

No reference at all where the row already has a parent:

- inflight_requests, melt_recovery are CHILD rows of a transaction (their
  primary key IS transactionId), so the parent owns "which mint". Their mintUrl
  and keysetId copies had ZERO readers — the keyset that is used comes from
  inside meltPreview. Both dropped; the one mint-scoped query joins through
  transactions.mintId. Nothing left to go stale.

Mint.id gets referential authority only, never identity authority: findById
answers "which mint is this row about?" and must never answer "are these the
same mint?" — it is random and unrelated to the keys, so that question stays
with the keysets. The backfills are IS NULL-guarded so a resolved row can never
be re-pointed at whichever mint now answers an old url.

Backfills run from JS (v38 seed), not SQL: mints live in the MST/MMKV snapshot,
so nothing in SQL can map url -> id. Matching on url is trustworthy at exactly
that moment and no other — until now a url could not change without these rows
being rewritten to match. The join is spent once, at rest, instead of on every
rename.

Migration-system fixes found along the way

- _dbVersion is now DERIVED from the migration list. It was a hand-maintained
  literal, and it was already wrong: it said 33 while migration 34 existed, so
  34 would never have run. The failure is silent and asymmetric — fresh installs
  build from schema.ts and are fine, while upgrading devices land on a schema
  the code does not have. dbMigrationRegistry.test.ts pins this and the ordering
  invariants.
- Migrations 26/28/29/31 built their tables from the LIVE schema constants. A
  device replaying them would get today's shape, and the later ALTER adding the
  column would fail with "duplicate column name" — breaking upgrades from
  exactly the versions those migrations serve. Historical shapes are frozen
  locally now, and the schema.ts header no longer recommends the sharing.
- rootStoreModelVersion was left at 37 while the seed guarded on < 38, so the
  backfill would have re-run on every launch forever.

Tests: 421 pass. Six hand-mirrored suites had drifted from the schema they
claim to mirror; transactionsMintUrl.test.ts was worse — still passing while
testing a function this commit deletes, so it is removed. The mirroring pattern
is worth revisiting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:41:00 +02:00
2024-09-14 22:55:53 +02:00
2024-09-17 00:52:26 +02:00
2026-06-02 15:46:14 +02:00
2026-06-14 23:25:15 +02:00
2026-06-16 23:10:11 +02:00
2026-06-02 15:46:14 +02:00
2025-02-04 08:35:31 +01:00
2024-09-17 00:52:26 +02:00
2024-09-17 00:52:26 +02:00
2024-09-14 22:55:53 +02:00
2024-09-17 00:52:26 +02:00
2026-03-11 23:27:51 +01:00
2025-03-31 14:01:01 +02:00
2026-06-27 23:55:08 +02:00
2024-09-17 00:52:26 +02:00
2026-05-28 17:19:14 +02:00
2026-05-11 22:33:22 +02:00

Minibits Wallet

Minibits is an ecash and Lightning wallet exploring how ₿-backed ecash can enable instant, cheap, and private value transfer. Ecash is issued by mints and backed by Bitcoin via the Cashu protocol and Lightning Network. Ecash is cash-like yet digital token with cheap and instant transfers and high privacy guarantees.

Disclaimer

⚠️ If you are using this app, please take the following into consideration:

  • This wallet should be used for research purposes only.
  • The wallet is a beta version with incomplete functionality and both known and unknown bugs.
  • Do not use it with large amounts of ecash.
  • The ecash stored in the wallet is issued by the mint. You trust the mint to back it with bitcoin until you transfer your holdings to another bitcoin lightning wallet.
  • The Cashu protocol that the wallet implements has not yet received extensive review or testing.

Roadmap

Platform support

  • Android app - Play Store, Zapstore [ New!] , Github
  • iOS app - AppStore, Testflight and Freedomstore.io
  • Light and dark mode
  • i18n support
  • EN, PT, ES, SK languange support

Mints

  • Multiple currency units issued by mints
  • Add multiple mints
  • Remove mints
  • Block receiving from mint
  • Show mint balances grouped by currency units
  • Handle mint keys rotation (not tested)
  • Mint status and information screen

Receive ecash

  • Scan QR code of a ecash token
  • Animated QR codes support for large tokens
  • Paste ecash token from the clipboard
  • Receive Nostr zaps or Lightning payments to minibits.cash address
  • Receive ecash from another wallet over NOSTR message sent to minibits.cash address
  • Receive ecash in person while being offline, redeem later (MVP version)
  • Realtime and encrypted push notifications on receive to minibits.cash lightning address
  • Display or send cashu payment requests

Send ecash

  • Share ecash token to send through another app
  • Show ecash token as a QR code
  • Show large ecash token as an animated QR code
  • Send ecash to contact (minibits.cash or another NOSTR address)
  • Lock ecash to the receiver wallet key (P2PK)
  • Set lock expiry to allow recovery of locked ecash after timeout (P2PK)
  • Scan and pay cashu payment requests

Top up wallet

  • Show QR code with bitcoin Lightning invoice to pay
  • Share encoded bitcoin Lightning invoice to pay
  • Share lightning invoice with a contact over NOSTR message
  • Top up balance with LNURL Withdraw
  • Enter transaction amount in fiat currency

Pay / Cash out from wallet

  • One click ZAPS - tip users of NOSTR social network
  • Pay bitcoin Lightning invoice with your ecash
  • Pay lightning invoices received from another contact
  • Pay to LNURL Pay static links / codes
  • Pay to Lightning address
  • Swap ecash between mints / currencies in a single step

Transaction history

  • Unified transaction history for all kinds of transactions
  • Audit trail of transaction events
  • Transaction search and filters [ New!]
  • Retry after recoverable transaction errors
  • Revert pending transaction in 1 click (get back tokens not claimed by receiver)
  • Tags and related filtering of transactions
  • Delete incomplete and failed transactions from history

Contacts

  • Private contacts address book for payments
  • Public contacts (followed users on NOSTR social network) for tipping and donations
  • Load public contacts from custom NOSTR relay
  • Wallet addresses as random public NOSTR addresses (random123@minibits.cash)
  • Custom wallet names (myname@minibits.cash)
  • Wallet addresses usable as Lightning addresses to receive payments from many Lightning wallets
  • Private contacts with other than minibits.cash NOSTR adresses and relays

Backup and recovery

  • Local append-only backup of all ecash in a database separate from wallet storage
  • Export wallet backup with ecash, mints, contacts and recent transactions
  • Recovery of ecash using 12 words menmonic phrase in case of lost device
  • Recovery by importing wallet backup
  • Move wallet address from another device using the seed phrase
  • Recover wallet in case spent ecash remains in the wallet
  • Retry transaction after recoverable errors
  • Auto-recover funds if wallet failed to receive ecash issued by mint due to network or device failure

Interoperability

  • Nostr Wallet Connect - lets you initiate payments from another app, such as Nostr client
  • Deeplinks - app reacts to lightning: and cashu: URIs
  • NFC - reads Cashu requests or Lightning invoices over the NFC and pays instantly [ New!]
  • NFC HCE - Android app shares Cashu tokens, requests or invoices over the NFC [ New!]

Security and Privacy

  • Use device biometry to login
  • Connect to the mints on .onion Tor addresses using own Tor daemon [discontinued from v0.1.7]
  • Connect to the mints on .onion Tor addresses using Orbot

Self-funding

  • Donation for custom wallet name

DevOps

  • OTA updates (opt in)
  • Automated tests
  • Automated release pipelines for both OTA updates and native releases

Architecture

The wallet's design has been crafted to prioritize the following primary quality properties:

  • Support both Android and iOS mobile platforms
  • Achieve fast UX and startup time (despite using React Native)
  • Minimize the risk of data/ecash loss
  • Bring ecash UX on par with the current standard of traditional finance (tradfi) mobile apps

As a result, the following architectural constraints are in place:

  • Wherever available, use libraries with a fast JSI (JavaScript Interface) to native modules.
  • Avoid Expo modules.
  • Use fastest available storage for most wallet operations and a separate local database storage to store data that incrementally grows.
  • Leverage local SQLite database as persistent storage for ecash notes.

Open architectural concepts that were still open for discussion when the wallet had been released

  • Contacts management - identities, sharing contacts, send ecash with the UX of tradfi instant payment while keeping privacy towards mints - Implemented as NOSTR keypairs and NIP05 public sharable names that ecash can be sent to
  • Off-device backup strategy - Implemented using @gandlafbtc concept of deterministic secrets + wallet export and import
  • UX and naming conventions - ecash is not always intuitive. UX for new users heavily depends on using the right abstractions or terms to describe what is going on. This wallet wants to serve as a means to test what could work. One of the first ideas is to avoid terms such as token or proof and propose the term --coin ++ecash instead.
  • Suitable Tor daemon available to replace not maintained react-native-tor. From v0.1.8-beta.33 connection through Orbot in VPN mode is possible.

Download and test

Minibits wallet is in early beta and available as of now only for Android devices. You have the following options to try it out:

  • Download it from Google Play
  • Join testing program on Google Play to get early releases to test (Submit your email to get an invite on Minibits.cash)
  • Download .apk file from Releases page and install it on your phone
  • Try on Testflight
  • Download from Freedomstore.io (for EU-based users)
  • Download from AppStore

Development

Minibits is a bare React Native app written in Typescript. The project structure and code itself are intentionally verbose to support readability. Critical wallet code is reasonably documented. However, there is vast space for existing code improvements, refactoring, and bug fixing. This is an early beta software and the author does not code for a living.

The code is derived from Ignite template, however with many libraries, notably Expo, stripped down to achieve fast startup times. Performance bottleneck on some Android devices is react-native-keychain. To overcome this, it has been patched not to warm-up on startup, caching for wallet operations is in place and its use to encrypt storage is opt-in.

Wallet state is managed by mobx-state-tree and persisted in fast MMKV storage. Only the basic mobx concepts are in place, whole model could be improved. All critical wallet code is in services/walletService.ts and all ecash state changes are in models/ProofsStore.ts. Wallet communication with the mints is in model/Wallet.ts and uses cashu-ts library.

Crypto operations are handled by react-native-quick-crypto, that is fast and does not require awful javascript shims. Transaction history and ecash notes are stored in sqlite, with fast react-native-quick-sqlite driver that enables to run lighter queries synchronously.

Wallet included own Tor daemon using react-native-tor library to connect to the mints over Tor network. However this seems not to be long term approach as this library is not properly maintained and future updates of React native will likely break it. Help with replacement would be appreciated.

In case of breaking state and data model changes, versioning and code is ready to run necessary migrations on wallet startup.

Running in development mode

To run Minibits wallet in dev mode, set up the React Native development environment and the Yarn package manager. Then clone this repository, navigate to the minibits_wallet directory, and run the following:

yarn install

There are post-install patches to some of the libraries that should run automatically and are necessary for a successful run. See the patches directory for more info. After the dependecies are installed, continue to create the following .env file in the root folder:

APP_ENV='DEV'
MINIBITS_SERVER_API_HOST='http://localhost/api/v2' 
MINIBITS_NIP05_DOMAIN='@localhost'
MINIBITS_RELAY_URL='ws://localhost/relay'
MINIBITS_MINT_URL='http://localhost/mint' 

Local NOSTR address and Lighnting brigde server are not necessary to run the wallet. Then make sure you have the Android device connected by running:

yarn adb

Finally run this and pray:

yarn start

In case of issues, repo includes commits history from the out of the box react native app up until the complete wallet. You can see build.gradle and other changes one by one and hopefully figure out what's wrong.

Building

Create debug .apk:

yarn android:dev

Automated testing

The app has the scaffolding for automated tests; they are yet to be implemented. For functional bugs or suggestions please raise an issue.

Contributing

Contributions are welcome, just start and we will figure out what's next.

S
Description
No description provided
Readme
20 MiB
Languages
TypeScript 98.8%
JavaScript 0.7%
Kotlin 0.2%
Ruby 0.2%