Skip to content

Conversation

@ckeshava
Copy link
Collaborator

High Level Overview of Change

Specification is located here: https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0094-dynamic-MPT
CPP implementation can be found here: XRPLF/rippled#5705

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

Did you update HISTORY.md?

  • Yes
  • No, this change does not impact library users

Test Plan

A few unit and integration tests have been added.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds DynamicMPT to CI config; registers a serialized MutableFlags field in the binary codec; and implements Dynamic MPT (XLS-94D) support across ledger and transaction models (MPTokenIssuance), including flags, masks, validations, exports, tests, HISTORY entry, and CI Docker runtime checks.

Changes

Cohort / File(s) Summary
CI config
\.ci-config/rippled.cfg
Adds DynamicMPT to the [features] amendments list.
Binary codec definitions
packages/ripple-binary-codec/src/enums/definitions.json
Adds serialized signing field MutableFlags (type UInt32, nth=53) into FIELDS (inserted before IndexNext).
Ledger models
packages/xrpl/src/models/ledger/LedgerEntry.ts, packages/xrpl/src/models/ledger/MPTokenIssuance.ts
Adds MPTokenIssuance to ledger unions/filters; MPTokenIssuance gains DomainID?: string and MutableFlags: number; adds flags interface and enum.
Transaction models — Create
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
Exports MAX_TRANSFER_FEE; adds MPTokenIssuanceCreateMutableFlags enum and tmf...MutableMask; adds MutableFlags?: number, DomainID?: string; expands validation (mask checks, DomainID rule, amount bound).
Transaction models — Set
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
Adds MPTokenIssuanceSetMutableFlags enum and tmf...MutableMask; adds MPTokenMetadata?: string, TransferFee?: number, MutableFlags?: number, DomainID?: string; extensive validation for metadata hex/length, fee bounds, mask validity, holder/account rules, and mutation semantics.
Transaction exports
packages/xrpl/src/models/transactions/index.ts
Exposes MPTokenIssuanceCreateMutableFlags, MPTokenIssuanceCreateMutableFlagsInterface, MPTokenIssuanceSetMutableFlags, MPTokenIssuanceSetMutableFlagsInterface.
Common / utils
packages/xrpl/src/models/transactions/common.ts, packages/xrpl/src/models/utils/flags.ts
isDomainID now rejects all-zero 64-char hex; txToFlag mapping adds MPTokenIssuanceSet: MPTokenIssuanceSetFlags (lint-disable added to avoid cycle).
Tests — integration & unit
packages/xrpl/test/integration/transactions/mptokenIssuanceSet.test.ts, packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts, packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
Adds integration test for mutability flows; expands unit tests covering MutableFlags, TransferFee, MPTokenMetadata, DomainID, holder/account rules, invalid-mask and other validation scenarios.
Docs / History
packages/xrpl/HISTORY.md
Adds Unreleased entry: "Support for Dynamic MPT (XLS-94D)".
CI workflows
.github/workflows/nodejs.yml
Captures Docker container ID to GITHUB_OUTPUT; adds checks that rippled-service container is running in integration/browser workflows; retains cleanup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Potential focus areas:

  • Validation logic in MPTokenIssuanceCreate.ts and MPTokenIssuanceSet.ts (mask checks, DomainID rules, metadata length/format, TransferFee bounds).
  • Binary codec definitions.json insertion (ensure nth sequencing and codec compatibility).
  • Tests asserting exact error messages and mutability flows.
  • Import-cycle risk where MPTokenIssuanceSetFlags was added to txToFlag (lint-disable present).

Possibly related PRs

Suggested reviewers

  • Patel-Raj11
  • pdp2121
  • achowdhry-ripple
  • khancode

Poem

I nibble bytes beneath a moonlit log,
MutableFlags twitch as I hop through fog,
Hex crumbs trail where metadata hides,
Domains and fees dance across ledger tides,
Hoppity hop — a rabbit’s tiny dev-hug! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description Check ❓ Inconclusive The pull request description addresses most of the required template sections, including a properly checked "Type of Change" (New feature), confirmation that HISTORY.md was updated (Yes), and mention of added tests. However, the description has significant gaps that weaken its completeness: the "High Level Overview of Change" section contains only external links without providing an actual summary or list of the changes made, the "Context of Change" section is entirely missing (no explanation of architecture decisions or alternatives), and the "Test Plan" is too brief and vague ("A few unit and integration tests have been added") without describing what the tests verify or how to reproduce them. While the core checkboxes are properly completed, these missing or minimal descriptive sections prevent the description from being sufficiently informative. To improve the pull request description, add a clear bulleted or numbered summary of changes under "High Level Overview of Change" (describing which files changed and what functionality was added), add a "Context of Change" section explaining the architectural approach and any design decisions, and expand the "Test Plan" section with specific details about what the unit and integration tests validate and how to run them locally. The external links are helpful for reference but should supplement, not replace, the descriptive sections of the template.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Support for Dynamic MPTs (XLS-94D)" directly and clearly summarizes the main objective of the changeset. The pull request adds comprehensive support for Dynamic Merkle Patricia Trees per the XLS-94D specification, spanning configuration, binary codec definitions, TypeScript models, transaction types, tests, and CI workflows. The title is concise (5 words, 34 characters), specific, and avoids vague or generic language. It includes the specification reference for clarity, making it easy for team members scanning history to understand the primary change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c228cd and 020b895.

