From c4ff610705e344532d86fd927e809f83ec1c5157 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 19 Aug 2025 16:10:02 -0400 Subject: [PATCH 1/5] Add XLS-17d: 0017 XLS-17d: XFL Developer-friendly representation of XRPL balances --- XLS-0017-xfl/README.md | 164 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 XLS-0017-xfl/README.md diff --git a/XLS-0017-xfl/README.md b/XLS-0017-xfl/README.md new file mode 100644 index 000000000..7606323ca --- /dev/null +++ b/XLS-0017-xfl/README.md @@ -0,0 +1,164 @@ +
+    XLS: XLS-17d
+    Title: 0017 XLS-17d: XFL Developer-friendly representation of XRPL balances
+    Author: None (RichardAH)
+    Created: 2021-03-19
+    Status: Final
+    Category: Protocol
+
+# Background +The XRP ledger allows for two types of balances on accounts: +- Native `xrp` balances +- IOU/Token `trustline` balances + +Native balances are encoded and processed as signed 63bit integer values with an implicit decimal point at the 6th place from the right. A single unit is referred to as a drop. Thus the smallest possible value is `1 drop` represented logically as: `0.000001 xrp`. + +Trustline balances are encoded and processed as a 63 bit decimal floating point number in the following format: +{`sign bit` `8 bits of exponent` `54 bits of mantissa`} +Note: This is not the IEEE floating point format (which is base 2.) +- The exponent is biased by 97. Thus an encoded exponent of `0b00000000` is `10^(-97)`. +- The mantissa is normalised between `10^15` and `10^16 - 1` +- The canonical zero of this format is all bits 0. + +A 64th disambiguation bit is prepended (most significant bit) to both datatypes, which is `0` in the case of a native balance and `1` in the case of a trustline balance. [[1]](https://xrpl.org/serialization.html#amount-fields) + +# Problem Definition + +Performing computations between these two number formats can be difficult and error-prone for third party developers. + +One existing partial solution is passing these amounts around as human-readable decimal numbers encoded as strings. This is computationally intensive and still does not allow numbers of one type to be mathematically operated on with numbers of the other type in a consistent and predictable way. + +Internally the XRPL requires two independent types, however any xrp balance less than `10B` will translate without loss of precision into the trustline balance type. For amounts of xrp above `10B` (but less than `100B`) only 1 significant figure is lost from the least significant side. Thus in the worst possible case (moving >= `10B` XRP) `10` drops may be lost from an amount. + +The benefits of representing xrp balances in the trustline balance format are: +- Much simpler developer experience (less mentally demanding, lower barrier to entry) +- Can compute exchange rates between trustline balances and xrp +- Can store all balance types in a single data type +- A unified singular set of safe math routines which are much less likely to be used wrongly by developers + +# XFL Format + +The XFL format is designed for ease of use and maximum compatibility across a wide variety of processors and platforms. + +For maximum ease of passing and returning from (pure) functions all XFL numbers are encoded into an `enclosing number` which is always a signed 64 bit integer, as follows: + +Note: bit 63 is the most significant bit +* bit 63: `enclosing sign bit` always 0 for a valid XFL +* bit 62: `internal sign bit` 0 = negative 1 = positive. note: this is not the int64_t's sign bit. +* bit 61 - 53: `exponent` biased such that `0b000000000` = -97 +* bit 53 - 0: `mantissa` between `10^15` and `10^16 - 1` + +Special case: +* Canonical zero: enclosing number = 0 + +Any XFL with a negative enclosing sign bit is `invalid`. This _DOES NOT_ refer to the internal sign bit inside the XFL format. It is definitely possible to have a negative value represented in an XFL, however these always exist with a _POSITIVE_ enclosing sign bit. + +Invalid (negative enclosing sign bit) XFL values are reserved for propagation of error codes. If an invalid XFL is passed to an XFL processing function (for example `float_multiply`) it too should return an invalid XFL. + +# Examples +| Number | Enclosing | To String | +|--------|---------------------|------------------------------| +| -1 | 1478180677777522688 | -1000000000000000 * 10^(-15) | +| 0 | 0000000000000000000 | [canonical zero] | +| +1 | 6089866696204910592 | +1000000000000000 * 10^(-15) | +| +PI | 6092008288858500385 | +3141592653589793 * 10^(-15) | +| -PI | 1480322270431112481 | -3141592653589793 * 10^(-15) | + +# Reference Implementations + +Javascript: +```js +const minMantissa = 1000000000000000n +const maxMantissa = 9999999999999999n +const minExponent = -96 +const maxExponent = 80 + +function make_xfl(exponent, mantissa) +{ + // convert types as needed + if (typeof(exponent) != 'bigint') + exponent = BigInt(exponent); + + if (typeof(mantissa) != 'bigint') + mantissa = BigInt(mantissa); + + // canonical zero + if (mantissa == 0n) + return 0n; + + // normalize + let is_negative = mantissa < 0; + if (is_negative) + mantissa *= -1n; + + while (mantissa > maxMantissa) + { + mantissa /= 10n; + exponent++; + } + while (mantissa < minMantissa) + { + mantissa *= 10n; + exponent--; + } + + // canonical zero on mantissa underflow + if (mantissa == 0) + return 0n; + + // under and overflows + if (exponent > maxExponent || exponent < minExponent) + return -1; // note this is an "invalid" XFL used to propagate errors + + exponent += 97n; + + let xfl = (!is_negative ? 1n : 0n); + xfl <<= 8n; + xfl |= BigInt(exponent); + xfl <<= 54n; + xfl |= BigInt(mantissa); + + return xfl; +} + +function get_exponent(xfl) +{ + if (xfl < 0n) + throw "Invalid XFL"; + if (xfl == 0n) + return 0n; + return ((xfl >> 54n) & 0xFFn) - 97n; +} + +function get_mantissa(xfl) +{ + if (xfl < 0n) + throw "Invalid XFL"; + if (xfl == 0n) + return 0n; + return xfl - ((xfl >> 54n)<< 54n); +} + +function is_negative(xfl) +{ + if (xfl < 0n) + throw "Invalid XFL"; + if (xfl == 0n) + return false; + return ((xfl >> 62n) & 1n) == 0n; +} + +function to_string(xfl) +{ + if (xfl < 0n) + throw "Invalid XFL"; + if (xfl == 0n) + return ""; + return (is_negative(xfl) ? "-" : "+") + + get_mantissa(xfl) + " * 10^(" + get_exponent(xfl) + ")"; + +} +``` + +C: +* See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86) From acfa3456b96811b57f0756e1af14464b81e2bc48 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:50:03 +0200 Subject: [PATCH 2/5] Update README.md --- XLS-0017-xfl/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/XLS-0017-xfl/README.md b/XLS-0017-xfl/README.md index 7606323ca..846c65ce5 100644 --- a/XLS-0017-xfl/README.md +++ b/XLS-0017-xfl/README.md @@ -1,11 +1,11 @@ -
-    XLS: XLS-17d
-    Title: 0017 XLS-17d: XFL Developer-friendly representation of XRPL balances
-    Author: None (RichardAH)
-    Created: 2021-03-19
-    Status: Final
-    Category: Protocol
-
+
+    XLS: XLS-17
+    Title: XLS-17: XFL Developer-friendly representation of XRPL balances
+    Author: None (RichardAH)
+    Created: 2021-03-19
+    Status: Final
+    Category: Protocol
+
# Background The XRP ledger allows for two types of balances on accounts: - Native `xrp` balances @@ -162,3 +162,4 @@ function to_string(xfl) C: * See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86) + From 461522c7568768cacc72b5dfe3d5fa1d97276b25 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:52:05 +0200 Subject: [PATCH 3/5] Update README.md --- XLS-0017-xfl/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/XLS-0017-xfl/README.md b/XLS-0017-xfl/README.md index 846c65ce5..64121cc84 100644 --- a/XLS-0017-xfl/README.md +++ b/XLS-0017-xfl/README.md @@ -1,10 +1,10 @@
-    XLS: XLS-17
-    Title: XLS-17: XFL Developer-friendly representation of XRPL balances
-    Author: None (RichardAH)
-    Created: 2021-03-19
-    Status: Final
-    Category: Protocol
+    xls: 17
+    title: XFL Developer-friendly representation of XRPL balances
+    author: None (RichardAH)
+    created: 2021-03-19
+    status: Final
+    category: Protocol
 
