Skip to content

Commit

Permalink
Merge pull request #1 from shawnxie999/mpt-base10
Browse files Browse the repository at this point in the history
Mpt base10
  • Loading branch information
shawnxie999 authored Oct 1, 2024
2 parents ca53e51 + cb92806 commit d367a83
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 150 deletions.
2 changes: 1 addition & 1 deletion packages/ripple-binary-codec/src/types/serialized-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down
11 changes: 9 additions & 2 deletions packages/ripple-binary-codec/src/types/st-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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) {
Expand All @@ -155,7 +158,11 @@ class STObject extends SerializedType {
// Account field
// The Account field must not be a part of the UNLModify pseudotransaction encoding, due to a bug in rippled
const isUnlModifyWorkaround = field.name == 'Account' && isUnlModify
bytes.writeFieldAndValue(field, associatedValue, isUnlModifyWorkaround)
bytes.writeFieldAndValue(
field,
associatedValue as SerializedType,
isUnlModifyWorkaround,
)
if (field.type.name === ST_OBJECT) {
bytes.put(OBJECT_END_MARKER_BYTE)
}
Expand All @@ -182,7 +189,7 @@ class STObject extends SerializedType {

accumulator[field.name] = objectParser
.readFieldValue(field)
.toJSON(definitions)
.toJSON(definitions, field.name)
}

return accumulator
Expand Down
40 changes: 35 additions & 5 deletions packages/ripple-binary-codec/src/types/uint-64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ 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)

/**
Expand All @@ -29,7 +31,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: string = '',
): UInt64 {
if (val instanceof UInt64) {
return val
}
Expand All @@ -51,11 +56,23 @@ class UInt64 extends UInt {
}

if (typeof val === 'string') {
if (!HEX_REGEX.test(val)) {
if (
fieldName == 'MaximumAmount' ||
fieldName == 'OutstandingAmount' ||
fieldName == 'LockedAmount' ||
fieldName == 'MPTAmount'
) {
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)
}
Expand All @@ -76,8 +93,21 @@ class UInt64 extends UInt {
*
* @returns a hex-string
*/
toJSON(): string {
return bytesToHex(this.bytes)
toJSON(
_definitions: XrplDefinitionsBase = DEFAULT_DEFINITIONS,
fieldName: string = '',
): string {
const hexString = bytesToHex(this.bytes)
if (
fieldName == 'MaximumAmount' ||
fieldName == 'OutstandingAmount' ||
fieldName == 'LockedAmount' ||
fieldName == 'MPTAmount'
) {
return BigInt('0x' + hexString).toString(10)
}

return hexString
}

/**
Expand Down
44 changes: 43 additions & 1 deletion packages/ripple-binary-codec/test/uint.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { UInt8, UInt64 } from '../src/types'
import { encode } from '../src'
import { encode, decode } from '../src'

const binary =
'11007222000300003700000000000000003800000000000000006280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5438D7EA4C680000000000000000000000000005553440000000000AE123A8556F3CF91154711376AFB0F894F832B3D67D5438D7EA4C680000000000000000000000000005553440000000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B90F'
Expand Down Expand Up @@ -96,6 +96,40 @@ const jsonEntry2 = {
index: '0000041EFD027808D3F78C8352F97E324CB816318E00B977C74ECDDC7CD975B2',
}

const mptIssuanceEntryBinary =
'11007E22000000222400000333250000033934000000000000000030187FFFFFFFFFFFFFFF3019000000000000006455854FCC08F5C602AEAF4F577316248BECCBEC82310B30DC66E1438E07F86E6E7D701EC1EC7B226E616D65223A2255532054726561737572792042696C6C20546F6B656E222C2273796D626F6C223A225553544254222C22646563696D616C73223A322C22746F74616C537570706C79223A313030303030302C22697373756572223A225553205472656173757279222C22697373756544617465223A22323032342D30332D3235222C226D6174757269747944617465223A22323032352D30332D3235222C226661636556616C7565223A2231303030222C22696E74657265737452617465223A22322E35222C22696E7465726573744672657175656E6379223A22517561727465726C79222C22636F6C6C61746572616C223A22555320476F7665726E6D656E74222C226A7572697364696374696F6E223A22556E6974656420537461746573222C22726567756C61746F7279436F6D706C69616E6365223A2253454320526567756C6174696F6E73222C22736563757269747954797065223A2254726561737572792042696C6C222C2265787465726E616C5F75726C223A2268747470733A2F2F6578616D706C652E636F6D2F742D62696C6C2D746F6B656E2D6D657461646174612E6A736F6E227D8414F52140C352B66DC5507C7C0236F7241469E56C1300101403'

const mptIssuanceEntryJson = {
AssetScale: 3,
Flags: 34,
Issuer: 'rPM3PTTfpJVjEcb3aSoZGizLbVBz2B7LQF',
LedgerEntryType: 'MPTokenIssuance',
MPTokenMetadata:
'7B226E616D65223A2255532054726561737572792042696C6C20546F6B656E222C2273796D626F6C223A225553544254222C22646563696D616C73223A322C22746F74616C537570706C79223A313030303030302C22697373756572223A225553205472656173757279222C22697373756544617465223A22323032342D30332D3235222C226D6174757269747944617465223A22323032352D30332D3235222C226661636556616C7565223A2231303030222C22696E74657265737452617465223A22322E35222C22696E7465726573744672657175656E6379223A22517561727465726C79222C22636F6C6C61746572616C223A22555320476F7665726E6D656E74222C226A7572697364696374696F6E223A22556E6974656420537461746573222C22726567756C61746F7279436F6D706C69616E6365223A2253454320526567756C6174696F6E73222C22736563757269747954797065223A2254726561737572792042696C6C222C2265787465726E616C5F75726C223A2268747470733A2F2F6578616D706C652E636F6D2F742D62696C6C2D746F6B656E2D6D657461646174612E6A736F6E227D',
MaximumAmount: '9223372036854775807',
OutstandingAmount: '100',
OwnerNode: '0000000000000000',
PreviousTxnID:
'854FCC08F5C602AEAF4F577316248BECCBEC82310B30DC66E1438E07F86E6E7D',
PreviousTxnLgrSeq: 825,
Sequence: 819,
}

const mptokenEntryJson = {
Account: 'rhWhvQ4kMaYB9sX25S13PYGJnF1H1pAmRp',
Flags: 0,
LedgerEntryType: 'MPToken',
MPTAmount: '100',
MPTokenIssuanceID: '0000032A0BEB4029FD60F77F484D8D16C2505E861DB5394B',
OwnerNode: '0000000000000000',
PreviousTxnID:
'FB4131A4FE8EE2E0A48C7BB668980DD975F529EABF55C190F3F1867469534669',
PreviousTxnLgrSeq: 816,
}

const mptokenEntryBinary =
'11007F22000000002500000330340000000000000000301B000000000000006455FB4131A4FE8EE2E0A48C7BB668980DD975F529EABF55C190F3F186746953466981142667B68E9BEA7242611581F0EB4630F2E844226901150000032A0BEB4029FD60F77F484D8D16C2505E861DB5394B'

it('compareToTests[0]', () => {
expect(UInt8.from(124).compareTo(UInt64.from(124))).toBe(0)
})
Expand Down Expand Up @@ -144,3 +178,11 @@ 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)
})
24 changes: 17 additions & 7 deletions packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { isHex } from '../utils'
import { BaseTransaction, GlobalFlags, validateBaseTransaction } from './common'
import type { TransactionMetadataBase } from './metadata'

const SANITY_CHECK = /^[0-9]+$/u

/**
* Transaction Flags for an MPTokenIssuanceCreate Transaction.
*
Expand Down Expand Up @@ -75,17 +77,13 @@ export interface MPTokenIssuanceCreate extends BaseTransaction {
*/
AssetScale?: number
/**
* Specifies the hex-encoded maximum asset amount of this token that should ever be issued.
* Specifies the maximum asset amount of this token that should ever be issued.
* It is a non-negative integer that can store a range of up to 63 bits. If not set, the max
* amount will default to the largest unsigned 63-bit integer (0x7FFFFFFFFFFFFFFF)
*
* Helper function `mptUint64ToHex` can be used to help converting from base 10 or 16 string
* to a valid value.
* amount will default to the largest unsigned 63-bit integer (0x7FFFFFFFFFFFFFFF or 9223372036854775807)
*
* Example:
* ```
* MaximumAmount: '3e8' // 0x3E8 in hex or 1000 in decimal
* MaximumAmount: mptUint64ToHex('1000') // 1000 in decimal using helper function
* MaximumAmount: '9223372036854775807'
* ```
*/
MaximumAmount?: string
Expand Down Expand Up @@ -129,4 +127,16 @@ export function validateMPTokenIssuanceCreate(
'MPTokenIssuanceCreate: MPTokenMetadata must be in hex format',
)
}

if (typeof tx.MaximumAmount === 'string') {
if (!SANITY_CHECK.exec(tx.MaximumAmount))
throw new ValidationError('MPTokenIssuanceCreate: Invalid MaximumAmount')
else if (
BigInt(tx.MaximumAmount) > BigInt(`9223372036854775807`) ||
BigInt(tx.MaximumAmount) < BigInt(`0`)
)
throw new ValidationError(
'MPTokenIssuanceCreate: MaximumAmount out of range',
)
}
}
2 changes: 0 additions & 2 deletions packages/xrpl/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ import {
} from './timeConversion'
import verifyPaymentChannelClaim from './verifyPaymentChannelClaim'
import { xrpToDrops, dropsToXrp } from './xrpConversion'
import { mptUint64ToHex } from './mptConversion'

/**
* Check if a secret is valid.
Expand Down Expand Up @@ -230,5 +229,4 @@ export {
getNFTokenID,
parseNFTokenID,
getXChainClaimID,
mptUint64ToHex,
}
61 changes: 0 additions & 61 deletions packages/xrpl/src/utils/mptConversion.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/xrpl/test/integration/transactions/clawback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ describe('Clawback', function () {
assert.equal(
// @ts-ignore
ledgerEntryResponse.result.node.MPTAmount,
'7fffffffffffffff',
'9223372036854775807',
)

// actual test - clawback
Expand All @@ -200,7 +200,7 @@ describe('Clawback', function () {
assert.equal(
// @ts-ignore
ledgerEntryResponse.result.node.MPTAmount,
'7ffffffffffffe0b',
'9223372036854775307',
)
},
TIMEOUT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
type XrplIntegrationTestContext,
} from '../setup'
import { testTransaction } from '../utils'
import { mptUint64ToHex } from '../../../src/utils'

// how long before each test case times out
const TIMEOUT = 20000
Expand All @@ -27,7 +26,7 @@ describe('MPTokenIssuanceCreate', function () {
const tx: MPTokenIssuanceCreate = {
TransactionType: 'MPTokenIssuanceCreate',
Account: testContext.wallet.classicAddress,
MaximumAmount: mptUint64ToHex('9223372036854775807'), // 0x7fffffffffffffff
MaximumAmount: '9223372036854775807', // 0x7fffffffffffffff
AssetScale: 2,
}

Expand All @@ -43,6 +42,11 @@ describe('MPTokenIssuanceCreate', function () {
1,
'Should be exactly one issuance on the ledger',
)
assert.equal(
// @ts-ignore
accountObjectsResponse.result.account_objects[0].MaximumAmount,
`9223372036854775807`,
)
},
TIMEOUT,
)
Expand Down
11 changes: 11 additions & 0 deletions packages/xrpl/test/integration/transactions/payment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ describe('Payment', function () {
}

await testTransaction(testContext.client, payTx, testContext.wallet)

accountObjectsResponse = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'mpt_issuance',
})
assert.equal(
// @ts-ignore
accountObjectsResponse.result.account_objects[0].OutstandingAmount,
`100`,
)
},
TIMEOUT,
)
Expand Down
Loading

0 comments on commit d367a83

Please sign in to comment.