lnurl / luds

lnurl specifications
584 stars 138 forks source link

Add proposal for currencies on payRequest #251

Open lsunsi opened 8 months ago

lsunsi commented 8 months ago

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.

jklein24 commented 8 months ago

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.

lsunsi commented 8 months ago

@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

jklein24 commented 7 months ago

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 commented 7 months ago

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 commented 7 months ago

@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 commented 7 months ago

@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:

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 commented 7 months ago

@jklein24 I just updated the proposal considering some of the discussed feedback. https://github.com/lnurl/luds/pull/251/commits/55384ff0c35027f67d8305e4dab58d13d61e35a9 Care to take a look?

lsunsi commented 7 months ago

@jklein24 @jaonoctus @lorenzolfm @ethanrose

I just updated the proposal to include all discussed changes and complement the implementation with another field (source). The text was changed to be more succinct and to the point, while the examples were expanded to cover all cases.

I think this is the best version so far and by far. I'll implement the proposal on @bipa-app right now so we can showcase it and test.

lsunsi commented 7 months ago

@jklein24 @lorenzolfm @jaonoctus I hacked this webapp to showcase the LUD. It's using just the API described in this PR. https://lnurlp-live.vercel.app/lorenzolfm@bipa.app

jaonoctus commented 7 months ago

@lsunsi noice! I wrote a similar interface ยน to do the samething ๐Ÿ˜‚ that I showed to @lorenzolfm

1

lsunsi commented 7 months ago

@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 commented 3 months ago

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.