From 32b9f3f40f2b7842fbcad08a041d9a2205b23de8 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 21 Jun 2024 17:22:32 +0700 Subject: [PATCH] feat: NIP-47 notifications --- 47.md | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/47.md b/47.md index 90338477..97b06ab3 100644 --- a/47.md +++ b/47.md @@ -8,32 +8,42 @@ Nostr Wallet Connect ## Rationale -This NIP describes a way for clients to access a remote Lightning wallet through a standardized protocol. Custodians may implement this, or the user may run a bridge that bridges their wallet/node and the Nostr Wallet Connect protocol. +This NIP describes a way for clients to access a remote lightning wallet through a standardized protocol. Custodians may implement this, or the user may run a bridge that bridges their wallet/node and the Nostr Wallet Connect protocol. ## Terms -* **client**: Nostr app on any platform that wants to pay Lightning invoices. -* **user**: The person using the **client**, and want's to connect their wallet app to their **client**. +* **client**: Nostr app on any platform that wants to interact with a lightning wallet. +* **user**: The person using the **client**, and wants to connect their wallet to their **client**. * **wallet service**: Nostr app that typically runs on an always-on computer (eg. in the cloud or on a Raspberry Pi). This app has access to the APIs of the wallets it serves. ## Theory of Operation - 1. **Users** who wish to use this NIP to send lightning payments to other nostr users must first acquire a special "connection" URI from their NIP-47 compliant wallet application. The wallet application may provide this URI using a QR screen, or a pasteable string, or some other means. + 1. **Users** who wish to use this NIP to allow **client(s)** to interact with their wallet must first acquire a special "connection" URI from their NIP-47 compliant wallet application. The wallet application may provide this URI using a QR screen, or a pasteable string, or some other means. - 2. The **user** should then copy this URI into their **client(s)** by pasting, or scanning the QR, etc. The **client(s)** should save this URI and use it later whenever the **user** makes a payment. The **client** should then request an `info` (13194) event from the relay(s) specified in the URI. The **wallet service** will have sent that event to those relays earlier, and the relays will hold it as a replaceable event. + 2. The **user** should then copy this URI into their **client(s)** by pasting, or scanning the QR, etc. The **client(s)** should save this URI and use it later whenever the **user** (or the **client** on the user's behalf) wants to interact with the wallet. The **client** should then request an `info` (13194) event from the relay(s) specified in the URI. The **wallet service** will have sent that event to those relays earlier, and the relays will hold it as a replaceable event. 3. When the **user** initiates a payment their nostr **client** create a `pay_invoice` request, encrypts it using a token from the URI, and sends it (kind 23194) to the relay(s) specified in the connection URI. The **wallet service** will be listening on those relays and will decrypt the request and then contact the **user's** wallet application to send the payment. The **wallet service** will know how to talk to the wallet application because the connection URI specified relay(s) that have access to the wallet app API. - 4. Once the payment is complete the **wallet service** will send an encrypted `response` (kind 23195) to the **user** over the relay(s) in the URI. + 4. Once the payment is complete the **wallet service** will send an encrypted `response` (kind 23195) to the **user** over the relay(s) in the URI. + + 5. The **wallet service** may send encrypted notifications (kind 23196) of wallet events (such as a received payment) to the **client**. ## Events -There are three event kinds: +There are four event kinds: - `NIP-47 info event`: 13194 - `NIP-47 request`: 23194 - `NIP-47 response`: 23195 +- `NIP-47 notification event`: 23196 -The info event should be a replaceable event that is published by the **wallet service** on the relay to indicate which commands it supports. The content should be -a plaintext string with the supported commands, space-separated, eg. `pay_invoice get_balance`. Only the `pay_invoice` command is described in this NIP, but other commands might be defined in different NIPs. +### Info Event + +The info event should be a replaceable event that is published by the **wallet service** on the relay to indicate which capabilities it supports. + +The content should be a plaintext string with the supported capabilities space-separated, eg. `pay_invoice get_balance notifications`. + +If the **wallet service** supports notifications, the info event SHOULD contain a `notifications` tag with the supported notification types space-separated, eg. `payment_received payment_sent`. + +### Request and Response Events Both the request and response events SHOULD contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **user** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to. Optionally, a request can have an `expiration` tag that has a unix timestamp in seconds. If the request is received after this timestamp, it should be ignored. @@ -68,6 +78,22 @@ The `result_type` field MUST contain the name of the method that this event is r The `error` field MUST contain a `message` field with a human readable error message and a `code` field with the error code if the command was not successful. If the command was successful, the `error` field must be null. +### Notification Events + +The notification event SHOULD contain one `p` tag, the public key of the **user**. + +The content of notifications is encrypted with [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md), and is a JSON-RPCish object with a semi-fixed structure: + +```jsonc +{ + "notification_type": "payment_received", //indicates the structure of the notification field + "notification": { + "payment_hash": "0123456789abcdef..." // notification-related data + } +} +``` + + ### Error codes - `RATE_LIMITED`: The client is sending commands too fast. It should retry in a few seconds. - `NOT_IMPLEMENTED`: The command is not known or is intentionally not implemented. @@ -120,7 +146,8 @@ Response: { "result_type": "pay_invoice", "result": { - "preimage": "0123456789abcdef..." // preimage of the payment + "preimage": "0123456789abcdef...", // preimage of the payment + "fees_paid": 123, // value in msats, optional } } ``` @@ -155,7 +182,8 @@ payment hash of the invoice should be used. { "result_type": "multi_pay_invoice", "result": { - "preimage": "0123456789abcdef..." // preimage of the payment + "preimage": "0123456789abcdef...", // preimage of the payment + "fees_paid": 123, // value in msats, optional } } ``` @@ -189,6 +217,7 @@ Response: "result_type": "pay_keysend", "result": { "preimage": "0123456789abcdef...", // preimage of the payment + "fees_paid": 123, // value in msats, optional } } ``` @@ -225,7 +254,8 @@ pubkey should be used. { "result_type": "multi_pay_keysend", "result": { - "preimage": "0123456789abcdef..." // preimage of the payment + "preimage": "0123456789abcdef...", // preimage of the payment + "fees_paid": 123, // value in msats, optional } } ``` @@ -396,6 +426,59 @@ Response: "block_height": 1, "block_hash": "hex string", "methods": ["pay_invoice", "get_balance", "make_invoice", "lookup_invoice", "list_transactions", "get_info"], // list of supported methods for this connection + "notifications": ["payment_received", "payment_sent"], // list of supported notifications for this connection, optional. + } +} +``` + +## Notifications + +### `payment_received` + +Description: A payment was successfully received by the wallet. + +Notification: +```jsonc +{ + "notification_type": "payment_received", + "notification": { + "type": "incoming", + "invoice": "string", // encoded invoice + "description": "string", // invoice's description, optional + "description_hash": "string", // invoice's description hash, optional + "preimage": "string", // payment's preimage + "payment_hash": "string", // Payment hash for the payment + "amount": 123, // value in msats + "fees_paid": 123, // value in msats + "created_at": unixtimestamp, // invoice/payment creation time + "expires_at": unixtimestamp, // invoice expiration time, optional if not applicable + "settled_at": unixtimestamp, // invoice/payment settlement time + "metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc. + } +} +``` + +### `payment_sent` + +Description: A payment was successfully sent by the wallet. + +Notification: +```jsonc +{ + "notification_type": "payment_sent", + "notification": { + "type": "outgoing", + "invoice": "string", // encoded invoice + "description": "string", // invoice's description, optional + "description_hash": "string", // invoice's description hash, optional + "preimage": "string", // payment's preimage + "payment_hash": "string", // Payment hash for the payment + "amount": 123, // value in msats + "fees_paid": 123, // value in msats + "created_at": unixtimestamp, // invoice/payment creation time + "expires_at": unixtimestamp, // invoice expiration time, optional if not applicable + "settled_at": unixtimestamp, // invoice/payment settlement time + "metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc. } } ``` @@ -409,3 +492,24 @@ Response: ## Using a dedicated relay This NIP does not specify any requirements on the type of relays used. However, if the user is using a custodial service it might make sense to use a relay that is hosted by the custodial service. The relay may then enforce authentication to prevent metadata leaks. Not depending on a 3rd party relay would also improve reliability in this case. + +## Appendix + +### Example NIP-47 info event + +```jsonc +{ + "id": "df467db0a9f9ec77ffe6f561811714ccaa2e26051c20f58f33c3d66d6c2b4d1c", + "pubkey": "c04ccd5c82fc1ea3499b9c6a5c0a7ab627fbe00a0116110d4c750faeaecba1e2", + "created_at": 1713883677, + "kind": 13194, + "tags": [ + [ + "notifications", + "payment_received payment_sent" + ] + ], + "content": "pay_invoice pay_keysend get_balance get_info make_invoice lookup_invoice list_transactions multi_pay_invoice multi_pay_keysend sign_message notifications", + "sig": "31f57b369459b5306a5353aa9e03be7fbde169bc881c3233625605dd12f53548179def16b9fe1137e6465d7e4d5bb27ce81fd6e75908c46b06269f4233c845d8" +} +```