-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add proposal for currencies on payRequest
- Loading branch information
Showing
1 changed file
with
202 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |