ripple-binary-codec 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/enums/definitions.json +1 -1
  2. package/dist/enums/src/enums/definitions.json +6 -1
  3. package/dist/src/enums/definitions.json +1 -1
  4. package/dist/src/types/hash-128.js +1 -1
  5. package/dist/src/types/hash-128.js.map +1 -1
  6. package/dist/src/types/hash-160.js +1 -1
  7. package/dist/src/types/hash-160.js.map +1 -1
  8. package/dist/src/types/hash-192.js +1 -1
  9. package/dist/src/types/hash-192.js.map +1 -1
  10. package/dist/src/types/index.d.ts +2 -1
  11. package/dist/src/types/index.js +4 -1
  12. package/dist/src/types/index.js.map +1 -1
  13. package/dist/src/types/int-32.d.ts +33 -0
  14. package/dist/src/types/int-32.js +64 -0
  15. package/dist/src/types/int-32.js.map +1 -0
  16. package/dist/src/types/int.d.ts +38 -0
  17. package/dist/src/types/int.js +57 -0
  18. package/dist/src/types/int.js.map +1 -0
  19. package/dist/src/types/st-number.js +88 -33
  20. package/dist/src/types/st-number.js.map +1 -1
  21. package/dist/src/types/uint-16.js +2 -2
  22. package/dist/src/types/uint-16.js.map +1 -1
  23. package/dist/src/types/uint-32.js +1 -1
  24. package/dist/src/types/uint-32.js.map +1 -1
  25. package/dist/src/types/uint-64.js +1 -1
  26. package/dist/src/types/uint-64.js.map +1 -1
  27. package/dist/src/types/uint-8.js +1 -1
  28. package/dist/src/types/uint-8.js.map +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/dist/types/hash-128.js +1 -1
  31. package/dist/types/hash-128.js.map +1 -1
  32. package/dist/types/hash-160.js +1 -1
  33. package/dist/types/hash-160.js.map +1 -1
  34. package/dist/types/hash-192.js +1 -1
  35. package/dist/types/hash-192.js.map +1 -1
  36. package/dist/types/index.d.ts +2 -1
  37. package/dist/types/index.js +4 -1
  38. package/dist/types/index.js.map +1 -1
  39. package/dist/types/int-32.d.ts +33 -0
  40. package/dist/types/int-32.js +64 -0
  41. package/dist/types/int-32.js.map +1 -0
  42. package/dist/types/int.d.ts +38 -0
  43. package/dist/types/int.js +57 -0
  44. package/dist/types/int.js.map +1 -0
  45. package/dist/types/st-number.js +88 -33
  46. package/dist/types/st-number.js.map +1 -1
  47. package/dist/types/uint-16.js +2 -2
  48. package/dist/types/uint-16.js.map +1 -1
  49. package/dist/types/uint-32.js +1 -1
  50. package/dist/types/uint-32.js.map +1 -1
  51. package/dist/types/uint-64.js +1 -1
  52. package/dist/types/uint-64.js.map +1 -1
  53. package/dist/types/uint-8.js +1 -1
  54. package/dist/types/uint-8.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/enums/definitions.json +6 -1
  57. package/src/types/hash-128.ts +1 -1
  58. package/src/types/hash-160.ts +1 -1
  59. package/src/types/hash-192.ts +1 -1
  60. package/src/types/index.ts +3 -0
  61. package/src/types/int-32.ts +72 -0
  62. package/src/types/int.ts +75 -0
  63. package/src/types/st-number.ts +107 -31
  64. package/src/types/uint-16.ts +2 -2
  65. package/src/types/uint-32.ts +1 -1
  66. package/src/types/uint-64.ts +1 -1
  67. package/src/types/uint-8.ts +1 -1
@@ -1,3 +1,4 @@
1
+ /* eslint-disable complexity -- required for various checks */
1
2
  import { BinaryParser } from '../serdes/binary-parser'
2
3
  import { SerializedType } from './serialized-type'
3
4
  import { writeInt32BE, writeInt64BE, readInt32BE, readInt64BE } from '../utils'
@@ -6,8 +7,9 @@ import { writeInt32BE, writeInt64BE, readInt32BE, readInt64BE } from '../utils'
6
7
  * Constants for mantissa and exponent normalization per XRPL Number spec.
7
8
  * These define allowed magnitude for mantissa and exponent after normalization.
8
9
  */
9
- const MIN_MANTISSA = BigInt('1000000000000000')
10
- const MAX_MANTISSA = BigInt('9999999999999999')
10
+ const MIN_MANTISSA = BigInt('1000000000000000000') // 10^18
11
+ const MAX_MANTISSA = BigInt('9999999999999999999') // 10^19 - 1
12
+ const MAX_INT64 = BigInt('9223372036854775807') // 2^63 - 1, max signed 64-bit integer
11
13
  const MIN_EXPONENT = -32768
12
14
  const MAX_EXPONENT = 32768
13
15
  const DEFAULT_VALUE_EXPONENT = -2147483648
@@ -62,6 +64,12 @@ function extractNumberPartsFromString(val: string): {
62
64
  }
63
65
  if (expPart) exponent += parseInt(expPart, 10)
64
66
 
67
+ // Remove trailing zeros from mantissa and adjust exponent
68
+ while (mantissaStr.length > 1 && mantissaStr.endsWith('0')) {
69
+ mantissaStr = mantissaStr.slice(0, -1)
70
+ exponent += 1
71
+ }
72
+
65
73
  let mantissa = BigInt(mantissaStr)
66
74
  if (sign === '-') mantissa = -mantissa
67
75
  const isNegative = mantissa < BigInt(0)
