Skip to content

Add proposal for currencies on payRequest#251

Open
lsunsi wants to merge 13 commits intolnurl:ludsfrom
bipa-app:add-lud-currencies
Open

Add proposal for currencies on payRequest#251
lsunsi wants to merge 13 commits intolnurl:ludsfrom
bipa-app:add-lud-currencies

Conversation

@lsunsi
Copy link
Copy Markdown

@lsunsi lsunsi commented Dec 1, 2023

Abstract

This PR proposes an extension to the payRequest specification to allow for denomination of amount in different currencies and negotiating a conversion to this currency. It aims to be backwards compatible while taking a further step on providing lightning wallets and services a common language about exchanges and remittances on foreign currencies.

The PR includes what I expect to be a detailed description with some examples of the proposed extension. It also includes some references to related works that can be competing or complementary proposals to this one. This proposal is inspired by those references.

Implementations

I've been discussing this proposal inside @bipa-app currently and I aim to implement the most recent version of it and testing with partners before merging this PR.

EDIT:

Current proposal implemented at lsunsi@bipa.app if anyone wants to check it out.

@lsunsi lsunsi force-pushed the add-lud-currencies branch 2 times, most recently from 6fceacf to 35ceb75 Compare December 1, 2023 14:25
@lsunsi lsunsi marked this pull request as ready for review December 1, 2023 17:00
@jklein24
Copy link
Copy Markdown

jklein24 commented Dec 2, 2023

