Skip to content

About

Nostr- and NWC-powered React Native Bitcoin Lightning wallet for kids and parents.

Resources

Stars

12 stars

Watchers

2 watching

Forks

Latest commit

Β 

History

1,279 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Lightning Piggy Mobile

A mobile Bitcoin Lightning wallet built with Expo/React Native, connecting via Nostr Wallet Connect (NWC) with Nostr social features.

Explore Map Home Piglet detail Geo-caches

View the Figma prototype

Features

  • Connect any Lightning wallet via NWC (Nostr Wallet Connect)
  • On-chain Bitcoin wallets (watch-only via xpub, or hot wallets via mnemonic)
  • Transfer funds between wallets (LN-to-LN, LN-to-on-chain, on-chain-to-LN, on-chain-to-on-chain)
  • Send payments by scanning QR codes or pasting invoices
  • Send to lightning addresses (user@domain) via LNURL-pay
  • Send to on-chain addresses (BIP-21 URI support with amount)
  • NIP-57 zaps to Nostr contacts
  • Receive payments with QR code generation (optional amount for BIP-21)
  • Real-time balance display with fiat conversion
  • Transaction history
  • Nostr identity login (nsec or Amber signer on Android)
  • Friends tab with Nostr contacts and phone contacts
  • Follow/unfollow Nostr contacts (kind 3 event publishing)
  • Add friends by pasting npub or scanning QR code
  • Contact profile cards with deep linking to Nostr apps
  • QR code sharing for npub and Lightning address
  • Secure credential storage (expo-secure-store)
  • Explore tab: discover Bitcoin merchants nearby (BTC Map / OpenStreetMap), opt-in geofence alerts when you walk past one (#467), hunt for hidden LNURL-withdraw "Piggies" stashed on NFC tags or QR codes (#468), and find Bitcoin meetups, conferences, and workshops via NIP-52 calendar events (kind 31923 β€” read-only, with native Add-to-Calendar via expo-calendar). Hunt Piggies adopt treasures.to's NIP-GC (kind 37516 listings, kind 7516 found-logs, kind 1111 NIP-22 comments) and tag the listing with a NIP-32 com.lightningpiggy.app / payout-lnurl-w label so LP renders the 🐷 claim UX on top while generic geocaching clients render the cache normally. The LNURL bearer token never goes on the public relay event β€” it lives only on the physical NFC tag / QR (the access control) and in the hider's local SecureStore. Hider can update location / hint photo / cooldown / expiry by re-publishing with the same d tag (PRE semantics). See docs/PROTOCOLS.adoc β†’ "Hunt: Lightning extension to NIP-GC" for the full tag schema, and docs/SOLUTION_DESIGN.adoc β†’ "Explore tab" for the BTC Map + Nostr cache architecture, bbox-driven list filtering, and offline cold-start guarantees.

Standards

Lightning Piggy Mobile implements the following open standards. See docs/STANDARDS.adoc for usage details and source references.

Bitcoin Improvement Proposals (BIPs)

Standard Name
BIP-21 URI Scheme (bitcoin: with ?amount=)
BIP-32 Hierarchical Deterministic Wallets
BIP-39 Mnemonic Seed Phrases
BIP-84 Native SegWit HD Wallets (bc1q...)
BIP-327 MuSig2 Schnorr Key Aggregation
BIP-340 Schnorr Signatures
BIP-341 Taproot (SegWit v1)

Nostr Implementation Possibilities (NIPs)

Standard Name
NIP-01 Basic Protocol (relays, events, subscriptions)
NIP-04 Encrypted Direct Messages (legacy β€” inbound read only; outbound migrated to NIP-17 in #334)
NIP-05 DNS Identity Verification
NIP-14 Subject Tag (used as group-chat name)
NIP-17 Private Direct Messages β€” canonical 1:1 and multi-recipient group chat (sealed via NIP-44 + gift-wrapped per recipient)
NIP-19 Bech32-encoded Entities (npub, nsec)
NIP-20 Command Results (OK messages)
NIP-21 nostr: URI Scheme
NIP-22 Comments (kind 1111 β€” used by NIP-GC for DNF / maintenance / archived cache statuses)
NIP-32 Labels (L namespace + l value tags β€” used to mark Hunt caches as Lightning-payout via the com.lightningpiggy.app / payout-lnurl-w label)
NIP-40 Expiration Timestamp (auto-retire Hunt cache listings)
NIP-44 Encrypted Payloads v2 (used by NIP-17 sealing)
NIP-47 Nostr Wallet Connect (NWC)
NIP-52 Calendar Events (kind 31923 time-based β€” read-only consumer for the Events sub-screen)
NIP-55 Android Signer (Amber)
NIP-57 Lightning Zaps
NIP-59 Gift Wrap (used by NIP-17 to mask sender + metadata)
NIP-65 Relay List Metadata
NIP-94 File Metadata
NIP-99 Classified Listings (kind 30402 β€” read-only consumer for the Robotechy Bag Charm purchase link on the Hide-a-Piglet flow)
NIP-GC (draft, on Nostr) Geocaching Events (kinds 37516 / 7516 / 1111 / 7517 β€” used by the Hunt feature, with an com.lightningpiggy.app NIP-32 label marker for Lightning-payout caches)

Group state for client-side group chat is propagated via a parameterised-replaceable kind:30200 event (custom to this client; receivers reconcile by groupId + created_at). Not a NIP β€” see src/services/nostrService.ts (GROUP_STATE_KIND) for the schema.

LUDs (LNURL specifications)

Standard Name
LUD-03 LNURL-withdraw β€” used by the Hunt feature to claim Piggies
LUD-16 Lightning Address (user@domain β†’ LNURL-pay endpoint)
LUD-17 URI scheme prefixes (lnurlw://, lnurlp://, …) β€” accepted as input alongside bech32 LNURL1…

Lightning Standards

Standard Name
BOLT-11 Lightning Invoice Encoding
LNURL / LUD-16 Lightning Address Protocol (user@domain)

On top of these, Lightning Piggy Mobile uses the Boltz v2 submarine swap API for trustless Lightning ↔ on-chain transfers.

Architecture

Lightning Piggy is an Expo / React Native app (SDK 55, RN 0.83) that runs from a custom dev-client β€” it ships custom native modules, so Expo Go is not used. Lightning payments ride on Nostr Wallet Connect (NWC); the social layer is Nostr (encrypted DMs, zaps, contacts, geo-caches). A fuller write-up lives in docs/ARCHITECTURE.adoc.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Screens & Components  (src/screens, src/components)         β”‚
β”‚  Home Β· Messages Β· Explore Β· Friends + Account drawer        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Navigation  (src/navigation/AppNavigator.tsx)              β”‚
β”‚  4 tabs β†’ Explore stack Β· account drawer Β· root stack        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Contexts  (src/contexts)          Hooks  (src/hooks)        β”‚
β”‚  WalletContext Β· NostrContext Β·    UI-facing per-feature     β”‚
β”‚  Groups Β· TrustGraph Β· LiveLoc     logic                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Services  (src/services) β€” by domain                        β”‚
β”‚  Nostr (nostrService, dm*, places) Β· Wallet (nwcService,     β”‚
β”‚  onchainService/BDK, lnurl, boltz) Β· location/geofence Β·     β”‚
β”‚  media upload Β· NFC Β· notifications                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Persistence                                                 β”‚
β”‚  SQLCipher DM store (op-sqlite) Β· expo-secure-store (keys) Β· β”‚
β”‚  AsyncStorage caches (per-account namespaced)                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Native  (modules/, plugins/)                                β”‚
β”‚  amber-signer (NIP-55 IPC) β€” the only native module Β·        β”‚
β”‚  Expo config plugins (NFC, Amber queries, fg-service perms)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚                    β”‚                     β”‚
   Nostr relays        NWC wallet svc        Electrum / Boltz
   (DMs, zaps,         (pay_invoice,         (on-chain sync,
    contacts, caches)   get_balance)          LN↔on-chain swaps)

Layers. Presentation (screens/components) sits over React Navigation; long-lived state lives in contexts (WalletContext for wallets/balances/transactions, NostrContext for identity/signer/relays/DMs), which compose per-responsibility hooks and a large, mostly-stateless service layer grouped by domain. Persistence is split three ways by sensitivity: an encrypted SQLCipher DM store, expo-secure-store for keys/secrets, and per-account-namespaced AsyncStorage caches. One Android-only native module (amber-signer, the NIP-55 Amber IPC bridge β€” the only Expo native module that ships) and a set of Expo config plugins round out the stack. Background DM delivery rides expo-background-task (Android WorkManager / iOS BGTaskScheduler), not a persistent native service.

Key data flows.

  • Sending a DM β€” build a NIP-17 chat rumor (kind 14) β†’ seal (kind 13) β†’ gift-wrap (kind 1059) with NIP-44 β†’ publish to the peer's and own relays. Inbound wraps are unwrapped once and stored plaintext in the encrypted SQLite DB. When the app is closed on Android, a periodic expo-background-task (WorkManager, ~15-min floor) does detect-and-ping β€” it notices new inbound traffic and fires a generic notification without decrypting; a persistent realtime relay foreground service is only scaffolded (manifest permissions via withForegroundService.js) and not yet implemented. See docs/architecture/notifications.adoc.
  • Paying over NWC β€” a scanned invoice (or an LNURL-pay / Lightning address resolved to a BOLT-11) is sent to the wallet service as an encrypted NIP-47 pay_invoice request over the NWC relay; the encrypted response updates balance and history. Zaps (kind 9734 request β†’ 9735 receipt) ride the same rail.
  • Publishing a geo-cache ("Piglet") β€” a NIP-GC listing (kind 37516) is published with a NIP-32 payout label and a stable d tag; the LNURL-withdraw bearer stays on the physical NFC tag / QR and in the hider's secure store, never on the public event. Claims record a found-log (kind 7516).

See docs/ARCHITECTURE.adoc for the module map, the full Nostr event-kind reference, signer options (nsec / Amber; NIP-46 planned), and persistence detail.

Getting Started

Prerequisites

  • Node.js (v18+)
  • Expo CLI
  • An Android device or emulator
  • A Lightning wallet that supports NWC (e.g. Alby, LNbits)

Installation

git clone https://github.com/BenGWeeks/lightning-piggy-mobile
cd lightning-piggy-mobile
npm install

Development

This project uses custom native modules (Amber signer), so it requires a dev client build rather than Expo Go.

# First time: build and install the dev client
npx expo run:android

# Subsequent runs: start Metro (connects to the dev client)
npm start

# Press 'a' to open on a connected Android device

Note: Always use npm start (not npx expo start) β€” the start script includes --dev-client which is required for custom native modules. Using npx expo start directly will launch in Expo Go mode and show "Something went wrong".

Environment variables

Optional keys are read from .env at the repo root (gitignored) and inlined into the bundle at build time via app.config.ts / Metro's EXPO_PUBLIC_* handling.

Variable Used by Notes
EXPO_PUBLIC_GIPHY_API_KEY In-conversation GIF picker Free key from developers.giphy.com/dashboard. Restrict it to the Android package / iOS bundle ID in the GIPHY dashboard β€” keys are public by design, not a secret. When unset, the "Send GIF" row is hidden from the attach menu and nothing else breaks.

After changing .env, restart Metro (npm start) so the new value gets inlined. No native rebuild is required for EXPO_PUBLIC_* changes.

Building an APK

# Generate the native Android project
npx expo prebuild --platform android

# Build the APK
cd android && ./gradlew assembleRelease

# Install on connected device
adb install app/build/outputs/apk/release/app-release.apk

EAS Build (cloud)

npm install -g eas-cli
eas login
eas build --platform android --profile preview

Project Structure

src/
  components/       # Reusable UI components (SendSheet, ContactProfileSheet, etc.)
  contexts/         # React contexts (WalletContext, NostrContext)
  navigation/       # React Navigation setup
  screens/          # App screens (Home, Earn, Learn, Friends, Account)
  services/         # Business logic (NWC, LNURL, Nostr, contacts)
  styles/           # Theme and shared styles
  types/            # TypeScript type definitions
assets/             # Images and icons
modules/            # Custom native Expo modules (Amber signer)
plugins/            # Expo config plugins

Notable Native Dependencies

The Explore tab pulls in a small set of Expo modules beyond the wallet + Nostr core. All are listed in docs/PACKAGES.adoc; the Explore-specific additions are:

Package Used by Why
react-native-webview ExploreMiniMap, MapScreen Leaflet WebView for OSM tile rendering β€” no Google Maps API key, no billing setup.
expo-asset + expo-file-system + expo-sharing HuntCreateScreen "Save STL" Ships the 188 KB Piggy Bag Charm STL inside the APK, copies it out on tap, and shares via the system sheet.
expo-calendar EventDetailScreen "Add to Calendar" Native calendar insert with lazy permission prompt.
expo-notifications Geofence "Nearby merchants" feature (#467) Foreground pings when the user walks past a Bitcoin merchant.

Screenshots

The Explore tab surfaces live under docs/screenshots/explore/:

  • 01-tab-bar-home.png β€” bottom tab bar with the new Explore tab
  • 02-explore-hub.png / 02b-explore-hub-scrolled.png β€” Lessons / Map / Hunt / Events rails
  • 03-lessons.png
  • 04-map-merchants-and-caches.png β€” full Leaflet view
  • 05-hunt-hub.png, 06-hunt-create.png, 06b-hunt-nfc-write.png, 07-hunt-discover.png
  • 08-events.png
  • 09-hunt-piggy-detail.png / 09b-hunt-piggy-detail-scrolled.png
  • 10-places-list.png, 11-place-detail.png

Figma Designs

https://www.figma.com/proto/ROutnkBQtGGGzqi8yz0Maf/Lightning-Piggy?node-id=1-26&m=dev&scaling=scale-down&page-id=0%3A1&starting-point-node-id=19%3A519&show-proto-sidebar=1&t=P3MkR2W1YVSwtmIQ-1

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

WTFPL

About

Nostr- and NWC-powered React Native Bitcoin Lightning wallet for kids and parents.

Resources

Stars

12 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages