nips icon indicating copy to clipboard operation
nips copied to clipboard

Adding support for main bolt12 commands to NIP-47

Open 21M4TW opened this issue 7 months ago • 62 comments

This is a request to add support for Bolt12 to NWC, such that a Lightning client can use NWC to generate and use Bolt12 offers, fetch and pay Bolt12 invoices, thus benefiting from the improvements over Bolt11.

21M4TW avatar Jun 11 '25 03:06 21M4TW

@TheBlueMatt is correct when it comes to bolt12 invoices. They should be kept out completely and only deal with offers. That means paying an offer not bolt12 invoice and dropping the fetch_invoice method completely. I was working on a PR like this aswell: https://github.com/daywalker90/nips/pull/1

daywalker90 avatar Jun 16 '25 21:06 daywalker90

@TheBlueMatt is correct when it comes to bolt12 invoices. They should be kept out completely and only deal with offers. That means paying an offer not bolt12 invoice and dropping the fetch_invoice method completely. I was working on a PR like this aswell: daywalker90#1

Thanks! As I mentioned I am also fine with dropping the fetch_invoice method. It could be replaced by a modified pay_invoice command that supports offer strings and also payer_note, and the addition of a get_offer_info command that provides the main decoded offer information. I will modify the PR to reflect this.

21M4TW avatar Jun 17 '25 12:06 21M4TW

@TheBlueMatt is correct when it comes to bolt12 invoices. They should be kept out completely and only deal with offers. That means paying an offer not bolt12 invoice and dropping the fetch_invoice method completely. I was working on a PR like this aswell: daywalker90#1

Ok so I did remove the fetch_invoice command and made the changes required to make it work with bolt12. I have also added support for the offer issuer field, as well as other currencies when making and decoding offers. The offer_issuer, offer_id and payer_note fields are now part of the results when listing transactions, when applicable.

21M4TW avatar Jun 17 '25 14:06 21M4TW

@21M4TW is there any NWC Wallet Service implementation that supports any of this?

rolznz avatar Jul 28 '25 05:07 rolznz

@21M4TW is there any NWC Wallet Service implementation that supports any of this?

I've done a prototype in preparation for a spec PR with my own nip spec here: https://github.com/daywalker90/cln-nip47/pull/2

PR for the library i use in cln-nip47: https://github.com/daywalker90/nips/pull/1 My own spec PR: https://github.com/daywalker90/nostr/pull/1

daywalker90 avatar Jul 28 '25 14:07 daywalker90

@21M4TW is there any NWC Wallet Service implementation that supports any of this?

Sorry, I just noticed this question. I had this PR for nwcprovider (https://github.com/riccardobl/nwcprovider/pull/12) . I also have implemented the bolt12 functionalities in lnbits (https://github.com/lnbits/lnbits/pull/3092). I also wanted to help adding Bolt12 NWC support to Zeus (which uses getAlby for NWC). Zeus already supports bolt12, including enabling and disabling offers, through CLNRest. All these (except lnbits') are pretty much pending on the inclusion of Bolt12 into NIP47.

21M4TW avatar Aug 05 '25 15:08 21M4TW

Is there anything I can do to get this moving? Thanks!

21M4TW avatar Nov 12 '25 02:11 21M4TW

Looks good to me, sounds like there are 2+ implementations? If so I think we can merge.

staab avatar Nov 12 '25 16:11 staab

@reneaaron @rolznz any opinions on this?

staab avatar Nov 12 '25 17:11 staab

Imo i agree that we should remove enable/disable offer

daywalker90 avatar Nov 12 '25 17:11 daywalker90

Also I was considering extracting the decoding code from CLN and package it into a library so it can be used anywhere. It seemed to be a sticking point and I agree it would be better to do it locally instead of relying on a get_offer_info command.

21M4TW avatar Nov 14 '25 00:11 21M4TW

Also I was considering extracting the decoding code from CLN and package it into a library so it can be used anywhere. It seemed to be a sticking point and I agree it would be better to do it locally instead of relying on a get_offer_info command.

I am working on a bolt12 offer decode now and making very good progress. CLN's code was relying on way too many header and source files to make it tidy enough though, so I am writing a minimal library that should be much lighter. The nice part is that since we are only dealing with offers and not invoices, we don't even have to deal with Merkle trees and signatures for this, as offers are not signed.

21M4TW avatar Nov 18 '25 14:11 21M4TW

Just removed the get_offer_info as well.

21M4TW avatar Nov 18 '25 15:11 21M4TW

Also I was considering extracting the decoding code from CLN and package it into a library so it can be used anywhere. It seemed to be a sticking point and I agree it would be better to do it locally instead of relying on a get_offer_info command.

I am working on a bolt12 offer decode now and making very good progress. CLN's code was relying on way too many header and source files to make it tidy enough though, so I am writing a minimal library that should be much lighter. The nice part is that since we are only dealing with offers and not invoices, we don't even have to deal with Merkle trees and signatures for this, as offers are not signed.

So my Bolt12 offer decoding project is now in very good shape and available (https://github.com/21M4TW/b12od ). There are bindings for Rust, Python and WASM in addition to C. The code itself has no dependency and should be very portable. Hopefully it will help for Bolt12 support a bit.

21M4TW avatar Dec 02 '25 14:12 21M4TW

FWIW you should also be able to just use the LDK lightning crate to decode offers pretty trivially. From the Rust end you can expose bindings to C/whatever language you want and on compilation all the unused/non-BOLT12 stuff will get dropped and not included in the binary.

TheBlueMatt avatar Dec 02 '25 14:12 TheBlueMatt

This is ready for review. Thanks!

21M4TW avatar Dec 09 '25 14:12 21M4TW

Now that we've gotten this far.....

Should we actually support BOLT 12 natively or should we support BIP 353? For making and receiving payments, ISTM most of the time you just want a payment instruction, which means BIP 321, not BOLT 12.

TheBlueMatt avatar Dec 09 '25 17:12 TheBlueMatt

Specifically, I'd really like to better understand how wallets actually use the NWC stuff like listing invoices and such. I assume that most NWC clients really just want to generate and pay payment instructions. They don't care about BOLT 11/12, some crazy disable-invoice nonsense, or even listing transactions.

In a future with Ark, silent payments, etc shouldn't we be generating BIP 321 and basically just do BIP 321-over-nostr?

TheBlueMatt avatar Dec 09 '25 17:12 TheBlueMatt

Specifically, I'd really like to better understand how wallets actually use the NWC stuff like listing invoices and such. I assume that most NWC clients really just want to generate and pay payment instructions. They don't care about BOLT 11/12, some crazy disable-invoice nonsense, or even listing transactions.

I disagree, this is certainly not true for Zeus, that supports all these features, and more.

21M4TW avatar Dec 09 '25 18:12 21M4TW

Right, there's two highly distinct use-cases here - there's wallet frontend, which wants All Of The Things, and then there's wallet remote control (eg nostr client), which just wants create + pay, and everything else is extra.

Still, ignoring disable and list and history, Im not sure if a wallet fronted needs anything more than a "give me a BIP 321 payment instruction" instruction, rather than wanting BOLT 11/12/etc low-level stuff.

TheBlueMatt avatar Dec 09 '25 18:12 TheBlueMatt

Right, there's two highly distinct use-cases here - there's wallet frontend, which wants All Of The Things, and then there's wallet remote control (eg nostr client), which just wants create + pay, and everything else is extra.

Still, ignoring disable and list and history, Im not sure if a wallet fronted needs anything more than a "give me a BIP 321 payment instruction" instruction, rather than wanting BOLT 11/12/etc low-level stuff.

Ok, but the goal of this PR was not to completely replace the existing NWC API, it was to extend it minimally, while remaining backward compatible, to enable support for Bolt12 as well...

21M4TW avatar Dec 09 '25 18:12 21M4TW

Right, my point here, though, is that we could almost entirely take exactly the changes here, and s/bolt12/bip321/, remove a few bolt-12 specific fields, and it would still be the same. Obviously for practical reasons we can't drop all the old bolt 11 stuff, but if the end goal is "BIP 321" then IMO we should just do that?

TheBlueMatt avatar Dec 09 '25 18:12 TheBlueMatt

Right, my point here, though, is that we could almost entirely take exactly the changes here, and s/bolt12/bip321/, remove a few bolt-12 specific fields, and it would still be the same. Obviously for practical reasons we can't drop all the old bolt 11 stuff, but if the end goal is "BIP 321" then IMO we should just do that?

I am not super familiar with BIP 321. Let's say I want to create a USD-denominated single use offer with BIP 321, and the payer wanted to provide extra tip and add a note, how would the sequence would look like with BIP 321?

21M4TW avatar Dec 09 '25 19:12 21M4TW

BIP 321 is just bitcoin:instructions, so it depends a bit, but I think every type of Bitcoin payment instruction I'm aware of allows for additional funds to be included. The operative flag for requesting a BIP 321 from a wallet is mostly "is this single-use", so if its a single-use request, you'd presumably get something like bitcoin:onchain?lightning=bolt11&amount=requested-onchain-amount and the paying wallet can select to use bolt11 or on-chain (if its included at all, it can be elided) and can always pay more than what was requested.

If the BIP 321 is not single-use, the wallet should likely return something more like bitcoin:onchain?lno=bolt12offer&sp=silentpaymentaddr. Again, all of these payment instruction formats allow for paying any arbitrary amount (though of course the wallet may not want to include on-chain stuff if it doesn't want to)

TheBlueMatt avatar Dec 09 '25 19:12 TheBlueMatt

The nice thing about using BIP 321 is everyone already has logic for it, so doubling down on that as the "machine-readable bitcoin payment instruction format" seems like the right direction.

TheBlueMatt avatar Dec 09 '25 19:12 TheBlueMatt

BIP 321 is just bitcoin:instructions, so it depends a bit, but I think every type of Bitcoin payment instruction I'm aware of allows for additional funds to be included. The operative flag for requesting a BIP 321 from a wallet is mostly "is this single-use", so if its a single-use request, you'd presumably get something like bitcoin:onchain?lightning=bolt11&amount=requested-onchain-amount and the paying wallet can select to use bolt11 or on-chain (if its included at all, it can be elided) and can always pay more than what was requested.

If the BIP 321 is not single-use, the wallet should likely return something more like bitcoin:onchain?lno=bolt12offer&sp=silentpaymentaddr. Again, all of these payment instruction formats allow for paying any arbitrary amount (though of course the wallet may not want to include on-chain stuff if it doesn't want to)

Ok thanks, this is when you receive a payment request, but my question was more about an equivalent to the "make_offer" method with BIP 321. Would BIP 321 only replace the "pay*" methods?

21M4TW avatar Dec 09 '25 19:12 21M4TW

I was imagining, basically, build_receive_instructions(single_use, min_amount, description) (where min_amount is only allowed if single_use is set) which returns a BIP 321 and pay_for_instructions(bip321, amount_incl_tip, payer_note_if_supported, proof_of_payment_metadata).

TheBlueMatt avatar Dec 09 '25 19:12 TheBlueMatt

I was imagining, basically, build_receive_instructions(single_use, min_amount, description) (where min_amount is only allowed if single_use is set) which returns a BIP 321 and pay_for_instructions(bip321, amount_incl_tip, payer_note_if_supported, proof_of_payment_metadata).

Ok thanks. Why would min_amount be only allowed if single_use is set though? What about expiry, issuer, and currrency options? Wouldn't be also useful to be able to select between bolt11/bolt12 (or silent payment, payjoin) since bolt12 (and silent payment or payjoin) support is not quite universal right now?

21M4TW avatar Dec 09 '25 19:12 21M4TW

Why would min_amount be only allowed if single_use is set though?

I'm not aware of a bitcoin payment instruction format that has an amount field that is intended as multi-use/donations. the amount field in BIP 321 URIs, plus the amount fields in BOLT 11 and BOLT 12 are really intended/have UX flows for "this is the amount for the payment" (and someone can add a tip if they want, but its really a payment for an amount). When no amount is provided, the UX flow changes to something more like a donation. Those UX flows are fairly distinct and the use-cases for a single-use invoice and multi-use invoice are different and I think furthering that makes sense.

What about expiry, issuer, and currrency options?

We can certainly include these fields, and expiry can map to both BOLT 11 and 12, but not all forms of Bitcoin payment instructions have expiries (do Cashu payment instructions? Ark? Spark? certainly not on-chain...). FWIW, BOLT 12 currency conversion is really a thing mostly for recurrence which is currently not supported.

Wouldn't be also useful to be able to select between bolt11/bolt12 (or silent payment, payjoin) since bolt12 (and silent payment or payjoin) support is not quite universal right now?

That's the nice thing about BIP 321 - you just shove everything you support in there and the payer picks whatever they also support. IMO the NWC client shouldn't concern itself with this (unless it has some specific requirement, eg its doing a zap so can only support BOLT 12 or whatever), and the wallet should be providing everything it supports.

TheBlueMatt avatar Dec 09 '25 20:12 TheBlueMatt

Why would min_amount be only allowed if single_use is set though?

I'm not aware of a bitcoin payment instruction format that has an amount field that is intended as multi-use/donations. the amount field in BIP 321 URIs, plus the amount fields in BOLT 11 and BOLT 12 are really intended/have UX flows for "this is the amount for the payment" (and someone can add a tip if they want, but its really a payment for an amount). When no amount is provided, the UX flow changes to something more like a donation. Those UX flows are fairly distinct and the use-cases for a single-use invoice and multi-use invoice are different and I think furthering that makes sense.

Bolt 12 offers do support multi-use with minimum amounts?

What about expiry, issuer, and currrency options?

We can certainly include these fields, and expiry can map to both BOLT 11 and 12, but not all forms of Bitcoin payment instructions have expiries (do Cashu payment instructions? Ark? Spark? certainly not on-chain...). FWIW, BOLT 12 currency conversion is really a thing mostly for recurrence which is currently not supported.

Wouldn't be also useful to be able to select between bolt11/bolt12 (or silent payment, payjoin) since bolt12 (and silent payment or payjoin) support is not quite universal right now?

That's the nice thing about BIP 321 - you just shove everything you support in there and the payer picks whatever they also support. IMO the NWC client shouldn't concern itself with this (unless it has some specific requirement, eg its doing a zap so can only support BOLT 12 or whatever), and the wallet should be providing everything it supports.

I can see how this can be useful in some situations to maximize the chance of support, but in other situations it is preferable to only include some of them. For example, amongst other things Bolt 12 addresses some privacy shortcomings of Bolt 11. If a Bolt 11 invoice is generated every time along with a Bolt 12 offer, it negates the benefits of Bolt 12... Also if a new onchain address was to be generated every single time, it could make wallet recovery problematic. I think one would want to be able to select which type of address should be generated. There could be a default that could be "any supported", but it should be selectable...

21M4TW avatar Dec 09 '25 20:12 21M4TW