MPT Support for library and binary codec
This commit is contained in:
Shawn Xie
2024-12-11 13:38:13 -08:00
committed by GitHub
parent e3188b83ed
commit b04efe8c9e
47 changed files with 3042 additions and 1067 deletions

View File

@@ -2,6 +2,9 @@
## Unreleased
### Added
* Support for the Multi-Purpose Token amendment (XLS-33)
## 2.1.0 (2024-06-03)
### Added

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@ import { JsonObject, SerializedType } from './serialized-type'
import BigNumber from 'bignumber.js'
import { bytesToHex, concat, hexToBytes } from '@xrplf/isomorphic/utils'
import { readUInt32BE, writeUInt32BE } from '../utils'
import { Hash192 } from './hash-192'
/**
* Constants for validating amounts
@@ -16,6 +17,7 @@ const MAX_IOU_PRECISION = 16
const MAX_DROPS = new BigNumber('1e17')
const MIN_XRP = new BigNumber('1e-6')
const mask = BigInt(0x00000000ffffffff)
const mptMask = BigInt(0x8000000000000000)
/**
* BigNumber configuration for Amount IOUs
@@ -27,20 +29,28 @@ BigNumber.config({
],
})
/**
* Interface for JSON objects that represent amounts
*/
interface AmountObject extends JsonObject {
interface AmountObjectIOU extends JsonObject {
value: string
currency: string
issuer: string
}
interface AmountObjectMPT extends JsonObject {
value: string
mpt_issuance_id: string
}
/**
* Type guard for AmountObject
* Interface for JSON objects that represent amounts
*/
function isAmountObject(arg): arg is AmountObject {
type AmountObject = AmountObjectIOU | AmountObjectMPT
/**
* Type guard for AmountObjectIOU
*/
function isAmountObjectIOU(arg): arg is AmountObjectIOU {
const keys = Object.keys(arg).sort()
return (
keys.length === 3 &&
keys[0] === 'currency' &&
@@ -49,6 +59,17 @@ function isAmountObject(arg): arg is AmountObject {
)
}
/**
* Type guard for AmountObjectMPT
*/
function isAmountObjectMPT(arg): arg is AmountObjectMPT {
const keys = Object.keys(arg).sort()
return (
keys.length === 2 && keys[0] === 'mpt_issuance_id' && keys[1] === 'value'
)
}
/**
* Class for serializing/Deserializing Amounts
*/
@@ -60,7 +81,7 @@ class Amount extends SerializedType {
}
/**
* Construct an amount from an IOU or string amount
* Construct an amount from an IOU, MPT or string amount
*
* @param value An Amount, object representing an IOU, or a string
* representing an integer amount
@@ -88,7 +109,7 @@ class Amount extends SerializedType {
return new Amount(amount)
}
if (isAmountObject(value)) {
if (isAmountObjectIOU(value)) {
const number = new BigNumber(value.value)
Amount.assertIouIsValid(number)
@@ -124,6 +145,24 @@ class Amount extends SerializedType {
return new Amount(concat([amount, currency, issuer]))
}
if (isAmountObjectMPT(value)) {
Amount.assertMptIsValid(value.value)
let leadingByte = new Uint8Array(1)
leadingByte[0] |= 0x60
const num = BigInt(value.value)
const intBuf = [new Uint8Array(4), new Uint8Array(4)]
writeUInt32BE(intBuf[0], Number(num >> BigInt(32)), 0)
writeUInt32BE(intBuf[1], Number(num & BigInt(mask)), 0)
amount = concat(intBuf)
const mptIssuanceID = Hash192.from(value.mpt_issuance_id).toBytes()
return new Amount(concat([leadingByte, amount, mptIssuanceID]))
}
throw new Error('Invalid type to construct an Amount')
}
@@ -134,8 +173,12 @@ class Amount extends SerializedType {
* @returns An Amount object
*/
static fromParser(parser: BinaryParser): Amount {
const isXRP = parser.peek() & 0x80
const numBytes = isXRP ? 48 : 8
const isIOU = parser.peek() & 0x80
if (isIOU) return new Amount(parser.read(48))
// the amount can be either MPT or XRP at this point
const isMPT = parser.peek() & 0x20
const numBytes = isMPT ? 33 : 8
return new Amount(parser.read(numBytes))
}
@@ -156,7 +199,9 @@ class Amount extends SerializedType {
const num = (msb << BigInt(32)) | lsb
return `${sign}${num.toString()}`
} else {
}
if (this.isIOU()) {
const parser = new BinaryParser(this.toString())
const mantissa = parser.read(8)
const currency = Currency.fromParser(parser) as Currency
@@ -182,6 +227,27 @@ class Amount extends SerializedType {
issuer: issuer.toJSON(),
}
}
if (this.isMPT()) {
const parser = new BinaryParser(this.toString())
const leadingByte = parser.read(1)
const amount = parser.read(8)
const mptID = Hash192.fromParser(parser) as Hash192
const isPositive = leadingByte[0] & 0x40
const sign = isPositive ? '' : '-'
const msb = BigInt(readUInt32BE(amount.slice(0, 4), 0))
const lsb = BigInt(readUInt32BE(amount.slice(4), 0))
const num = (msb << BigInt(32)) | lsb
return {
value: `${sign}${num.toString()}`,
mpt_issuance_id: mptID.toString(),
}
}
throw new Error('Invalid amount to construct JSON')
}
/**
@@ -224,6 +290,29 @@ class Amount extends SerializedType {
}
}
/**
* Validate MPT.value amount
*
* @param decimal BigNumber object representing MPT.value
* @returns void, but will throw if invalid amount
*/
private static assertMptIsValid(amount: string): void {
if (amount.indexOf('.') !== -1) {
throw new Error(`${amount.toString()} is an illegal amount`)
}
const decimal = new BigNumber(amount)
if (!decimal.isZero()) {
if (decimal < BigNumber(0)) {
throw new Error(`${amount.toString()} is an illegal amount`)
}
if (Number(BigInt(amount) & BigInt(mptMask)) != 0) {
throw new Error(`${amount.toString()} is an illegal amount`)
}
}
}
/**
* Ensure that the value after being multiplied by the exponent does not
* contain a decimal.
@@ -248,7 +337,25 @@ class Amount extends SerializedType {
* @returns true if Native (XRP)
*/
private isNative(): boolean {
return (this.bytes[0] & 0x80) === 0
return (this.bytes[0] & 0x80) === 0 && (this.bytes[0] & 0x20) === 0
}
/**
* Test if this amount is in units of MPT
*
* @returns true if MPT
*/
private isMPT(): boolean {
return (this.bytes[0] & 0x80) === 0 && (this.bytes[0] & 0x20) !== 0
}
/**
* Test if this amount is in units of IOU
*
* @returns true if IOU
*/
private isIOU(): boolean {
return (this.bytes[0] & 0x80) !== 0
}
}

View File

@@ -0,0 +1,19 @@
import { Hash } from './hash'
/**
* Hash with a width of 192 bits
*/
class Hash192 extends Hash {
static readonly width = 24
static readonly ZERO_192: Hash192 = new Hash192(new Uint8Array(Hash192.width))
constructor(bytes?: Uint8Array) {
if (bytes && bytes.byteLength === 0) {
bytes = Hash192.ZERO_192.bytes
}
super(bytes ?? Hash192.ZERO_192.bytes)
}
}
export { Hash192 }

View File

@@ -4,6 +4,7 @@ import { Blob } from './blob'
import { Currency } from './currency'
import { Hash128 } from './hash-128'
import { Hash160 } from './hash-160'
import { Hash192 } from './hash-192'
import { Hash256 } from './hash-256'
import { Issue } from './issue'
import { PathSet } from './path-set'
@@ -25,6 +26,7 @@ const coreTypes: Record<string, typeof SerializedType> = {
Currency,
Hash128,
Hash160,
Hash192,
Hash256,
Issue,
PathSet,
@@ -51,6 +53,7 @@ export {
Currency,
Hash128,
Hash160,
Hash192,
Hash256,
PathSet,
STArray,

View File

@@ -67,7 +67,7 @@ class SerializedType {
* Can be customized for sidechains and amendments.
* @returns any type, if not overloaded returns hexString representation of bytes
*/
toJSON(_definitions?: XrplDefinitionsBase): JSON {
toJSON(_definitions?: XrplDefinitionsBase, _fieldName?: string): JSON {
return this.toHex()
}

View File

@@ -10,6 +10,7 @@ import { BinaryParser } from '../serdes/binary-parser'
import { BinarySerializer, BytesList } from '../serdes/binary-serializer'
import { STArray } from './st-array'
import { UInt64 } from './uint-64'
const OBJECT_END_MARKER_BYTE = Uint8Array.from([0xe1])
const OBJECT_END_MARKER = 'ObjectEndMarker'
@@ -137,6 +138,8 @@ class STObject extends SerializedType {
? this.from(xAddressDecoded[field.name], undefined, definitions)
: field.type.name === 'STArray'
? STArray.from(xAddressDecoded[field.name], definitions)
: field.type.name === 'UInt64'
? UInt64.from(xAddressDecoded[field.name], field.name)
: field.associatedType.from(xAddressDecoded[field.name])
if (associatedValue == undefined) {
@@ -182,7 +185,7 @@ class STObject extends SerializedType {
accumulator[field.name] = objectParser
.readFieldValue(field)
.toJSON(definitions)
.toJSON(definitions, field.name)
}
return accumulator

View File

@@ -2,10 +2,20 @@ import { UInt } from './uint'
import { BinaryParser } from '../serdes/binary-parser'
import { bytesToHex, concat, hexToBytes } from '@xrplf/isomorphic/utils'
import { readUInt32BE, writeUInt32BE } from '../utils'
import { DEFAULT_DEFINITIONS, XrplDefinitionsBase } from '../enums'
const HEX_REGEX = /^[a-fA-F0-9]{1,16}$/
const BASE10_REGEX = /^[0-9]{1,20}$/
const mask = BigInt(0x00000000ffffffff)
function useBase10(fieldName: string): boolean {
return (
fieldName === 'MaximumAmount' ||
fieldName === 'OutstandingAmount' ||
fieldName === 'MPTAmount'
)
}
/**
* Derived UInt class for serializing/deserializing 64 bit UInt
*/
@@ -29,7 +39,10 @@ class UInt64 extends UInt {
* @param val A UInt64, hex-string, bigInt, or number
* @returns A UInt64 object
*/
static from<T extends UInt64 | string | bigint | number>(val: T): UInt64 {
static from<T extends UInt64 | string | bigint | number>(
val: T,
fieldName = '',
): UInt64 {
if (val instanceof UInt64) {
return val
}
@@ -51,11 +64,18 @@ class UInt64 extends UInt {
}
if (typeof val === 'string') {
if (!HEX_REGEX.test(val)) {
if (useBase10(fieldName)) {
if (!BASE10_REGEX.test(val)) {
throw new Error(`${fieldName} ${val} is not a valid base 10 string`)
}
val = BigInt(val).toString(16) as T
}
if (typeof val === 'string' && !HEX_REGEX.test(val)) {
throw new Error(`${val} is not a valid hex-string`)
}
const strBuf = val.padStart(16, '0')
const strBuf = (val as string).padStart(16, '0')
buf = hexToBytes(strBuf)
return new UInt64(buf)
}
@@ -76,8 +96,16 @@ class UInt64 extends UInt {
*
* @returns a hex-string
*/
toJSON(): string {
return bytesToHex(this.bytes)
toJSON(
_definitions: XrplDefinitionsBase = DEFAULT_DEFINITIONS,
fieldName = '',
): string {
const hexString = bytesToHex(this.bytes)
if (useBase10(fieldName)) {
return BigInt('0x' + hexString).toString(10)
}
return hexString
}
/**

View File

@@ -1,5 +1,7 @@
import { coreTypes } from '../src/types'
import fixtures from './fixtures/data-driven-tests.json'
import { makeParser } from '../src/binary'
const { Amount } = coreTypes
function amountErrorTests() {
@@ -25,6 +27,16 @@ describe('Amount', function () {
it('can be parsed from', function () {
expect(Amount.from('1000000') instanceof Amount).toBe(true)
expect(Amount.from('1000000').toJSON()).toEqual('1000000')
// it not valid to have negative XRP. But we test it anyways
// to ensure logic correctness for toJSON of the Amount class
{
const parser = makeParser('0000000000000001')
const value = parser.readType(Amount)
const json = value.toJSON()
expect(json).toEqual('-1')
}
const fixture = {
value: '1',
issuer: '0000000000000000000000000000000000000000',
@@ -38,5 +50,35 @@ describe('Amount', function () {
}
expect(amt.toJSON()).toEqual(rewritten)
})
it('can be parsed from MPT', function () {
let fixture = {
value: '100',
mpt_issuance_id: '00002403C84A0A28E0190E208E982C352BBD5006600555CF',
}
let amt = Amount.from(fixture)
expect(amt.toJSON()).toEqual(fixture)
fixture = {
value: '9223372036854775807',
mpt_issuance_id: '00002403C84A0A28E0190E208E982C352BBD5006600555CF',
}
amt = Amount.from(fixture)
expect(amt.toJSON()).toEqual(fixture)
// it not valid to have negative MPT. But we test it anyways
// to ensure logic correctness for toJSON of the Amount class
{
const parser = makeParser(
'20000000000000006400002403C84A0A28E0190E208E982C352BBD5006600555CF',
)
const value = parser.readType(Amount)
const json = value.toJSON()
expect(json).toEqual({
mpt_issuance_id: '00002403C84A0A28E0190E208E982C352BBD5006600555CF',
value: '-100',
})
}
})
amountErrorTests()
})

View File

@@ -22,6 +22,7 @@ function assertEqualAmountJSON(actual, expected) {
}
expect(actual.currency).toEqual(expected.currency)
expect(actual.issuer).toEqual(expected.issuer)
expect(actual.mpt_issuance_id).toEqual(expected.mpt_issuance_id)
expect(
actual.value === expected.value ||
new BigNumber(actual.value).eq(new BigNumber(expected.value)),
@@ -207,12 +208,12 @@ function amountParsingTests() {
return
}
const parser = makeParser(f.expected_hex)
const testName = `values_tests[${i}] parses ${f.expected_hex.slice(
const hexToJsonTestName = `values_tests[${i}] parses ${f.expected_hex.slice(
0,
16,
)}...
as ${JSON.stringify(f.test_json)}`
it(testName, () => {
it(hexToJsonTestName, () => {
const value = parser.readType(Amount)
// May not actually be in canonical form. The fixtures are to be used
// also for json -> binary;
@@ -223,6 +224,15 @@ function amountParsingTests() {
expect((exponent.e ?? 0) - 15).toEqual(f?.exponent)
}
})
const jsonToHexTestName = `values_tests[${i}] parses ${JSON.stringify(
f.test_json,
)}...
as ${f.expected_hex.slice(0, 16)}`
it(jsonToHexTestName, () => {
const amt = Amount.from(f.test_json)
expect(amt.toHex()).toEqual(f.expected_hex)
})
})
}

View File

@@ -2499,7 +2499,7 @@
"type_id": 6,
"is_native": true,
"type": "Amount",
"expected_hex": "0000000000000001",
"error": "Value is negative",
"is_negative": true
},
{
@@ -2914,6 +2914,170 @@
"type": "Amount",
"error": "10000000000000000000 absolute XRP is bigger than max native value 100000000000.0",
"is_negative": true
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "9223372036854775808"
},
"type": "Amount",
"error": "Value is too large"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "18446744073709551615"
},
"type": "Amount",
"error": "Value is too large"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "-1"
},
"type": "Amount",
"error": "Value is negative"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "10.1"
},
"type": "Amount",
"error": "Value has decimal point"
},
{
"test_json": {
"mpt_issuance_id": "10",
"value": "10"
},
"type": "Amount",
"error": "mpt_issuance_id has invalid hash length"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "10",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji"
},
"type": "Amount",
"error": "Issuer not valid for MPT"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "10",
"currency": "USD"
},
"type": "Amount",
"error": "Currency not valid for MPT"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "a"
},
"type": "Amount",
"error": "Value has incorrect hex format"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "0xy"
},
"type": "Amount",
"error": "Value has bad hex character"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "/"
},
"type": "Amount",
"error": "Value has bad character"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "0x8000000000000000"
},
"type": "Amount",
"error": "Hex value out of range"
},
{
"test_json": {
"mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "0xFFFFFFFFFFFFFFFF"
},
"type": "Amount",
"error": "Hex value out of range"
},
{
"test_json": {
"mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "9223372036854775807"
},
"type_id": 6,
"is_native": false,
"type": "Amount",
"expected_hex": "607FFFFFFFFFFFFFFF00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"is_negative": false
},
{
"test_json": {
"mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "0"
},
"type_id": 6,
"is_native": false,
"type": "Amount",
"expected_hex": "60000000000000000000002403C84A0A28E0190E208E982C352BBD5006600555CF",
"is_negative": false
},
{
"test_json": {
"mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "-0"
},
"type_id": 6,
"is_native": false,
"type": "Amount",
"expected_hex": "60000000000000000000002403C84A0A28E0190E208E982C352BBD5006600555CF",
"is_negative": false
},
{
"test_json": {
"mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "100"
},
"type_id": 6,
"is_native": false,
"type": "Amount",
"expected_hex": "60000000000000006400002403C84A0A28E0190E208E982C352BBD5006600555CF",
"is_negative": false
},
{
"test_json": {
"mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "0xa"
},
"type_id": 6,
"is_native": false,
"type": "Amount",
"expected_hex": "60000000000000000A00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"is_negative": false
},
{
"test_json": {
"mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"value": "0x7FFFFFFFFFFFFFFF"
},
"type_id": 6,
"is_native": false,
"type": "Amount",
"expected_hex": "607FFFFFFFFFFFFFFF00002403C84A0A28E0190E208E982C352BBD5006600555CF",
"is_negative": false
}
]
}

View File

@@ -1,4 +1,11 @@
import { Hash128, Hash160, Hash256, AccountID, Currency } from '../src/types'
import {
Hash128,
Hash160,
Hash192,
Hash256,
AccountID,
Currency,
} from '../src/types'
describe('Hash128', function () {
it('has a static width member', function () {
@@ -51,6 +58,33 @@ describe('Hash160', function () {
})
})
describe('Hash192', function () {
it('has a static width member', function () {
expect(Hash192.width).toBe(24)
})
it('has a ZERO_192 member', function () {
expect(Hash192.ZERO_192.toJSON()).toBe(
'000000000000000000000000000000000000000000000000',
)
})
it('can be compared against another', function () {
const h1 = Hash192.from('100000000000000000000000000000000000000000000000')
const h2 = Hash192.from('200000000000000000000000000000000000000000000000')
const h3 = Hash192.from('000000000000000000000000000000000000000000000003')
expect(h1.lt(h2)).toBe(true)
expect(h3.lt(h2)).toBe(true)
})
it('throws when constructed from invalid hash length', () => {
expect(() =>
Hash192.from('10000000000000000000000000000000000000000000000'),
).toThrow(new Error('Invalid Hash length 23'))
expect(() =>
Hash192.from('10000000000000000000000000000000000000000000000000'),
).toThrow(new Error('Invalid Hash length 25'))
})
})
describe('Hash256', function () {
it('has a static width member', function () {
expect(Hash256.width).toBe(32)

View File

@@ -1,5 +1,5 @@
import { UInt8, UInt64 } from '../src/types'
import { encode } from '../src'
import { encode, decode } from '../src'
const binary =
'11007222000300003700000000000000003800000000000000006280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5438D7EA4C680000000000000000000000000005553440000000000AE123A8556F3CF91154711376AFB0F894F832B3D67D5438D7EA4C680000000000000000000000000005553440000000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B90F'
@@ -96,6 +96,40 @@ const jsonEntry2 = {
index: '0000041EFD027808D3F78C8352F97E324CB816318E00B977C74ECDDC7CD975B2',
}
const mptIssuanceEntryBinary =
'11007E220000006224000002DF25000002E434000000000000000030187FFFFFFFFFFFFFFF30190000000000000064552E78C1FFBDDAEE077253CEB12CFEA83689AA0899F94762190A357208DADC76FE701EC1EC7B226E616D65223A2255532054726561737572792042696C6C20546F6B656E222C2273796D626F6C223A225553544254222C22646563696D616C73223A322C22746F74616C537570706C79223A313030303030302C22697373756572223A225553205472656173757279222C22697373756544617465223A22323032342D30332D3235222C226D6174757269747944617465223A22323032352D30332D3235222C226661636556616C7565223A2231303030222C22696E74657265737452617465223A22322E35222C22696E7465726573744672657175656E6379223A22517561727465726C79222C22636F6C6C61746572616C223A22555320476F7665726E6D656E74222C226A7572697364696374696F6E223A22556E6974656420537461746573222C22726567756C61746F7279436F6D706C69616E6365223A2253454320526567756C6174696F6E73222C22736563757269747954797065223A2254726561737572792042696C6C222C2265787465726E616C5F75726C223A2268747470733A2F2F6578616D706C652E636F6D2F742D62696C6C2D746F6B656E2D6D657461646174612E6A736F6E227D8414A4D893CFBC4DC6AE877EB585F90A3B47528B958D051003'
const mptIssuanceEntryJson = {
AssetScale: 3,
Flags: 98,
Issuer: 'rGpdGXDV2RFPeLEfWS9RFo5Nh9cpVDToZa',
LedgerEntryType: 'MPTokenIssuance',
MPTokenMetadata:
'7B226E616D65223A2255532054726561737572792042696C6C20546F6B656E222C2273796D626F6C223A225553544254222C22646563696D616C73223A322C22746F74616C537570706C79223A313030303030302C22697373756572223A225553205472656173757279222C22697373756544617465223A22323032342D30332D3235222C226D6174757269747944617465223A22323032352D30332D3235222C226661636556616C7565223A2231303030222C22696E74657265737452617465223A22322E35222C22696E7465726573744672657175656E6379223A22517561727465726C79222C22636F6C6C61746572616C223A22555320476F7665726E6D656E74222C226A7572697364696374696F6E223A22556E6974656420537461746573222C22726567756C61746F7279436F6D706C69616E6365223A2253454320526567756C6174696F6E73222C22736563757269747954797065223A2254726561737572792042696C6C222C2265787465726E616C5F75726C223A2268747470733A2F2F6578616D706C652E636F6D2F742D62696C6C2D746F6B656E2D6D657461646174612E6A736F6E227D',
MaximumAmount: '9223372036854775807',
OutstandingAmount: '100',
OwnerNode: '0000000000000000',
PreviousTxnID:
'2E78C1FFBDDAEE077253CEB12CFEA83689AA0899F94762190A357208DADC76FE',
PreviousTxnLgrSeq: 740,
Sequence: 735,
}
const mptokenEntryJson = {
Account: 'raDQsd1s8rqGjL476g59a9vVNi1rSwrC44',
Flags: 0,
LedgerEntryType: 'MPToken',
MPTAmount: '100',
MPTokenIssuanceID: '000002DF71CAE59C9B7E56587FFF74D4EA5830D9BE3CE0CC',
OwnerNode: '0000000000000000',
PreviousTxnID:
'222EF3C7E82D8A44984A66E2B8E357CB536EC2547359CCF70E56E14BC4C284C8',
PreviousTxnLgrSeq: 741,
}
const mptokenEntryBinary =
'11007F220000000025000002E5340000000000000000301A000000000000006455222EF3C7E82D8A44984A66E2B8E357CB536EC2547359CCF70E56E14BC4C284C881143930DB9A74C26D96CB58ADFFD7E8BB78BCFE62340115000002DF71CAE59C9B7E56587FFF74D4EA5830D9BE3CE0CC'
it('compareToTests[0]', () => {
expect(UInt8.from(124).compareTo(UInt64.from(124))).toBe(0)
})
@@ -144,3 +178,20 @@ it('valueOf tests', () => {
expect(val.valueOf() | 0x2).toBe(3)
})
it('UInt64 is parsed as base 10 for MPT amounts', () => {
expect(encode(mptIssuanceEntryJson)).toEqual(mptIssuanceEntryBinary)
expect(decode(mptIssuanceEntryBinary)).toEqual(mptIssuanceEntryJson)
expect(encode(mptokenEntryJson)).toEqual(mptokenEntryBinary)
expect(decode(mptokenEntryBinary)).toEqual(mptokenEntryJson)
const decodedIssuance = decode(mptIssuanceEntryBinary)
expect(typeof decodedIssuance.MaximumAmount).toBe('string')
expect(decodedIssuance.MaximumAmount).toBe('9223372036854775807')
expect(decodedIssuance.OutstandingAmount).toBe('100')
const decodedToken = decode(mptokenEntryBinary)
expect(typeof decodedToken.MPTAmount).toBe('string')
expect(decodedToken.MPTAmount).toBe('100')
})