Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0681e0a
models and integration tests for Dynamic MPTs (XLS-94D)
ckeshava Sep 15, 2025
ad316b7
unit tests suggested by rabbit
ckeshava Sep 16, 2025
fd1513d
fix linter errors
ckeshava Sep 16, 2025
2a56be3
Merge branch 'main' into xls-94d
ckeshava Sep 16, 2025
29b8e80
Merge branch 'main' into xls-94d
ckeshava Sep 16, 2025
058ad5b
updated flages in Dynamic MPT amendment to use tmf and lmf prefixes
ckeshava Sep 22, 2025
1e0aaa0
add Github Actions step to inspect Docker containter
ckeshava Sep 22, 2025
fc51d7e
helpful message in Github Actions output
ckeshava Sep 22, 2025
b792695
reorder the conditions
ckeshava Sep 22, 2025
7e2e712
Github Actions: include docker-inspect command to investigate the con…
ckeshava Sep 22, 2025
0876e86
Merge branch 'main' into xls-94d
ckeshava Sep 25, 2025
0e07910
Merge branch 'main' into xls-94d
ckeshava Sep 29, 2025
02d4a96
simplify Github Actions steps
ckeshava Sep 29, 2025
d6ec04a
fix: rectify the syntax error in bash script
ckeshava Sep 29, 2025
bda0c2e
additional comments for MPToken transaction flags
ckeshava Sep 30, 2025
cbdcbc3
Additional validation on sfMutableFlags field for MPTokenIssuance(Cre…
ckeshava Sep 30, 2025
565402b
Rename ledger flags corresponding to rippled commit: 8e4fda160d9fb66d…
ckeshava Sep 30, 2025
747bfd2
Merge branch 'main' into xls-94d
ckeshava Oct 6, 2025
ce16616
address Phu comment: Introduce distinct MutableFlags interface
ckeshava Oct 6, 2025
42049ac
address Phu comment: refactor MutableFlags in MPTokenIssuanceSet tran…
ckeshava Oct 6, 2025
13f9ec9
Additional validations+tests for MPTokenIssuanceCreate transaction
ckeshava Oct 7, 2025
7260f2f
Additional validations and tests for MPTokenIssuanceSet txn
ckeshava Oct 7, 2025
0f3ae2f
Merge branch 'main' into xls-94d
ckeshava Oct 16, 2025
9a6c46c
feat: Add MPT Metadata validation check in MPTokenIssuanceSet transac…
ckeshava Oct 17, 2025
4a3a978
add docstring for mutable flags field
ckeshava Oct 30, 2025
0c228cd
[docs] add docstring to explain the stature of DomainID in MPTokenIss…
ckeshava Oct 30, 2025
020b895
Merge branch 'main' into xls-94d
ckeshava Oct 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ci-config/rippled.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ Batch
PermissionedDEX
TokenEscrow
SingleAssetVault
DynamicMPT

# This section can be used to simulate various FeeSettings scenarios for rippled node in standalone mode
[voting]
Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,11 @@ jobs:
fetch-depth: 0

- name: Run docker in background
id: run-docker
run: |
docker run --detach --rm -p 6006:6006 --volume "${{ github.workspace }}/.ci-config/":"/etc/opt/ripple/" --name rippled-service --health-cmd="rippled server_info || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s --env GITHUB_ACTIONS=true --env CI=true --entrypoint bash ${{ env.RIPPLED_DOCKER_IMAGE }} -c "rippled -a"
CONTAINER_ID=$(docker ps -aqf "name=rippled-service")
echo "docker-container-id=$CONTAINER_ID" >> $GITHUB_OUTPUT

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
Expand Down Expand Up @@ -154,6 +157,17 @@ jobs:

- run: npm run build

- name: Check if Docker container is running
id: check-docker-container
run: |
if ! docker ps | grep -q rippled-service; then
echo "INFO: Currently running docker containers:"
docker ps
echo "ERROR: rippled-service Docker container is not running"
exit 1
fi
docker inspect ${{ steps.run-docker.outputs.docker-container-id }}

- name: Run integration test
run: npm run test:integration

Expand Down Expand Up @@ -181,8 +195,11 @@ jobs:
node-version: ${{ matrix.node-version }}

- name: Run docker in background
id: run-docker
run: |
docker run --detach --rm -p 6006:6006 --volume "${{ github.workspace }}/.ci-config/":"/etc/opt/ripple/" --name rippled-service --health-cmd="rippled server_info || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s --env GITHUB_ACTIONS=true --env CI=true --entrypoint bash ${{ env.RIPPLED_DOCKER_IMAGE }} -c "rippled -a"
CONTAINER_ID=$(docker ps -aqf "name=rippled-service")
echo "docker-container-id=$CONTAINER_ID" >> $GITHUB_OUTPUT

- name: Setup npm version 10
run: |
Expand All @@ -208,6 +225,17 @@ jobs:

- run: npm run build

- name: Check if Docker container is running
id: check-docker-container
run: |
if ! docker ps | grep -q rippled-service; then
echo "INFO: Currently running docker containers:"
docker ps
echo "ERROR: rippled-service Docker container is not running"
exit 1
fi
docker inspect ${{ steps.run-docker.outputs.docker-container-id }}

- name: Run integration test
run: npm run test:browser

Expand Down
10 changes: 10 additions & 0 deletions packages/ripple-binary-codec/src/enums/definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,16 @@
"type": "UInt32"
}
],
[
"MutableFlags",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 53,
"type": "UInt32"
}
],
[
"IndexNext",
{
Expand Down
3 changes: 3 additions & 0 deletions packages/xrpl/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr

## Unreleased

### Added
* Support for `Dynamic MPT` (XLS-94D)

### Fixed
* Fix incorrect type checking in `validateVaultCreate` that prevented vault creation with MPT as an asset.

Expand Down
2 changes: 2 additions & 0 deletions packages/xrpl/src/models/ledger/LedgerEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import DirectoryNode from './DirectoryNode'
import Escrow from './Escrow'
import FeeSettings from './FeeSettings'
import LedgerHashes from './LedgerHashes'
import { MPTokenIssuance } from './MPTokenIssuance'
import NegativeUNL from './NegativeUNL'
import Offer from './Offer'
import Oracle from './Oracle'
Expand Down Expand Up @@ -46,6 +47,7 @@ type LedgerEntry =
| Vault
| XChainOwnedClaimID
| XChainOwnedCreateAccountClaimID
| MPTokenIssuance

type LedgerEntryFilter =
| 'account'
Expand Down
88 changes: 88 additions & 0 deletions packages/xrpl/src/models/ledger/MPTokenIssuance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,92 @@ export interface MPTokenIssuance extends BaseLedgerEntry, HasPreviousTxnID {
MPTokenMetadata?: string
OwnerNode?: string
LockedAmount?: string
DomainID?: string
MutableFlags: number
}

export interface MPTokenIssuanceFlagsInterface {
lsfMPTLocked?: boolean
lsfMPTCanLock?: boolean
lsfMPTRequireAuth?: boolean
lsfMPTCanEscrow?: boolean
lsfMPTCanTrade?: boolean
lsfMPTCanTransfer?: boolean
lsfMPTCanClawback?: boolean

/**
* Indicates flag lsfMPTCanLock can be changed
*/
lsmfMPTCanMutateCanLock?: boolean
/**
* Indicates flag lsfMPTRequireAuth can be changed
*/
lsmfMPTCanMutateRequireAuth?: boolean
/**
* Indicates flag lsfMPTCanEscrow can be changed
*/
lsmfMPTCanMutateCanEscrow?: boolean
/**
* Indicates flag lsfMPTCanTrade can be changed
*/
lsmfMPTCanMutateCanTrade?: boolean
/**
* Indicates flag lsfMPTCanTransfer can be changed
*/
lsmfMPTCanMutateCanTransfer?: boolean
/**
* Indicates flag lsfMPTCanClawback can be changed
*/
lsmfMPTCanMutateCanClawback?: boolean
/**
* Allows field MPTokenMetadata to be modified
*/
lsmfMPTCanMutateMetadata?: boolean
/**
* Allows field TransferFee to be modified
*/
lsmfMPTCanMutateTransferFee?: boolean
}

export enum MPTokenIssuanceFlags {
lsfMPTLocked = 0x00000001,
lsfMPTCanLock = 0x00000002,
lsfMPTRequireAuth = 0x00000004,
lsfMPTCanEscrow = 0x00000008,
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,

/**
* Indicates flag lsfMPTCanLock can be changed
*/
lsmfMPTCanMutateCanLock = 0x00000002,
/**
* Indicates flag lsfMPTRequireAuth can be changed
*/
lsmfMPTCanMutateRequireAuth = 0x00000004,
/**
* Indicates flag lsfMPTCanEscrow can be changed
*/
lsmfMPTCanMutateCanEscrow = 0x00000008,
/**
* Indicates flag lsfMPTCanTrade can be changed
*/
lsmfMPTCanMutateCanTrade = 0x00000010,
/**
* Indicates flag lsfMPTCanTransfer can be changed
*/
lsmfMPTCanMutateCanTransfer = 0x00000020,
/**
* Indicates flag lsfMPTCanClawback can be changed
*/
lsmfMPTCanMutateCanClawback = 0x00000040,
/**
* Allows field MPTokenMetadata to be modified
*/
lsmfMPTCanMutateMetadata = 0x00010000,
/**
* Allows field TransferFee to be modified
*/
lsmfMPTCanMutateTransferFee = 0x00020000,
}
120 changes: 117 additions & 3 deletions packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type { TransactionMetadataBase } from './metadata'

// 2^63 - 1
const MAX_AMT = '9223372036854775807'
const MAX_TRANSFER_FEE = 50000
export const MAX_TRANSFER_FEE = 50000

/**
* Transaction Flags for an MPTokenIssuanceCreate Transaction.
Expand Down Expand Up @@ -53,8 +53,54 @@ export enum MPTokenIssuanceCreateFlags {
* to clawback value from individual holders.
*/
tfMPTCanClawback = 0x00000040,

/**
* If set, Indicates flag lsfMPTCanLock can be changed.
*/
tmfMPTCanMutateCanLock = 0x00000002,
/**
* If set, Indicates flag lsfMPTRequireAuth can be changed
*/
tmfMPTCanMutateRequireAuth = 0x00000004,
/**
* If set, Indicates flag lsfMPTCanEscrow can be changed.
*/
tmfMPTCanMutateCanEscrow = 0x00000008,
/**
* If set, Indicates flag lsfMPTCanTrade can be changed.
*/
tmfMPTCanMutateCanTrade = 0x00000010,
/**
* If set, Indicates flag lsfMPTCanTransfer can be changed.
*/
tmfMPTCanMutateCanTransfer = 0x00000020,
/**
* If set, Indicates flag lsfMPTCanClawback can be changed.
*/
tmfMPTCanMutateCanClawback = 0x00000040,
/**
* If set, Allows field MPTokenMetadata to be modified.
*/
tmfMPTCanMutateMetadata = 0x00010000,
/**
* If set, Allows field TransferFee to be modified.
*/
tmfMPTCanMutateTransferFee = 0x00020000,
}

/* eslint-disable no-bitwise -- Need bitwise operations to replicate rippled behavior */
export const tmfMPTokenIssuanceCreateMutableMask = ~(
MPTokenIssuanceCreateFlags.tmfMPTCanMutateCanLock |
MPTokenIssuanceCreateFlags.tmfMPTCanMutateRequireAuth |
MPTokenIssuanceCreateFlags.tmfMPTCanMutateCanEscrow |
MPTokenIssuanceCreateFlags.tmfMPTCanMutateCanTrade |
MPTokenIssuanceCreateFlags.tmfMPTCanMutateCanTransfer |
MPTokenIssuanceCreateFlags.tmfMPTCanMutateCanClawback |
MPTokenIssuanceCreateFlags.tmfMPTCanMutateMetadata |
MPTokenIssuanceCreateFlags.tmfMPTCanMutateTransferFee
)
/* eslint-enable no-bitwise */

/**
* Map of flags to boolean values representing {@link MPTokenIssuanceCreate} transaction
* flags.
Expand All @@ -63,12 +109,68 @@ export enum MPTokenIssuanceCreateFlags {
*/
export interface MPTokenIssuanceCreateFlagsInterface
extends GlobalFlagsInterface {
/**
* If set, indicates that the MPT can be locked both individually and globally.
* If not set, the MPT cannot be locked in any way.
*/
tfMPTCanLock?: boolean
/**
* If set, indicates that individual holders must be authorized.
* This enables issuers to limit who can hold their assets.
*/
tfMPTRequireAuth?: boolean
/**
* If set, indicates that individual holders can place their balances into an escrow.
*/
tfMPTCanEscrow?: boolean
/**
* If set, indicates that individual holders can trade their balances
* using the XRP Ledger DEX or AMM.
*/
tfMPTCanTrade?: boolean
/**
* If set, indicates that tokens may be transferred to other accounts
* that are not the issuer.
*/
tfMPTCanTransfer?: boolean
/**
* If set, indicates that the issuer may use the Clawback transaction
* to clawback value from individual holders.
*/
tfMPTCanClawback?: boolean

/**
* If set, Indicates flag lsfMPTCanLock can be changed.
*/
tmfMPTCanMutateCanLock?: boolean
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These flags are only available for MutableFlags field. Should it be separated into its own interface, similar to what we did with SetFlag options in AccountSet?

Copy link
Collaborator Author

@ckeshava ckeshava Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The flags associated with Dynamic MPTs have tmf... in their prefix. This is used to indicate transaction-mutable-flag. This difference in names (tf... versus tmf...) should help users differentiate between the intent behind the flags.
  2. If we use a separate interface for these flags, it could potentially cause confusion. The xrpl.org documentation (and the rippled cpp code, other programming language clients) organize these flags under the MPTokenIssuanceCreate transaction's flags. This change would have been more impactful if it was done at the XLS level, instead of only xrpl.js deviating from the specification.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The xrpl.org documentation (and the rippled cpp code, other programming language clients) organize these flags under the MPTokenIssuanceCreate transaction's flags

Is the xrpl.org documentation updated already?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the XLS specification is a good source of info for unreleased amendments. Usually, you can find the pre-release documentation for amendments on https://opensource.ripple.com/ as well, however LendingProtocol docs have not been published yet. You can look at pre-release docs for other amendments on that website.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were separating the flags that can be set with Flags and SetFlags for AccountSet transactions:
https://github.com/XRPLF/xrpl.js/blob/main/packages/xrpl/src/models/transactions/accountSet.ts
I think it's clearer that way, and also allows users to distinguish which flags they can set with Flags and MutableFlags. With this setup user can set a mutable flag with Flags field and there will be no warning, so I think it's more confusing

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pdp2121 ok. I have introduced separate interface and enum for the Mutable Flags: ce16616

/**
* If set, Indicates flag lsfMPTRequireAuth can be changed.
*/
tmfMPTCanMutateRequireAuth?: boolean
/**
* If set, Indicates flag lsfMPTCanEscrow can be changed.
*/
tmfMPTCanMutateCanEscrow?: boolean
/**
* If set, Indicates flag lsfMPTCanTrade can be changed.
*/
tmfMPTCanMutateCanTrade?: boolean
/**
* If set, Indicates flag lsfMPTCanTransfer can be changed.
*/
tmfMPTCanMutateCanTransfer?: boolean
/**
* If set, Indicates flag lsfMPTCanClawback can be changed.
*/
tmfMPTCanMutateCanClawback?: boolean
/**
* If set, Allows field MPTokenMetadata to be modified.
*/
tmfMPTCanMutateMetadata?: boolean
/**
* If set, Allows field TransferFee to be modified.
*/
tmfMPTCanMutateTransferFee?: boolean
}

/**
Expand Down Expand Up @@ -120,13 +222,14 @@ export interface MPTokenIssuanceCreate extends BaseTransaction {
MPTokenMetadata?: string

Flags?: number | MPTokenIssuanceCreateFlagsInterface
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flags cannot accept the mutable flags so the above change will be needed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code has been changed in ce16616

MutableFlags?: number
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add docstring for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a recent commit to fix this. However, I could not find the documentation for the domain_id field on the xrpl.org website ://

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: I have added a docstring for the DomainID field as well, based on my understanding of the topic. I could not find any other documentation to that effect. Commit: 0c228cd

}

export interface MPTokenIssuanceCreateMetadata extends TransactionMetadataBase {
mpt_issuance_id?: string
}

/* eslint-disable max-lines-per-function -- Not needed to reduce function */
/* eslint-disable max-lines-per-function, max-statements -- Not needed to reduce function */
/**
* Verify the form and type of an MPTokenIssuanceCreate at runtime.
*
Expand All @@ -141,6 +244,17 @@ export function validateMPTokenIssuanceCreate(
validateOptionalField(tx, 'MPTokenMetadata', isString)
validateOptionalField(tx, 'TransferFee', isNumber)
validateOptionalField(tx, 'AssetScale', isNumber)
validateOptionalField(tx, 'MutableFlags', isNumber)

if (
tx.MutableFlags != null &&
// eslint-disable-next-line no-bitwise -- Need bitwise operations to replicate rippled behavior
tx.MutableFlags & tmfMPTokenIssuanceCreateMutableMask
) {
throw new ValidationError(
'MPTokenIssuanceCreate: Invalid MutableFlags value',
)
}

if (
typeof tx.MPTokenMetadata === 'string' &&
Expand Down Expand Up @@ -202,4 +316,4 @@ export function validateMPTokenIssuanceCreate(
}
}
}
/* eslint-enable max-lines-per-function */
/* eslint-enable max-lines-per-function, max-statements */
Loading
Loading