@@ -72,7 +80,7 @@ function extractNumberPartsFromString(val: string): {
72
80
  /**
73
81
  * Normalize the mantissa and exponent to XRPL constraints.
74
82
  *
75
- * Ensures that after normalization, the mantissa is between MIN_MANTISSA and MAX_MANTISSA (unless zero).
83
+ * Ensures that after normalization, the mantissa is between MIN_MANTISSA and MAX_INT64.
76
84
  * Adjusts the exponent as needed by shifting the mantissa left/right (multiplying/dividing by 10).
77
85
  *
78
86
  * @param mantissa - The unnormalized mantissa (BigInt).
@@ -87,16 +95,65 @@ function normalize(
87
95
  let m = mantissa < BigInt(0) ? -mantissa : mantissa
88
96
  const isNegative = mantissa < BigInt(0)
89
97
 
90
- while (m !== BigInt(0) && m < MIN_MANTISSA && exponent > MIN_EXPONENT) {
98
+ // Handle zero
99
+ if (m === BigInt(0)) {
100
+ return { mantissa: BigInt(0), exponent: DEFAULT_VALUE_EXPONENT }
101
+ }
102
+
103
+ // Grow mantissa until it reaches MIN_MANTISSA
104
+ while (m < MIN_MANTISSA && exponent > MIN_EXPONENT) {
91
105
  exponent -= 1
92
106
  m *= BigInt(10)
93
107
  }
108
+
109
+ let lastDigit: bigint | null = null
110
+
111
+ // Shrink mantissa until it fits within MAX_MANTISSA
94
112
  while (m > MAX_MANTISSA) {
95
- if (exponent >= MAX_EXPONENT)
113
+ if (exponent >= MAX_EXPONENT) {
96
114
  throw new Error('Mantissa and exponent are too large')
115
+ }
97
116
  exponent += 1
117
+ lastDigit = m % BigInt(10)
98
118
  m /= BigInt(10)
99
119
  }
120
+
121
+ // Handle underflow: if exponent too small or mantissa too small, throw error
122
+ if (exponent < MIN_EXPONENT || m < MIN_MANTISSA) {
123
+ throw new Error('Underflow: value too small to represent')
124
+ }
125
+
126
+ // Handle overflow: if exponent exceeds MAX_EXPONENT after growing.
127
+ if (exponent > MAX_EXPONENT) {
128
+ throw new Error('Exponent overflow: value too large to represent')
129
+ }
130
+
131
+ // Handle overflow: if mantissa exceeds MAX_INT64 (2^63-1) after growing.
132
+ if (m > MAX_INT64) {
133
+ if (exponent >= MAX_EXPONENT) {
134
+ throw new Error('Exponent overflow: value too large to represent')
135
+ }
136
+ exponent += 1
137
+ lastDigit = m % BigInt(10)
138
+ m /= BigInt(10)
139
+ }
140
+
141
+ if (lastDigit != null && lastDigit >= BigInt(5)) {
142
+ m += BigInt(1)
143
+ // After rounding, mantissa may exceed MAX_INT64 again
144
+ if (m > MAX_INT64) {
145
+ if (exponent >= MAX_EXPONENT) {
146
+ throw new Error('Exponent overflow: value too large to represent')
147
+ }
148
+ lastDigit = m % BigInt(10)
149
+ exponent += 1
150
+ m /= BigInt(10)
151
+ if (lastDigit >= BigInt(5)) {
152
+ m += BigInt(1)
153
+ }
154
+ }
155
+ }
156
+
100
157
  if (isNegative) m = -m
101
158
  return { mantissa: m, exponent }
102
159
  }
@@ -159,17 +216,9 @@ export class STNumber extends SerializedType {
159
216
  * @throws Error if val is not a valid number string.
160
217
  */
161
218
  static fromValue(val: string): STNumber {
162
- const { mantissa, exponent, isNegative } = extractNumberPartsFromString(val)
163
- let normalizedMantissa: bigint
164
- let normalizedExponent: number
165
-
166
- if (mantissa === BigInt(0) && exponent === 0 && !isNegative) {
167
- normalizedMantissa = BigInt(0)
168
- normalizedExponent = DEFAULT_VALUE_EXPONENT
169
- } else {
170
- ;({ mantissa: normalizedMantissa, exponent: normalizedExponent } =
171
- normalize(mantissa, exponent))
172
- }
219
+ const { mantissa, exponent } = extractNumberPartsFromString(val)
220
+ const { mantissa: normalizedMantissa, exponent: normalizedExponent } =
221
+ normalize(mantissa, exponent)
173
222
 
174
223
  const bytes = new Uint8Array(12)
175
224
  writeInt64BE(bytes, normalizedMantissa, 0)
@@ -193,39 +242,66 @@ export class STNumber extends SerializedType {
193
242
  *
194
243
  * @returns String representation of the value
195
244
  */
196
- // eslint-disable-next-line complexity -- required
197
245
  toJSON(): string {
198
246
  const b = this.bytes
199
- if (!b || b.length !== 12)
247
+ if (!b || b?.length !== 12)
200
248
  throw new Error('STNumber internal bytes not set or wrong length')
201
249
 
202
250
  // Signed 64-bit mantissa
203
251
  const mantissa = readInt64BE(b, 0)
204
252
  // Signed 32-bit exponent
205
- const exponent = readInt32BE(b, 8)
253
+ let exponent = readInt32BE(b, 8)
206
254
 
207
255
  // Special zero: XRPL encodes canonical zero as mantissa=0, exponent=DEFAULT_VALUE_EXPONENT.
208
256
  if (mantissa === BigInt(0) && exponent === DEFAULT_VALUE_EXPONENT) {
209
257
  return '0'
210
258
  }
211
- if (exponent === 0) return mantissa.toString()
212
259
 
213
- // Use scientific notation for small/large exponents, decimal otherwise
214
- if (exponent < -25 || exponent > -5) {
215
- return `${mantissa}e${exponent}`
260
+ const isNegative = mantissa < BigInt(0)
261
+ let mantissaAbs = isNegative ? -mantissa : mantissa
262
+
263
+ // If mantissa < MIN_MANTISSA, it was shrunk for int64 serialization (mantissa > 2^63-1).
264
+ // Restore it for proper string rendering to match rippled's internal representation.
265
+ if (mantissaAbs !== BigInt(0) && mantissaAbs < MIN_MANTISSA) {
266
+ mantissaAbs *= BigInt(10)
267
+ exponent -= 1
216
268
  }
217
269
 
218
- // Decimal rendering for -25 <= exp <= -5
219
- const isNegative = mantissa < BigInt(0)
220
- const mantissaAbs = mantissa < BigInt(0) ? -mantissa : mantissa
270
+ // For large mantissa range (default), rangeLog = 18
271
+ const rangeLog = 18
272
+
273
+ // Use scientific notation for exponents that are too small or too large
274
+ // Condition from rippled: exponent != 0 AND (exponent < -(rangeLog + 10) OR exponent > -(rangeLog - 10))
275
+ // For rangeLog = 18: exponent != 0 AND (exponent < -28 OR exponent > -8)
276
+ if (
277
+ exponent !== 0 &&
278
+ (exponent < -(rangeLog + 10) || exponent > -(rangeLog - 10))
279
+ ) {
280
+ // Strip trailing zeros from mantissa (matches rippled behavior)
281
+ let exp = exponent
282
+ while (
283
+ mantissaAbs !== BigInt(0) &&
284
+ mantissaAbs % BigInt(10) === BigInt(0) &&
285
+ exp < MAX_EXPONENT
286
+ ) {
287
+ mantissaAbs /= BigInt(10)
288
+ exp += 1
289
+ }
290
+ const sign = isNegative ? '-' : ''
291
+ return `${sign}${mantissaAbs}e${exp}`
292
+ }
293
+
294
+ // Decimal rendering for -(rangeLog + 10) <= exponent <= -(rangeLog - 10)
295
+ // i.e., -28 <= exponent <= -8, or exponent == 0
296
+ const padPrefix = rangeLog + 12 // 30
297
+ const padSuffix = rangeLog + 8 // 26
221
298
 
222
- const padPrefix = 27
223
- const padSuffix = 23
224
299
  const mantissaStr = mantissaAbs.toString()
225
300
  const rawValue = '0'.repeat(padPrefix) + mantissaStr + '0'.repeat(padSuffix)
226
- const OFFSET = exponent + 43
227
- const integerPart = rawValue.slice(0, OFFSET).replace(/^0+/, '') || '0'
228
- const fractionPart = rawValue.slice(OFFSET).replace(/0+$/, '')
301
+ const offset = exponent + padPrefix + rangeLog + 1 // exponent + 49
302
+
303
+ const integerPart = rawValue.slice(0, offset).replace(/^0+/, '') || '0'
304
+ const fractionPart = rawValue.slice(offset).replace(/0+$/, '')
229
305
 
230
306
  return `${isNegative ? '-' : ''}${integerPart}${
231
307
  fractionPart ? '.' + fractionPart : ''
@@ -29,7 +29,7 @@ class UInt16 extends UInt {
29
29
  return val
30
30
  }
31
31
 
32
- if (typeof val === 'number') {
32
+ if (typeof val === 'number' && Number.isInteger(val)) {
33
33
  UInt16.checkUintRange(val, 0, 0xffff)
34
34
 
35
35
  const buf = new Uint8Array(UInt16.width)
@@ -37,7 +37,7 @@ class UInt16 extends UInt {
37
37
  return new UInt16(buf)
38
38
  }
39
39
 
40
- throw new Error('Can not construct UInt16 with given value')
40
+ throw new Error('Cannot construct UInt16 from given value')
41
41
  }
42
42
 
43
43
  /**
@@ -37,7 +37,7 @@ class UInt32 extends UInt {
37
37
  return new UInt32(buf)
38
38
  }
39
39
 
40
- if (typeof val === 'number') {
40
+ if (typeof val === 'number' && Number.isInteger(val)) {
41
41
  UInt32.checkUintRange(val, 0, 0xffffffff)
42
42
  writeUInt32BE(buf, val, 0)
43
43
  return new UInt32(buf)
@@ -53,7 +53,7 @@ class UInt64 extends UInt {
53
53
 
54
54
  let buf = new Uint8Array(UInt64.width)
55
55
 
56
- if (typeof val === 'number') {
56
+ if (typeof val === 'number' && Number.isInteger(val)) {
57
57
  if (val < 0) {
58
58
  throw new Error('value must be an unsigned integer')
59
59
  }
@@ -28,7 +28,7 @@ class UInt8 extends UInt {
28
28
  return val
29
29
  }
30
30
 
31
- if (typeof val === 'number') {
31
+ if (typeof val === 'number' && Number.isInteger(val)) {
32
32
  UInt8.checkUintRange(val, 0, 0xff)
33
33
 
34
34
  const buf = new Uint8Array(UInt8.width)