Skip to content
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

NIP-59: Lightning Zaps v2 #243

Closed
wants to merge 6 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions 59.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
NIP-59
======

Lightning Zaps v2
-----------------

`draft` `optional` `author:ok300`


We propose a different way to implement Zaps than described in NIP-57.


## Definitions

> Zap: a way to make LN payments on nostr that allows paid invoices to be tallied publicly and associated with the Sender and Recipient nostr pubkeys

> Sender: a nostr account who sends a Zap

> Recipient: a nostr account who receives Zaps, either on posted kind-1 notes or directly on his or her profile

> Pairing: the state when a Client and a Zapper point to each other using profile metadata:
> - the Zapper's profile has the Client pubkey in its `nip59c` field
> - the Client's profile has the Zapper pubkey in its `nip59z` field

> Zapper: a service with access to its own LN wallet and nostr client, which can send and receive Zaps on behalf of its paired Client


## Workflow

A Client (Sender or Receiver) needs a one-time pairing with a Zapper, before they can send or receive Zaps.

- Sender
- [1] create a `ZapRequestTentative` event

- Sender Zapper
- [2] if the `ZapRequestTentative` comes from the paired Client, send a `ZapRequestConfirmed` event
- [4] if the `ZapInvoice` is for this request:
- pay the invoice
- using the preimage, decrypt the `ZapReceipt`
- post the decrypted `ZapReceipt`

- Recipient Zapper
- [3] if the `ZapRequestConfirmed` is intended for its paired Client, send a `ZapInvoice` event
- create an LN invoice matching the requested amount
- create and sign a `ZapReceipt` event containing the original Zap request (amount, comment), but do not broadcast it
- encrypt the `ZapReceipt` with the invoice preimage
- the resulting `ZapInvoice` payload is `[ bolt11, encrypt( preimage, zap_request ) ]`

In more detail, this consists of the following.

### [1] Sender initiates Zap

The Sender posts a `ZapRequestTentative`:

```json
{
"kind": 21123,
"tags": [
["e", <...>], // Optional field indicating Zapped Note ID
["p", <...>], // Pubkey of Zap Recipient
],
"content": "{...}", // Zap amount, optional Zap comment
...
}
```

### [2] Sender Zapper validates, forwards it

The Sender Zapper listens for such requests for its paired Client.

When it sees one, it sends a `ZapRequestConfirmed` (identical structure, but with kind `21124`).


### [3] Recipient Zapper creates invoice, encrypted Zap confirmation

Similarly, the Recipient Zapper listens for `ZapRequestConfirmed` for its paired Client.

When receiving one, it issues an LN invoice with the requested amount and `expiry` set to 1 minute.

It also creates and signs a `ZapReceipt` event:

```json
{
"kind": 1126,
"tags": [
["e", <...>], // Note ID of original ZapRequest
["p", <...>], // Pubkey of Zap Recipient
],
"content": "{...}", // Zap amount, optional Zap comment
"sig": "..." // Signed by the Recipient Zapper
}
```

and encrypts it with the invoice preimage.

Finally, it broadcasts a `ZapInvoice` with:

```json
{
"kind": 21125,
"tags": [
["e", <...>], // Note ID of original ZapRequest
["p", <...>], // Pubkey of Zap Sender
],
"content": "[ bolt11, encrypt( preimage, zap_receipt ) ]"
...
}
```

### [4] Sender Zapper pays invoice, broadcasts Zap confirmation

The Sender Zapper pays the invoice and, with the preimage, decrypts and broadcasts the `ZapReceipt`.



## Client Notes

A Client MUST only show or tally Zaps from a paired Zapper.

A Client MUST only show a Zap button if the current user and the Recipient both have paired Zappers.

Clients that only wish to tally the Zaps only need to query for `ZapReceipts`.


## Zapper Notes

The Zapper SHOULD have a valid NIP-05 profile with a list of relays where it can be reached.


## Benefits vs. NIP-57

- No LNURL modifications needed
- Simpler clients: no parsing of BOLT11 or LNURL required
- Non-custodial friendly: no components need to be publicly reachable. No LN Address or LNURL endpoint needed. Therefore, anyone can run a Zapper (e.g. on an Umbrel) and start receiving Zaps.
- Lower footprint on relays: 3 ephemeral messages, only 1 persistent


## Limitations

- The Zapper is a trusted service. The `ZapReceipt` is only meaningful when the Recipient Zapper is honest. A dishonest Recipient can use a custom Zapper to "self-zap".
- This can be somewhat mitigated by requiring the final `ZapReceipt` to be signed by both Sender or Receiver (Zappers). This way, a dishonest Recipient Zapper can fake incoming Zaps, but would have to create each time new nostr IDs. This also means a dishonest Recipient Zapper would not be able to fake incoming zaps from famous or otherwise reputable Nostr accounts. This could be achieved with aggregate Schnorr signatures, or by embedding a signed nonce from the Sender in the final `ZapReceipt`.

- When the Sender or Recipient changes to a different Zapper, this will cause the previously sent or received Zaps to be lost. A viewer who wants to tally the Zaps will now query events signed by the new Zapper, meaning the previous ones are not counted.


## Future Work

- Once NIP-26 Signing Delegation is supported across relays, the Zapper should start using a delegated key. This way, changing to a new Zapper will keep previous Zaps associated with this Sender or Receiver.

- The Zapper could notify its paired Client when LN funds run low. This can be done with a separate event type, which could already include a LNURL-pay link or a LN invoice.