Nice :-D. As discussed on telegram, it would be great to align this with the existing LUD-21 proposal (and UMA) where possible. UMA actually already extended that proposal in a few of the ways you've done here:

  1. Using multiple currencies in the first response.
  2. Locking in the exchange rate implicitly via invoice expiration time (see https://arc.net/l/quote/nbilkwwa)
  3. Including displayDecimals in the Currency object (would be nice to align on naming).

A few super simple changes to make these more consistent:

  1. Change the name and semantics of price to match multiplier in the existing spec
  2. Use displayDecimals instead of decimals
  3. Use an array instead of an object for currencies and add the code field to the currency. The main reason for this is that it allows a sense of ordering for preferences by the payee. If I prefer BRL, but can also receive BTC, I'm able to set that preference via the ordering within the array.
  4. Optional: include the max and min sendable fields from the other proposal.

Also a very minor spelling nit - convertable -> convertible.

Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
@lsunsi lsunsi force-pushed the add-lud-currencies branch from 35ceb75 to 7b19a21 Compare December 3, 2023 12:53
@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Dec 3, 2023

@jklein24 Some comments on your

Change the name and semantics of price to match multiplier in the existing spec

I have no problems making this change, but would you explain to me why it's better? It feels to me like multiplier makes sense on the context of @ethanrose proposal, but in mine it can even hinder understanding (since you have to multiply it if you input the value in currency, but you have to divide by it in case you input in BTC). Again, I'm just trying to understand better, because readon 38000 in this field might be easier to understand than 2500000 (in case of USD,

Use displayDecimals instead of decimals

Changed!

Use an array instead of an object for currencies and add the code field to the currency. The main reason for this is that it allows a sense of ordering for preferences by the payee. If I prefer BRL, but can also receive BTC, I'm able to set that preference via the ordering within the array.

This is really interesting. Thinking about a little, how would you say the preference is BTC if BTC is not listed in the currencies? Moreover, maybe this could be achieved with a separate preferredCurrency field or something like that? I really like this idea, just thinking of the best way to achieve it.

Optional: include the max and min sendable fields from the other proposal.

I thought about that as well, but thought that maybe it could be an extension of the proposal, since the max/min already present kind of fulfills this role? What do you think? I'd love to hear what @ethanrose thinks as well.

Also a very minor spelling nit - convertable -> convertible.

Not very minor, cmon, I'd hate to merge a spelling mistake into this repo haha thanks! fixed

@lsunsi lsunsi force-pushed the add-lud-currencies branch from 7b19a21 to 56bbdd8 Compare December 3, 2023 13:01
@jklein24
Copy link
Copy Markdown

jklein24 commented Dec 4, 2023

This is really interesting. Thinking about a little, how would you say the preference is BTC if BTC is not listed in the currencies? Moreover, maybe this could be achieved with a separate preferredCurrency field or something like that? I really like this idea, just thinking of the best way to achieve it.

The order of currencies in the array dictates order of preferences between currencies. So if I prefer BRL, but can also get BTC, you'd have your currencies array contain BRL first and then BTC. You can check out the UMA protocol to see the full structure, but essentially it would look like:

"currencies": [
    {
      "code": "BRL",
      "name": "Brazilian Real",
      "symbol": "R$",
      "minSendable": 1,
      "maxSendable": 1_000_000,
      // Estimated millisats per "unit" (eg. 1 cent in USD)
      "multiplier": 489,
      // Number of digits after the decimal point for display on the sender side. For example,
      // in USD, by convention, there are 2 digits for cents - $5.95. in this case, `displayDecimals`
      // would be 2. Note that the multiplier is still always in the smallest unit (cents). This field
      // is only for display purposes. The sender should assume zero if this field is omitted, unless
      // they know the proper display format of the target currency.
      "displayDecimals": 2,
    },
    {
      "code": "SAT",
      "name": "Satoshis",
      "symbol": "sat",
      "minSendable": 1,
      "maxSendable": 10_000_000,
      "multiplier": 1_000, // estimated millisats per "unit" (eg. 1 cent in USD)
      "displayDecimals": 0,
    },
  ],

@jklein24
Copy link
Copy Markdown

jklein24 commented Dec 4, 2023

I have no problems making this change, but would you explain to me why it's better? It feels to me like multiplier makes sense on the context of @ethanrose proposal, but in mine it can even hinder understanding (since you have to multiply it if you input the value in currency, but you have to divide by it in case you input in BTC). Again, I'm just trying to understand better, because reading 38000 in this field might be easier to understand than 2500000 (in case of USD,

Would love @ethanrose's take on this too. Tbh I wasn't thinking it was better for some technical reason, but more just because so many entities are already live with the multiplier name, so staying consistent would be nice. It sounds like maybe I'm still missing some difference between price and multiplier though (besides just the exponent). I wasn't thinking you'd need to change whether you multiply or divide based on the input currency, but rather that it's just always the multipler from your input currency to mSats. What am I missing there?

@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Dec 4, 2023

@jklein24 Maybe I am the one missing something!

Let me try to explain.
Considering the price is 200000 and multiplier is 500000, consider this example.

First example

The user inputs that it wants to send 1 BRL to the other side.
The request equivalent of this input would be GET bipa.app/callback?amount=1BRL&convert=BRL.

If the WALLET wants to preview (before calling the callback) what the amount of the invoice would be, what should it do?
If it has a price, it needs to approximate amount = 1 / price * 1e3 in millisatoshis.
If it has a multiplier, it needs to approximate amount = multiplier * 1 in millisatoshis.

This is the use-case supported by @ethanrose PR and it looks nicer having the multiplier in this case.

Second example

The user inputs that it wants to send 500 sats as BRL to the other side.
The request equivalent of this inputGET bipa.app/callback?amount=500000&convert=BRL

If the WALLET wants to preview (before calling the callback) what the amount reaching the destination, what should it do?
If it has a price, it needs to approximate amount = 500 / 1e8 * price in BRL.
If it has a multiplier, it needs to approximate amount = 500 * 1e3 / multiplier in BRL.

Conclusion

In the second case you use the multiplier as the denominator, which I think can be confusing this it's called "multiplier".
Both cases work of course, because price and multiplier are equivalent, my only question is about the name "multiplier" being confusing, but maybe I'm the only one getting confused haha.

Does these examples help in any way clear up the confusion?
Also, since you first proposed the multiplier field, maybe you want to weight in on what do you prefer for this proposal too @fiatjaf

@jklein24
Copy link
Copy Markdown

jklein24 commented Dec 4, 2023

@jklein24 Maybe I am the one missing something!

Let me try to explain. Considering the price is 200000 and multiplier is 500000, consider this example.

First example

The user inputs that it wants to send 1 BRL to the other side. The request equivalent of this input would be GET bipa.app/callback?amount=1BRL&convert=BRL.

If the WALLET wants to preview (before calling the callback) what the amount of the invoice would be, what should it do? If it has a price, it needs to approximate amount = 1 / price * 1e3 in millisatoshis. If it has a multiplier, it needs to approximate amount = multiplier * 1 in millisatoshis.

This is the use-case supported by @ethanrose PR and it looks nicer having the multiplier in this case.

Second example

The user inputs that it wants to send 500 sats as BRL to the other side. The request equivalent of this inputGET bipa.app/callback?amount=500000&convert=BRL

If the WALLET wants to preview (before calling the callback) what the amount reaching the destination, what should it do? If it has a price, it needs to approximate amount = 500 / 1e8 * price in BRL. If it has a multiplier, it needs to approximate amount = 500 * 1e3 / multiplier in BRL.

Conclusion

In the second case you use the multiplier as the denominator, which I think can be confusing this it's called "multiplier". Both cases work of course, because price and multiplier are equivalent, my only question is about the name "multiplier" being confusing, but maybe I'm the only one getting confused haha.

Does these examples help in any way clear up the confusion? Also, since you first proposed the multiplier field, maybe you want to weight in on what do you prefer for this proposal too @fiatjaf

Thanks for the writeup - that makes things much more clear and concrete. I guess the thing is that in your two scenarios, you're asking 2 different questions:

  • Scenario 1: If the WALLET wants to preview (before calling the callback) what the amount of the invoice would be
  • Scenario 2: If the WALLET wants to preview (before calling the callback) what the amount reaching the destination

To me, it makes sense that these different questions would require some different math. In fact for the first scenario, the easiest thing to do would just be to decode the invoice you get back in the pr field, rather than doing the conversion again out-of-band.

@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Dec 5, 2023

@jklein24 I just updated the proposal considering some of the discussed feedback. 55384ff Care to take a look?

Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
Comment thread 21.md Outdated
@lsunsi lsunsi force-pushed the add-lud-currencies branch 2 times, most recently from 408c732 to 801f196 Compare December 6, 2023 17:14
@lsunsi lsunsi force-pushed the add-lud-currencies branch from 801f196 to 2d37cec Compare December 6, 2023 17:15
@lsunsi lsunsi force-pushed the add-lud-currencies branch from f670c07 to f70a0d6 Compare December 28, 2023 12:15
@jaonoctus
Copy link
Copy Markdown

jaonoctus commented Dec 30, 2023

@lsunsi noice! I wrote a similar interface ¹ to do the samething 😂 that I showed to @lorenzolfm

1

@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Dec 30, 2023

@lsunsi noice! I wrote a similar interface to do the samething 😂 that I showed to @lorenzolfm

Yeah my theory is that he is incepting us with his ideas at the same time!

@jklein24
Copy link
Copy Markdown

FYI this is now live in UMA v1 and VASPs are rolling it out. Xapo, Ripio, and Bitnob are upgraded and several more are on the way.

jklein24 added a commit to jklein24/nips that referenced this pull request Jul 10, 2024
This will help serve use cases like e-cash wallets, bolt-12 offers which allow for other currency denominations, and [LUD-21](lnurl/luds#251 wallet providers like UMA VASPs.
@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Sep 21, 2024

I'd like to point out that besides being part of UMA v1 like @jklein24 , we have a working implementation at bipa, it's a shame this spec doesn't get more attention from maintainers. Maybe @hsjoberg or @fiatjaf could share some thoughts here!

@lsunsi lsunsi force-pushed the add-lud-currencies branch from be2081c to 2446ff5 Compare March 11, 2025 15:00
@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Mar 11, 2025

@lorenzolfm @jaonoctus Updated LUD to 22

@dni
Copy link
Copy Markdown
Collaborator

dni commented Apr 14, 2025

👀

@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Apr 14, 2025

@dni 👀

@dni dni self-assigned this Apr 23, 2025
@dni
Copy link
Copy Markdown
Collaborator

dni commented May 11, 2025

any interest in this proposal left? :D should we roll it up again?

@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented May 11, 2025

@dni definitely! I even have a functional demo of receiving through it since back when hahaha.

We should definitely roll it up, but one question, what does rolling it up mean?

@jklein24
Copy link
Copy Markdown

This is also now live with Bringin in Europe in addition to being used for UMA (Nubank, Bitso, Ripio, Coins, Xapo, Zerohash, etc.) and by Bipa as @lsunsi noted above.

@dni dni requested review from andrerfneves, dni and hsjoberg July 1, 2025 07:34
@lsunsi
Copy link
Copy Markdown
Author

lsunsi commented Jul 1, 2025

@jklein24 Bringin is actually pretty off-spec, right? Judging from https://api.bringin.xyz/.well-known/lnurlp/prashanth

@jklein24
Copy link
Copy Markdown

jklein24 commented Jul 2, 2025

@jklein24 Bringin is actually pretty off-spec, right? Judging from https://api.bringin.xyz/.well-known/lnurlp/prashanth

Good catch! I hadn't actually tested their implementation! Will flag to them on twitter :-)

mandelmonkey added a commit to mandelmonkey/luds that referenced this pull request Mar 8, 2026
- Renumber spec from LUD-22 to LUD-23 (LUD-22 reserved for currency denomination PR lnurl#251)
- Switch wallet callback from POST+JSON to GET with query params to match existing LUD conventions
- Rename `metadata` field to `description` to avoid collision with LUD-06
- Update address validation regex to LUD-16 compliant character set ([a-z0-9\-_.+])

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mandelmonkey added a commit to mandelmonkey/luds that referenced this pull request Mar 8, 2026
- Renumber spec from LUD-22 to LUD-23 (LUD-22 reserved for currency denomination PR lnurl#251)
- Switch wallet callback from POST+JSON to GET with query params to match existing LUD conventions
- Rename `metadata` field to `description` to avoid collision with LUD-06
- Update address validation regex to LUD-16 compliant character set ([a-z0-9\-_.+])

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants