Skip to content

feat: ENG-179 Cashu NFC card top-up#582

Draft
forge0x wants to merge 3 commits intomainfrom
feat/cashu-card-topup
Draft

feat: ENG-179 Cashu NFC card top-up#582
forge0x wants to merge 3 commits intomainfrom
feat/cashu-card-topup

Conversation

@forge0x
Copy link
Contributor

@forge0x forge0x commented Mar 7, 2026

Summary

Users can top up their Flash card with Cashu proofs directly from the mobile app, enabling offline bearer payments (NUT-XX Profile B).

New NFC layer

  • app/nfc/cashu-apdu.ts — APDU layer for CashuApplet (D2 76 00 00 85 01 02)
  • app/nfc/useCashuCard.tsNfcTech.IsoDep session hook with startSession / cleanup / readBlankCardPubkey / writeProofs

New screen

app/screens/card-screen/cashu-topup.tsx

  • Amount entry → PIN setup → NFC tap → proof minting → proof writing → success
  • Blank card: sets PIN + writes proofs (first issue)
  • Existing card: verifies PIN + writes to free slots (top-up)

UX entry points

  • EmptyCard: new 'Top Up Flash Card' button
  • Flashcard: new 'Flash Top-Up' icon button alongside existing Reload / QR Topup

Depends on

patoo0x added 2 commits March 3, 2026 05:43
- 'Experimental' → 'Chat' (section only contains Chat Settings)
- 'Key management' → 'Wallet backup' (section only contains Backup)

Reported by Lori (app audit). Copy fix, no structural changes.
Users can now top up their Flash card with Cashu proofs directly
from the mobile app, enabling offline bearer payments.

New files:
  app/nfc/cashu-apdu.ts          APDU layer for CashuApplet (AID D2 76 00 00 85 01 02)
  app/nfc/useCashuCard.ts        IsoDep NFC session hook (NfcTech.IsoDep)
  app/screens/card-screen/cashu-topup.tsx
                                  6-step top-up screen:
                                  amount → pin → tap card → mint → write → success
  app/screens/card-screen/cashu-topup.helpers.ts
                                  extractNonceFromSecret() + toCardWriteProof()

Flow:
  1. User enters USD amount + card PIN
  2. NFC: SELECT + GET_INFO + GET_PUBKEY (blank card detection)
  3. GQL: cashuCardProvision (mints P2PK-locked proofs from user's USD wallet)
  4. NFC: SET_PIN (blank) + VERIFY_PIN + LOAD_PROOF × N
  5. Card is loaded, ready for offline tap-to-pay at Flash POS

Depends on: lnflash/flash#296 (cashuCardProvision mutation)

Modified:
  app/screens/card-screen/card.tsx         onCashuTopup() → cashuTopup route
  app/components/card/EmptyCard.tsx        optional 'Top Up Flash Card' button
  app/components/card/Flashcard.tsx        optional 'Flash Top-Up' icon button
  app/navigation/stack-param-lists.ts      cashuTopup: undefined route
  app/navigation/root-navigator.tsx        CashuTopup screen registered
  app/screens/card-screen/index.ts         exports CashuTopup

Closes ENG-179
@linear
Copy link

linear bot commented Mar 7, 2026

Pure-logic helper tests using babel-jest (avoids ts-jest/TS5.0 incompatibility).
10 tests covering extractNonceFromSecret and toCardWriteProof.

Also adds jest.helpers.config.js + tsconfig.helpers.json for running
helper-layer tests without ttypescript/ts-auto-mock transforms.

Note: the main jest.config.js has a pre-existing breakage — ttypescript is
incompatible with TypeScript 5 (read-only createProgram); ts-jest 29.4.6
requires TS >= 5.3 but flash-mobile has 5.0.4. These are pre-existing issues
unrelated to ENG-179.

Run: npx jest --config jest.helpers.config.js __tests__/card/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants