nips icon indicating copy to clipboard operation
nips copied to clipboard

NIP-57: Lightning Zaps

Open jb55 opened this issue 2 years ago • 47 comments

This NIP defines a new note type called a lightning zap of kind 9735. These represent paid lightning invoice receipts sent by a lightning node called the zapper. We also define a another note type of kind 9734 which are zap request notes, which will be described in this document.

Having lightning receipts on nostr allows clients to display lightning payments from entities on the network. These can be used for fun or for spam deterrence.

https://github.com/nostr-protocol/nips/blob/zaps/57.md

jb55 avatar Feb 05 '23 19:02 jb55

nice! awesome spec! Will work on implementing this.

lylepratt avatar Feb 05 '23 20:02 lylepratt

Can we remove the allowsNostr field entirely and just rely on the presence of nostrPubkey?

fiatjaf avatar Feb 05 '23 20:02 fiatjaf

probably, I can't remember why I had them separate.

jb55 avatar Feb 05 '23 20:02 jb55

I kinda rushed this and might be underspecified, let me know if anything seems unclear.

jb55 avatar Feb 05 '23 20:02 jb55

probably, I can't remember why I had them separate.

maybe for temporarily disabling it while still allowing clients to validate older zaps? I think I had some kind of reasoning along those lines.

jb55 avatar Feb 05 '23 20:02 jb55

maybe for temporarily disabling it while still allowing clients to validate older zaps? I think I had some kind of reasoning along those lines.

Makes sense.

fiatjaf avatar Feb 05 '23 20:02 fiatjaf

I don't understand the need for the encrypted zap request, since HTTPS calls are already encrypted. What have I misunderstood?

fiatjaf avatar Feb 05 '23 20:02 fiatjaf

This would allow you to hide who created the invoice. you could still tally zaps and only the person receiving the zap could see who it was from. The description would be an encrypted zap request note.

jb55 avatar Feb 05 '23 20:02 jb55

Well it would be slightly more complicated, it would have to be a kind 9733 note with a random key, or potentially just a json blob with no key; with relays encrypted to the zapper (to reduce fingerprinting), in this json is a 9734 note encrypted to the user getting zapped.

I have this all thought out but didn't want to complicate this spec with it just yet.

jb55 avatar Feb 05 '23 20:02 jb55

I am excited to see the interest of deeply integrating lightning payments into Nostr. The name zaps is great!

Here are a few thoughts/questions:

Do I understand it correctly that the zap request should be stored in the invoice description? Or only the hash of the zap request in the invoice description_hash? "Parse the bolt11 description as a JSON nostr note" indicates that the whole nostr node should be saved in the invoice description. This would be different to what is currently described in the lnurl-pay spec?

Would it be possible to reuse LUD18 payerdata for this? afaik LUD18 is already generic and allows the storage generic payer data. It is also committed in the description_hash.

Would it be possible to allow the client to publish the zap? This would remove the requirement that LNURL implementations/service providers need to implement Nostr connections. The smaller the usage specific requirement is the more tools/service providers will be able to be used. I am worried that this is a too big limitation. IMO ideally the LNURL spec should not have any social network specific requirements and is independent.

The main data that would be needed is the preimage, correct? With the preimage and the payment request/hash the validation can also be done. I could imagine that clients will deeply integrate lightning wallets and thus also have access to the preimage or alternatively with a proposal like the lnnurl-verify the preimage could be retrieved without any Nostr specific requirement on side of the the LNURL-spec.

Thanks for pushing this forward! This can bring the value4value idea to so many more usecases!

bumi avatar Feb 05 '23 23:02 bumi

I was pretty happy with this NIP, but after thinking about it for many minutes, this question has occurred to me: is there a reason for reusing LNURL endpoints even though this is not an LNURL payment at all?

Wouldn't it be more elegant and much simpler to decouple this entirely from LNURL and use an independent spec (and a different /.well-known endpoint) for requesting the invoice? It wouldn't need all the other LNURL things, and it would be automatically a nostrified endpoint (so no need for the nostrEnabled flag).

Moreover the communication could be reduced to a single step. Could be something like:

A user sets a field in their Nostr metadata like: "nip57": "https://zapper.com/"

Then a client can call

GET https://zapper.com/.well-known/zap

and get back

{"pubkey": "<hex pubkey of the zapper>"}

And when sending a zap the client would call:

POST /.well-known/zap
{
  ...zap request event, as it is today, but also including the amount as a tag
}

and immediately get an invoice back, check that the description hash matches against the event id of the zap request, and pay it. The rest is the same.

The only reason I can see for keeping the lud06/lud16 things in place and reusing them are that people don't have to change their metadata and can start getting zaps immediately as soon as their zapper provider (currently LNURL provider) implements this NIP. But I think this reason is not good enough and that a dedicated protocol that doesn't ever say anything about LNURL such as above would be less confusing and easier to implement for everybody.

fiatjaf avatar Feb 05 '23 23:02 fiatjaf

Another idea is to embed the zapper pubkey in the metadata of the user who is receiving the zaps directly, so the field could be something like "nip57": "["<zapperpubkeyhex>", https://zapper.com"] instead. Then other clients don't have to actually call the zapper endpoint to verify the zaps, they can just use the zapper key taken from the metadata directly.

Now I am thinking: why restrict the number of zappers a user can have to only one? Maybe I am crazy, but users may want to have more than one, as fallbacks. So the field could be "nip57": [["key", "url"], ["key", "url"]], but this is ugly, better put it on the event tags: "tags": [["zapper", "key", "url"], ["zapper", "key", "url"]].

Or maybe it is even better to put these things in yet another dedicated replaceable event, as explained by @staab at https://github.com/nostr-protocol/nips/pull/218#issuecomment-1416823469, that would mitigate the issue of unwanted metadata overwrites and then only clients that implemented zaps could fetch that specific event to the get zapper addresses for people.

fiatjaf avatar Feb 06 '23 00:02 fiatjaf

The problem with my spec suggestion above is that it assumes the zapper service will know who is receiving the funds from the zap request event, which is definitely a possibility, but shouldn't be mandatory as it may complicate things for some potential providers.

It is probably better that the specified zapper URL be just an arbitrary URL and clients just POST to that URL directly, without the .well-known shenanigans. Using arbitrary URLs solve things since zapper services can give URLs with internal IDs for their users and use these to discriminate incoming zap payments.

OK, now I'll shut up.

fiatjaf avatar Feb 06 '23 00:02 fiatjaf

I think being able to build on all the adoption of LNURL/LUD16 out there is a great advantage. Imo ideally people can use their current setup as much as possible and developers also don't need to start from scratch again building nostr LN support. So I am +1 for LNURL

What exactly do we miss from LNURL?

bumi avatar Feb 06 '23 00:02 bumi

Would it be possible to allow the client to publish the zap? This would remove the requirement that LNURL implementations/service providers need to implement Nostr connections. The smaller the usage specific requirement is the more tools/service providers will be able to be used. I am worried that this is a too big limitation. IMO ideally the LNURL spec should not have any social network specific requirements and is independent.

The main data that would be needed is the preimage, correct? With the preimage and the payment request/hash the validation can also be done. I could imagine that clients will deeply integrate lightning wallets and thus also have access to the preimage or alternatively with a proposal like the lnnurl-verify the preimage could be retrieved without any Nostr specific requirement on side of the the LNURL-spec.

Thanks for pushing this forward! This can bring the value4value idea to so many more usecases!

Would it be possible to allow the client to publish the zap? - I like Bhumi's point on having this option. There are many wallets such as Strike don't have LNURL implemented. Zaps break if wallet does not support it.

There should be a manual option for zapper user to input the preimage after the payment and the client creates kind 9735 event on behalf of the zapper user. This eliminates dependency on wallets implementing LNURL and Zapper specs.

starbackr-dev avatar Feb 06 '23 00:02 starbackr-dev

Zaps break if wallet does not support it.

I don't think this is true. The Nostr client requests a Bolt11 invoice from the server which all wallets should be able to pay.

lylepratt avatar Feb 06 '23 15:02 lylepratt

Zaps break if wallet does not support it.

I don't think this is true. The Nostr client requests a Bolt11 invoice from the server which all wallets should be able to pay.

Sorry for not being clear. I was referring to Zapper node/wallet which needs to create note kind 9735.

starbackr-dev avatar Feb 06 '23 18:02 starbackr-dev

developers also don't need to start from scratch again building nostr LN support

This is not correct. What is "from scratch"? To implement zap support everybody has to very much start from scratch since there is nothing in common with LNURL here. This is not a bad thing since it is so simple and the functionality and purpose is so different anyway.

LNURL libraries cannot be used at all without heavy patching. LNURL providers will also require heavy patching (relatively heavy, of course, since everything is still simple) that is equivalent to implementing a handler for this from scratch -- probably worse.

Most likely any server patching will introduce spaghetti code for switching between normal lnurl and zaps, so it would be better to just have an HTTP handler dedicated to zaps instead. Much cleaner.

Other advantages of dropping LNURL:

  1. doesn't confuse people into thinking this is LNURL in any sense
  2. Nostr clients don't need to parse lnurl... bech32 codes
  3. Nostr clients don't need to parse lud16 addresses
  4. with a new single-step communication protocol I think bolt12 support can be introduced very easily if that becomes a thing, we will not need a new zap-bolt12 protocol or anything like that

For reference: https://github.com/nostr-protocol/nips/pull/224#issuecomment-1418302436

fiatjaf avatar Feb 06 '23 18:02 fiatjaf

Some of the issues raised here made me wonder if there is a better way to do zaps.

So I came up #228. The interesting part is:

Benefits vs current approach:

  • no LNURL changes needed
  • minimal nostr client changes needed (send 1 new ephemeral msg kind; validate and tally zaps)
  • nostr clients don't need to parse BOLT11 invoices
  • nostr clients don't need to parse LUD06 or LUD16 to zap

It's still theoretical and early, but from what I can tell it solves or even avoids some of the main issues raised here.

ok300 avatar Feb 07 '23 00:02 ok300

For this PR, I agree with @fiatjaf that a separate non-LNURL approach is better.

@bumi is right that LNURL is better because of the network effects. But this doesn't look like LNURL anymore. It needs an extra arg for LNURL-pay, extra validation, etc. It's not even a successAction extension like AES ^1.

One thing I disagree with is https://github.com/nostr-protocol/nips/pull/224/files#diff-503ae34c31b21445fb1624ece3f7924b5a0b7ab1ab31b19ea065fb7975b3f83bR31 :

  1. Clients may choose to display a lightning zap button on each post or on the users profile, if the user's lnurl pay request endpoint supports nostr, the client SHOULD generate a zap invoice instead of a normal lnurl invoice.

Seems to indicate zaps "override" LNURL if a recipient supports both. I hope its a vestige of the original idea, where LNURL was planned for both. But if zaps use a non-LNURL way, the clients should still support standard LNURL. Could be offered as checkbox in zap popup, or as client setting.

ok300 avatar Feb 07 '23 01:02 ok300

Another idea is to embed the zapper pubkey in the metadata of the user who is receiving the zaps directly, so the field could be something like "nip57": "["<zapperpubkeyhex>", https://zapper.com"] instead. Then other clients don't have to actually call the zapper endpoint to verify the zaps, they can just use the zapper key taken from the metadata directly.

How would clients set this key? The zapper pubkey is determined by the zapper's private key which is implemented by the wallet service.

jb55 avatar Feb 07 '23 01:02 jb55

I still think it's worth making this an lnurl extension, users don't have to do anything and wallet providers can just implement zaps if their users demand it. seemed pretty natural to me 🤷‍♂️

jb55 avatar Feb 07 '23 01:02 jb55

I was pretty happy with this NIP, but after thinking about it for many minutes, this question has occurred to me: is there a reason for reusing LNURL endpoints even though this is not an LNURL payment at all?

Wouldn't it be more elegant and much simpler to decouple this entirely from LNURL and use an independent spec (and a different /.well-known endpoint) for requesting the invoice? It wouldn't need all the other LNURL things, and it would be automatically a nostrified endpoint (so no need for the nostrEnabled flag).

Moreover the communication could be reduced to a single step. Could be something like:

A user sets a field in their Nostr metadata like: "nip57": "https://zapper.com/"

Then a client can call

GET https://zapper.com/.well-known/zap

and get back

{"pubkey": "<hex pubkey of the zapper>"}

And when sending a zap the client would call:

POST /.well-known/zap
{
  ...zap request event, as it is today, but also including the amount as a tag
}

and immediately get an invoice back, check that the description hash matches against the event id of the zap request, and pay it. The rest is the same.

The only reason I can see for keeping the lud06/lud16 things in place and reusing them are that people don't have to change their metadata and can start getting zaps immediately as soon as their zapper provider (currently LNURL provider) implements this NIP. But I think this reason is not good enough and that a dedicated protocol that doesn't ever say anything about LNURL such as above would be less confusing and easier to implement for everybody.

I'm fine with this even though I think its busywork... what do you think @v0l ?

jb55 avatar Feb 07 '23 01:02 jb55

Since all the proposed solutions involves some heavy lifting work on the wallet side as well, We should also hear from wallet providers such as WOS, breez, Zeus on supporting this. They need to be connected and publish the event to relays where the zapper sender and receiver are connected.

starbackr-dev avatar Feb 07 '23 02:02 starbackr-dev

Please correct me if I'm wrong, but currently zaps are simply a way to show receipts of "paid upvotes" a post/profile in a way that adds more signal than empty likes/reposts (unless you are on a relay which requires payment per action).

I see there is a "content" field on the zap:

The content MAY be an additional comment from the user which can be displayed when listing zaps on posts and profiles.

Will this create a weird scenario where there are two separate threads stored in two different locations (zaps with comments and standard replies) which could break conversation flow. Would another option for the person zapping to create a standard text_note, and then attach the zap to that rather than including text content inside the zap (I feel like this will be difficult to expand outside of simple comments)?

Then zaps would simply verify a payment of X amount on any type of note, And the note the zap is attached to can contain any type of metadata.

This could allow zaps to be used on all other types of content, such as replies to polls.

rolznz avatar Feb 07 '23 04:02 rolznz

How would clients set this key? The zapper pubkey is determined by the zapper's private key which is implemented by the wallet service.

They would get the data from the zapper's website beforehand.

This is just so other clients don't have to query the zapper HTTP endpoint all the time to get their public key.

Probably the user could input just their zap address like https://zapper.com/myuser and the Nostr client queries that URL to get the public key immediately, then stores the public key in the user's metadata.

fiatjaf avatar Feb 07 '23 09:02 fiatjaf

There are many wallets such as Strike don't have LNURL implemented

https://strike.army adds LNURL support and Zaps, its currently the only public LNURL service which supports Zaps

I'm fine with this even though I think its busywork... what do you think @v0l ?

I don't see what all the fuss is about, it's very simple already (i added Zaps support to strike.army in a day)

But if you want to change it, not really a problem..

v0l avatar Feb 07 '23 10:02 v0l

I don't see what all the fuss is about, it's very simple already (i added Zaps support to strike.army in a day)

There is no fuss, it's just that zaps and lnurl are completely separate things and keeping them together gain us nothing, it's just strictly worse. See my comments above.

Think about all the non-lnurl providers that could become zap providers.

Implementing standalone zap support anywhere would take less than a day. And it would be much easier than implementing lnurl and then heavily modifying it to add zaps.

fiatjaf avatar Feb 07 '23 10:02 fiatjaf

Without the LNURL garbage it becomes much simpler to implement a zaps provider.

For example, one could do a zaps provider that doesn't require any registration. The provider just has a static URL https://zapper.com and infer from the p tag of the zap request who should receive that money.

Then pay it out to the lud06 or lud16 addresses on that profile metadata -- or allow them to withdraw later, or even send it out to the Bitcoin address implied by the Nostr key (probably not a good idea, but I'm sure people will do it).

fiatjaf avatar Feb 07 '23 10:02 fiatjaf

Without the LNURL garbage it becomes much simpler to implement a zaps provider.

For example, one could do a zaps provider that doesn't require any registration. The provider just has a static URL https://zapper.com and infer from the p tag of the zap request who should receive that money.

Then pay it out to the lud06 or lud16 addresses on that profile metadata -- or allow them to withdraw later, or even send it out to the Bitcoin address implied by the Nostr key (probably not a good idea, but I'm sure people will do it).

This all sounds great, but seems more like additional to the LNURL flow.

If you decouple it from LNURL flow then you somehow have to determine who the recipient is, at least with LNURL support you already know who the recipient is.

v0l avatar Feb 07 '23 10:02 v0l