Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UMA 1.0] Migrate the currency conversion mechanics to the new LUD-21 spec #19

Merged
merged 3 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions umad-04-lnurlp-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The response to the LNURLP request is an extension of LNURL's [LUD-06](https://github.com/lnurl/luds/blob/luds/06.md).
It also utilizes the payer data spec as described in [LUD-18](https://github.com/lnurl/luds/blob/luds/18.md) and a
slightly modified version of the local currency spec proposed in [LUD-21](https://github.com/lnurl/luds/pull/207).
slightly modified version of the local currency spec proposed in [LUD-21](https://github.com/lnurl/luds/pull/251).
The full structure of the LNURLP response is:

```raw
Expand All @@ -20,8 +20,6 @@ The full structure of the LNURLP response is:
"code": string, // eg. "PHP",
"name": string, // eg. "Philippine Pesos",
"symbol": string, // eg. "₱",
"minSendable": number,
"maxSendable": number,
// Estimated millisats per "unit" (eg. 1 cent in USD). A double-precision floating point number.
"multiplier": number,
// Number of digits after the decimal point for display on the sender side, and to add clarity around what the
Expand All @@ -31,6 +29,14 @@ The full structure of the LNURLP response is:
// means. For example, if the currency is "BTC" and the multiplier is 1000, really we're exchanging in SATs, so
// `decimals` would be 8.
"decimals": number,
// The inclusion of a convertible field implies the receiving VASP can quote and guarantee a price for a given
// currency.
"convertible": {
// Minimum and maximium amounts the receiver is willing/able to convert to this currency in the smallest unit of
// the currency. For example, if the currency is USD, the smallest unit is cents.
"min": number, // 64-bit integer (long/int64)
"max": number, // 64-bit integer (long/int64)
}
},
],
// Required data about the payer. See LUD-18 for details.
Expand Down Expand Up @@ -67,10 +73,12 @@ Here are some additional examples of the `currencies` field to illustrate how th
"code": "USD",
"name": "US Dollars",
"symbol": "$",
"minSendable": 1,
"maxSendable": 1000000, // 1M
"multiplier": 23400,
"decimals": 2
"decimals": 2,
"convertible": {
"min": 1,
"max": 1000000
},
}
```

Expand All @@ -85,10 +93,12 @@ invoice for 13,923,000 millisats (595 * 23,400) plus applicable conversion fees.
"code": "BTC",
"name": "Bitcoin",
"symbol": "₿",
"minSendable": 1,
"maxSendable": 100000000, // 100M
"multiplier": 1000,
"decimals": 8
"decimals": 8,
"convertible": {
"min": 1,
"max": 100000000
},
},
```

Expand All @@ -103,10 +113,12 @@ Lightning invoice for 10,000 millisats (10 * 1,000) plus applicable conversion f
"code": "USDC",
"name": "USDC",
"symbol": "USDC",
"minSendable": 1000000, // 1M
"maxSendable": 1000000000000, // 1T
"multiplier": 2.34,
"decimals": 6
"decimals": 6,
"convertible": {
"min": 1000000, // 1M
"max": 1000000000000 // 1T
},
}
```

Expand Down
56 changes: 51 additions & 5 deletions umad-05-payreq-request.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ The body of the request is a JSON object with the following fields:
// See LUD-18 for details.
<json payerdata>
},
// An int64 - This is the amount in the smallest unit of the specified receiving currency (eg. cents for USD).
"amount": number,
// The currency code of the receiving currency (eg. "USD"). This must be one of the currencies returned in the
// LNURLP response.
"currency": string,
// An amount (int64) followed optionally by a "." and the sending currency code. For example: "100.USD" would send
// an amount equivalent to $1 USD. Note that the amount is specified in the smallest unit of the specified
// currency (eg. cents for USD). Omitting the currency code will default to specifying the amount in millisats.
"amount": string,
// The currency code of the receiving currency (eg. "USD") to which the receiving VASP will convert into when the
// transaction completes. This must be one of the currencies returned in the LNURLP response, and it must have
// been a currency with a "convertible" field.
"convert": string,
// The UMA protocol version that will be used for this transaction. See [UMAD-08](/umad-08-versioning.md).
"umaVersion": "1.0",
"payeeData": {
Expand All @@ -38,6 +41,49 @@ The body of the request is a JSON object with the following fields:
}
```

## Currency field examples

The currency spec here is as specified by [LUD-21](https://github.com/lnurl/luds/pull/251), with the caveat that UMA's
payreq request uses a POST and JSON body instead of a GET request with query parameters. Please see the spec for more
examples. As it pertains to UMA, there are two main UX cases to accommodate:

1. **The sender wants to send exactly a certain amount in the receiving currency.**

For example, if a user in the US is paying for some goods or services in Europe, they might need to send *exactly* some
amount in euros. In this case, the sender would enter the amount in the receiving currency. Fields specified by the
sending VASP in the payreq request would look like:

```json
{
"amount": "100.EUR",
"convert": "EUR",
// ... other fields
}
```

This informs the receiving VASP to construct a Lightning invoice which will be converted to 100 euros for their user. This
should include the conversion rate and any fees in the invoice itself to ensure that the receiver gets exactly 100 euros.

1. **The sender wants to send exactly a certain amount in their own currency.**

For example, the sending user has $100 USD and they want to send exactly that amount to their family in Mexico. They would
enter the amount in their own currency. However, their own sending VASP is responsible for the onramp from their sending
currency to bitcoin. The sending VASP can guarantee that conversion rate to their user out-of-band of the UMA protocol.
For example, maybe they've agreed that for $100, they will give the user exactly 191,000 satoshis. Fields specified by
the sending VASP in the payreq request would then look like:

```json
{
"amount": "191000000", // 191,000,000 millisats, so the currency code is omitted.
"convert": "MXN",
// ... other fields
}
```

This informs the receiving VASP to construct a Lightning invoice for exactly 191,000 satoshis and to give their receiving
user the equivalent in Mexican pesos according to their agreed-upon conversion rate. This allows the sender to lock in
the amount they want to send in their own currency.

## Payee Data

The `payeeData` field is optional and is used to request additional information about the receiving user. The `mandatory`
Expand Down
9 changes: 6 additions & 3 deletions umad-06-payreq-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ The full structure of the LNURLP response is:
// the utxos used to complete the transaction. See [UMAD-07](/umad-07-post-tx-hooks.md).
"utxoCallback": string
},
"paymentInfo": {
"converted": {
// The amount that the receiver will receive in the receiving currency not including fees. The amount is specified
// in the smallest unit of the currency (eg. cents for USD).
"amount": number, // int64
// The currency code of the receiving currency (eg. "USD"). This should match the requested currency in the payreq
// request.
"currencyCode": string,
// Millisats per "unit" of the receiving currency (eg. 1 cent in USD). A double-precision floating point number.
// In this context, this is just for convenience. The conversion rate is also baked into the invoice amount itself.
// `invoice amount = amount * multiplier + exchangeFeesMillisatoshi`
// `invoice amount = amount * multiplier + fee`
"multiplier": number,
// Number of digits after the decimal point for the receiving currency. For example, in USD, by convention, there are
// 2 digits for cents - $5.95. In this case, `decimals` would be 2. This should align with the currency's `decimals`
Expand All @@ -34,7 +37,7 @@ The full structure of the LNURLP response is:
"decimals": number,
// The fees charged (in millisats) by the receiving VASP to convert to the target currency.
// This is separate from the multiplier rate.
"exchangeFeesMillisatoshi": number
"fee": number
},
"payeeData": {
"name": string,
Expand Down
Loading