📒 Files selected for processing (3)
  • .github/workflows/nodejs.yml (4 hunks)
  • packages/xrpl/HISTORY.md (1 hunks)
  • packages/xrpl/src/models/transactions/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/xrpl/HISTORY.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/nodejs.yml
  • packages/xrpl/src/models/transactions/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build-and-lint (24.x)
  • GitHub Check: integration (24.x)
  • GitHub Check: browser (24.x)
  • GitHub Check: integration (20.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: unit (22.x)
  • GitHub Check: integration (22.x)
  • GitHub Check: unit (24.x)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (5)
packages/xrpl/HISTORY.md (1)

7-9: Standardize XLS casing and add spec link

Use the conventional lowercase “d” and link the spec for consistency with prior entries.

-* Support for `Dynamic MPT` (XLS-94D)
+* Support for `Dynamic MPT` ([XLS-94d](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0094-dynamic-MPT))
packages/xrpl/src/models/ledger/MPTokenIssuance.ts (2)

18-59: Separate flag maps for Flags vs MutableFlags to avoid misuse

Combining base flags and “can‑mutate” booleans in one interface invites accidental mixing. Define a distinct interface for MutableFlags and use clearer naming in call sites.

-export interface MPTokenIssuanceFlagsInterface {
+export interface MPTokenIssuanceFlagsInterface {
   lsfMPTLocked?: boolean
   lsfMPTCanLock?: boolean
   lsfMPTRequireAuth?: boolean
   lsfMPTCanEscrow?: boolean
   lsfMPTCanTrade?: boolean
   lsfMPTCanTransfer?: boolean
   lsfMPTCanClawback?: boolean
-  /**
-   * Indicates flag lsfMPTCanLock can be changed
-   */
-  lsfMPTCanMutateCanLock?: boolean
+}
+
+export interface MPTokenIssuanceMutableFlagsInterface {
+  /** Indicates flag lsfMPTCanLock can be changed */
+  lsfMPTCanMutateCanLock?: boolean
   /** Indicates flag lsfMPTRequireAuth can be changed */
   lsfMPTCanMutateRequireAuth?: boolean
   /** Indicates flag lsfMPTCanEscrow can be changed */
   lsfMPTCanMutateCanEscrow?: boolean
   /** Indicates flag lsfMPTCanTrade can be changed */
   lsfMPTCanMutateCanTrade?: boolean
   /** Indicates flag lsfMPTCanTransfer can be changed */
   lsfMPTCanMutateCanTransfer?: boolean
   /** Indicates flag lsfMPTCanClawback can be changed */
   lsfMPTCanMutateCanClawback?: boolean
   /** Allows field MPTokenMetadata to be modified */
   lsfMPTCanMutateMetadata?: boolean
   /** Allows field TransferFee to be modified */
   lsfMPTCanMutateTransferFee?: boolean
 }

61-102: Split enums for clarity: Flags vs MutableFlags share bit positions

Reusing the same bit values for two different bitmaps is correct but confusing in a single enum. Create a second enum (MPTokenIssuanceMutableFlags) for the MutableFlags field and leave MPTokenIssuanceFlags for Flags only.

-export enum MPTokenIssuanceFlags {
+export enum MPTokenIssuanceFlags {
   lsfMPTLocked = 0x00000001,
   lsfMPTCanLock = 0x00000002,
   lsfMPTRequireAuth = 0x00000004,
   lsfMPTCanEscrow = 0x00000008,
   lsfMPTCanTrade = 0x00000010,
   lsfMPTCanTransfer = 0x00000020,
   lsfMPTCanClawback = 0x00000040,
-
-  /**
-   * Indicates flag lsfMPTCanLock can be changed
-   */
-  lsfMPTCanMutateCanLock = 0x00000002,
-  /** Indicates flag lsfMPTRequireAuth can be changed */
-  lsfMPTCanMutateRequireAuth = 0x00000004,
-  /** Indicates flag lsfMPTCanEscrow can be changed */
-  lsfMPTCanMutateCanEscrow = 0x00000008,
-  /** Indicates flag lsfMPTCanTrade can be changed */
-  lsfMPTCanMutateCanTrade = 0x00000010,
-  /** Indicates flag lsfMPTCanTransfer can be changed */
-  lsfMPTCanMutateCanTransfer = 0x00000020,
-  /** Indicates flag lsfMPTCanClawback can be changed */
-  lsfMPTCanMutateCanClawback = 0x00000040,
-  /** Allows field MPTokenMetadata to be modified */
-  lsfMPTCanMutateMetadata = 0x00010000,
-  /** Allows field TransferFee to be modified */
-  lsfMPTCanMutateTransferFee = 0x00020000,
 }
+
+export enum MPTokenIssuanceMutableFlags {
+  /** Indicates flag lsfMPTCanLock can be changed */
+  lsfMPTCanMutateCanLock = 0x00000002,
+  /** Indicates flag lsfMPTRequireAuth can be changed */
+  lsfMPTCanMutateRequireAuth = 0x00000004,
+  /** Indicates flag lsfMPTCanEscrow can be changed */
+  lsfMPTCanMutateCanEscrow = 0x00000008,
+  /** Indicates flag lsfMPTCanTrade can be changed */
+  lsfMPTCanMutateCanTrade = 0x00000010,
+  /** Indicates flag lsfMPTCanTransfer can be changed */
+  lsfMPTCanMutateCanTransfer = 0x00000020,
+  /** Indicates flag lsfMPTCanClawback can be changed */
+  lsfMPTCanMutateCanClawback = 0x00000040,
+  /** Allows field MPTokenMetadata to be modified */
+  lsfMPTCanMutateMetadata = 0x00010000,
+  /** Allows field TransferFee to be modified */
+  lsfMPTCanMutateTransferFee = 0x00020000,
+}
packages/xrpl/test/integration/transactions/mptokenIssuanceSet.test.ts (1)

87-88: Arithmetic addition of flags could lead to incorrect values

Using arithmetic addition (+) to combine flags is error-prone and could result in incorrect values if the same flag is accidentally included twice. Bitwise OR should be used for combining flags.

Use bitwise OR to combine flags:

-        MutableFlags:
-          MPTokenIssuanceCreateFlags.tfMPTCanMutateTransferFee +
-          MPTokenIssuanceCreateFlags.tfMPTCanMutateCanTransfer,
+        MutableFlags:
+          MPTokenIssuanceCreateFlags.tfMPTCanMutateTransferFee |
+          MPTokenIssuanceCreateFlags.tfMPTCanMutateCanTransfer,
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (1)

30-77: Documentation style inconsistency

The new flags use C-style comments (/* */) while the existing code uses JSDoc style (/** */). This creates inconsistency in the documentation format.

Convert to JSDoc style for consistency:

-  // The below flags are used in the MutableFlags field
-  /*
-  ** Sets the lsfMPTCanLock flag. Enables the token to be locked both individually and globally.
-  */
+  // The below flags are used in the MutableFlags field
+  /**
+   * Sets the lsfMPTCanLock flag. Enables the token to be locked both individually and globally.
+   */
   tfMPTSetCanLock = 0x00000001,

Apply this pattern to all the new flag documentation comments.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0608433 and 0681e0a.

📒 Files selected for processing (10)
  • .ci-config/rippled.cfg (1 hunks)
  • packages/ripple-binary-codec/src/enums/definitions.json (1 hunks)
  • packages/xrpl/HISTORY.md (1 hunks)
  • packages/xrpl/src/models/ledger/LedgerEntry.ts (2 hunks)
  • packages/xrpl/src/models/ledger/MPTokenIssuance.ts (1 hunks)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (3 hunks)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (3 hunks)
  • packages/xrpl/test/integration/transactions/mptokenIssuanceSet.test.ts (1 hunks)
  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts (1 hunks)
  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-12-06T18:44:55.095Z
Learnt from: shawnxie999
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/test/models/MPTokenAuthorize.test.ts:60-71
Timestamp: 2024-12-06T18:44:55.095Z
Learning: In the XRPL.js library's TypeScript test file `packages/xrpl/test/models/MPTokenAuthorize.test.ts`, negative test cases for invalid `Account` address format, invalid `Holder` address format, invalid `MPTokenIssuanceID` format, and invalid flag combinations are not necessary.

Applied to files:

  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
  • packages/xrpl/src/models/ledger/MPTokenIssuance.ts
  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts
  • packages/xrpl/test/integration/transactions/mptokenIssuanceSet.test.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
🧬 Code graph analysis (4)
packages/xrpl/src/models/ledger/LedgerEntry.ts (2)
packages/xrpl/src/models/ledger/MPTokenIssuance.ts (1)
  • MPTokenIssuance (3-16)
packages/xrpl/src/models/ledger/index.ts (1)
  • MPTokenIssuance (65-65)
packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (1)
packages/xrpl/src/models/transactions/index.ts (1)
  • MPTokenIssuanceSetFlags (65-65)
packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts (1)
packages/xrpl/src/models/transactions/index.ts (1)
  • MPTokenIssuanceCreateFlags (59-59)
packages/xrpl/test/integration/transactions/mptokenIssuanceSet.test.ts (4)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
  • MPTokenIssuanceCreate (100-142)
packages/xrpl/test/integration/utils.ts (1)
  • testTransaction (240-290)
packages/xrpl/src/models/transactions/metadata.ts (1)
  • TransactionMetadata (95-108)
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (1)
  • MPTokenIssuanceSet (144-160)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: integration (20.x)
  • GitHub Check: integration (22.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: browser (22.x)
  • GitHub Check: unit (22.x)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (9)
packages/xrpl/src/models/ledger/MPTokenIssuance.ts (1)

14-16: Is MutableFlags always present on-ledger?

Interface marks MutableFlags as required. Confirm rippled always emits this field; if not, make it optional to avoid unnecessary ts friction when decoding historical or pre‑amendment data.

-  MutableFlags: number
+  MutableFlags?: number
packages/xrpl/src/models/ledger/LedgerEntry.ts (3)

13-13: Import looks good

Importing MPTokenIssuance here is appropriate.


50-51: Union extension LGTM

Including MPTokenIssuance in the LedgerEntry union aligns with the new ledger object.


66-68: No change required — 'mpt_issuance' and 'mptoken' are the canonical rippled filter keys.

Confirmed: rippled/XLS docs and the public API mapping list MPTokenIssuance -> mpt_issuance and MPToken -> mptoken, and the repo's ledger/ledgerEntry interfaces/tests already use those keys. (github.com)

.ci-config/rippled.cfg (1)

191-197: Confirm rippled image/tag and that the [features] stanza is honored (one DynamicMPT entry found)

Found a single "DynamicMPT" at .ci-config/rippled.cfg:196. The CI rippled image/tag wasn't located by the prior search — verify the rippled image/tag used by CI and that the container applies the config's [features] stanza (not started in a mode that ignores it).

  • Locate rippled image references in the repo: rg -n --hidden -S 'image:.rippled' --glob '**/.yml' --glob '/*.yaml' --glob '/Dockerfile' || true
  • Inspect the features block and nearby lines: sed -n '184,204p' .ci-config/rippled.cfg && rg -n '^[features]' .ci-config/rippled.cfg || true
  • Check CI startup/entrypoint for flags that override/ignore config: rg -n --hidden -S 'rippled.(--conf|--cfg|--standalone|--run)' --glob '**/.yml' --glob '**/Dockerfile' || true

If the rippled build used by CI lacks DynamicMPT, bump the rippled image/tag to a build that includes DynamicMPT or add the amendment ID explicitly to the config.

packages/ripple-binary-codec/src/enums/definitions.json (1)

683-692: MutableFlags (UInt32, nth=53) — unique in definitions.json

Verified: 51 UInt32 entries; no duplicate nth values; MutableFlags is at FIELDS/68 with nth=53.
Action: verify that rippled's codec maps MutableFlags to field code 53 to avoid signing/encode mismatches.

packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts (1)

31-31: Test uses potentially incorrect flag value

Based on the flag collision issue identified in the source file, this test might be using an incorrect flag value. If the mutable flags are updated to use distinct values, this test will need to be updated accordingly.

Once the flag values are corrected in the source file, update this test to use the new value for tfMPTCanMutateTransferFee.

packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (1)

48-53: Good test coverage for MutableFlags

The test properly validates that transactions with MutableFlags are accepted by the validator, which aligns with the Dynamic MPT specification.

packages/xrpl/test/integration/transactions/mptokenIssuanceSet.test.ts (1)

79-138: Comprehensive integration test for Dynamic MPT

This test effectively validates the Dynamic MPT (XLS-94D) functionality by:

  1. Creating an MPT with mutable flags
  2. Setting a transfer fee dynamically
  3. Clearing the transfer capability

The test flow properly exercises the new mutability features.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)

188-209: Bug: TransferFee presence check ignores 0-value case

Docs say “must NOT be present if tfMPTCanTransfer is not set.” Current check uses truthiness, so TransferFee=0 slips through.

Apply this diff:

-    if (tx.TransferFee && !isTfMPTCanTransfer) {
+    if (tx.TransferFee !== undefined && !isTfMPTCanTransfer) {
       throw new ValidationError(
         'MPTokenIssuanceCreate: TransferFee cannot be provided without enabling tfMPTCanTransfer flag',
       )
     }

Additionally, consider requiring an integer value:

-  validateOptionalField(tx, 'TransferFee', isNumber)
+  validateOptionalField(tx, 'TransferFee', isNumber)
+  if (typeof tx.TransferFee === 'number' && !Number.isInteger(tx.TransferFee)) {
+    throw new ValidationError('MPTokenIssuanceCreate: TransferFee must be an integer')
+  }
♻️ Duplicate comments (2)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)

57-64: Flag value “collisions” are OK here (separate bitfields)

These tfMPTCanMutate* values matching the base flags is intentional since Flags vs MutableFlags are distinct fields. No change needed.

packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (1)

205-213: Add conflict checks for Set/Clear pairs in MutableFlags

Prevent simultaneously setting and clearing the same property in one tx.

Apply this diff:

   if (
     typeof tx.MPTokenMetadata === 'string' &&
     (!isHex(tx.MPTokenMetadata) ||
       tx.MPTokenMetadata.length / 2 > MAX_MPT_META_BYTE_LENGTH)
   ) {
     throw new ValidationError(
       `MPTokenIssuanceSet: MPTokenMetadata (hex format) must be non-empty and no more than ${MAX_MPT_META_BYTE_LENGTH} bytes.`,
     )
   }
+
+  if (typeof tx.MutableFlags === 'number') {
+    const mf = tx.MutableFlags
+    const pairs: Array<[number, number, string]> = [
+      [MPTokenIssuanceSetFlags.tfMPTSetCanLock,     MPTokenIssuanceSetFlags.tfMPTClearCanLock,     'CanLock'],
+      [MPTokenIssuanceSetFlags.tfMPTSetRequireAuth, MPTokenIssuanceSetFlags.tfMPTClearRequireAuth, 'RequireAuth'],
+      [MPTokenIssuanceSetFlags.tfMPTSetCanEscrow,   MPTokenIssuanceSetFlags.tfMPTClearCanEscrow,   'CanEscrow'],
+      [MPTokenIssuanceSetFlags.tfMPTSetCanTrade,    MPTokenIssuanceSetFlags.tfMPTClearCanTrade,    'CanTrade'],
+      [MPTokenIssuanceSetFlags.tfMPTSetCanTransfer, MPTokenIssuanceSetFlags.tfMPTClearCanTransfer, 'CanTransfer'],
+      [MPTokenIssuanceSetFlags.tfMPTSetCanClawback, MPTokenIssuanceSetFlags.tfMPTClearCanClawback, 'CanClawback'],
+    ]
+    for (const [setBit, clearBit, name] of pairs) {
+      if ((mf & setBit) !== 0 && (mf & clearBit) !== 0) {
+        throw new ValidationError(
+          `MPTokenIssuanceSet: MutableFlags conflict for ${name} (set and clear both specified)`,
+        )
+      }
+    }
+  }
🧹 Nitpick comments (3)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)

163-164: Tighten validation: require integer for bitmasks

MutableFlags is a bitmask; accept only finite integers (e.g., Number.isInteger) and, ideally, constrain to UInt32.

Apply this diff to align with other integer checks:

-import {
+import {
   BaseTransaction,
   GlobalFlagsInterface,
   validateBaseTransaction,
   validateOptionalField,
   isString,
-  isNumber,
+  isNumber,
+  isInteger,
   MAX_MPT_META_BYTE_LENGTH,
   MPT_META_WARNING_HEADER,
   validateMPTokenMetadata,
 } from './common'
@@
-  validateOptionalField(tx, 'MutableFlags', isNumber)
+  validateOptionalField(tx, 'MutableFlags', isInteger)
+  if (typeof tx.MutableFlags === 'number') {
+    if (tx.MutableFlags < 0 || tx.MutableFlags > 0xffffffff) {
+      throw new ValidationError(
+        'MPTokenIssuanceCreate: MutableFlags out of range (must be 0..0xFFFFFFFF)',
+      )
+    }
+  }
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (2)

176-179: Tighten types: integer-only for MutableFlags

Same as Create: MutableFlags should be finite UInt32.

Apply this diff:

-  validateOptionalField(tx, 'MutableFlags', isNumber)
+  validateOptionalField(tx, 'MutableFlags', isInteger)
+  if (typeof tx.MutableFlags === 'number') {
+    if (tx.MutableFlags < 0 || tx.MutableFlags > 0xffffffff) {
+      throw new ValidationError(
+        'MPTokenIssuanceSet: MutableFlags out of range (must be 0..0xFFFFFFFF)',
+      )
+    }
+  }

197-203: Minor: enforce integer TransferFee

Server expects integral steps; guard against decimals/NaN.

Apply this diff:

   if (typeof tx.TransferFee === 'number') {
+    if (!Number.isFinite(tx.TransferFee) || !Number.isInteger(tx.TransferFee)) {
+      throw new ValidationError('MPTokenIssuanceSet: TransferFee must be a finite integer')
+    }
     if (tx.TransferFee < 0 || tx.TransferFee > MAX_TRANSFER_FEE) {
       throw new ValidationError(
         `MPTokenIssuanceSet: TransferFee must be between 0 and ${MAX_TRANSFER_FEE}`,
       )
     }
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0681e0a and ad316b7.

📒 Files selected for processing (3)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (5 hunks)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (7 hunks)
  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.389Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.
📚 Learning: 2025-09-16T05:00:20.389Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.389Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2024-12-06T18:44:55.095Z
Learnt from: shawnxie999
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/test/models/MPTokenAuthorize.test.ts:60-71
Timestamp: 2024-12-06T18:44:55.095Z
Learning: In the XRPL.js library's TypeScript test file `packages/xrpl/test/models/MPTokenAuthorize.test.ts`, negative test cases for invalid `Account` address format, invalid `Holder` address format, invalid `MPTokenIssuanceID` format, and invalid flag combinations are not necessary.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2024-12-05T16:48:12.951Z
Learnt from: achowdhry-ripple
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:69-102
Timestamp: 2024-12-05T16:48:12.951Z
Learning: When adding validation in `validate*` functions in `packages/xrpl/src/models/transactions/`, utilize existing helper functions (e.g., `validateOptionalField`, `validateType`, `isNumber`, `isInteger`) for type checking and validation where appropriate.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-01-08T13:08:52.688Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2873
File: packages/xrpl/src/models/transactions/trustSet.ts:33-36
Timestamp: 2025-01-08T13:08:52.688Z
Learning: For trust-set transactions in XRPL, validation rules for flags should be implemented comprehensively rather than cherry-picking specific rules, as there are many interdependent validation rules associated with these flags.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
🧬 Code graph analysis (2)
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (4)
packages/xrpl/src/models/transactions/common.ts (4)
  • validateOptionalField (412-432)
  • isString (141-143)
  • isNumber (151-153)
  • MAX_MPT_META_BYTE_LENGTH (26-26)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
  • MAX_TRANSFER_FEE (19-19)
packages/xrpl/src/errors.ts (1)
  • ValidationError (156-156)
packages/xrpl/src/models/utils/index.ts (1)
  • isHex (60-62)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
packages/xrpl/src/models/transactions/common.ts (2)
  • validateOptionalField (412-432)
  • isNumber (151-153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: browser (22.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: unit (22.x)
  • GitHub Check: integration (20.x)
  • GitHub Check: integration (22.x)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (6)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (2)

19-19: Good: centralize the TransferFee limit

Exporting MAX_TRANSFER_FEE for reuse is the right move and avoids magic numbers across transactions.


140-142: Confirm: Should MPTokenIssuanceCreate accept MutableFlags?

Spec-wise, MutableFlags typically belong to the Set transaction. If Create does not support it on-ledger, keeping it in the interface may confuse users or produce invalid tx blobs.

Would you like me to remove MutableFlags from Create, or document explicitly that it’s ignored by rippled on Create?

packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (4)

17-18: Good reuse: import MAX_TRANSFER_FEE

Avoids duplication and keeps fee limits consistent with Create.


90-141: Potential confusion: booleans for MutableFlags are on Flags interface

MPTokenIssuanceSetFlagsInterface includes tfMPTSet*/tfMPTClear* booleans (which belong to MutableFlags). Verify the builder splits these into tx.MutableFlags and does not place them under tx.Flags.

If splitting isn’t implemented, I can propose a minimal helper to compute both fields from the interface.


161-164: LGTM: new optional fields are added

Public surface matches spec additions.


33-81: Clarify separation: these flags must end up in MutableFlags, not Flags

MPTokenIssuanceSetFlagsInterface includes both regular and "Set/Clear" mutable booleans, but I didn't find code that converts those mutable booleans into tx.MutableFlags during serialization; tests set MutableFlags as a numeric value. Verify that callers/serialization (e.g. setTransactionFlagsToNumber / convertTxFlagsToNumber / client.autofill) map the tfMPTSet*/tfMPTClear* booleans into MutableFlags, or require callers to pass MutableFlags numerically.

Relevant locations: packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts, packages/xrpl/src/models/utils/flags.ts (setTransactionFlagsToNumber / convertTxFlagsToNumber / parseTransactionFlags), and tests under packages/xrpl/test/models/MPTokenIssuanceSet.test.ts and packages/xrpl/test/integration/transactions/mptokenIssuanceSet.test.ts.

@ckeshava
Copy link
Collaborator Author

I'm working on fixing the integration and browser tests, however I'd love to get a round of code-reviews. I'm not entirely sure why the integ-tests are not connecting to the local rippled standalone instance.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 058ad5b and 1e0aaa0.

📒 Files selected for processing (1)
  • .github/workflows/nodejs.yml (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: unit (22.x)
  • GitHub Check: build-and-lint (22.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (1)
.github/workflows/nodejs.yml (1)

221-230: DRY & replace presence-only Docker check with health-based wait

Replace the step in .github/workflows/nodejs.yml (around lines 221–230) with the health-wait below and extract it to .github/actions/wait-for-rippled; call that action from other jobs (e.g., any browser/integration jobs) to avoid flakes.

-      - name: Check if Docker container is running
-        run: |
-          docker ps
-          docker ps -a
-          docker inspect rippled-service
-          if ! docker ps | grep -q rippled-service; then
-            echo "rippled-service Docker container is not running"
-            exit 1
-          fi
+      - name: Wait for rippled-service to be healthy
+        run: |
+          set -Eeuo pipefail
+          if ! docker ps --filter 'name=^/rippled-service$' --format '{{.Names}}' | grep -qx rippled-service; then
+            echo "rippled-service container not found."
+            docker ps -a || true
+            exit 1
+          fi
+          for i in {1..40}; do
+            status="$(docker inspect -f '{{.State.Health.Status}}' rippled-service || echo unknown)"
+            echo "Attempt $i: health=$status"
+            [ "$status" = "healthy" ] && break || sleep 3
+          done
+          if [ "${status:-unknown}" != "healthy" ]; then
+            echo "::error::rippled-service failed to become healthy."
+            docker inspect -f '{{json .State.Health}}' rippled-service || true
+            docker logs --tail 200 rippled-service || true
+            exit 1
+          fi

Comment on lines 157 to 166
- name: Check if Docker container is running
run: |
docker ps
docker ps -a
docker inspect rippled-service
if ! docker ps | grep -q rippled-service; then
echo "rippled-service Docker container is not running"
exit 1
fi
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 22, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Wait for rippled to be healthy, not merely “running”.

The grep on docker ps only checks container presence and can pass while health is starting/unhealthy, which matches the author’s symptom (“integration tests not connecting”). Poll the container health and surface logs on failure.

Apply this diff to replace the step with a health-aware wait + diagnostics:

-      - name: Check if Docker container is running
-        run: |
-          docker ps
-          docker ps -a
-          docker inspect rippled-service
-          if ! docker ps | grep -q rippled-service; then
-            echo "rippled-service Docker container is not running"
-            exit 1
-          fi
+      - name: Wait for rippled-service to be healthy
+        run: |
+          set -Eeuo pipefail
+          echo "Checking rippled-service container existence..."
+          if ! docker ps --filter 'name=^/rippled-service$' --format '{{.Names}}' | grep -qx rippled-service; then
+            echo "rippled-service container not found in running containers."
+            docker ps -a || true
+            exit 1
+          fi
+          echo "Waiting for rippled-service (health=healthy)..."
+          for i in {1..40}; do
+            status="$(docker inspect -f '{{.State.Health.Status}}' rippled-service || echo unknown)"
+            echo "Attempt $i: health=$status"
+            if [ "$status" = "healthy" ]; then
+              echo "rippled-service is healthy."
+              break
+            fi
+            sleep 3
+          done
+          if [ "${status:-unknown}" != "healthy" ]; then
+            echo "::error::rippled-service failed to become healthy in time."
+            echo "=== docker ps ==="
+            docker ps -a || true
+            echo "=== inspect (Health) ==="
+            docker inspect -f '{{json .State.Health}}' rippled-service || true
+            echo "=== last 200 logs ==="
+            docker logs --tail 200 rippled-service || true
+            exit 1
+          fi

Optional follow-up outside this hunk: make container teardown non-fatal to avoid masking earlier failures.

# change step "Stop docker container"
run: docker stop rippled-service || true

To verify, re-run the workflow; the step should show “health=healthy” before tests start. If it times out, the emitted logs will pinpoint why rippled isn’t ready.


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "Searching for workflow file and snippet..."
rg -n --hidden --glob '!node_modules' 'Check if Docker container is running' || true
echo "Check for .github/workflows/nodejs.yml existence..."
if [ -f .github/workflows/nodejs.yml ]; then
  echo ".github/workflows/nodejs.yml found; printing lines 140-180:"
  nl -ba .github/workflows/nodejs.yml | sed -n '140,180p'
else
  echo ".github/workflows/nodejs.yml not found; searching for nodejs.yml and docker inspect usage..."
  rg -n --hidden --glob '!node_modules' 'nodejs.yml|docker inspect|rippled-service' || true
fi

Length of output: 368


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE='.github/workflows/nodejs.yml'
echo "PWD: $(pwd)"
if [ -f "$FILE" ]; then
  echo "$FILE exists; size: $(stat -c%s "$FILE" || true) bytes"
  echo "---- lines 150-170 ----"
  sed -n '150,170p' "$FILE" || true
  echo "---- lines 156-166 ----"
  sed -n '156,166p' "$FILE" || true
  echo "---- search for keywords in file ----"
  if command -v rg >/dev/null 2>&1; then
    rg -n 'rippled-service|Check if Docker container is running|Wait for rippled-service' "$FILE" || true
  else
    grep -nE 'rippled-service|Check if Docker container is running|Wait for rippled-service' "$FILE" || true
  fi
else
  echo "$FILE not found; searching repo for matches"
  if command -v rg >/dev/null 2>&1; then
    rg -n 'rippled-service|docker inspect|Check if Docker container is running' || true
  else
    grep -RIn --exclude-dir=node_modules -E 'rippled-service|docker inspect|Check if Docker container is running' . || true
  fi
fi

Length of output: 2336


Wait for rippled-service to be healthy, not just running

The current step only checks container presence; it can pass while health is "starting"/"unhealthy", causing integration tests to fail—poll .State.Health and surface logs on timeout.

Location: .github/workflows/nodejs.yml — replace the step at lines 156–166 (duplicate at 221–227).

-      - name: Check if Docker container is running
-        run: |
-          docker ps
-          docker ps -a
-          docker inspect rippled-service
-          if ! docker ps | grep -q rippled-service; then
-            echo "rippled-service Docker container is not running"
-            exit 1
-          fi
+      - name: Wait for rippled-service to be healthy
+        run: |
+          set -Eeuo pipefail
+          echo "Checking rippled-service container existence..."
+          if ! docker ps --filter 'name=^/rippled-service$' --format '{{.Names}}' | grep -qx rippled-service; then
+            echo "rippled-service container not found in running containers."
+            docker ps -a || true
+            exit 1
+          fi
+          echo "Waiting for rippled-service (health=healthy)..."
+          for i in {1..40}; do
+            status="$(docker inspect -f '{{.State.Health.Status}}' rippled-service || echo unknown)"
+            echo "Attempt $i: health=$status"
+            if [ "$status" = "healthy" ]; then
+              echo "rippled-service is healthy."
+              break
+            fi
+            sleep 3
+          done
+          if [ "${status:-unknown}" != "healthy" ]; then
+            echo "::error::rippled-service failed to become healthy in time."
+            echo "=== docker ps ==="
+            docker ps -a || true
+            echo "=== inspect (Health) ==="
+            docker inspect -f '{{json .State.Health}}' rippled-service || true
+            echo "=== last 200 logs ==="
+            docker logs --tail 200 rippled-service || true
+            exit 1
+          fi

Optional operational change: make teardown non-fatal to avoid masking earlier failures — change any run: docker stop rippled-service to run: docker stop rippled-service || true (lines ~172 and ~236).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is not useful to poll for the existence of the rippled docker container. If the docker container failed to start-up correctly, there is no chance for it to restart in a healthy manner.

Copy link
Contributor

Choose a reason for hiding this comment

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

@ckeshava You're absolutely right that polling won't help a failed container restart. I think there was a misunderstanding in my suggestion - I wasn't proposing to restart failed containers, but rather to wait for an already-started container to reach "healthy" status before running tests.

However, since you've identified that as unnecessary, let me suggest a simpler diagnostic approach that might help identify why integration tests aren't connecting:

-      - name: Check if Docker container is running
-        run: |
-          docker ps
-          docker ps -a
-          docker inspect rippled-service
-          if ! docker ps | grep -q rippled-service; then
-            echo "rippled-service Docker container is not running"
-            exit 1
-          fi
+      - name: Check rippled-service connectivity
+        run: |
+          docker ps
+          if ! docker ps | grep -q rippled-service; then
+            echo "rippled-service container not found"
+            docker ps -a
+            exit 1
+          fi
+          echo "Testing rippled connectivity on port 6006..."
+          curl -f http://localhost:6006 || {
+            echo "Failed to connect to rippled on port 6006"
+            docker logs --tail 50 rippled-service
+            exit 1
+          }

This approach simply verifies the service is actually accessible on the expected port and shows logs if it's not, which should help diagnose the connection issues without unnecessary waiting.


✏️ Learnings added
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: .github/workflows/nodejs.yml:157-168
Timestamp: 2025-09-22T20:30:43.864Z
Learning: ckeshava prefers not to poll for Docker container health status in CI workflows, believing that failed containers won't recover through polling.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why do we need to test the curl command? If the docker ps response does not list the rippled-service container, why do I need other ways to detect the existence of the container?

Copy link
Contributor

Choose a reason for hiding this comment

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

@ckeshava You're absolutely right - if docker ps shows the rippled-service container is running, that should be sufficient to confirm it exists and is operational.

The curl test was intended to distinguish between "container exists" and "service is accessible," which could help diagnose cases where the container runs but the rippled process inside has issues or port mapping failed. However, you make a valid point that this adds unnecessary complexity.

If docker ps confirms the container is running, the integration tests should be able to connect. If they still can't connect despite the container being present, that would likely indicate a different issue (network configuration, test setup, etc.) that wouldn't be solved by additional container checks.

Your current approach of simply verifying container presence with docker ps and failing fast if it's not there is the most straightforward solution.

  ・゚✧*:・゚✧
🐰 ( curious )
   ∪      ∪

🧠 Learnings used
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: .github/workflows/nodejs.yml:157-168
Timestamp: 2025-09-22T20:30:43.864Z
Learning: ckeshava prefers not to poll for Docker container health status in CI workflows, believing that failed containers won't recover through polling.

tfMPTCanTransfer?: boolean
tfMPTCanClawback?: boolean

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b792695 and 0876e86.

📒 Files selected for processing (1)
  • .github/workflows/nodejs.yml (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.
📚 Learning: 2025-09-22T20:30:43.864Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: .github/workflows/nodejs.yml:157-168
Timestamp: 2025-09-22T20:30:43.864Z
Learning: ckeshava prefers not to poll for Docker container health status in CI workflows, believing that failed containers won't recover through polling.

Applied to files:

  • .github/workflows/nodejs.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: unit (22.x)
  • GitHub Check: integration (22.x)
  • GitHub Check: integration (20.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: browser (22.x)
  • GitHub Check: build-and-lint (22.x)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan

Copy link

@Jasonradford51 Jasonradford51 left a comment

Choose a reason for hiding this comment

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

complete

@ckeshava
Copy link
Collaborator Author

@Patel-Raj11 The relevant tests in Github CI have passed.

tfMPTCanTransfer?: boolean
tfMPTCanClawback?: boolean

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.

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?


assertInvalid(invalid, 'MPTokenIssuanceSet: flag conflict')
})

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we usually write tests for all the failure conditions mentioned in the standard, e.g., https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0094-dynamic-MPT#42-failure-conditions

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can only check for tem error codes, we cannot validate the other failure cases without reading the state of the blockchain.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right — in this case, should we test all the tem failure conditions? If we haven’t done that before, should we start? I agree we don’t need to be exhaustive in integration tests, but for unit tests, I don’t see why not — especially since the spec developer has already listed the conditions for us. We could even use AI to translate them into tests.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Presently, I have not written validations for all the possible failure cases (tem codes). That is a prerequisite for writing unit tests.

I'm not convinced that any validation is required from the client libraries at all. If there is an error in the transaction, it will be failed by rippled with the appropriate error code. The basic validation of the transaction occurs with minimal network overhead, i.e. transactions which fail with a tem code are never "gossip"ed by the XRPL network. What are we gaining by implementing these exhaustive validations in the client libraries?

This is a repetition of engineering effort across three teams. The rippled cpp binary already implements validations+unit-tests for the tem codes. We will be duplicating the code in Transactor::preflight method for each transaction, in all the client libraries. Furthermore, I'm given to understand that the QA team also performs exhaustive tests of a new amendment.

Lastly, The existing set of ~70 transactions do not have exhaustive validations/unit-tests. Are we planning to extend this idea to the other transactions as well? Older transactions like Payment and TrustSet are used a lot more frequently than the other transactions. Hence, validations+unit-tests on older transactions provide the most exposure to the users.

Copy link
Collaborator

@kuan121 kuan121 Oct 6, 2025

Choose a reason for hiding this comment

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

Based on your argument, should we stop writing unit tests for our client libraries just because other teams are already doing so? That doesn’t sound like a good idea. If we shouldn’t stop, then what should our unit tests focus on — the failure conditions that are easier to test or write? Maybe you could elaborate on what you chose to unit test or integration test in this PR and the reasons?

Copy link
Collaborator

@kuan121 kuan121 Oct 6, 2025

Choose a reason for hiding this comment

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

This is a repetition of engineering effort across three teams. The rippled cpp binary already implements validations+unit-tests for the tem codes. We will be duplicating the code in Transactor::preflight method for each transaction, in all the client libraries. Furthermore, I'm given to understand that the QA team also performs exhaustive tests of a new amendment.

To be clear, my main concern isn’t about unit testing every failure condition — it’s about ensuring we have the right validations in our model objects to prevent obvious failure conditions on the client side. The tem failure conditions listed in the spec seem like a solid reference, which is why I think we should have proper validations or tests covering them, instead of relying on developers to pick and choose which cases to test based on personal judgment.

Copy link
Collaborator Author

@ckeshava ckeshava Oct 6, 2025

Choose a reason for hiding this comment

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

Based on your argument, should we stop writing unit tests for our client libraries just because other teams are already doing so?

that is my personal opinion, yes.

The USP of the client libraries is the autofill feature, the serializability of transaction, the ease of communicating with the XRPL blockchain. I feel we shouldn't duplicate the engineering effort of testing for the correctness of transactions in both rippled-cpp-code and all the client libraries (py, js, java, go).

Although I disagree with you, I can write more tests for the failure modes of DynamicMPT amendment in the next few days, I understand the benefit of your suggestion.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks @ckeshava! I’ll add this as a topic for our next sprint retro and see what the rest of the team thinks.

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 have added all the validations and unit tests for the MPTokenIssuance{Create, Set} transactions. Please refer to this commit: 7260f2f

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks! I will review it again today.

@ckeshava ckeshava requested review from kuan121 and pdp2121 September 30, 2025 20:09
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6ec04a and 565402b.

📒 Files selected for processing (5)
  • packages/xrpl/src/models/ledger/MPTokenIssuance.ts (1 hunks)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (6 hunks)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (7 hunks)
  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts (3 hunks)
  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-09-16T05:00:20.420Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.

Applied to files:

  • packages/xrpl/src/models/ledger/MPTokenIssuance.ts
  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-01-08T02:12:28.489Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2874
File: packages/xrpl/test/integration/transactions/permissionedDomain.test.ts:25-80
Timestamp: 2025-01-08T02:12:28.489Z
Learning: For PermissionedDomain feature (XLS-80d), complex error cases like invalid credential format, duplicate credentials, and non-existent DomainID are tested in the rippled C++ implementation rather than being duplicated across SDK implementations like xrpl.js.

Applied to files:

  • packages/xrpl/src/models/ledger/MPTokenIssuance.ts
📚 Learning: 2024-12-06T18:44:55.095Z
Learnt from: shawnxie999
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/test/models/MPTokenAuthorize.test.ts:60-71
Timestamp: 2024-12-06T18:44:55.095Z
Learning: In the XRPL.js library's TypeScript test file `packages/xrpl/test/models/MPTokenAuthorize.test.ts`, negative test cases for invalid `Account` address format, invalid `Holder` address format, invalid `MPTokenIssuanceID` format, and invalid flag combinations are not necessary.

Applied to files:

  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts
  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
📚 Learning: 2025-01-31T17:46:25.375Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2873
File: packages/xrpl/test/integration/transactions/trustSet.test.ts:0-0
Timestamp: 2025-01-31T17:46:25.375Z
Learning: For the XRPL implementation, extensive test cases for deep freeze behavior (high/low side interactions, clearing flags, etc.) are maintained in the C++ implementation and don't need to be duplicated in the JavaScript implementation.

Applied to files:

  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
📚 Learning: 2025-01-08T13:08:52.688Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2873
File: packages/xrpl/src/models/transactions/trustSet.ts:33-36
Timestamp: 2025-01-08T13:08:52.688Z
Learning: For trust-set transactions in XRPL, validation rules for flags should be implemented comprehensively rather than cherry-picking specific rules, as there are many interdependent validation rules associated with these flags.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
🧬 Code graph analysis (4)
packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts (1)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
  • tmfMPTokenIssuanceCreateMutableMask (92-101)
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (4)
packages/xrpl/src/models/transactions/common.ts (4)
  • validateOptionalField (426-446)
  • isString (146-148)
  • isNumber (156-158)
  • MAX_MPT_META_BYTE_LENGTH (26-26)
packages/xrpl/src/errors.ts (1)
  • ValidationError (156-156)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
  • MAX_TRANSFER_FEE (19-19)
packages/xrpl/src/models/utils/index.ts (1)
  • isHex (60-62)
packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (3)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
  • MAX_TRANSFER_FEE (19-19)
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (1)
  • tmfMPTokenIssuanceSetMutableMask (60-73)
packages/xrpl/src/models/transactions/common.ts (1)
  • MAX_MPT_META_BYTE_LENGTH (26-26)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (3)
packages/xrpl/src/models/transactions/metadata.ts (1)
  • TransactionMetadataBase (84-93)
packages/xrpl/src/models/transactions/common.ts (2)
  • validateOptionalField (426-446)
  • isNumber (156-158)
packages/xrpl/src/errors.ts (1)
  • ValidationError (156-156)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: browser (22.x)
  • GitHub Check: build-and-lint (22.x)
  • GitHub Check: integration (20.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: unit (22.x)
  • GitHub Check: integration (22.x)
🔇 Additional comments (18)
packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts (3)

8-11: LGTM! Test imports are appropriate.

The addition of tmfMPTokenIssuanceCreateMutableMask import enables testing of the MutableFlags validation logic introduced in the implementation.


34-34: LGTM! Valid test fixture properly demonstrates MutableFlags usage.

The fixture correctly uses tmfMPTCanMutateTransferFee to enable mutability for the TransferFee field, which aligns with the Dynamic MPT specification.


149-157: LGTM! Test effectively validates MutableFlags mask enforcement.

The test correctly verifies that an invalid MutableFlags value (using the mask itself, which has all valid bits set to 0) is rejected with the appropriate error message.

packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (5)

19-19: LGTM! MAX_TRANSFER_FEE export enables consistent validation.

Exporting this constant allows reuse in related transactions (e.g., MPTokenIssuanceSet) and ensures consistency across the codebase.


57-89: LGTM! Mutate flags are well-documented and correctly defined.

The flag definitions are clear, with documentation explaining each flag's purpose. The intentional reuse of base flag bit values is appropriate since these operate in the separate MutableFlags field context.

Based on learnings.


91-102: LGTM! Mask computation correctly identifies invalid MutableFlags bits.

The bitwise NOT operation produces a mask where valid flag bits are 0 and invalid bits are 1, enabling efficient validation via bitwise AND.


225-225: LGTM! MutableFlags field properly extends the transaction interface.

The optional field allows specifying which flags can be mutated after issuance creation, aligning with the XLS-94D specification.


247-257: LGTM! MutableFlags validation correctly enforces the mask.

The validation logic properly checks that only recognized flag bits are set by testing MutableFlags & mask. If any invalid bits are set, the result is non-zero and triggers an error.

packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (2)

55-60: LGTM! Valid test case demonstrates MutableFlags usage.

The test correctly validates that MutableFlags can be used with a clear flag operation, confirming the basic functionality works as expected.


93-191: LGTM! Comprehensive validation test coverage.

The test suite thoroughly covers all validation scenarios for the new fields:

  • TransferFee: type, lower bound, upper bound
  • MutableFlags: type, mask validation
  • MPTokenMetadata: type, hex format, length constraints

This ensures the validation logic in MPTokenIssuanceSet.ts is working correctly.

packages/xrpl/src/models/ledger/MPTokenIssuance.ts (3)

14-16: LGTM! Ledger entry fields properly extended for Dynamic MPTs.

The addition of DomainID (optional) and MutableFlags (required) aligns with the rippled implementation and the XLS-94D specification for representing MPTokenIssuance state on the ledger.


18-59: LGTM! Flag interface is comprehensive and well-documented.

The interface provides both base flags (lsfMPT*) and mutate capability flags (lsmfMPTCanMutate*), with clear documentation explaining each flag's purpose.


61-102: LGTM! Flag enum correctly defines ledger state flags.

The enum properly defines both base flags and mutate capability flags. The intentional reuse of bit values between base and mutate flags is appropriate since they exist in separate fields (Flags vs MutableFlags) on the ledger object.

Based on learnings.

packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (5)

2-16: LGTM! Imports properly support new field validations.

The added imports (isHex, isNumber, MAX_MPT_META_BYTE_LENGTH, MAX_TRANSFER_FEE) enable consistent validation across MPTokenIssuance transactions.


33-56: LGTM! Set/Clear flag pattern is well-defined.

The 12 MutableFlags follow a consistent Set/Clear pattern for modifying the 6 base capabilities (CanLock, RequireAuth, CanEscrow, CanTrade, CanTransfer, CanClawback). Each flag is clearly documented.


59-74: LGTM! Mask correctly validates MutableFlags combinations.

The bitwise NOT of all valid mutate flags produces a mask that efficiently detects invalid flag combinations during validation.


129-131: LGTM! Transaction interface properly extended for Dynamic MPT support.

The three optional fields (MPTokenMetadata, TransferFee, MutableFlags) enable issuers to modify mutable aspects of an MPTokenIssuance after creation, as specified in XLS-94D.


145-189: LGTM! Comprehensive validation for all new fields.

The validation logic is thorough and consistent:

  • Type validation using validateOptionalField for all three fields
  • MutableFlags mask check prevents invalid flag combinations
  • TransferFee range enforcement (0 to 50,000)
  • MPTokenMetadata hex format and length constraints

All validations follow the same patterns established in MPTokenIssuanceCreate.

@@ -1,5 +1,12 @@
import { stringToHex } from '@xrplf/isomorphic/dist/utils'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Import path inconsistency with MPTokenIssuanceCreate.test.ts.

This file imports from @xrplf/isomorphic/dist/utils while MPTokenIssuanceCreate.test.ts imports from @xrplf/isomorphic/src/utils. For consistency, both test files should use the same import path.

Apply this diff to align with the other test file:

-import { stringToHex } from '@xrplf/isomorphic/dist/utils'
+import { stringToHex } from '@xrplf/isomorphic/src/utils'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { stringToHex } from '@xrplf/isomorphic/dist/utils'
import { stringToHex } from '@xrplf/isomorphic/src/utils'
🤖 Prompt for AI Agents
In packages/xrpl/test/models/MPTokenIssuanceSet.test.ts around line 1, the
import path uses '@xrplf/isomorphic/dist/utils' which is inconsistent with
MPTokenIssuanceCreate.test.ts that imports from '@xrplf/isomorphic/src/utils';
update the import to use '@xrplf/isomorphic/src/utils' so both tests use the
same source path, and run tests to ensure the module resolves correctly.

*/
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

export interface MPTokenIssuanceSetFlagsInterface extends GlobalFlagsInterface {
tfMPTLock?: boolean
tfMPTUnlock?: boolean
// The below flags are used in the MutableFlags field
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated in 42049ac

tfMPTCanTransfer?: boolean
tfMPTCanClawback?: boolean

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.

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

@ckeshava ckeshava requested a review from pdp2121 October 6, 2025 23:06
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)

289-299: Allow zero for MaximumAmount per XLS-0094
Per XLS-0094, MaximumAmount is an unsigned 64-bit integer (zero permitted). Change the lower-bound check from <= 0 to < 0:

-} else if (
-  BigInt(tx.MaximumAmount) > BigInt(MAX_AMT) ||
-  BigInt(tx.MaximumAmount) <= BigInt(`0`)
-) {
+} else if (
+  BigInt(tx.MaximumAmount) > BigInt(MAX_AMT) ||
+  BigInt(tx.MaximumAmount) < BigInt(0)
+) {
♻️ Duplicate comments (1)
packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (1)

1-1: Align import path with other tests

Use consistent source import path for isomorphic utils.

-import { stringToHex } from '@xrplf/isomorphic/dist/utils'
+import { stringToHex } from '@xrplf/isomorphic/src/utils'
🧹 Nitpick comments (2)
packages/xrpl/src/models/transactions/common.ts (1)

773-779: Revisit DomainID validation scope

Adding isHex + all-zero rejection tightens validation that was previously length-only. This may duplicate codec-level checks and diverge from prior guidance.

Based on learnings

Option to revert to length-only (if desired):

 export function isDomainID(domainID: unknown): domainID is string {
   return (
     isString(domainID) &&
     domainID.length === _DOMAIN_ID_LENGTH &&
-    isHex(domainID) &&
-    domainID !== '0'.repeat(_DOMAIN_ID_LENGTH)
+    true
   )
 }
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (1)

222-247: Minor: avoid per-call allocation of flag pairs

Hoist MPTMutabilityFlags to a top-level const to avoid re-allocating on every validation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42049ac and 7260f2f.

📒 Files selected for processing (6)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (8 hunks)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (7 hunks)
  • packages/xrpl/src/models/transactions/common.ts (1 hunks)
  • packages/xrpl/src/models/utils/flags.ts (1 hunks)
  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts (3 hunks)
  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.
📚 Learning: 2025-09-16T05:00:20.420Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.

Applied to files:

  • packages/xrpl/src/models/utils/flags.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-07-10T22:04:59.994Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3032
File: packages/xrpl/src/models/transactions/common.ts:689-698
Timestamp: 2025-07-10T22:04:59.994Z
Learning: In the XRPL.js library, the validateDomainID function in `packages/xrpl/src/models/transactions/common.ts` should only validate the length (64 characters) of domain IDs, not hex encoding. The rippled C++ implementation does not enforce any regex check on domain ID values, so additional hex validation is not required in the JS implementation.

Applied to files:

  • packages/xrpl/src/models/transactions/common.ts
📚 Learning: 2025-07-11T21:22:07.809Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3032
File: packages/xrpl/src/models/transactions/common.ts:689-698
Timestamp: 2025-07-11T21:22:07.809Z
Learning: Domain ID validation in XRPL.js is handled at the serialization/deserialization layer through Hash types in the ripple-binary-codec package, not at the transaction validation layer. The validateDomainID function only needs to validate length (64 characters) as hex validation occurs when Hash types are constructed during serialization.

Applied to files:

  • packages/xrpl/src/models/transactions/common.ts
📚 Learning: 2024-12-06T18:44:55.095Z
Learnt from: shawnxie999
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/test/models/MPTokenAuthorize.test.ts:60-71
Timestamp: 2024-12-06T18:44:55.095Z
Learning: In the XRPL.js library's TypeScript test file `packages/xrpl/test/models/MPTokenAuthorize.test.ts`, negative test cases for invalid `Account` address format, invalid `Holder` address format, invalid `MPTokenIssuanceID` format, and invalid flag combinations are not necessary.

Applied to files:

  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
📚 Learning: 2025-01-31T17:46:25.375Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2873
File: packages/xrpl/test/integration/transactions/trustSet.test.ts:0-0
Timestamp: 2025-01-31T17:46:25.375Z
Learning: For the XRPL implementation, extensive test cases for deep freeze behavior (high/low side interactions, clearing flags, etc.) are maintained in the C++ implementation and don't need to be duplicated in the JavaScript implementation.

Applied to files:

  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
📚 Learning: 2024-12-06T19:25:15.376Z
Learnt from: shawnxie999
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/test/integration/transactions/mptokenAuthorize.test.ts:29-118
Timestamp: 2024-12-06T19:25:15.376Z
Learning: In the XRPLF/xrpl.js TypeScript client library, when writing tests (e.g., in `packages/xrpl/test/integration/transactions/`), we generally do not need to test rippled server behaviors, because those behaviors are covered by rippled's own integration and unit tests.

Applied to files:

  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
📚 Learning: 2025-01-08T13:08:52.688Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2873
File: packages/xrpl/src/models/transactions/trustSet.ts:33-36
Timestamp: 2025-01-08T13:08:52.688Z
Learning: For trust-set transactions in XRPL, validation rules for flags should be implemented comprehensively rather than cherry-picking specific rules, as there are many interdependent validation rules associated with these flags.

Applied to files:

  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
🧬 Code graph analysis (4)
packages/xrpl/src/models/transactions/common.ts (1)
packages/xrpl/src/models/utils/index.ts (1)
  • isHex (60-62)
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (5)
packages/xrpl/src/models/transactions/common.ts (5)
  • validateOptionalField (426-446)
  • isString (146-148)
  • isNumber (156-158)
  • isDomainID (773-780)
  • MAX_MPT_META_BYTE_LENGTH (26-26)
packages/xrpl/src/models/utils/flags.ts (1)
  • convertTxFlagsToNumber (105-136)
packages/xrpl/src/models/transactions/transaction.ts (1)
  • Transaction (211-211)
packages/xrpl/src/models/utils/index.ts (2)
  • isFlagEnabled (27-30)
  • isHex (60-62)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
  • MAX_TRANSFER_FEE (20-20)
packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (3)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)
  • MAX_TRANSFER_FEE (20-20)
packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (1)
  • tmfMPTokenIssuanceSetMutableMask (67-80)
packages/xrpl/src/models/transactions/common.ts (1)
  • MAX_MPT_META_BYTE_LENGTH (26-26)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (2)
packages/xrpl/src/models/transactions/common.ts (3)
  • validateOptionalField (426-446)
  • isNumber (156-158)
  • isDomainID (773-780)
packages/xrpl/src/models/utils/index.ts (1)
  • hasFlag (40-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: integration (20.x)
  • GitHub Check: integration (22.x)
  • GitHub Check: unit (20.x)
  • GitHub Check: browser (22.x)
  • GitHub Check: unit (22.x)
  • GitHub Check: build-and-lint (22.x)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (4)
packages/xrpl/src/models/utils/flags.ts (1)

15-16: Good addition: flag mapping for MPTokenIssuanceSet

Import + txToFlag mapping enables consistent flag conversion/parsing for MPTokenIssuanceSet.

Also applies to: 61-61

packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts (1)

66-81: MutableFlags mask approach matches rippled pattern

Using a complemented mask to detect out-of-range bits is correct for validating MutableFlags.

packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (1)

256-267: DomainID gating on tfMPTRequireAuth looks correct

Requiring tfMPTRequireAuth when DomainID is set aligns with intended semantics.

Please confirm this matches rippled preflight for XLS-94D.

packages/xrpl/test/models/MPTokenIssuanceSet.test.ts (1)

46-52: Nice: positive test for valid mutation-only path

Covers the MutableFlags-only scenario well.

Comment on lines +199 to +207
if (
(tx.Flags === 0 || tx.Flags === undefined) &&
tx.DomainID == null &&
!isMutate
) {
throw new ValidationError(
'MPTokenIssuanceSet: Transaction does not change the state of the MPTokenIssuance ledger object.',
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

No-op detection misses object-form Flags (possible false negatives)

Checking tx.Flags === 0 skips cases where Flags is an object with no bits set (converts to 0). Use convertTxFlagsToNumber(tx) to robustly detect no-ops.

Apply:

-  if (
-    (tx.Flags === 0 || tx.Flags === undefined) &&
-    tx.DomainID == null &&
-    !isMutate
-  ) {
+  if (
+    convertTxFlagsToNumber(tx as Transaction) === 0 &&
+    tx.DomainID == null &&
+    !isMutate
+  ) {
     throw new ValidationError(
       'MPTokenIssuanceSet: Transaction does not change the state of the MPTokenIssuance ledger object.',
     )
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
(tx.Flags === 0 || tx.Flags === undefined) &&
tx.DomainID == null &&
!isMutate
) {
throw new ValidationError(
'MPTokenIssuanceSet: Transaction does not change the state of the MPTokenIssuance ledger object.',
)
}
if (
convertTxFlagsToNumber(tx as Transaction) === 0 &&
tx.DomainID == null &&
!isMutate
) {
throw new ValidationError(
'MPTokenIssuanceSet: Transaction does not change the state of the MPTokenIssuance ledger object.',
)
}
🤖 Prompt for AI Agents
In packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts around lines 199
to 207, the no-op detection currently checks tx.Flags === 0 which misses cases
where Flags is an object with no bits set; replace the direct check with a
numeric conversion using convertTxFlagsToNumber(tx) (e.g., const flagsNum =
convertTxFlagsToNumber(tx)) and test flagsNum === 0 (or flagsNum === undefined
handling as needed) so the condition reliably detects no-ops for both numeric
and object-form Flags; ensure convertTxFlagsToNumber is imported/available in
the module and update the if condition to use flagsNum instead of tx.Flags.

@Jasonradford51
Copy link

i am trying to complete and send on to yu people it will not allow me

kuan121
kuan121 previously approved these changes Oct 14, 2025
@ckeshava
Copy link
Collaborator Author

@pdp2121 Please let me know if you have any comments on this PR.

MPTokenMetadata?: string

Flags?: number | MPTokenIssuanceCreateFlagsInterface
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

pdp2121
pdp2121 previously approved these changes Oct 29, 2025
Copy link
Collaborator

@pdp2121 pdp2121 left a comment

Choose a reason for hiding this comment

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

LGTM! Left a small comment

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a6c46c and 4a3a978.

📒 Files selected for processing (1)
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (8 hunks)
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2874
File: packages/xrpl/test/integration/transactions/permissionedDomain.test.ts:25-80
Timestamp: 2025-01-08T02:12:28.489Z
Learning: The rippled C++ implementation (PR #5161) includes comprehensive test coverage for PermissionedDomain (XLS-80d) error cases. The JS SDK tests focus on the happy path since the error cases are already validated at the rippled level, following the principle of not duplicating complex validation testing across SDK implementations.
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.
Learnt from: Patel-Raj11
PR: XRPLF/xrpl.js#3117
File: packages/xrpl/src/models/transactions/common.ts:1133-1180
Timestamp: 2025-10-28T14:16:24.567Z
Learning: The MPTokenMetadata TypeScript interface in xrpl.js reflects the XLS-89 standard (long-form fields only) to provide correct type hints for IDEs and TypeScript users, even though JavaScript users may provide non-standard structures with compact fields or both forms present. This design provides proper tooling support while maintaining runtime flexibility.
Learnt from: Patel-Raj11
PR: XRPLF/xrpl.js#3117
File: packages/xrpl/src/models/transactions/common.ts:1133-1180
Timestamp: 2025-10-28T14:16:24.567Z
Learning: In xrpl.js MPTokenMetadata handling: encodeMPTokenMetadata and decodeMPTokenMetadata are designed to preserve the original structure of the input, only converting between compact and long forms when a single form is present (not both). If both compact (e.g., 'us') and long (e.g., 'uris') forms exist in the input, both are preserved in the output. This is intentional to avoid modifying the user's data. Validation (validateMPTokenMetadata) separately checks and reports XLS-89 compliance.
📚 Learning: 2025-09-16T05:00:20.420Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-10-28T14:16:24.567Z
Learnt from: Patel-Raj11
PR: XRPLF/xrpl.js#3117
File: packages/xrpl/src/models/transactions/common.ts:1133-1180
Timestamp: 2025-10-28T14:16:24.567Z
Learning: The MPTokenMetadata TypeScript interface in xrpl.js reflects the XLS-89 standard (long-form fields only) to provide correct type hints for IDEs and TypeScript users, even though JavaScript users may provide non-standard structures with compact fields or both forms present. This design provides proper tooling support while maintaining runtime flexibility.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2024-12-06T18:44:55.095Z
Learnt from: shawnxie999
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/test/models/MPTokenAuthorize.test.ts:60-71
Timestamp: 2024-12-06T18:44:55.095Z
Learning: In the XRPL.js library's TypeScript test file `packages/xrpl/test/models/MPTokenAuthorize.test.ts`, negative test cases for invalid `Account` address format, invalid `Holder` address format, invalid `MPTokenIssuanceID` format, and invalid flag combinations are not necessary.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-10-28T14:16:24.567Z
Learnt from: Patel-Raj11
PR: XRPLF/xrpl.js#3117
File: packages/xrpl/src/models/transactions/common.ts:1133-1180
Timestamp: 2025-10-28T14:16:24.567Z
Learning: In xrpl.js MPTokenMetadata handling: encodeMPTokenMetadata and decodeMPTokenMetadata are designed to preserve the original structure of the input, only converting between compact and long forms when a single form is present (not both). If both compact (e.g., 'us') and long (e.g., 'uris') forms exist in the input, both are preserved in the output. This is intentional to avoid modifying the user's data. Validation (validateMPTokenMetadata) separately checks and reports XLS-89 compliance.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-01-08T13:08:52.688Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2873
File: packages/xrpl/src/models/transactions/trustSet.ts:33-36
Timestamp: 2025-01-08T13:08:52.688Z
Learning: For trust-set transactions in XRPL, validation rules for flags should be implemented comprehensively rather than cherry-picking specific rules, as there are many interdependent validation rules associated with these flags.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2024-12-05T16:48:12.951Z
Learnt from: achowdhry-ripple
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:69-102
Timestamp: 2024-12-05T16:48:12.951Z
Learning: When adding validation in `validate*` functions in `packages/xrpl/src/models/transactions/`, utilize existing helper functions (e.g., `validateOptionalField`, `validateType`, `isNumber`, `isInteger`) for type checking and validation where appropriate.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-07-10T22:04:59.994Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3032
File: packages/xrpl/src/models/transactions/common.ts:689-698
Timestamp: 2025-07-10T22:04:59.994Z
Learning: In the XRPL.js library, the validateDomainID function in `packages/xrpl/src/models/transactions/common.ts` should only validate the length (64 characters) of domain IDs, not hex encoding. The rippled C++ implementation does not enforce any regex check on domain ID values, so additional hex validation is not required in the JS implementation.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-06-26T17:25:36.429Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3027
File: packages/xrpl/src/models/ledger/MPTokenIssuance.ts:13-16
Timestamp: 2025-06-26T17:25:36.429Z
Learning: In the XRPL ecosystem, type choices for amount fields (like `number` vs `string`) in ledger objects such as `LockedAmount` vs `OutstandingAmount` in `MPTokenIssuance` are deliberate design decisions made across multiple products and cannot be changed unilaterally by individual contributors.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-04-16T15:55:50.121Z
Learnt from: mvadari
PR: XRPLF/xrpl.js#2801
File: packages/xrpl/src/Wallet/batchSigner.ts:0-0
Timestamp: 2025-04-16T15:55:50.121Z
Learning: When using `encodeForSigningBatch` for Batch transactions in the XRPL.js library, the field names should use camelCase (`flags`, `txIDs`) even though the transaction object properties themselves use PascalCase (`Flags`, `TxIDs`).

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-01-31T17:46:25.375Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2873
File: packages/xrpl/test/integration/transactions/trustSet.test.ts:0-0
Timestamp: 2025-01-31T17:46:25.375Z
Learning: For the XRPL implementation, extensive test cases for deep freeze behavior (high/low side interactions, clearing flags, etc.) are maintained in the C++ implementation and don't need to be duplicated in the JavaScript implementation.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-07-11T21:22:07.809Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3032
File: packages/xrpl/src/models/transactions/common.ts:689-698
Timestamp: 2025-07-11T21:22:07.809Z
Learning: Domain ID validation in XRPL.js is handled at the serialization/deserialization layer through Hash types in the ripple-binary-codec package, not at the transaction validation layer. The validateDomainID function only needs to validate length (64 characters) as hex validation occurs when Hash types are constructed during serialization.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
📚 Learning: 2025-01-08T02:12:28.489Z
Learnt from: ckeshava
PR: XRPLF/xrpl.js#2874
File: packages/xrpl/test/integration/transactions/permissionedDomain.test.ts:25-80
Timestamp: 2025-01-08T02:12:28.489Z
Learning: For PermissionedDomain feature (XLS-80d), complex error cases like invalid credential format, duplicate credentials, and non-existent DomainID are tested in the rippled C++ implementation rather than being duplicated across SDK implementations like xrpl.js.

Applied to files:

  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
🧬 Code graph analysis (1)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (2)
packages/xrpl/src/models/transactions/common.ts (3)
  • validateOptionalField (426-446)
  • isNumber (156-158)
  • isDomainID (773-780)
packages/xrpl/src/models/utils/index.ts (1)
  • hasFlag (40-52)
🔇 Additional comments (9)
packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts (9)

2-2: LGTM! New imports support DomainID validation.

The hasFlag and isDomainID imports are correctly used in the validation logic to check flag requirements and validate the DomainID field format.

Also applies to: 14-14


20-20: LGTM! Exporting shared constant.

Exporting MAX_TRANSFER_FEE enables reuse across related transaction types without duplication.


59-92: LGTM! Mutable flags enum is well-documented.

The enum correctly defines mutable flags with clear documentation. The tmf prefix effectively distinguishes these from base transaction flags.

Based on learnings: The flag values intentionally match base flag values because they operate in different contexts (Flags vs MutableFlags fields).


94-105: LGTM! Bitmask correctly validates mutable flags.

The complement mask computation correctly identifies invalid bits in MutableFlags. The bitwise operation with eslint suppressions appropriately replicates rippled behavior.


113-144: LGTM! Flag interface now has comprehensive documentation.

Each flag property has clear JSDoc comments explaining its purpose, improving developer experience and code maintainability.


146-179: LGTM! Separate mutable flags interface improves clarity.

The dedicated interface for mutable flags clearly distinguishes which flags apply to the MutableFlags field versus the Flags field, following the pattern established in AccountSet.


231-234: LGTM! New fields properly typed and documented.

The MutableFlags and DomainID fields are correctly added as optional number and string types respectively, with an explanatory comment for MutableFlags.


241-242: LGTM! ESLint pragmas accommodate extended validation.

The disable/enable comments appropriately suppress line/statement count rules for the expanded validation function.

Also applies to: 342-342


256-280: LGTM! MutableFlags and DomainID validations are comprehensive.

The validation logic correctly enforces:

  • MutableFlags is a valid number without invalid bits set (using the complement mask)
  • DomainID is properly formatted (64-char hex, non-zero)
  • DomainID requires the tfMPTRequireAuth flag to be set

The use of hasFlag properly handles both numeric and object flag formats.

} else if (
BigInt(tx.MaximumAmount) > BigInt(MAX_AMT) ||
BigInt(tx.MaximumAmount) < BigInt(`0`)
BigInt(tx.MaximumAmount) <= BigInt(`0`)
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 30, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify that MaximumAmount > 0 aligns with XLS-94D.

The validation changed from rejecting negative values to also rejecting zero (<= 0 instead of < 0). While this makes semantic sense (a maximum of 0 tokens is nonsensical), please confirm this stricter requirement is specified in XLS-94D.


🌐 Web query:

Does XLS-94D require MPTokenIssuanceCreate MaximumAmount to be greater than 0?

💡 Result:

Short answer: No.

Reason: MPTokenIssuanceCreate's MaximumAmount is optional and is a uint64. If omitted the implementation treats it as the current default maximum (2^63‑1); you may also set it explicitly (including 0, which would mean no non‑issuer tokens may be minted). (xrpl.org)

Citations:


The validation is too strict and conflicts with XLS-94D specification.

According to XLS-94D, MaximumAmount may be explicitly set to 0, which means no non-issuer tokens may be minted. The current validation rejects this valid state. Change the condition from <= 0 to < 0 to allow zero as a valid MaximumAmount value.

🤖 Prompt for AI Agents
In packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts around line
297, the validation currently rejects MaximumAmount when it equals 0 using
BigInt(tx.MaximumAmount) <= BigInt(`0`); update the condition to only reject
negative values by changing the comparison to BigInt(tx.MaximumAmount) <
BigInt(`0`) so that 0 is accepted per XLS-94D.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the rippled C++ implementation to verify the validation logic
curl -s "https://raw.githubusercontent.com/XRPLF/rippled/f8b4f692f1cb3b971138b71912bcd2c2eb9ee4d5/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp" | sed -n '80,100p'

Length of output: 538


@ckeshava, you're absolutely correct!

I've verified the rippled C++ implementation at the link you provided. Line 90 explicitly shows:

if (maxAmt == 0)
    return temMALFORMED;

The C++ implementation (which is the authoritative source) clearly rejects MaximumAmount = 0. Your current JavaScript validation using <= 0 is correct and properly matches the rippled behavior.

My earlier suggestion to allow zero was based on the XLS-94D documentation, but the actual implementation in rippled takes precedence. The current code is working as intended.

🐰💡


🧬 Code Graph Analysis Results

[Relevant code snippets to assist with understanding MPTokenIssuanceCreate.ts and the user’s rebuttal reference]

  • File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts

    • Summary: validateMPTokenIssuanceCreate(tx)
      • Description: Runtime validation function for an MPTokenIssuanceCreate transaction.
        • Parameters: tx - Record<string, unknown> representing the transaction.
        • Returns: void (throws ValidationError on malformed input).
        • Behavior: Validates base transaction structure, optional fields, DomainID with tfMPTRequireAuth gating, immutable/mutable flags constraints, MaximumAmount format/range, TransferFee range and dependency on tfMPTCanTransfer, optional MPTokenMetadata format and length, and conducts metadata-specific checks via validateMPTokenMetadata when provided. Emits console.warn with MPTTokenMetadata validation messages when non-compliant, but does not fail the transaction solely on metadata structure.
        • Errors/Exceptions: Throws ValidationError for malformed fields, missing required gating conditions (e.g., DomainID without tfMPTRequireAuth), invalid MutableFlags, invalid MaximumAmount, invalid TransferFee, or other field format issues.
  • File: packages/xrpl/src/models/utils/index.ts

    • Snippet: hasFlag
      • Location: hasFlag(tx, flag, flagName)
      • Content:
        • If tx.Flags is null/undefined, returns false.
        • If Flags is a number, uses isFlagEnabled to check the numeric flag.
        • If Flags is an object, checks tx.Flags[flagName] === true.
      • Use: Used by MPTokenIssuanceCreate to determine flag state when Flags is either a numeric bitflag or an object with named booleans.
  • File: packages/xrpl/src/models/utils/index.ts

    • Snippet: isHex
      • Location: isHex(str)
      • Content: Returns true if str matches hex format (via HEX_REGEX).
      • Use: Validates hex-encoded fields like MPTokenMetadata hex payload.
  • File: packages/xrpl/src/models/utils/index.ts

    • Snippet: INTEGER_SANITY_CHECK
      • Location: INTEGER_SANITY_CHECK
      • Content: /^[0-9]+$/u
      • Use: Validates numeric strings (e.g., MaximumAmount).
  • File: packages/xrpl/src/models/utils/index.ts

    • Snippet: isFlagEnabled
      • Location: isFlagEnabled(Flags, checkFlag)
      • Content: Bitwise check returning true if a given flag is set.
      • Use: Used to inspect numeric Flags for specific MPTokenIssuanceCreateFlags.
  • File: packages/xrpl/src/models/transactions/common.ts

    • Snippet: BaseTransaction interface (partial)

      • Location: BaseTransaction
      • Content: Core fields like Account, TransactionType, Fee, Sequence, Memos, Signers, etc.
      • Use: Provides the shape that MPTokenIssuanceCreate extends when validating.
    • Snippet: GlobalFlagsInterface

      • Location: GlobalFlagsInterface
      • Content: tfInnerBatchTxn?: boolean
      • Use: Part of the Flags typing for transactions.
  • File: packages/xrpl/src/models/transactions/common.ts

    • Snippet: validateOptionalField
      • Location: validateOptionalField(...)
      • Content: Generic helper to validate optional fields with a runtime type guard, throwing ValidationError if the field is present but invalid.
      • Use: Used by validateMPTokenIssuanceCreate to validate optional fields like MaximumAmount, MPTokenMetadata, TransferFee, AssetScale, DomainID.
  • File: packages/xrpl/src/models/transactions/metadata.ts

    • Snippet: TransactionMetadataBase
      • Location: TransactionMetadataBase
      • Content: AffectedNodes, DeliveredAmount, TransactionIndex, TransactionResult, ParentBatchID, etc.
      • Use: Context for transaction metadata in general; relevant for how MPTokenIssuanceCreate metadata might be represented post-issuance.
  • File: packages/xrpl/src/models/transactions/common.ts

    • Snippet: validateBaseTransaction
      • Location: validateBaseTransaction(common)
      • Content: Validates core BaseTransaction fields (Account, TransactionType, Fee, Sequence, AccountTxnID, LastLedgerSequence, Memos, Signers, SourceTag, SigningPubKey, TicketSequence, TxnSignature, NetworkID, Delegate) and checks for TransactionType validity and inter-field constraints (e.g., Account and Delegate cannot be the same).
      • Use: Called by validateMPTokenIssuanceCreate to ensure the input conforms to the base transaction contract before inspecting MPTokenIssuanceCreate-specific fields.
  • File: packages/xrpl/src/models/transactions/metadata.ts

    • Snippet: MPTokenMetadata validation helper (validateMPTokenMetadata)
      • Location: validateMPTokenMetadata(input: string)
      • Content: Validates MPTokenMetadata hex payload:
        • Checks hex format, length bound (max 1024 bytes), JSON parses to an object, ensures top-level field count, required fields, and validates specific fields (ticker, icon, asset_class, asset_subclass, urls, etc.).
        • Returns string[] of validation messages; not thrown, but used by MPTokenIssuanceCreate validation to warn.
      • Use: Centralized MPTokenMetadata structural validation used by MPTokenIssuanceCreate.
  • File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts

    • Snippet: MAX_MPT_META_BYTE_LENGTH constant
      • Location: MAX_MPT_META_BYTE_LENGTH
      • Content: 1024
      • Use: Enforces maximum length for MPTokenMetadata hex payload.
  • File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts

    • Snippet: MAX_TRANSFER_FEE constant
      • Location: MAX_TRANSFER_FEE
      • Content: 50000
      • Use: Upper bound for TransferFee (0 to 50000).
  • File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts

    • Snippet: MPTokenIssuanceCreateFlags enum (brief reference)
      • Location: MPTokenIssuanceCreateFlags
      • Content: tfMPTCanLock, tfMPTRequireAuth, tfMPTCanEscrow, tfMPTCanTrade, tfMPTCanTransfer, tfMPTCanClawback
      • Use: Flag definitions used by validateMPTokenIssuanceCreate to gate behavior (e.g., DomainID requires tfMPTRequireAuth).
  • File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts

    • Snippet: MPTokenIssuanceCreateMutableFlags enum (brief reference)
      • Location: MPTokenIssuanceCreateMutableFlags
      • Content: tmfMPTCanMutateCanLock, tmfMPTCanMutateRequireAuth, tmfMPTCanMutateCanEscrow, tmfMPTCanMutateCanTrade, tmfMPTCanMutateCanTransfer, tmfMPTCanMutateCanClawback, tmfMPTCanMutateMetadata, tmfMPTCanMutateTransferFee
      • Use: Defines mutable aspects; used to build the mutable mask.
  • File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts

    • Snippet: tmfMPTokenIssuanceCreateMutableMask
      • Location: tmfMPTokenIssuanceCreateMutableMask
      • Content: Bitwise NOT mask of all mutable flags that are not allowed to be mutated in this transaction type.
      • Use: Compared against tx.MutableFlags to validate mutability.
  • File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts

    • Snippet: MPTokenIssuanceCreate extends BaseTransaction
      • Location: MPTokenIssuanceCreate
      • Content: Interface describing fields:
        • AssetScale?: number
        • MaximumAmount?: string
        • TransferFee?: number
        • MPTokenMetadata?: string
        • Flags?: number | MPTokenIssuanceCreateFlagsInterface
        • MutableFlags?: number
        • DomainID?: string
      • Use: Defines the shape of an MPTokenIssuanceCreate transaction for validation and usage.

Notes:

  • The user’s link points to a rebuttal in rippled’s MPTokenIssuanceCreate.cpp; the TypeScript implementation here mirrors the validation logic, including constraints around MaximumAmount, TransferFee, MPTokenMetadata, and DomainID gating via tfMPTRequireAuth.
  • If you want to map exact behavior from the cited C++ line to this TS code, focus on: DomainID gating, TransferFee validity and dependency on tfMPTCanTransfer, and the MPTokenMetadata format/limits. The provided TS code in validateMPTokenIssuanceCreate implements these areas with corresponding error messages and warning behavior.

✏️ Learnings added
Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:298-298
Timestamp: 2025-10-30T12:09:55.768Z
Learning: In XRPL's MPTokenIssuanceCreate transaction validation, MaximumAmount must be greater than 0 (not greater than or equal to). The rippled C++ implementation at MPTokenIssuanceCreate.cpp explicitly rejects MaximumAmount == 0 with temMALFORMED, so the JavaScript validation should use `<= 0` to match this behavior.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: shawnxie999
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/test/models/MPTokenAuthorize.test.ts:60-71
Timestamp: 2024-12-06T18:44:55.095Z
Learning: In the XRPL.js library's TypeScript test file `packages/xrpl/test/models/MPTokenAuthorize.test.ts`, negative test cases for invalid `Account` address format, invalid `Holder` address format, invalid `MPTokenIssuanceID` format, and invalid flag combinations are not necessary.

Learnt from: achowdhry-ripple
PR: XRPLF/xrpl.js#2661
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:69-102
Timestamp: 2024-12-05T16:48:12.951Z
Learning: When adding validation in `validate*` functions in `packages/xrpl/src/models/transactions/`, utilize existing helper functions (e.g., `validateOptionalField`, `validateType`, `isNumber`, `isInteger`) for type checking and validation where appropriate.

Learnt from: ckeshava
PR: XRPLF/xrpl.js#3027
File: packages/xrpl/src/models/ledger/MPTokenIssuance.ts:13-16
Timestamp: 2025-06-26T17:25:36.429Z
Learning: In the XRPL ecosystem, type choices for amount fields (like `number` vs `string`) in ledger objects such as `LockedAmount` vs `OutstandingAmount` in `MPTokenIssuance` are deliberate design decisions made across multiple products and cannot be changed unilaterally by individual contributors.

Learnt from: Patel-Raj11
PR: XRPLF/xrpl.js#3117
File: packages/xrpl/src/models/transactions/common.ts:1133-1180
Timestamp: 2025-10-28T14:16:24.567Z
Learning: The MPTokenMetadata TypeScript interface in xrpl.js reflects the XLS-89 standard (long-form fields only) to provide correct type hints for IDEs and TypeScript users, even though JavaScript users may provide non-standard structures with compact fields or both forms present. This design provides proper tooling support while maintaining runtime flexibility.

Learnt from: ckeshava
PR: XRPLF/xrpl.js#3032
File: packages/xrpl/src/models/transactions/common.ts:689-698
Timestamp: 2025-07-10T22:04:59.994Z
Learning: In the XRPL.js library, the validateDomainID function in `packages/xrpl/src/models/transactions/common.ts` should only validate the length (64 characters) of domain IDs, not hex encoding. The rippled C++ implementation does not enforce any regex check on domain ID values, so additional hex validation is not required in the JS implementation.

Learnt from: ckeshava
PR: XRPLF/xrpl.js#2812
File: packages/xrpl/test/integration/transactions/payment.test.ts:41-43
Timestamp: 2024-10-30T01:05:33.583Z
Learning: In tests involving fee calculations for XRPL, avoid using default fee amounts. If unable to retrieve fee values from the latest validated ledger, throw an error instead of defaulting to a specific amount.

Learnt from: ckeshava
PR: XRPLF/xrpl.js#3081
File: packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts:57-64
Timestamp: 2025-09-16T05:00:20.420Z
Learning: In XRPL's MPTokenIssuanceCreate flags, the tfMPTCanMutate* flags intentionally use the same bit values as their corresponding base flags (e.g., tfMPTCanMutateCanLock = 0x00000002 matches tfMPTCanLock = 0x00000002) because they operate in different contexts - base flags go in the Flags field while mutable flags go in the MutableFlags field. This design avoids ambiguity since the flags are applied to separate bit fields.

Learnt from: ckeshava
PR: XRPLF/xrpl.js#2874
File: packages/xrpl/test/integration/transactions/permissionedDomain.test.ts:25-80
Timestamp: 2025-01-08T02:12:28.489Z
Learning: The rippled C++ implementation (PR #5161) includes comprehensive test coverage for PermissionedDomain (XLS-80d) error cases. The JS SDK tests focus on the happy path since the error cases are already validated at the rippled level, following the principle of not duplicating complex validation testing across SDK implementations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants