diff --git a/umad-04-lnurlp-response.md b/umad-04-lnurlp-response.md index 05cd756..1892dc8 100644 --- a/umad-04-lnurlp-response.md +++ b/umad-04-lnurlp-response.md @@ -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 @@ -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 @@ -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. @@ -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 + }, } ``` @@ -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 + }, }, ``` @@ -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 + }, } ``` diff --git a/umad-05-payreq-request.md b/umad-05-payreq-request.md index 5e42088..faac036 100644 --- a/umad-05-payreq-request.md +++ b/umad-05-payreq-request.md @@ -22,11 +22,14 @@ The body of the request is a JSON object with the following fields: // See LUD-18 for details. }, - // 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": { @@ -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` diff --git a/umad-06-payreq-response.md b/umad-06-payreq-response.md index f73e602..b6e34e9 100644 --- a/umad-06-payreq-response.md +++ b/umad-06-payreq-response.md @@ -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` @@ -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,