From b853913904653e07bba0d6bac9e088fac5afa64d Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Mon, 18 Dec 2023 17:05:39 -0800 Subject: [PATCH 1/2] Add more details to the description of the decimals field with examples. The decimals field actually isn't only useful for display. It's useful to disambiguate what the "smallest unit" of the destination currency actually is. I'm including a few examples for clarity. --- umad-04-lnurlp-response.md | 80 +++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/umad-04-lnurlp-response.md b/umad-04-lnurlp-response.md index 9ef08bc..b06b127 100644 --- a/umad-04-lnurlp-response.md +++ b/umad-04-lnurlp-response.md @@ -22,22 +22,14 @@ The full structure of the LNURLP response is: "symbol": string, // eg. "₱", "minSendable": number, "maxSendable": number, - // Estimated millisats per "unit" (eg. 1 cent in USD) + // 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. For example, - // in USD, by convention, there are 2 digits for cents - $5.95. in this case, `decimals` - // 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. - "decimals": number, - }, - { - "code": string, // eg. "BTC", - "name": string, // eg. "Bitcoin", - "symbol": string, // eg. "₿", - "minSendable": number, - "maxSendable": number, - "multiplier": 1 // estimated millisats per "unit" (eg. 1 cent in USD) + // Number of digits after the decimal point for display on the sender side, and to add clarity around what the + // "smallest unit" of the currency is. For example, in USD, by convention, there are 2 digits for cents - $5.95. + // In this case, `decimals` would be 2. Note that the multiplier is still always in the smallest unit (cents). + // In addition to display purposes, this field can be used to resolve ambiguity in what the multiplier + // 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, }, ], @@ -65,3 +57,61 @@ The full structure of the LNURLP response is: The signature here is over `sha256_hash( (eg. "$bob@vasp2.com") + nonce + timestamp)`. The receiving VASPs `signingPubKey` can be used by the sending VASP to verify the signature as described in [UMAD-02](/umad-02-keys-and-authentication.md). + +## Currency examples + +Here are some additional examples of the `currencies` field to illustrate how the `multiplier` and `decimals` fields work. + +```json +{ + "code": "USD", + "name": "US Dollars", + "symbol": "$", + "minSendable": 1, + "maxSendable": 1000000, // 1M + "multiplier": 23400, + "decimals": 2 +} +``` + +In this case, the `decimals` field is 2, indicating that the smallest unit of the currency is cents (one hundredth of a dollar). +The `multiplier` field is 23400, indicating that there are 23,400 millisats per USD cent. This struct also indicates that +the receiving user can receive between 1 cent and $10,000 USD. If a sender wanted to send $5.95 USD, they would specify +`amount: 595, currency: USD` in their [payreq request](/umad-05-payreq-request.md), which should in turn create a Lightning +invoice for 13,923,000 millisats (595 * 23,400) plus applicable conversion fees. + +```json +{ + "code": "BTC", + "name": "Bitcoin", + "symbol": "₿", + "minSendable": 1, + "maxSendable": 100000000, // 100M + "multiplier": 1000, + "decimals": 8 +}, +``` + +In this case, the `decimals` field is 8, indicating that the smallest unit of the currency is SATs (one hundred millionth +of a BTC). The `multiplier` field is 1,000, indicating that there are 1,000 millisats per SAT. This struct also indicates +that the receiving user can receive between 1 SAT and 1 BTC. If a sender wanted to send 0.0000001 BTC (10 sats), they would +specify `amount: 10, currency: BTC` in their [payreq request](/umad-05-payreq-request.md), which should in turn create a +Lightning invoice for 10,000 millisats (10 * 1,000) plus applicable conversion fees. + +```json +{ + "code": "USDC", + "name": "USDC", + "symbol": "USDC", + "minSendable": 100000, // 100K + "maxSendable": 10000000000, // 10B + "multiplier": 2.34, + "decimals": 6 +} +``` + +In this case, the `decimals` field is 6, indicating that the smallest unit of the currency is one USDC / 10^6. +The `multiplier` field is 2.466, indicating that there are 2.466 millisats per USDC/10^6. This struct also indicates that +the receiving user can receive between 1 USDCent and 100,000,000 USDCents. If a sender wanted to send 5.95 USDC, they would +specify `amount: 5950000, currency: USDC` in their [payreq request](/umad-05-payreq-request.md), which should in turn create +a Lightning invoice for 14,677,700 millisats (5,950,000 * 2.466) plus applicable conversion fees. From a8fe012f545c1ad373ed1e70cb8fb7ce6d6c9bb3 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Mon, 18 Dec 2023 17:32:30 -0800 Subject: [PATCH 2/2] Add a note about small currency units --- umad-04-lnurlp-response.md | 12 ++++++++++++ umad-05-payreq-request.md | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/umad-04-lnurlp-response.md b/umad-04-lnurlp-response.md index b06b127..6b6207f 100644 --- a/umad-04-lnurlp-response.md +++ b/umad-04-lnurlp-response.md @@ -115,3 +115,15 @@ The `multiplier` field is 2.466, indicating that there are 2.466 millisats per U the receiving user can receive between 1 USDCent and 100,000,000 USDCents. If a sender wanted to send 5.95 USDC, they would specify `amount: 5950000, currency: USDC` in their [payreq request](/umad-05-payreq-request.md), which should in turn create a Lightning invoice for 14,677,700 millisats (5,950,000 * 2.466) plus applicable conversion fees. + +## Note for very small currency units + +If the smallest unit of a currency is very small (eg. `multiplier` is .0001), it may be necessary to round up to a larger +unit when actually sending the payment so that the `amount` field in the [payreq request](/umad-05-payreq-request.md) is +can fit in an int64 and can be represented in millisats. For example, DAI has 18 decimals, so the smallest unit is 10^-18. +In this case, trying to send 20 DAI would result in an `amount` of 20 * 10^18, which is too large to fit in an int64. For +this reason, the maximum `decimals` allowed is 8. If a currency has more than 8 decimals, the `multiplier` should be +increased to reduce the number of decimals. For example, if a currency has 10 decimals, the `multiplier` should be +`100 * the number of millisats per the real smallest unit`, and you should set `decimals` to 8. Tweaking the `multiplier` +and `decimals` fields in this way should allow the smallest unit to be represented in millisats and fit in an int64, +although it may result in some loss of precision. diff --git a/umad-05-payreq-request.md b/umad-05-payreq-request.md index b265e5d..b216afb 100644 --- a/umad-05-payreq-request.md +++ b/umad-05-payreq-request.md @@ -22,7 +22,7 @@ The body of the request is a JSON object with the following fields: // See LUD-18 for details. }, - // This is the amount in the smallest unit of the specified receiving currency (eg. cents for USD). + // 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.