From e2f088286fcd324bb4754dcdf5ff10f4dc823b5b Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Thu, 30 Mar 2023 00:35:13 +0300 Subject: NIP-47 Wallet Connect --- 47.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 47.md (limited to '47.md') diff --git a/47.md b/47.md new file mode 100644 index 0000000..326100d --- /dev/null +++ b/47.md @@ -0,0 +1,112 @@ +NIP-47 +====== + +Nostr Wallet Connect +-------------------- + +`draft` `optional` `author:kiwiidb` `author:bumi` `author:semisol` `author:vitorpamplona` + +## Rationale + +Paying zaps should be possible without the user needing to open a different app to only pay a Lightning invoice. +This NIP describes a way for users to control a remote Lightning node or a custodial Lightning wallet. When self-hosting, this setup does not require the user to run their own server, thereby bypassing certain hurdles that are commonly encountered when trying to connect to a Lightning node remotely. + +## Terms + +* **client**: Nostr app on any platform that wants to pay Lightning invoices +* **wallet service**: Nostr app that typically runs on an always-on computer (eg. in the cloud or on a Raspberry Pi). + +## Events + +There are two event kinds: +- `NIP-47 request`: 23194 +- `NIP-47 response`: 23195 + +Both the request and response events SHOULD only contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **client** if this is a response. + +The content is encrypted with [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md), and is a JSON object. The content depends on the kind. + +Request: +```jsonc +{ + "cmd": "pay_invoice", // command, string + "data": { // data, object + "invoice": "lnbc50n1..." // command-related data + } +} +``` + +Response: +```jsonc +{ + "status": "ok", // status, "ok" | "error" + "event": "0123456789abcdef...", // event the command is in response to, string + "data": { // response data + "preimage": "0123456789abcdef..." // command-related data + } +} +``` + +The data field SHOULD contain a `message` field with a human readable error message if the status is `error`. + +## Nostr Wallet Connect URI +**client** discovers **wallet service** by scanning a QR code, handling a deeplink or pasting in a URI. + +The **wallet service** generates this connection URI with protocol `nostr+walletconnect:` and base path it's hex-encoded `pubkey` with the following query string parameters: + +- `relay` Required. URL of the relay where the **wallet service** is connected and will be listening for events. May be more than one. +- `secret` Required. 32-byte randomly generated hex encoded string. The **client** should use this to sign events when communicating with the **wallet service**. + - Authorization does not require passing keys back and forth. + - The user can have different keys for different applications. Keys can be revoked and created at will and have arbitrary constraints (eg. budgets). + - The key is harder to leak since it is not shown to the user and backed up. + - It improves privacy because the user's main key would not be linked to their payments. + +The **client** should then store this connection and use it when the user wants to perform actions like paying an invoice. Optionally it can display metadata about the connected **wallet service** from it's profile (name, image, url). + +### Example connection string +```sh +nostrwalletconnect:b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c +``` + +## Commands + +### `pay_invoice` + +Description: Requests payment of an invoice. + +Request: +```jsonc +{ + "invoice": "lnbc50n1..." // BOLT11 invoice, string +} +``` + +Response: +```jsonc +{ + "preimage": "0123456789abcdef..." // preimage after payment, string +} +``` + +### `balance` + +Description: Requests the balance of the wallet. + +Request: an empty JSON object. + +Response: +```jsonc +{ + "balance": 100000 // balance in msat, int +} +``` + +## Example pay invoice flow + +0. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostrwalletconnect:` deeplink or configure the connection details manually. +1. **client** sends an event to with **wallet service** service with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above. +2. **nostr-wallet-connect-service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment. +3. **nostr-wallet-connect-service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage. + +## 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. -- cgit v1.2.3 From 961f28285a739000277f489573f5b4c22053f27c Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Thu, 30 Mar 2023 01:23:04 +0300 Subject: NIP-47: Fix up some things --- 47.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to '47.md') diff --git a/47.md b/47.md index 326100d..96bf906 100644 --- a/47.md +++ b/47.md @@ -65,7 +65,7 @@ The **client** should then store this connection and use it when the user wants ### Example connection string ```sh -nostrwalletconnect:b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c +nostr+walletconnect:b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c ``` ## Commands @@ -103,10 +103,10 @@ Response: ## Example pay invoice flow -0. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostrwalletconnect:` deeplink or configure the connection details manually. +0. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostr+walletconnect:` deeplink or configure the connection details manually. 1. **client** sends an event to with **wallet service** service with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above. -2. **nostr-wallet-connect-service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment. -3. **nostr-wallet-connect-service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage. +2. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment. +3. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage. ## 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. -- cgit v1.2.3 From c232c9a46a279883f7d8e07db9f2ae6a79a784c6 Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:18:11 +0300 Subject: NIP-47: feedbacj --- 47.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 12 deletions(-) (limited to '47.md') diff --git a/47.md b/47.md index 96bf906..fd85e13 100644 --- a/47.md +++ b/47.md @@ -8,8 +8,7 @@ Nostr Wallet Connect ## Rationale -Paying zaps should be possible without the user needing to open a different app to only pay a Lightning invoice. -This NIP describes a way for users to control a remote Lightning node or a custodial Lightning wallet. When self-hosting, this setup does not require the user to run their own server, thereby bypassing certain hurdles that are commonly encountered when trying to connect to a Lightning node remotely. +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 @@ -47,7 +46,17 @@ Response: } ``` -The data field SHOULD contain a `message` field with a human readable error message if the status is `error`. +The data field SHOULD contain a `message` field with a human readable error message and a `code` field with the error code if the status is `error`. + +### 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. +- `INSUFFICIENT_BALANCE`: The wallet does not have enough funds to cover a fee reserve or the payment amount. +- `QUOTA_EXCEEDED`: The wallet has exceeded +- `RESTRICTED`: This public key is not allowed to do this operation. +- `UNAUTHORIZED`: This public key has no wallet connected. +- `INTERNAL`: An internal error. +- `OTHER`: Other error. ## Nostr Wallet Connect URI **client** discovers **wallet service** by scanning a QR code, handling a deeplink or pasting in a URI. @@ -61,7 +70,7 @@ The **wallet service** generates this connection URI with protocol `nostr+wallet - The key is harder to leak since it is not shown to the user and backed up. - It improves privacy because the user's main key would not be linked to their payments. -The **client** should then store this connection and use it when the user wants to perform actions like paying an invoice. Optionally it can display metadata about the connected **wallet service** from it's profile (name, image, url). +The **client** should then store this connection and use it when the user wants to perform actions like paying an invoice. Due to this NIP using ephemeral events, it is recommended to pick relays that do not close connections on inactivity to not drop events. ### Example connection string ```sh @@ -70,34 +79,60 @@ nostr+walletconnect:b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558 ## Commands -### `pay_invoice` +### `get_info` -Description: Requests payment of an invoice. +Description: Get information about the wallet and service. + +Request: Empty object. + +Response: +```jsonc +{ + "balance": 100000, // balance in msat, int + // this should be capped at the quota allowed for this client + // to not report unspendable balance. + "implemented_commands": ["get_info", "pay_invoice"] // commands supported, string array + // extensions can be specified via command+extension: + // get_info+node_info +} +``` + +### `create_invoice` + +Description: Requests creation of an invoice. Request: ```jsonc { - "invoice": "lnbc50n1..." // BOLT11 invoice, string + "amount": 1000, // amount in msat, int + // must be a whole number of sats unless + // create_invoice+msat_amount is implemented. + "description": "memo" // a description, string, optional } ``` Response: ```jsonc { - "preimage": "0123456789abcdef..." // preimage after payment, string + "invoice": "lnbc50n1..." // BOLT11 invoice, string } ``` -### `balance` +### `pay_invoice` -Description: Requests the balance of the wallet. +Description: Requests payment of an invoice. -Request: an empty JSON object. +Request: +```jsonc +{ + "invoice": "lnbc50n1..." // BOLT11 invoice, string +} +``` Response: ```jsonc { - "balance": 100000 // balance in msat, int + "preimage": "0123456789abcdef..." // preimage after payment, string } ``` -- cgit v1.2.3 From de095e47580e63d91faf541e6a0c84ae5c0ca8fd Mon Sep 17 00:00:00 2001 From: kiwiidb <33457577+kiwiidb@users.noreply.github.com> Date: Thu, 27 Apr 2023 16:21:15 +0200 Subject: NIP-47: Implement feedback Co-authored-by: Semisol --- 47.md | 79 +++++++++++++++++++++++-------------------------------------------- 1 file changed, 27 insertions(+), 52 deletions(-) (limited to '47.md') diff --git a/47.md b/47.md index fd85e13..083dabe 100644 --- a/47.md +++ b/47.md @@ -17,19 +17,22 @@ This NIP describes a way for clients to access a remote Lightning wallet through ## Events -There are two event kinds: +There are three event kinds: +- `NIP-47 info event`: 13194 - `NIP-47 request`: 23194 - `NIP-47 response`: 23195 -Both the request and response events SHOULD only contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **client** if this is a response. +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-seperated, eg. `pay_invoice get_balance`. Only the `pay_invoice` command is described in this NIP, but other commands might be defined in different NIPs. +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 **client** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to. -The content is encrypted with [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md), and is a JSON object. The content depends on the kind. +The content of requests and responses 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: Request: ```jsonc { - "cmd": "pay_invoice", // command, string - "data": { // data, object + "method": "pay_invoice", // method, string + "params": { // params, object "invoice": "lnbc50n1..." // command-related data } } @@ -38,21 +41,26 @@ Request: Response: ```jsonc { - "status": "ok", // status, "ok" | "error" - "event": "0123456789abcdef...", // event the command is in response to, string - "data": { // response data + "result_type": "pay_invoice", //indicates the structure of the result field + "error": { //object, non-null in case of error + "code": "UNAUTHORIZED", //string error code, see below + "message": "human readable error message" + }, + "result": { // result, object. null in case of error. "preimage": "0123456789abcdef..." // command-related data } } ``` -The data field SHOULD contain a `message` field with a human readable error message and a `code` field with the error code if the status is `error`. +The `result_type` field MUST contain the name of the method that this event is responding to. +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 succesful. +If the command was succesful, the `error` field must be null. ### 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. - `INSUFFICIENT_BALANCE`: The wallet does not have enough funds to cover a fee reserve or the payment amount. -- `QUOTA_EXCEEDED`: The wallet has exceeded +- `QUOTA_EXCEEDED`: The wallet has exceeded its spending quota. - `RESTRICTED`: This public key is not allowed to do this operation. - `UNAUTHORIZED`: This public key has no wallet connected. - `INTERNAL`: An internal error. @@ -64,7 +72,7 @@ The data field SHOULD contain a `message` field with a human readable error mess The **wallet service** generates this connection URI with protocol `nostr+walletconnect:` and base path it's hex-encoded `pubkey` with the following query string parameters: - `relay` Required. URL of the relay where the **wallet service** is connected and will be listening for events. May be more than one. -- `secret` Required. 32-byte randomly generated hex encoded string. The **client** should use this to sign events when communicating with the **wallet service**. +- `secret` Required. 32-byte randomly generated hex encoded string. The **client** MUST use this to sign events and encrypt payloads when communicating with the **wallet service**. - Authorization does not require passing keys back and forth. - The user can have different keys for different applications. Keys can be revoked and created at will and have arbitrary constraints (eg. budgets). - The key is harder to leak since it is not shown to the user and backed up. @@ -79,45 +87,6 @@ nostr+walletconnect:b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558 ## Commands -### `get_info` - -Description: Get information about the wallet and service. - -Request: Empty object. - -Response: -```jsonc -{ - "balance": 100000, // balance in msat, int - // this should be capped at the quota allowed for this client - // to not report unspendable balance. - "implemented_commands": ["get_info", "pay_invoice"] // commands supported, string array - // extensions can be specified via command+extension: - // get_info+node_info -} -``` - -### `create_invoice` - -Description: Requests creation of an invoice. - -Request: -```jsonc -{ - "amount": 1000, // amount in msat, int - // must be a whole number of sats unless - // create_invoice+msat_amount is implemented. - "description": "memo" // a description, string, optional -} -``` - -Response: -```jsonc -{ - "invoice": "lnbc50n1..." // BOLT11 invoice, string -} -``` - ### `pay_invoice` Description: Requests payment of an invoice. @@ -125,14 +94,20 @@ Description: Requests payment of an invoice. Request: ```jsonc { - "invoice": "lnbc50n1..." // BOLT11 invoice, string + "method": "pay_invoice", + "params": { + "invoice": "lnbc50n1..." // bolt11 invoice + } } ``` Response: ```jsonc { - "preimage": "0123456789abcdef..." // preimage after payment, string + "result_type": "pay_invoice", + "result": { + "preimage": "0123456789abcdef..." // preimage of the payment + } } ``` -- cgit v1.2.3 From 5a8c463641b3dbaf927f7e0479ff046f0f6fb646 Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Thu, 27 Apr 2023 17:24:20 +0300 Subject: NIP-47: Add error for payment failed --- 47.md | 3 +++ 1 file changed, 3 insertions(+) (limited to '47.md') diff --git a/47.md b/47.md index 083dabe..aa58b5c 100644 --- a/47.md +++ b/47.md @@ -111,6 +111,9 @@ Response: } ``` +Errors: +- `PAYMENT_FAILED`: The payment failed. This may be due to a timeout, exhausting all routes, insufficient capacity or similar. + ## Example pay invoice flow 0. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostr+walletconnect:` deeplink or configure the connection details manually. -- cgit v1.2.3 From 60aa6ae168e8d71e4fd247e49f8ba70fbe511fda Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Tue, 9 May 2023 10:17:15 -0500 Subject: A few changes to some nips. (#510) --- 01.md | 1 + 47.md | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to '47.md') diff --git a/01.md b/01.md index 713e655..e36f4f5 100644 --- a/01.md +++ b/01.md @@ -109,3 +109,4 @@ A relay may choose to treat different message kinds differently, and it may or m - Clients should not open more than one websocket to each relay. One channel can support an unlimited number of subscriptions, so clients should do that. - The `tags` array can store a tag identifier as the first element of each subarray, plus arbitrary information afterward (always as strings). This NIP defines `"p"` — meaning "pubkey", which points to a pubkey of someone that is referred to in the event —, and `"e"` — meaning "event", which points to the id of an event this event is quoting, replying to or referring to somehow. See [NIP-10](https://github.com/nostr-protocol/nips/blob/127d5518bfa9a4e4e7510490c0b8d95e342dfa4b/10.md) for a detailed description of "e" and "p" tags. - The `` item present on the `"e"` and `"p"` tags is an optional (could be set to `""`) URL of a relay the client could attempt to connect to fetch the tagged event or other events from a tagged profile. It MAY be ignored, but it exists to increase censorship resistance and make the spread of relay addresses more seamless across clients. +- Clients should use the created_at field to judge the age of a metadata event and completely replace older metadata events with newer metadata events regardless of the order in which they arrive. Clients should not merge any filled fields within older metadata events into empty fields of newer metadata events. diff --git a/47.md b/47.md index aa58b5c..fe3d575 100644 --- a/47.md +++ b/47.md @@ -12,8 +12,18 @@ This NIP describes a way for clients to access a remote Lightning wallet through ## Terms -* **client**: Nostr app on any platform that wants to pay Lightning invoices -* **wallet service**: Nostr app that typically runs on an always-on computer (eg. in the cloud or on a Raspberry Pi). +* **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**. +* **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 which 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. + + 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. + + 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. ## Events @@ -24,7 +34,8 @@ There are three event kinds: 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-seperated, eg. `pay_invoice get_balance`. Only the `pay_invoice` command is described in this NIP, but other commands might be defined in different NIPs. -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 **client** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to. + +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. The content of requests and responses 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: @@ -117,7 +128,7 @@ Errors: ## Example pay invoice flow 0. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostr+walletconnect:` deeplink or configure the connection details manually. -1. **client** sends an event to with **wallet service** service with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above. +1. **client** sends an event to the **wallet service** service with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above. 2. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment. 3. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage. -- cgit v1.2.3 From 4208652dc7a39c63c39559b13c656ec30400fcba Mon Sep 17 00:00:00 2001 From: William Casarin Date: Tue, 9 May 2023 15:02:14 -0700 Subject: nip47: add lud16 parameter to connection string This adds an optional but recommended lud16 parameter to nostr wallet connection strings. This enables seamless onboarding of new users, allowing clients to automatically configure the receive address for zaps. --- 47.md | 1 + 1 file changed, 1 insertion(+) (limited to '47.md') diff --git a/47.md b/47.md index fe3d575..c884b97 100644 --- a/47.md +++ b/47.md @@ -88,6 +88,7 @@ The **wallet service** generates this connection URI with protocol `nostr+wallet - The user can have different keys for different applications. Keys can be revoked and created at will and have arbitrary constraints (eg. budgets). - The key is harder to leak since it is not shown to the user and backed up. - It improves privacy because the user's main key would not be linked to their payments. +- `lud16` Recommended. A lightning address that clients can use to automatically setup the `lud16` field on the user's profile if they have none configured. The **client** should then store this connection and use it when the user wants to perform actions like paying an invoice. Due to this NIP using ephemeral events, it is recommended to pick relays that do not close connections on inactivity to not drop events. -- cgit v1.2.3