# Background The XRP ledger allows for two types of balances on accounts: @@ -163,3 +163,4 @@ function to_string(xfl) C: * See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86) + From efead9c09768ee732fa7f7bb322f5ea6816f527b Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Mon, 8 Sep 2025 11:48:20 +0200 Subject: [PATCH 4/5] Update README.md --- XLS-0017-xfl/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/XLS-0017-xfl/README.md b/XLS-0017-xfl/README.md index 64121cc84..ef9ff372b 100644 --- a/XLS-0017-xfl/README.md +++ b/XLS-0017-xfl/README.md @@ -1,7 +1,8 @@
     xls: 17
     title: XFL Developer-friendly representation of XRPL balances
-    author: None (RichardAH)
+    descrption: Introduces developer-friendly representation of XRPL balances.
+    author: RichardAH (@RichardAH)
     created: 2021-03-19
     status: Final
     category: Protocol
@@ -164,3 +165,4 @@ C:
 * See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86)
 
 
+

From f78bd31ff1ba264e37ba30ef8c3250ddfb1a32da Mon Sep 17 00:00:00 2001
From: Mayukha Vadari 
Date: Mon, 8 Sep 2025 12:41:35 -0400
Subject: [PATCH 5/5] lint

---
 XLS-0017-xfl/README.md | 326 ++++++++++++++++++++---------------------
 1 file changed, 158 insertions(+), 168 deletions(-)

