@transia/ripple-binary-codec 2.5.4-alpha.0 → 2.5.5-alpha.2
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.
- package/dist/enums/definitions.json +1308 -974
- package/dist/enums/src/enums/definitions.json +1313 -974
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/src/enums/definitions.json +1308 -974
- package/dist/src/index.js +0 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/hash-128.js +1 -1
- package/dist/src/types/hash-128.js.map +1 -1
- package/dist/src/types/hash-160.js +1 -1
- package/dist/src/types/hash-160.js.map +1 -1
- package/dist/src/types/hash-192.js +1 -1
- package/dist/src/types/hash-192.js.map +1 -1
- package/dist/src/types/index.d.ts +2 -1
- package/dist/src/types/index.js +4 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/int-32.d.ts +33 -0
- package/dist/src/types/int-32.js +64 -0
- package/dist/src/types/int-32.js.map +1 -0
- package/dist/src/types/int.d.ts +38 -0
- package/dist/src/types/int.js +57 -0
- package/dist/src/types/int.js.map +1 -0
- package/dist/src/types/issue.d.ts +10 -13
- package/dist/src/types/issue.js +42 -29
- package/dist/src/types/issue.js.map +1 -1
- package/dist/src/types/st-number.js +88 -33
- package/dist/src/types/st-number.js.map +1 -1
- package/dist/src/types/st-object.d.ts +3 -3
- package/dist/src/types/st-object.js +12 -12
- package/dist/src/types/st-object.js.map +1 -1
- package/dist/src/types/uint-16.js +2 -2
- package/dist/src/types/uint-16.js.map +1 -1
- package/dist/src/types/uint-32.js +1 -1
- package/dist/src/types/uint-32.js.map +1 -1
- package/dist/src/types/uint-64.js +1 -1
- package/dist/src/types/uint-64.js.map +1 -1
- package/dist/src/types/uint-8.js +1 -1
- package/dist/src/types/uint-8.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/hash-128.js +1 -1
- package/dist/types/hash-128.js.map +1 -1
- package/dist/types/hash-160.js +1 -1
- package/dist/types/hash-160.js.map +1 -1
- package/dist/types/hash-192.js +1 -1
- package/dist/types/hash-192.js.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +4 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/int-32.d.ts +33 -0
- package/dist/types/int-32.js +64 -0
- package/dist/types/int-32.js.map +1 -0
- package/dist/types/int.d.ts +38 -0
- package/dist/types/int.js +57 -0
- package/dist/types/int.js.map +1 -0
- package/dist/types/issue.d.ts +10 -13
- package/dist/types/issue.js +42 -29
- package/dist/types/issue.js.map +1 -1
- package/dist/types/st-number.js +88 -33
- package/dist/types/st-number.js.map +1 -1
- package/dist/types/st-object.d.ts +3 -3
- package/dist/types/st-object.js +12 -12
- package/dist/types/st-object.js.map +1 -1
- package/dist/types/uint-16.js +2 -2
- package/dist/types/uint-16.js.map +1 -1
- package/dist/types/uint-32.js +1 -1
- package/dist/types/uint-32.js.map +1 -1
- package/dist/types/uint-64.js +1 -1
- package/dist/types/uint-64.js.map +1 -1
- package/dist/types/uint-8.js +1 -1
- package/dist/types/uint-8.js.map +1 -1
- package/package.json +8 -5
- package/src/enums/definitions.json +1313 -974
- package/src/index.ts +1 -3
- package/src/types/hash-128.ts +1 -1
- package/src/types/hash-160.ts +1 -1
- package/src/types/hash-192.ts +1 -1
- package/src/types/index.ts +3 -0
- package/src/types/int-32.ts +72 -0
- package/src/types/int.ts +75 -0
- package/src/types/issue.ts +90 -57
- package/src/types/st-number.ts +107 -31
- package/src/types/st-object.ts +77 -73
- package/src/types/uint-16.ts +2 -2
- package/src/types/uint-32.ts +1 -1
- package/src/types/uint-64.ts +1 -1
- package/src/types/uint-8.ts +1 -1
package/src/index.ts
CHANGED
|
@@ -100,9 +100,7 @@ function encodeForMultisigning(
|
|
|
100
100
|
if (typeof json !== 'object') {
|
|
101
101
|
throw new Error()
|
|
102
102
|
}
|
|
103
|
-
|
|
104
|
-
throw new Error()
|
|
105
|
-
}
|
|
103
|
+
|
|
106
104
|
const definitionsOpt = definitions ? { definitions } : undefined
|
|
107
105
|
return bytesToHex(
|
|
108
106
|
multiSigningData(json as JsonObject, signer, definitionsOpt),
|
package/src/types/hash-128.ts
CHANGED
|
@@ -10,7 +10,7 @@ class Hash128 extends Hash {
|
|
|
10
10
|
static readonly ZERO_128: Hash128 = new Hash128(new Uint8Array(Hash128.width))
|
|
11
11
|
|
|
12
12
|
constructor(bytes: Uint8Array) {
|
|
13
|
-
if (bytes
|
|
13
|
+
if (bytes?.byteLength === 0) {
|
|
14
14
|
bytes = Hash128.ZERO_128.bytes
|
|
15
15
|
}
|
|
16
16
|
|
package/src/types/hash-160.ts
CHANGED
|
@@ -9,7 +9,7 @@ class Hash160 extends Hash {
|
|
|
9
9
|
static readonly ZERO_160: Hash160 = new Hash160(new Uint8Array(Hash160.width))
|
|
10
10
|
|
|
11
11
|
constructor(bytes?: Uint8Array) {
|
|
12
|
-
if (bytes
|
|
12
|
+
if (bytes?.byteLength === 0) {
|
|
13
13
|
bytes = Hash160.ZERO_160.bytes
|
|
14
14
|
}
|
|
15
15
|
|
package/src/types/hash-192.ts
CHANGED
|
@@ -9,7 +9,7 @@ class Hash192 extends Hash {
|
|
|
9
9
|
static readonly ZERO_192: Hash192 = new Hash192(new Uint8Array(Hash192.width))
|
|
10
10
|
|
|
11
11
|
constructor(bytes?: Uint8Array) {
|
|
12
|
-
if (bytes
|
|
12
|
+
if (bytes?.byteLength === 0) {
|
|
13
13
|
bytes = Hash192.ZERO_192.bytes
|
|
14
14
|
}
|
|
15
15
|
|
package/src/types/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Hash128 } from './hash-128'
|
|
|
8
8
|
import { Hash160 } from './hash-160'
|
|
9
9
|
import { Hash192 } from './hash-192'
|
|
10
10
|
import { Hash256 } from './hash-256'
|
|
11
|
+
import { Int32 } from './int-32'
|
|
11
12
|
import { Issue } from './issue'
|
|
12
13
|
import { STNumber } from './st-number'
|
|
13
14
|
import { PathSet } from './path-set'
|
|
@@ -33,6 +34,7 @@ const coreTypes: Record<string, typeof SerializedType> = {
|
|
|
33
34
|
Hash160,
|
|
34
35
|
Hash192,
|
|
35
36
|
Hash256,
|
|
37
|
+
Int32,
|
|
36
38
|
Issue,
|
|
37
39
|
Number: STNumber,
|
|
38
40
|
PathSet,
|
|
@@ -63,6 +65,7 @@ export {
|
|
|
63
65
|
Hash160,
|
|
64
66
|
Hash192,
|
|
65
67
|
Hash256,
|
|
68
|
+
Int32,
|
|
66
69
|
PathSet,
|
|
67
70
|
STArray,
|
|
68
71
|
STObject,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Int } from './int'
|
|
2
|
+
import { BinaryParser } from '../serdes/binary-parser'
|
|
3
|
+
import { readInt32BE, writeInt32BE } from '../utils'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Derived Int class for serializing/deserializing signed 32-bit integers.
|
|
7
|
+
*/
|
|
8
|
+
class Int32 extends Int {
|
|
9
|
+
protected static readonly width: number = 32 / 8 // 4 bytes
|
|
10
|
+
static readonly defaultInt32: Int32 = new Int32(new Uint8Array(Int32.width))
|
|
11
|
+
|
|
12
|
+
// Signed 32-bit integer range
|
|
13
|
+
static readonly MIN_VALUE: number = -2147483648 // -2^31
|
|
14
|
+
static readonly MAX_VALUE: number = 2147483647 // 2^31 - 1
|
|
15
|
+
|
|
16
|
+
constructor(bytes: Uint8Array) {
|
|
17
|
+
super(bytes ?? Int32.defaultInt32.bytes)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Construct an Int32 from a BinaryParser
|
|
22
|
+
*
|
|
23
|
+
* @param parser BinaryParser to read Int32 from
|
|
24
|
+
* @returns An Int32 object
|
|
25
|
+
*/
|
|
26
|
+
static fromParser(parser: BinaryParser): Int {
|
|
27
|
+
return new Int32(parser.read(Int32.width))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Construct an Int32 object from a number or string
|
|
32
|
+
*
|
|
33
|
+
* @param val Int32 object, number, or string
|
|
34
|
+
* @returns An Int32 object
|
|
35
|
+
*/
|
|
36
|
+
static from<T extends Int32 | number | string>(val: T): Int32 {
|
|
37
|
+
if (val instanceof Int32) {
|
|
38
|
+
return val
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const buf = new Uint8Array(Int32.width)
|
|
42
|
+
|
|
43
|
+
if (typeof val === 'string') {
|
|
44
|
+
const num = Number(val)
|
|
45
|
+
if (!Number.isFinite(num) || !Number.isInteger(num)) {
|
|
46
|
+
throw new Error(`Cannot construct Int32 from string: ${val}`)
|
|
47
|
+
}
|
|
48
|
+
Int32.checkIntRange('Int32', num, Int32.MIN_VALUE, Int32.MAX_VALUE)
|
|
49
|
+
writeInt32BE(buf, num, 0)
|
|
50
|
+
return new Int32(buf)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof val === 'number' && Number.isInteger(val)) {
|
|
54
|
+
Int32.checkIntRange('Int32', val, Int32.MIN_VALUE, Int32.MAX_VALUE)
|
|
55
|
+
writeInt32BE(buf, val, 0)
|
|
56
|
+
return new Int32(buf)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw new Error('Cannot construct Int32 from given value')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the value of the Int32 object
|
|
64
|
+
*
|
|
65
|
+
* @returns the signed 32-bit integer represented by this.bytes
|
|
66
|
+
*/
|
|
67
|
+
valueOf(): number {
|
|
68
|
+
return readInt32BE(this.bytes, 0)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { Int32 }
|
package/src/types/int.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Comparable } from './serialized-type'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compare numbers and bigInts n1 and n2
|
|
5
|
+
*
|
|
6
|
+
* @param n1 First object to compare
|
|
7
|
+
* @param n2 Second object to compare
|
|
8
|
+
* @returns -1, 0, or 1, depending on how the two objects compare
|
|
9
|
+
*/
|
|
10
|
+
function compare(n1: number | bigint, n2: number | bigint): number {
|
|
11
|
+
return n1 < n2 ? -1 : n1 == n2 ? 0 : 1
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base class for serializing and deserializing signed integers.
|
|
16
|
+
*/
|
|
17
|
+
abstract class Int extends Comparable<Int | number> {
|
|
18
|
+
protected static width: number
|
|
19
|
+
|
|
20
|
+
constructor(bytes: Uint8Array) {
|
|
21
|
+
super(bytes)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Overload of compareTo for Comparable
|
|
26
|
+
*
|
|
27
|
+
* @param other other Int to compare this to
|
|
28
|
+
* @returns -1, 0, or 1 depending on how the objects relate to each other
|
|
29
|
+
*/
|
|
30
|
+
compareTo(other: Int | number): number {
|
|
31
|
+
return compare(this.valueOf(), other.valueOf())
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Convert an Int object to JSON
|
|
36
|
+
*
|
|
37
|
+
* @returns number or string represented by this.bytes
|
|
38
|
+
*/
|
|
39
|
+
toJSON(): number | string {
|
|
40
|
+
const val = this.valueOf()
|
|
41
|
+
return typeof val === 'number' ? val : val.toString()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the value of the Int represented by this.bytes
|
|
46
|
+
*
|
|
47
|
+
* @returns the value
|
|
48
|
+
*/
|
|
49
|
+
abstract valueOf(): number | bigint
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Validate that a number is within the specified signed integer range
|
|
53
|
+
*
|
|
54
|
+
* @param typeName The name of the type (for error messages)
|
|
55
|
+
* @param val The number to validate
|
|
56
|
+
* @param min The minimum allowed value
|
|
57
|
+
* @param max The maximum allowed value
|
|
58
|
+
* @throws Error if the value is out of range
|
|
59
|
+
*/
|
|
60
|
+
// eslint-disable-next-line max-params -- for error clarity in browsers
|
|
61
|
+
static checkIntRange(
|
|
62
|
+
typeName: string,
|
|
63
|
+
val: number | bigint,
|
|
64
|
+
min: number | bigint,
|
|
65
|
+
max: number | bigint,
|
|
66
|
+
): void {
|
|
67
|
+
if (val < min || val > max) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Invalid ${typeName}: ${val} must be >= ${min} and <= ${max}`,
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { Int }
|
package/src/types/issue.ts
CHANGED
|
@@ -1,140 +1,173 @@
|
|
|
1
|
-
import { concat } from
|
|
2
|
-
import { BinaryParser } from
|
|
3
|
-
|
|
4
|
-
import { AccountID } from
|
|
5
|
-
import { Currency } from
|
|
6
|
-
import {
|
|
7
|
-
|
|
1
|
+
import { bytesToHex, concat } from "@transia/isomorphic/utils";
|
|
2
|
+
import { BinaryParser } from "../serdes/binary-parser";
|
|
3
|
+
|
|
4
|
+
import { AccountID } from "./account-id";
|
|
5
|
+
import { Currency } from "./currency";
|
|
6
|
+
import {
|
|
7
|
+
JsonObject,
|
|
8
|
+
SerializedType,
|
|
9
|
+
SerializedTypeID,
|
|
10
|
+
} from "./serialized-type";
|
|
11
|
+
import { Hash192 } from "./hash-192";
|
|
12
|
+
import { readUInt32BE, writeUInt32BE } from "../utils";
|
|
8
13
|
|
|
9
14
|
interface XRPIssue extends JsonObject {
|
|
10
|
-
currency: string
|
|
15
|
+
currency: string;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
interface IOUIssue extends JsonObject {
|
|
14
|
-
currency: string
|
|
15
|
-
issuer: string
|
|
19
|
+
currency: string;
|
|
20
|
+
issuer: string;
|
|
16
21
|
}
|
|
17
22
|
interface MPTIssue extends JsonObject {
|
|
18
|
-
mpt_issuance_id: string
|
|
23
|
+
mpt_issuance_id: string;
|
|
19
24
|
}
|
|
20
25
|
/**
|
|
21
26
|
* Interface for JSON objects that represent issues
|
|
22
27
|
*/
|
|
23
|
-
type IssueObject = XRPIssue | IOUIssue | MPTIssue
|
|
28
|
+
type IssueObject = XRPIssue | IOUIssue | MPTIssue;
|
|
24
29
|
|
|
25
30
|
/**
|
|
26
31
|
* Type guard for Issue Object
|
|
27
32
|
*/
|
|
28
33
|
function isIssueObject(arg): arg is IssueObject {
|
|
29
|
-
const keys = Object.keys(arg).sort()
|
|
30
|
-
const isXRP = keys.length === 1 && keys[0] ===
|
|
34
|
+
const keys = Object.keys(arg).sort();
|
|
35
|
+
const isXRP = keys.length === 1 && keys[0] === "currency";
|
|
31
36
|
const isIOU =
|
|
32
|
-
keys.length === 2 && keys[0] ===
|
|
33
|
-
const isMPT = keys.length === 1 && keys[0] ===
|
|
37
|
+
keys.length === 2 && keys[0] === "currency" && keys[1] === "issuer";
|
|
38
|
+
const isMPT = keys.length === 1 && keys[0] === "mpt_issuance_id";
|
|
34
39
|
|
|
35
|
-
return isXRP || isIOU || isMPT
|
|
40
|
+
return isXRP || isIOU || isMPT;
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
const MPT_WIDTH = 44;
|
|
44
|
+
const NO_ACCOUNT = AccountID.from("0000000000000000000000000000000000000001");
|
|
45
|
+
|
|
38
46
|
/**
|
|
39
|
-
* Class for serializing/Deserializing
|
|
47
|
+
* Class for serializing/Deserializing Issue
|
|
40
48
|
*/
|
|
41
49
|
class Issue extends SerializedType {
|
|
42
|
-
static readonly
|
|
50
|
+
static readonly XRP_ISSUE: Issue = new Issue(new Uint8Array(20));
|
|
43
51
|
|
|
44
52
|
constructor(bytes: Uint8Array) {
|
|
45
|
-
super(bytes ?? Issue.
|
|
53
|
+
super(bytes ?? Issue.XRP_ISSUE.bytes);
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
|
-
* Construct
|
|
57
|
+
* Construct Issue from XRPIssue, IOUIssue or MPTIssue
|
|
50
58
|
*
|
|
51
|
-
* @param value An
|
|
52
|
-
* representing an integer amount
|
|
59
|
+
* @param value An object representing an XRPIssue, IOUIssue or MPTIssue
|
|
53
60
|
* @returns An Issue object
|
|
54
61
|
*/
|
|
55
62
|
static from<T extends Issue | IssueObject>(value: T): Issue {
|
|
56
63
|
if (value instanceof Issue) {
|
|
57
|
-
return value
|
|
64
|
+
return value;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
if (isIssueObject(value)) {
|
|
61
68
|
if (value.currency) {
|
|
62
|
-
const currency = Currency.from(value.currency.toString()).toBytes()
|
|
69
|
+
const currency = Currency.from(value.currency.toString()).toBytes();
|
|
63
70
|
|
|
64
71
|
//IOU case
|
|
65
72
|
if (value.issuer) {
|
|
66
|
-
const issuer = AccountID.from(value.issuer.toString()).toBytes()
|
|
67
|
-
return new Issue(concat([currency, issuer]))
|
|
73
|
+
const issuer = AccountID.from(value.issuer.toString()).toBytes();
|
|
74
|
+
return new Issue(concat([currency, issuer]));
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
//XRP case
|
|
71
|
-
return new Issue(currency)
|
|
78
|
+
return new Issue(currency);
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
// MPT case
|
|
75
82
|
if (value.mpt_issuance_id) {
|
|
76
83
|
const mptIssuanceIdBytes = Hash192.from(
|
|
77
84
|
value.mpt_issuance_id.toString(),
|
|
78
|
-
).toBytes()
|
|
79
|
-
|
|
85
|
+
).toBytes();
|
|
86
|
+
const issuerAccount = mptIssuanceIdBytes.slice(4);
|
|
87
|
+
const sequence = Number(
|
|
88
|
+
readUInt32BE(mptIssuanceIdBytes.slice(0, 4), 0),
|
|
89
|
+
); // sequence is in Big-endian format in mpt_issuance_id
|
|
90
|
+
|
|
91
|
+
// Convert to Little-endian
|
|
92
|
+
const sequenceBuffer = new Uint8Array(4);
|
|
93
|
+
new DataView(sequenceBuffer.buffer).setUint32(0, sequence, true);
|
|
94
|
+
|
|
95
|
+
return new Issue(
|
|
96
|
+
concat([issuerAccount, NO_ACCOUNT.toBytes(), sequenceBuffer]),
|
|
97
|
+
);
|
|
80
98
|
}
|
|
81
99
|
}
|
|
82
100
|
|
|
83
|
-
throw new Error(
|
|
101
|
+
throw new Error("Invalid type to construct an Issue");
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
/**
|
|
87
|
-
* Read
|
|
105
|
+
* Read Issue from a BinaryParser
|
|
88
106
|
*
|
|
89
|
-
* @param parser BinaryParser to read the
|
|
90
|
-
* @param hint The number of bytes to consume from the parser.
|
|
91
|
-
* For an MPT amount, pass 24 (the fixed length for Hash192).
|
|
107
|
+
* @param parser BinaryParser to read the Issue from
|
|
92
108
|
*
|
|
93
109
|
* @returns An Issue object
|
|
94
110
|
*/
|
|
95
|
-
static fromParser(parser: BinaryParser
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
111
|
+
static fromParser(parser: BinaryParser): Issue {
|
|
112
|
+
// XRP
|
|
113
|
+
const currencyOrAccount = parser.read(20);
|
|
114
|
+
if (new Currency(currencyOrAccount).toJSON() === "XRP") {
|
|
115
|
+
return new Issue(currencyOrAccount);
|
|
99
116
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
|
|
118
|
+
// MPT
|
|
119
|
+
const issuerAccountId = new AccountID(parser.read(20));
|
|
120
|
+
if (NO_ACCOUNT.toHex() === issuerAccountId.toHex()) {
|
|
121
|
+
const sequence = parser.read(4);
|
|
122
|
+
return new Issue(
|
|
123
|
+
concat([currencyOrAccount, NO_ACCOUNT.toBytes(), sequence]),
|
|
124
|
+
);
|
|
103
125
|
}
|
|
104
|
-
|
|
105
|
-
|
|
126
|
+
|
|
127
|
+
// IOU
|
|
128
|
+
return new Issue(concat([currencyOrAccount, issuerAccountId.toBytes()]));
|
|
106
129
|
}
|
|
107
130
|
|
|
108
131
|
/**
|
|
109
|
-
* Get the JSON representation of this
|
|
132
|
+
* Get the JSON representation of this IssueObject
|
|
110
133
|
*
|
|
111
134
|
* @returns the JSON interpretation of this.bytes
|
|
112
135
|
*/
|
|
113
136
|
toJSON(): IssueObject {
|
|
114
|
-
// If the buffer is exactly
|
|
115
|
-
if (this.toBytes().length ===
|
|
137
|
+
// If the buffer is exactly 44 bytes, treat it as an MPTIssue.
|
|
138
|
+
if (this.toBytes().length === MPT_WIDTH) {
|
|
139
|
+
const issuerAccount = this.toBytes().slice(0, 20);
|
|
140
|
+
const sequence = new DataView(this.toBytes().slice(40).buffer).getUint32(
|
|
141
|
+
0,
|
|
142
|
+
true,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// sequence part of mpt_issuance_id should be in Big-endian
|
|
146
|
+
const sequenceBuffer = new Uint8Array(4);
|
|
147
|
+
writeUInt32BE(sequenceBuffer, sequence, 0);
|
|
148
|
+
|
|
116
149
|
return {
|
|
117
|
-
mpt_issuance_id:
|
|
118
|
-
}
|
|
150
|
+
mpt_issuance_id: bytesToHex(concat([sequenceBuffer, issuerAccount])),
|
|
151
|
+
};
|
|
119
152
|
}
|
|
120
153
|
|
|
121
|
-
const parser = new BinaryParser(this.toString())
|
|
154
|
+
const parser = new BinaryParser(this.toString());
|
|
122
155
|
|
|
123
|
-
const currency = Currency.fromParser(parser) as Currency
|
|
124
|
-
if (currency.toJSON() ===
|
|
125
|
-
return { currency: currency.toJSON() }
|
|
156
|
+
const currency = Currency.fromParser(parser) as Currency;
|
|
157
|
+
if (currency.toJSON() === "XRP") {
|
|
158
|
+
return { currency: currency.toJSON() };
|
|
126
159
|
}
|
|
127
|
-
const issuer = AccountID.fromParser(parser) as AccountID
|
|
160
|
+
const issuer = AccountID.fromParser(parser) as AccountID;
|
|
128
161
|
|
|
129
162
|
return {
|
|
130
163
|
currency: currency.toJSON(),
|
|
131
164
|
issuer: issuer.toJSON(),
|
|
132
|
-
}
|
|
165
|
+
};
|
|
133
166
|
}
|
|
134
167
|
|
|
135
168
|
getSType(): SerializedTypeID {
|
|
136
|
-
return SerializedTypeID.STI_ISSUE
|
|
169
|
+
return SerializedTypeID.STI_ISSUE;
|
|
137
170
|
}
|
|
138
171
|
}
|
|
139
172
|
|
|
140
|
-
export { Issue, IssueObject }
|
|
173
|
+
export { Issue, IssueObject };
|
package/src/types/st-number.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable complexity -- required for various checks */
|
|
1
2
|
import { BinaryParser } from '../serdes/binary-parser'
|
|
2
3
|
import { SerializedType, SerializedTypeID } 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('
|
|
10
|
-
const MAX_MANTISSA = BigInt('
|
|
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
|
|
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
|
-
|
|
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
|
|
163
|
-
|
|
164
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
//
|
|
219
|
-
const
|
|
220
|
-
|
|
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
|
|
227
|
-
|
|
228
|
-
const
|
|
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 : ''
|