mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-27 07:35:52 +00:00
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
19
packages/ripple-binary-codec/src/types/hash-192.ts
Normal file
19
packages/ripple-binary-codec/src/types/hash-192.ts
Normal 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 }
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user