feat: CREQ (NUT-18) tap-to-pay support for Numo compatibility#4053
feat: CREQ (NUT-18) tap-to-pay support for Numo compatibility#4053kaloudis wants to merge 13 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for Cashu tap-to-pay (NUT-18) using NFC. It includes the implementation of CREQ payment request encoding/decoding, new UI components for CREQ payments, and integration with the existing NFC scanning logic. Feedback focuses on critical runtime issues such as the incorrect handling of event subscriptions in NFCButton.tsx, the use of non-standard global TextEncoder/TextDecoder in React Native, and missing type validation during CBOR decoding. Additionally, there are recommendations to refactor duplicated logic for finding compatible mints and to avoid inline require statements to align with standard React patterns.
| style={[styles.rectButton, rowDisabled && { opacity: 0.5 }]} | ||
| onPress={() => { | ||
| if (rowDisabled) return; | ||
| const creqParams = decodeCREQ(creq); |
There was a problem hiding this comment.
nit: I think we should use try/catch here
Description
Adds CREQ (NUT-18) tap-to-pay support, enabling ZEUS to send and receive Cashu ecash payments via NFC, compatible with the Numo POS app and other NUT-18 wallets.
CREQ is the Cashu payment request format defined in NUT-18. A merchant broadcasts a CREQ containing the requested amount, unit, and accepted mints. The payer reads it, creates a Cashu token from local proofs, and writes the token back. This PR implements both sides of that exchange: ZEUS can act as a payer on Android and iOS, and as a merchant on Android via HCE.
The payer flow uses a single-tap model with no confirmation dialog, matching Numo's behavior. When the user taps the NFC icon on the Send screen and encounters a CREQ tag, ZEUS automatically reads the request, creates a token from the best matching local mint, and writes it back to the merchant, all within one NFC session. For CREQ strings received via QR code or clipboard (including BIP-21 URIs with a
creq=parameter), a confirmation view is shown instead, with an option to tap the merchant's device to deliver the token.The merchant flow leverages the writable tag support already present in
react-native-hce0.3.0. When a user creates a Lightning invoice in ReceiveEcash, ZEUS also generates a CREQ and broadcasts it via NFC with the writable flag enabled. When a payer writes a token back, the HCE_STATE_WRITE_FULL event fires and the token is automatically redeemed.Key changes
utils/CREQUtils.ts- NUT-18 CREQ encode/decode/detect using CBOR (via cborg)utils/NFCUtils.ts- IsoDep APDU helpers,readCREQFromTag,writeTokenToTag,payViaNfcTap(single-session auto-pay),buildNdefTextMessage, NDEF text record parserutils/AddressUtils.ts- BIP-21?creq=parameter extractionutils/handleAnything.ts- CREQ routing: standalone, from BIP-21, and multi-method via ChoosePaymentMethodviews/Cashu/CREQPayment.tsx- Payer confirmation view for QR/clipboard flowviews/Send.tsx- NFC button attempts CREQ single-tap auto-pay before falling back to regular tag scanviews/ChoosePaymentMethod.tsx- Accepts and passes throughcreqparamcomponents/LayerBalances/PaymentMethodList.tsx- "Ecash (tap-to-pay)" row for unified BIP-21 URIs containing CREQcomponents/NFCButton.tsx-writableandonTokenReceivedprops for merchant HCE modecomponents/CollapsedQR.tsx- Passthrough for writable NFC propsviews/Cashu/ReceiveEcash.tsx- Generates CREQ from invoice, broadcasts via writable HCE, redeems received tokensPlatform support
Test plan
bitcoin:?creq=creqA...&lightning=lnbc...QR code, verify ChoosePaymentMethod shows "Ecash (tap-to-pay)" optioncreqA...QR code, verify CREQPayment confirmation view appearsPR Type
Checklist
yarn run tscand made sure my code compiles correctlyyarn run lintand made sure my code didn’t contain any problematic patternsyarn run prettierand made sure my code is formatted correctlyyarn run testand made sure all of the tests passTesting
If you modified or added a utility file, did you add new unit tests?
I have tested this PR on the following platforms (please specify OS version and phone model/VM):
I have tested this PR with the following types of nodes (please specify node version and API version where appropriate):
On-device
Remote
Locales
Third Party Dependencies and Packages
yarnafter this PR is merged inpackage.jsonandyarn.lockhave been properly updatedOther: