Skip to content

Commit

Permalink
Add proposal for currencies on payRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
lsunsi committed Dec 3, 2023
1 parent 42c6e32 commit 7b19a21
Showing 1 changed file with 202 additions and 0 deletions.
202 changes: 202 additions & 0 deletions 21.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
LUD-21: Currencies in `payRequest`.
=================================

`author: lsunsi`
`author: luizParreira`
`author: lorenzolfm`

---

## Support for LNURL-pay currencies

This document describes an extension to the [payRequest](https://github.com/lnurl/luds/blob/luds/06.md) base specification that allows the `WALLET` to send money to a `SERVICE` denominating the amount in a different currencies. The main use cases supported by this extension are sending sats denominating the value in a currency with an exchange rate defined by the `SERVICE` and optionally converting the value into the currency upon payment of the invoice. Widespread implementation of this extension by broker-enabled wallets would enable a remittance-like experience for users around the globe.

The extension is opt-in and backwards compatible. Further, a supporting `WALLET` can always tell if a `SERVICE` is also supporting beforehand so the communication is never ambiguous.

### Wallet-side first request

The first request is unchanged from the base specification.

### Service-side first response

`SERVICE` must alter its JSON response to the first callback to include a `currencies` field, as follows:

```typescript
type BaseResponse = {
tag: "payRequest",
metadata: string,
callback: string,
maxSendable: number,
minSendable: number
};

type Currency = {
name: string, // Name of the currency. E.g.: Reais
symbol: string, // Symbol of the currency. E.g.: R$
decimals: number, // Number of decimal places. E.g.: 2
price: number, // Number of currency per BTC. E.g.: 185000
convertible?: bool // Whether the currency can be converted into.
}

type ExtendedResponse = BaseResponse & {
currencies: { [key: string]: Currency }
}
```
```diff
{
"tag": "payRequest",
"metadata": '[["text/plain","$kenu ⚡ bipa.app"]]',
"callback": "https://api.bipa.app/ln/request/invoice/kenu",
"maxSendable": 1000000000,
"minSendable": 1000,
+ "currencies": {
+ "BRL": {
+ "name": "Real",
+ "symbol": "R$",
+ "decimals": 2,
+ "price": 185000,
+ "convertible": true
+ }
+ }
}
```
- The inclusion of the `currencies` field implies the support of this extension
- The inclusion of a `currency` implies it can be used for denomination of an amount
- The inclusion of a `convertible currency` implies the `SERVICE` can quote and guarantee a price for given currency
- The price returned on this response is not guaranteed by the `SERVICE` and is subject to change
- The `key` of a `currency` will be used as an identifier for the next request
- The `key` and other information must be according to [ISO-4217](https://en.wikipedia.org/wiki/ISO_4217) for currencies that are included in it
### Wallet-side second request
Upon seeing the `currencies` field on the response from `SERVICE`, a `WALLET` may show the user it has the option of denominating the amount in different currencies or even of sending sats to be credited as the different currency for the receiver.
After gathering the input from the user, it must be encoded into the callback as query parameters as follows.
The most general case, when the user is denominating the amount in `CURRENCY1`, but wants it to be converted to `CURRENCY2` on `SERVICE`.
`CURRENCY1` and `CURRENCY2` can be the same, which would the common use case for remittances.
```diff
- <callback><?|&>amount=<milliSatoshi>
+ <callback><?|&>amount=<AMOUNT><CURRENCY1>&convert=<CURRENCY2>

In a more particular case, when user is denominating the amount in `CURRENCY`, but does not want to convert it or the service does not support conversion.
```diff
- <callback><?|&>amount=<milliSatoshi>
+ <callback><?|&>amount=<AMOUNT><CURRENCY>
```

In another particular case, when the user is denominating the amount in millisatoshi, but wants it to be converted to `CURRENCY` on `SERVICE`.
```diff
- <callback><?|&>amount=<milliSatoshi>
+ <callback><?|&>amount=<milliSatoshi>&convert=<CURRENCY>
```

### Service-side second response

Upon receiving a currency denominated request from `WALLET`, the `SERVICE` must return a lightning invoice with an amount matching the converted rate from currency. The rate does not need to be the same as provided on the first response.

If the `WALLET` requested an actual conversion, the `SERVICE` must provide an additional field alongside the invoice informing the guaranteed `converted` amount that will be credit to the receiver upon payment. The `converted` amount, and therefore the conversion rate, must be guaranteed by the `SERVICE` for as long as the lightning invoice is not expired.

```typescript
type BaseResponse = {
pr: string,
routes: [],
}
type ExtendedResponse = BaseResponse & {
converted?: number, // Present if and only if `convert` was requested.
}
```

```diff
{
"pr": "lnbc1230n1pjknkl...ju36m3lyytlwv42fee8gpt6vd2v",
"routes": [],
+ "converted": 123
}
```

### Examples
These examples show all the possible uses of this extension by a supporting `WALLET` and `SERVICE`.

#### Payer queries the payee service

```json
// GET bipa.app/.well-known/lnurlp/lsunsi
{
"tag": "payRequest",
"callback": "bipa.app/callback",
"metadata": "...",
"minSendable": 1000,
"maxSendable": 1000000,
"currencies": {
"BRL": {
"name": "Reais",
"symbol": "R$",
"decimals": 2,
"price": 185000,
"convertible": true
},
"USDT": {
"name": "Tether",
"symbol": "₮",
"decimals": 2,
"price": 38000
}
}
}
```

#### Payer sends 538 sats
```json
// GET bipa.app/callback?amount=538000
{
"pr": "(invoice of 538 sats)"
}
```
#### Payer sends 538 sats as BRL
```json
// GET bipa.app/callback?amount=538000&convert=BRL
{
"pr": "(invoice of 538 sats)",
"converted": 1
}
```
#### Payer sends 1 BRL worth of BTC as BTC
```json
// GET bipa.app/callback?amount=1BRL
{
"pr": "(invoice of 538 sats)"
}
```
#### Payer sends 1 BRL worth of BTC as BRL
```json
// GET bipa.app/callback?amount=1BRL&convert=BRL
{
"pr": "(invoice of 538 sats)",
"converted": 1
}
```
#### Payer sends 1 BRL worth of BTC as USDT
```json
// GET bipa.app/callback?amount=1BRL&convert=USDT
{
"pr": "(invoice of 538 sats)",
"converted": 0.2
}
```
#### Payer sends 1 BRL worth of BTC to unsupported
```json
// GET bipa.app/callback?amount=1BRL
// ERROR, because 1BRL is not a number and can't
// be interpret as milisatoshis by the unsupported service.
```

### Related work

- Some of the ideas included in this PR were taken from the implementation and discussion on [this PR](https://github.com/lnurl/luds/pull/207). Most precisely, @ethanrose (author) and @callebtc (contributor).

- Some early ideas for this including some other aspects of it were hashed out (but not pull-requested) in this [earlier draft](https://github.com/bipa-app/lnurl-rfc/pull/1) too. Thanks @luizParreira (author), @joosjager (contributor), @za-kk (contributor).

0 comments on commit 7b19a21

Please sign in to comment.