-
Notifications
You must be signed in to change notification settings - Fork 761
NIP-47 Nostr Wallet Connect #406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,125 @@ | ||||||||||||||
| NIP-47 | ||||||||||||||
| ====== | ||||||||||||||
|
|
||||||||||||||
| Nostr Wallet Connect | ||||||||||||||
| -------------------- | ||||||||||||||
|
|
||||||||||||||
| `draft` `optional` `author:kiwiidb` `author:bumi` `author:semisol` `author:vitorpamplona` | ||||||||||||||
|
|
||||||||||||||
| ## 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. | ||||||||||||||
|
|
||||||||||||||
| ## 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 three event kinds: | ||||||||||||||
| - `NIP-47 info event`: 13194 | ||||||||||||||
| - `NIP-47 request`: 23194 | ||||||||||||||
| - `NIP-47 response`: 23195 | ||||||||||||||
|
|
||||||||||||||
| 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 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 | ||||||||||||||
| { | ||||||||||||||
| "method": "pay_invoice", // method, string | ||||||||||||||
| "params": { // params, object | ||||||||||||||
| "invoice": "lnbc50n1..." // command-related data | ||||||||||||||
|
Semisol marked this conversation as resolved.
|
||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Response: | ||||||||||||||
| ```jsonc | ||||||||||||||
| { | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The response should have the same structure as NIP-46:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In JSONRPC error is an object with "code" and "message" if we go the generic route maybe we should allow this? |
||||||||||||||
| "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 | ||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does anybody need this preimage? I don't really use this in the current implementation
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was some talk about preimage being used to decrypt content for other uses.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It acts like a proof of payment and I think Will said that it would allow for an update to NIP-57 where the client dispatches the Zap event. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. YES, the preimage is needed and allows all kind of things, starting with a simple check if the that requested invoice was actually paid by comparing the payment hash to the preimage. I also still think that NIP-57 should events published by the clients and with the preimage the same proof of payment can be provided. (making it much easier for the LNURL servers) |
||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 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. | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error codes should be in the |
||||||||||||||
| - `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 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. | ||||||||||||||
| - `OTHER`: Other 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: | ||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no + in the current implementations:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is intentional. Signaling breaking change and makes it clearer it's nostr. |
||||||||||||||
|
|
||||||||||||||
| - `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** 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. | ||||||||||||||
| - 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. Due to this NIP using ephemeral events, it is recommended to pick relays that do not close connections on inactivity to not drop events. | ||||||||||||||
|
|
||||||||||||||
|
Semisol marked this conversation as resolved.
|
||||||||||||||
| ### Example connection string | ||||||||||||||
| ```sh | ||||||||||||||
| nostr+walletconnect:b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ## Commands | ||||||||||||||
|
|
||||||||||||||
| ### `pay_invoice` | ||||||||||||||
|
|
||||||||||||||
| Description: Requests payment of an invoice. | ||||||||||||||
|
|
||||||||||||||
| Request: | ||||||||||||||
| ```jsonc | ||||||||||||||
| { | ||||||||||||||
| "method": "pay_invoice", | ||||||||||||||
| "params": { | ||||||||||||||
| "invoice": "lnbc50n1..." // bolt11 invoice | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Response: | ||||||||||||||
| ```jsonc | ||||||||||||||
| { | ||||||||||||||
| "result_type": "pay_invoice", | ||||||||||||||
| "result": { | ||||||||||||||
| "preimage": "0123456789abcdef..." // preimage of the payment | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 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. | ||||||||||||||
| 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. **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. | ||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This NIP is designed to forward the responsibility of executing lightning payments to specialized Nostr micro apps whose sole purpose is to connect with the user's desired lightning service, generally using the service's proprietary API, and establish security controls to approve such transactions. Regular Nostr Clients are then exempt from implementing payment interfaces with each lightning provider out there and from implementing security settings to authorize certain payment types.