ripple-binary-codec 2.0.0 → 2.2.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 (61) hide show
  1. package/dist/enums/definitions.json +1248 -973
  2. package/dist/enums/src/enums/definitions.json +1248 -978
  3. package/dist/serdes/binary-parser.js +2 -2
  4. package/dist/serdes/binary-parser.js.map +1 -1
  5. package/dist/src/enums/definitions.json +1248 -973
  6. package/dist/src/serdes/binary-parser.js +2 -2
  7. package/dist/src/serdes/binary-parser.js.map +1 -1
  8. package/dist/src/types/amount.d.ts +29 -5
  9. package/dist/src/types/amount.js +85 -8
  10. package/dist/src/types/amount.js.map +1 -1
  11. package/dist/src/types/blob.js +3 -0
  12. package/dist/src/types/blob.js.map +1 -1
  13. package/dist/src/types/hash-192.d.ts +10 -0
  14. package/dist/src/types/hash-192.js +19 -0
  15. package/dist/src/types/hash-192.js.map +1 -0
  16. package/dist/src/types/index.d.ts +2 -1
  17. package/dist/src/types/index.js +4 -1
  18. package/dist/src/types/index.js.map +1 -1
  19. package/dist/src/types/serialized-type.d.ts +1 -1
  20. package/dist/src/types/serialized-type.js +1 -1
  21. package/dist/src/types/serialized-type.js.map +1 -1
  22. package/dist/src/types/st-array.js +4 -1
  23. package/dist/src/types/st-array.js.map +1 -1
  24. package/dist/src/types/st-object.js +5 -2
  25. package/dist/src/types/st-object.js.map +1 -1
  26. package/dist/src/types/uint-64.d.ts +3 -2
  27. package/dist/src/types/uint-64.js +21 -4
  28. package/dist/src/types/uint-64.js.map +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/dist/types/amount.d.ts +29 -5
  31. package/dist/types/amount.js +85 -8
  32. package/dist/types/amount.js.map +1 -1
  33. package/dist/types/blob.js +3 -0
  34. package/dist/types/blob.js.map +1 -1
  35. package/dist/types/hash-192.d.ts +10 -0
  36. package/dist/types/hash-192.js +19 -0
  37. package/dist/types/hash-192.js.map +1 -0
  38. package/dist/types/index.d.ts +2 -1
  39. package/dist/types/index.js +4 -1
  40. package/dist/types/index.js.map +1 -1
  41. package/dist/types/serialized-type.d.ts +1 -1
  42. package/dist/types/serialized-type.js +1 -1
  43. package/dist/types/serialized-type.js.map +1 -1
  44. package/dist/types/st-array.js +4 -1
  45. package/dist/types/st-array.js.map +1 -1
  46. package/dist/types/st-object.js +5 -2
  47. package/dist/types/st-object.js.map +1 -1
  48. package/dist/types/uint-64.d.ts +3 -2
  49. package/dist/types/uint-64.js +21 -4
  50. package/dist/types/uint-64.js.map +1 -1
  51. package/package.json +4 -4
  52. package/src/enums/definitions.json +1248 -978
  53. package/src/serdes/binary-parser.ts +6 -2
  54. package/src/types/amount.ts +119 -12
  55. package/src/types/blob.ts +3 -0
  56. package/src/types/hash-192.ts +19 -0
  57. package/src/types/index.ts +3 -0
  58. package/src/types/serialized-type.ts +1 -1
  59. package/src/types/st-array.ts +7 -1
  60. package/src/types/st-object.ts +4 -1
  61. package/src/types/uint-64.ts +33 -5
@@ -144,14 +144,18 @@ class BinaryParser {
144
144
  if (type === 0) {
145
145
  type = this.readUInt8()
146
146
  if (type === 0 || type < 16) {
147
- throw new Error('Cannot read FieldOrdinal, type_code out of range')
147
+ throw new Error(
148
+ `Cannot read FieldOrdinal, type_code ${type} out of range`,
149
+ )
148
150
  }
149
151
  }
150
152
 
151
153
  if (nth === 0) {
152
154
  nth = this.readUInt8()
153
155
  if (nth === 0 || nth < 16) {
154
- throw new Error('Cannot read FieldOrdinal, field_code out of range')
156
+ throw new Error(
157
+ `Cannot read FieldOrdinal, field_code ${nth} out of range`,
158
+ )
155
159
  }
156
160
  }
157
161
 
@@ -6,6 +6,7 @@ import { JsonObject, SerializedType } from './serialized-type'
6
6
  import BigNumber from 'bignumber.js'
7
7
  import { bytesToHex, concat, hexToBytes } from '@xrplf/isomorphic/utils'
8
8
  import { readUInt32BE, writeUInt32BE } from '../utils'
9
+ import { Hash192 } from './hash-192'
9
10
 
10
11
  /**
11
12
  * Constants for validating amounts
@@ -16,6 +17,7 @@ const MAX_IOU_PRECISION = 16
16
17
  const MAX_DROPS = new BigNumber('1e17')
17
18
  const MIN_XRP = new BigNumber('1e-6')
18
19
  const mask = BigInt(0x00000000ffffffff)
20
+ const mptMask = BigInt(0x8000000000000000)
19
21
 
20
22
  /**
21
23
  * BigNumber configuration for Amount IOUs
@@ -27,20 +29,28 @@ BigNumber.config({
27
29
  ],
28
30
  })
29
31
 
30
- /**
31
- * Interface for JSON objects that represent amounts
32
- */
33
- interface AmountObject extends JsonObject {
32
+ interface AmountObjectIOU extends JsonObject {
34
33
  value: string
35
34
  currency: string
36
35
  issuer: string
37
36
  }
38
37
 
38
+ interface AmountObjectMPT extends JsonObject {
39
+ value: string
40
+ mpt_issuance_id: string
41
+ }
42
+
43
+ /**
44
+ * Interface for JSON objects that represent amounts
45
+ */
46
+ type AmountObject = AmountObjectIOU | AmountObjectMPT
47
+
39
48
  /**
40
- * Type guard for AmountObject
49
+ * Type guard for AmountObjectIOU
41
50
  */
42
- function isAmountObject(arg): arg is AmountObject {
51
+ function isAmountObjectIOU(arg): arg is AmountObjectIOU {
43
52
  const keys = Object.keys(arg).sort()
53
+
44
54
  return (
45
55
  keys.length === 3 &&
46
56
  keys[0] === 'currency' &&
@@ -49,6 +59,17 @@ function isAmountObject(arg): arg is AmountObject {
49
59
  )
50
60
  }
51
61
 
62
+ /**
63
+ * Type guard for AmountObjectMPT
64
+ */
65
+ function isAmountObjectMPT(arg): arg is AmountObjectMPT {
66
+ const keys = Object.keys(arg).sort()
67
+
68
+ return (
69
+ keys.length === 2 && keys[0] === 'mpt_issuance_id' && keys[1] === 'value'
70
+ )
71
+ }
72
+
52
73
  /**
53
74
  * Class for serializing/Deserializing Amounts
54
75
  */
@@ -60,7 +81,7 @@ class Amount extends SerializedType {
60
81
  }
61
82
 
62
83
  /**
63
- * Construct an amount from an IOU or string amount
84
+ * Construct an amount from an IOU, MPT or string amount
64
85
  *
65
86
  * @param value An Amount, object representing an IOU, or a string
66
87
  * representing an integer amount
@@ -88,7 +109,7 @@ class Amount extends SerializedType {
88
109
  return new Amount(amount)
89
110
  }
90
111
 
91
- if (isAmountObject(value)) {
112
+ if (isAmountObjectIOU(value)) {
92
113
  const number = new BigNumber(value.value)
93
114
  Amount.assertIouIsValid(number)
94
115
 
@@ -124,6 +145,24 @@ class Amount extends SerializedType {
124
145
  return new Amount(concat([amount, currency, issuer]))
125
146
  }
126
147
 
148
+ if (isAmountObjectMPT(value)) {
149
+ Amount.assertMptIsValid(value.value)
150
+
151
+ let leadingByte = new Uint8Array(1)
152
+ leadingByte[0] |= 0x60
153
+
154
+ const num = BigInt(value.value)
155
+
156
+ const intBuf = [new Uint8Array(4), new Uint8Array(4)]
157
+ writeUInt32BE(intBuf[0], Number(num >> BigInt(32)), 0)
158
+ writeUInt32BE(intBuf[1], Number(num & BigInt(mask)), 0)
159
+
160
+ amount = concat(intBuf)
161
+
162
+ const mptIssuanceID = Hash192.from(value.mpt_issuance_id).toBytes()
163
+ return new Amount(concat([leadingByte, amount, mptIssuanceID]))
164
+ }
165
+
127
166
  throw new Error('Invalid type to construct an Amount')
128
167
  }
129
168
 
@@ -134,8 +173,12 @@ class Amount extends SerializedType {
134
173
  * @returns An Amount object
135
174
  */
136
175
  static fromParser(parser: BinaryParser): Amount {
137
- const isXRP = parser.peek() & 0x80
138
- const numBytes = isXRP ? 48 : 8
176
+ const isIOU = parser.peek() & 0x80
177
+ if (isIOU) return new Amount(parser.read(48))
178
+
179
+ // the amount can be either MPT or XRP at this point
180
+ const isMPT = parser.peek() & 0x20
181
+ const numBytes = isMPT ? 33 : 8
139
182
  return new Amount(parser.read(numBytes))
140
183
  }
141
184
 
@@ -156,7 +199,9 @@ class Amount extends SerializedType {
156
199
  const num = (msb << BigInt(32)) | lsb
157
200
 
158
201
  return `${sign}${num.toString()}`
159
- } else {
202
+ }
203
+
204
+ if (this.isIOU()) {
160
205
  const parser = new BinaryParser(this.toString())
161
206
  const mantissa = parser.read(8)
162
207
  const currency = Currency.fromParser(parser) as Currency
@@ -182,6 +227,27 @@ class Amount extends SerializedType {
182
227
  issuer: issuer.toJSON(),
183
228
  }
184
229
  }
230
+
231
+ if (this.isMPT()) {
232
+ const parser = new BinaryParser(this.toString())
233
+ const leadingByte = parser.read(1)
234
+ const amount = parser.read(8)
235
+ const mptID = Hash192.fromParser(parser) as Hash192
236
+
237
+ const isPositive = leadingByte[0] & 0x40
238
+ const sign = isPositive ? '' : '-'
239
+
240
+ const msb = BigInt(readUInt32BE(amount.slice(0, 4), 0))
241
+ const lsb = BigInt(readUInt32BE(amount.slice(4), 0))
242
+ const num = (msb << BigInt(32)) | lsb
243
+
244
+ return {
245
+ value: `${sign}${num.toString()}`,
246
+ mpt_issuance_id: mptID.toString(),
247
+ }
248
+ }
249
+
250
+ throw new Error('Invalid amount to construct JSON')
185
251
  }
186
252
 
187
253
  /**
@@ -224,6 +290,29 @@ class Amount extends SerializedType {
224
290
  }
225
291
  }
226
292
 
293
+ /**
294
+ * Validate MPT.value amount
295
+ *
296
+ * @param decimal BigNumber object representing MPT.value
297
+ * @returns void, but will throw if invalid amount
298
+ */
299
+ private static assertMptIsValid(amount: string): void {
300
+ if (amount.indexOf('.') !== -1) {
301
+ throw new Error(`${amount.toString()} is an illegal amount`)
302
+ }
303
+
304
+ const decimal = new BigNumber(amount)
305
+ if (!decimal.isZero()) {
306
+ if (decimal < BigNumber(0)) {
307
+ throw new Error(`${amount.toString()} is an illegal amount`)
308
+ }
309
+
310
+ if (Number(BigInt(amount) & BigInt(mptMask)) != 0) {
311
+ throw new Error(`${amount.toString()} is an illegal amount`)
312
+ }
313
+ }
314
+ }
315
+
227
316
  /**
228
317
  * Ensure that the value after being multiplied by the exponent does not
229
318
  * contain a decimal.
@@ -248,7 +337,25 @@ class Amount extends SerializedType {
248
337
  * @returns true if Native (XRP)
249
338
  */
250
339
  private isNative(): boolean {
251
- return (this.bytes[0] & 0x80) === 0
340
+ return (this.bytes[0] & 0x80) === 0 && (this.bytes[0] & 0x20) === 0
341
+ }
342
+
343
+ /**
344
+ * Test if this amount is in units of MPT
345
+ *
346
+ * @returns true if MPT
347
+ */
348
+ private isMPT(): boolean {
349
+ return (this.bytes[0] & 0x80) === 0 && (this.bytes[0] & 0x20) !== 0
350
+ }
351
+
352
+ /**
353
+ * Test if this amount is in units of IOU
354
+ *
355
+ * @returns true if IOU
356
+ */
357
+ private isIOU(): boolean {
358
+ return (this.bytes[0] & 0x80) !== 0
252
359
  }
253
360
  }
254
361
 
package/src/types/blob.ts CHANGED
@@ -33,6 +33,9 @@ class Blob extends SerializedType {
33
33
  }
34
34
 
35
35
  if (typeof value === 'string') {
36
+ if (!/^[A-F0-9]*$/iu.test(value)) {
37
+ throw new Error('Cannot construct Blob from a non-hex string')
38
+ }
36
39
  return new Blob(hexToBytes(value))
37
40
  }
38
41
 
@@ -0,0 +1,19 @@
1
+ import { Hash } from './hash'
2
+
3
+ /**
4
+ * Hash with a width of 192 bits
5
+ */
6
+ class Hash192 extends Hash {
7
+ static readonly width = 24
8
+ static readonly ZERO_192: Hash192 = new Hash192(new Uint8Array(Hash192.width))
9
+
10
+ constructor(bytes?: Uint8Array) {
11
+ if (bytes && bytes.byteLength === 0) {
12
+ bytes = Hash192.ZERO_192.bytes
13
+ }
14
+
15
+ super(bytes ?? Hash192.ZERO_192.bytes)
16
+ }
17
+ }
18
+
19
+ export { Hash192 }
@@ -4,6 +4,7 @@ import { Blob } from './blob'
4
4
  import { Currency } from './currency'
5
5
  import { Hash128 } from './hash-128'
6
6
  import { Hash160 } from './hash-160'
7
+ import { Hash192 } from './hash-192'
7
8
  import { Hash256 } from './hash-256'
8
9
  import { Issue } from './issue'
9
10
  import { PathSet } from './path-set'
@@ -25,6 +26,7 @@ const coreTypes: Record<string, typeof SerializedType> = {
25
26
  Currency,
26
27
  Hash128,
27
28
  Hash160,
29
+ Hash192,
28
30
  Hash256,
29
31
  Issue,
30
32
  PathSet,
@@ -51,6 +53,7 @@ export {
51
53
  Currency,
52
54
  Hash128,
53
55
  Hash160,
56
+ Hash192,
54
57
  Hash256,
55
58
  PathSet,
56
59
  STArray,
@@ -67,7 +67,7 @@ class SerializedType {
67
67
  * Can be customized for sidechains and amendments.
68
68
  * @returns any type, if not overloaded returns hexString representation of bytes
69
69
  */
70
- toJSON(_definitions?: XrplDefinitionsBase): JSON {
70
+ toJSON(_definitions?: XrplDefinitionsBase, _fieldName?: string): JSON {
71
71
  return this.toHex()
72
72
  }
73
73
 
@@ -14,7 +14,13 @@ const OBJECT_END_MARKER = Uint8Array.from([0xe1])
14
14
  */
15
15
  function isObjects(args): args is Array<JsonObject> {
16
16
  return (
17
- Array.isArray(args) && (args.length === 0 || typeof args[0] === 'object')
17
+ Array.isArray(args) &&
18
+ args.every(
19
+ (arg) =>
20
+ typeof arg === 'object' &&
21
+ Object.keys(arg).length === 1 &&
22
+ typeof Object.values(arg)[0] === 'object',
23
+ )
18
24
  )
19
25
  }
20
26
 
@@ -10,6 +10,7 @@ import { BinaryParser } from '../serdes/binary-parser'
10
10
  import { BinarySerializer, BytesList } from '../serdes/binary-serializer'
11
11
 
12
12
  import { STArray } from './st-array'
13
+ import { UInt64 } from './uint-64'
13
14
 
14
15
  const OBJECT_END_MARKER_BYTE = Uint8Array.from([0xe1])
15
16
  const OBJECT_END_MARKER = 'ObjectEndMarker'
@@ -137,6 +138,8 @@ class STObject extends SerializedType {
137
138
  ? this.from(xAddressDecoded[field.name], undefined, definitions)
138
139
  : field.type.name === 'STArray'
139
140
  ? STArray.from(xAddressDecoded[field.name], definitions)
141
+ : field.type.name === 'UInt64'
142
+ ? UInt64.from(xAddressDecoded[field.name], field.name)
140
143
  : field.associatedType.from(xAddressDecoded[field.name])
141
144
 
142
145
  if (associatedValue == undefined) {
@@ -182,7 +185,7 @@ class STObject extends SerializedType {
182
185
 
183
186
  accumulator[field.name] = objectParser
184
187
  .readFieldValue(field)
185
- .toJSON(definitions)
188
+ .toJSON(definitions, field.name)
186
189
  }
187
190
 
188
191
  return accumulator
@@ -2,10 +2,20 @@ import { UInt } from './uint'
2
2
  import { BinaryParser } from '../serdes/binary-parser'
3
3
  import { bytesToHex, concat, hexToBytes } from '@xrplf/isomorphic/utils'
4
4
  import { readUInt32BE, writeUInt32BE } from '../utils'
5
+ import { DEFAULT_DEFINITIONS, XrplDefinitionsBase } from '../enums'
5
6
 
6
7
  const HEX_REGEX = /^[a-fA-F0-9]{1,16}$/
8
+ const BASE10_REGEX = /^[0-9]{1,20}$/
7
9
  const mask = BigInt(0x00000000ffffffff)
8
10
 
11
+ function useBase10(fieldName: string): boolean {
12
+ return (
13
+ fieldName === 'MaximumAmount' ||
14
+ fieldName === 'OutstandingAmount' ||
15
+ fieldName === 'MPTAmount'
16
+ )
17
+ }
18
+
9
19
  /**
10
20
  * Derived UInt class for serializing/deserializing 64 bit UInt
11
21
  */
@@ -29,7 +39,10 @@ class UInt64 extends UInt {
29
39
  * @param val A UInt64, hex-string, bigInt, or number
30
40
  * @returns A UInt64 object
31
41
  */
32
- static from<T extends UInt64 | string | bigint | number>(val: T): UInt64 {
42
+ static from<T extends UInt64 | string | bigint | number>(
43
+ val: T,
44
+ fieldName = '',
45
+ ): UInt64 {
33
46
  if (val instanceof UInt64) {
34
47
  return val
35
48
  }
@@ -51,11 +64,18 @@ class UInt64 extends UInt {
51
64
  }
52
65
 
53
66
  if (typeof val === 'string') {
54
- if (!HEX_REGEX.test(val)) {
67
+ if (useBase10(fieldName)) {
68
+ if (!BASE10_REGEX.test(val)) {
69
+ throw new Error(`${fieldName} ${val} is not a valid base 10 string`)
70
+ }
71
+ val = BigInt(val).toString(16) as T
72
+ }
73
+
74
+ if (typeof val === 'string' && !HEX_REGEX.test(val)) {
55
75
  throw new Error(`${val} is not a valid hex-string`)
56
76
  }
57
77
 
58
- const strBuf = val.padStart(16, '0')
78
+ const strBuf = (val as string).padStart(16, '0')
59
79
  buf = hexToBytes(strBuf)
60
80
  return new UInt64(buf)
61
81
  }
@@ -76,8 +96,16 @@ class UInt64 extends UInt {
76
96
  *
77
97
  * @returns a hex-string
78
98
  */
79
- toJSON(): string {
80
- return bytesToHex(this.bytes)
99
+ toJSON(
100
+ _definitions: XrplDefinitionsBase = DEFAULT_DEFINITIONS,
101
+ fieldName = '',
102
+ ): string {
103
+ const hexString = bytesToHex(this.bytes)
104
+ if (useBase10(fieldName)) {
105
+ return BigInt('0x' + hexString).toString(10)
106
+ }
107
+
108
+ return hexString
81
109
  }
82
110
 
83
111
  /**