diff --git a/XLS-0017-xfl/README.md b/XLS-0017-xfl/README.md
index ef9ff372b..4b1687df8 100644
--- a/XLS-0017-xfl/README.md
+++ b/XLS-0017-xfl/README.md
@@ -1,168 +1,158 @@
-
-    xls: 17
-    title: XFL Developer-friendly representation of XRPL balances
-    descrption: Introduces developer-friendly representation of XRPL balances.
-    author: RichardAH (@RichardAH)
-    created: 2021-03-19
-    status: Final
-    category: Protocol
-
-# Background -The XRP ledger allows for two types of balances on accounts: -- Native `xrp` balances -- IOU/Token `trustline` balances - -Native balances are encoded and processed as signed 63bit integer values with an implicit decimal point at the 6th place from the right. A single unit is referred to as a drop. Thus the smallest possible value is `1 drop` represented logically as: `0.000001 xrp`. - -Trustline balances are encoded and processed as a 63 bit decimal floating point number in the following format: -{`sign bit` `8 bits of exponent` `54 bits of mantissa`} -Note: This is not the IEEE floating point format (which is base 2.) -- The exponent is biased by 97. Thus an encoded exponent of `0b00000000` is `10^(-97)`. -- The mantissa is normalised between `10^15` and `10^16 - 1` -- The canonical zero of this format is all bits 0. - -A 64th disambiguation bit is prepended (most significant bit) to both datatypes, which is `0` in the case of a native balance and `1` in the case of a trustline balance. [[1]](https://xrpl.org/serialization.html#amount-fields) - -# Problem Definition - -Performing computations between these two number formats can be difficult and error-prone for third party developers. - -One existing partial solution is passing these amounts around as human-readable decimal numbers encoded as strings. This is computationally intensive and still does not allow numbers of one type to be mathematically operated on with numbers of the other type in a consistent and predictable way. - -Internally the XRPL requires two independent types, however any xrp balance less than `10B` will translate without loss of precision into the trustline balance type. For amounts of xrp above `10B` (but less than `100B`) only 1 significant figure is lost from the least significant side. Thus in the worst possible case (moving >= `10B` XRP) `10` drops may be lost from an amount. - -The benefits of representing xrp balances in the trustline balance format are: -- Much simpler developer experience (less mentally demanding, lower barrier to entry) -- Can compute exchange rates between trustline balances and xrp -- Can store all balance types in a single data type -- A unified singular set of safe math routines which are much less likely to be used wrongly by developers - -# XFL Format - -The XFL format is designed for ease of use and maximum compatibility across a wide variety of processors and platforms. - -For maximum ease of passing and returning from (pure) functions all XFL numbers are encoded into an `enclosing number` which is always a signed 64 bit integer, as follows: - -Note: bit 63 is the most significant bit -* bit 63: `enclosing sign bit` always 0 for a valid XFL -* bit 62: `internal sign bit` 0 = negative 1 = positive. note: this is not the int64_t's sign bit. -* bit 61 - 53: `exponent` biased such that `0b000000000` = -97 -* bit 53 - 0: `mantissa` between `10^15` and `10^16 - 1` - -Special case: -* Canonical zero: enclosing number = 0 - -Any XFL with a negative enclosing sign bit is `invalid`. This _DOES NOT_ refer to the internal sign bit inside the XFL format. It is definitely possible to have a negative value represented in an XFL, however these always exist with a _POSITIVE_ enclosing sign bit. - -Invalid (negative enclosing sign bit) XFL values are reserved for propagation of error codes. If an invalid XFL is passed to an XFL processing function (for example `float_multiply`) it too should return an invalid XFL. - -# Examples -| Number | Enclosing | To String | -|--------|---------------------|------------------------------| -| -1 | 1478180677777522688 | -1000000000000000 * 10^(-15) | -| 0 | 0000000000000000000 | [canonical zero] | -| +1 | 6089866696204910592 | +1000000000000000 * 10^(-15) | -| +PI | 6092008288858500385 | +3141592653589793 * 10^(-15) | -| -PI | 1480322270431112481 | -3141592653589793 * 10^(-15) | - -# Reference Implementations - -Javascript: -```js -const minMantissa = 1000000000000000n -const maxMantissa = 9999999999999999n -const minExponent = -96 -const maxExponent = 80 - -function make_xfl(exponent, mantissa) -{ - // convert types as needed - if (typeof(exponent) != 'bigint') - exponent = BigInt(exponent); - - if (typeof(mantissa) != 'bigint') - mantissa = BigInt(mantissa); - - // canonical zero - if (mantissa == 0n) - return 0n; - - // normalize - let is_negative = mantissa < 0; - if (is_negative) - mantissa *= -1n; - - while (mantissa > maxMantissa) - { - mantissa /= 10n; - exponent++; - } - while (mantissa < minMantissa) - { - mantissa *= 10n; - exponent--; - } - - // canonical zero on mantissa underflow - if (mantissa == 0) - return 0n; - - // under and overflows - if (exponent > maxExponent || exponent < minExponent) - return -1; // note this is an "invalid" XFL used to propagate errors - - exponent += 97n; - - let xfl = (!is_negative ? 1n : 0n); - xfl <<= 8n; - xfl |= BigInt(exponent); - xfl <<= 54n; - xfl |= BigInt(mantissa); - - return xfl; -} - -function get_exponent(xfl) -{ - if (xfl < 0n) - throw "Invalid XFL"; - if (xfl == 0n) - return 0n; - return ((xfl >> 54n) & 0xFFn) - 97n; -} - -function get_mantissa(xfl) -{ - if (xfl < 0n) - throw "Invalid XFL"; - if (xfl == 0n) - return 0n; - return xfl - ((xfl >> 54n)<< 54n); -} - -function is_negative(xfl) -{ - if (xfl < 0n) - throw "Invalid XFL"; - if (xfl == 0n) - return false; - return ((xfl >> 62n) & 1n) == 0n; -} - -function to_string(xfl) -{ - if (xfl < 0n) - throw "Invalid XFL"; - if (xfl == 0n) - return ""; - return (is_negative(xfl) ? "-" : "+") + - get_mantissa(xfl) + " * 10^(" + get_exponent(xfl) + ")"; - -} -``` - -C: -* See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86) - - - +
+    xls: 17
+    title: XFL Developer-friendly representation of XRPL balances
+    descrption: Introduces developer-friendly representation of XRPL balances.
+    author: RichardAH (@RichardAH)
+    created: 2021-03-19
+    status: Final
+    category: Protocol
+
+ +# Background + +The XRP ledger allows for two types of balances on accounts: + +- Native `xrp` balances +- IOU/Token `trustline` balances + +Native balances are encoded and processed as signed 63bit integer values with an implicit decimal point at the 6th place from the right. A single unit is referred to as a drop. Thus the smallest possible value is `1 drop` represented logically as: `0.000001 xrp`. + +Trustline balances are encoded and processed as a 63 bit decimal floating point number in the following format: +{`sign bit` `8 bits of exponent` `54 bits of mantissa`} +Note: This is not the IEEE floating point format (which is base 2.) + +- The exponent is biased by 97. Thus an encoded exponent of `0b00000000` is `10^(-97)`. +- The mantissa is normalised between `10^15` and `10^16 - 1` +- The canonical zero of this format is all bits 0. + +A 64th disambiguation bit is prepended (most significant bit) to both datatypes, which is `0` in the case of a native balance and `1` in the case of a trustline balance. [[1]](https://xrpl.org/serialization.html#amount-fields) + +# Problem Definition + +Performing computations between these two number formats can be difficult and error-prone for third party developers. + +One existing partial solution is passing these amounts around as human-readable decimal numbers encoded as strings. This is computationally intensive and still does not allow numbers of one type to be mathematically operated on with numbers of the other type in a consistent and predictable way. + +Internally the XRPL requires two independent types, however any xrp balance less than `10B` will translate without loss of precision into the trustline balance type. For amounts of xrp above `10B` (but less than `100B`) only 1 significant figure is lost from the least significant side. Thus in the worst possible case (moving >= `10B` XRP) `10` drops may be lost from an amount. + +The benefits of representing xrp balances in the trustline balance format are: + +- Much simpler developer experience (less mentally demanding, lower barrier to entry) +- Can compute exchange rates between trustline balances and xrp +- Can store all balance types in a single data type +- A unified singular set of safe math routines which are much less likely to be used wrongly by developers + +# XFL Format + +The XFL format is designed for ease of use and maximum compatibility across a wide variety of processors and platforms. + +For maximum ease of passing and returning from (pure) functions all XFL numbers are encoded into an `enclosing number` which is always a signed 64 bit integer, as follows: + +Note: bit 63 is the most significant bit + +- bit 63: `enclosing sign bit` always 0 for a valid XFL +- bit 62: `internal sign bit` 0 = negative 1 = positive. note: this is not the int64_t's sign bit. +- bit 61 - 53: `exponent` biased such that `0b000000000` = -97 +- bit 53 - 0: `mantissa` between `10^15` and `10^16 - 1` + +Special case: + +- Canonical zero: enclosing number = 0 + +Any XFL with a negative enclosing sign bit is `invalid`. This _DOES NOT_ refer to the internal sign bit inside the XFL format. It is definitely possible to have a negative value represented in an XFL, however these always exist with a _POSITIVE_ enclosing sign bit. + +Invalid (negative enclosing sign bit) XFL values are reserved for propagation of error codes. If an invalid XFL is passed to an XFL processing function (for example `float_multiply`) it too should return an invalid XFL. + +# Examples + +| Number | Enclosing | To String | +| ------ | ------------------- | ----------------------------- | +| -1 | 1478180677777522688 | -1000000000000000 \* 10^(-15) | +| 0 | 0000000000000000000 | [canonical zero] | +| +1 | 6089866696204910592 | +1000000000000000 \* 10^(-15) | +| +PI | 6092008288858500385 | +3141592653589793 \* 10^(-15) | +| -PI | 1480322270431112481 | -3141592653589793 \* 10^(-15) | + +# Reference Implementations + +Javascript: + +```js +const minMantissa = 1000000000000000n; +const maxMantissa = 9999999999999999n; +const minExponent = -96; +const maxExponent = 80; + +function make_xfl(exponent, mantissa) { + // convert types as needed + if (typeof exponent != "bigint") exponent = BigInt(exponent); + + if (typeof mantissa != "bigint") mantissa = BigInt(mantissa); + + // canonical zero + if (mantissa == 0n) return 0n; + + // normalize + let is_negative = mantissa < 0; + if (is_negative) mantissa *= -1n; + + while (mantissa > maxMantissa) { + mantissa /= 10n; + exponent++; + } + while (mantissa < minMantissa) { + mantissa *= 10n; + exponent--; + } + + // canonical zero on mantissa underflow + if (mantissa == 0) return 0n; + + // under and overflows + if (exponent > maxExponent || exponent < minExponent) return -1; // note this is an "invalid" XFL used to propagate errors + + exponent += 97n; + + let xfl = !is_negative ? 1n : 0n; + xfl <<= 8n; + xfl |= BigInt(exponent); + xfl <<= 54n; + xfl |= BigInt(mantissa); + + return xfl; +} + +function get_exponent(xfl) { + if (xfl < 0n) throw "Invalid XFL"; + if (xfl == 0n) return 0n; + return ((xfl >> 54n) & 0xffn) - 97n; +} + +function get_mantissa(xfl) { + if (xfl < 0n) throw "Invalid XFL"; + if (xfl == 0n) return 0n; + return xfl - ((xfl >> 54n) << 54n); +} + +function is_negative(xfl) { + if (xfl < 0n) throw "Invalid XFL"; + if (xfl == 0n) return false; + return ((xfl >> 62n) & 1n) == 0n; +} + +function to_string(xfl) { + if (xfl < 0n) throw "Invalid XFL"; + if (xfl == 0n) return ""; + return ( + (is_negative(xfl) ? "-" : "+") + + get_mantissa(xfl) + + " * 10^(" + + get_exponent(xfl) + + ")" + ); +} +``` + +C: + +- See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86)