xls: 94 title: Dynamic Multi-Purpose Tokens description: This amendment enables selected fields and flags of MPTokenIssuance to be updated after creation. implementation: https://github.com/XRPLF/rippled/commit/033dca2f0e709360cc305b0e7a18615d642d114a; https://github.com/XRPLF/rippled/pull/7439; https://github.com/XRPLF/rippled/pull/5705 proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/289 author: Yinyi Qian, Mayukha Vadari status: Final category: Amendment created: 2025-06-09 updated: 2026-09-03
Dynamic Multi-Purpose Tokens¶
1. Abstract¶
This proposal introduces a new amendment DynamicMPT as an extension to XLS-33 Multi-Purpose Tokens. Dynamic Multi-Purpose Tokens (Dynamic MPTs) allow specific fields and MPT issuance flags within an MPTokenIssuance to be modified after creation via the MPTokenIssuanceSet transaction. By default, MPTokenMetadata, TransferFee, and a defined set of MPT issuance flags remain mutable for the life of the issuance. Issuers may instead choose to permanently make any of these fields or flags immutable — at creation, or at any later point — by setting the corresponding bit in a new ImmutableFlags field. Once a bit is set in ImmutableFlags, the corresponding field or flag can never be modified again. This accommodates evolving token use cases and compliance demands while giving issuers a way to make firm, permanent commitments about their issuance's behavior.
2. Specification¶
This proposal introduces:
- Transaction update for
MPTokenIssuanceCreate: -
A new optional field:
ImmutableFlags- permanently makes specific fields or flags immutable.
-
Ledger object update in
MPTokenIssuance: -
A new optional field:
ImmutableFlags- records which fields or flags are immutable.
-
Transaction update for
MPTokenIssuanceSet: - New optional fields:
MPTokenMetadata- updates the metadata.TransferFee- updates the transfer fee.ImmutableFlags- permanently makes specific fields or flags immutable.
-
A set of new values under
Flagsto set MPT issuance flags. -
The target flags or fields to be set or updated for the ledger object
MPTokenIssuance: - Field:
MPTokenMetadata - Field:
TransferFee - Bits of
Flags:lsfMPTCanLocklsfMPTRequireAuthlsfMPTCanEscrowlsfMPTCanTradelsfMPTCanTransferlsfMPTCanClawbacklsfMPTCanHoldConfidentialBalance(from XLS-96 Confidential MPT)
This feature will require an amendment, DynamicMPT.
Setting lsfMPTCanHoldConfidentialBalance also requires the ConfidentialTransfer amendment to be enabled.
3. Transaction: MPTokenIssuanceCreate¶
By default without declaring ImmutableFlags:
MPTokenMetadataandTransferFeemay be freely modified viaMPTokenIssuanceSet.- Each of the MPT issuance flags listed below may be enabled (one-way) via
MPTokenIssuanceSetafter issuance.
Declaring immutability via ImmutableFlags:
- Flags set in
ImmutableFlagsindicate the corresponding field/flag is immutable. The issuer cannot modify the field/flag viaMPTokenIssuanceSetafter issuance.
3.1. Fields¶
| Field Name | Required? | JSON Type | Internal Type | Description |
|---|---|---|---|---|
ImmutableFlags |
number |
UInt32 |
Indicates specific fields or flags that are permanently immutable. |
This is a new field added to MPTokenIssuanceCreate transaction, the other fields remain the same (see MPTokenIssuanceCreate fields).
3.1.1. ImmutableFlags¶
Transaction-level bits are prefixed tif (Transaction Immutable Flag). The corresponding on-ledger bits (prefixed lsif for Ledger-Specific Immutable Flag) share the same numeric values. (See Section 5.1.1. for the on-ledger bits.) The value starts at 0x00000002 and 0x00000001 is reserved for consistency purposes.
| Flag Name | Hex Value | Decimal Value | Description |
|---|---|---|---|
tifMPTCanLock |
0x00000002 |
2 | Make flag lsfMPTCanLock immutable. |
tifMPTRequireAuth |
0x00000004 |
4 | Make flag lsfMPTRequireAuth immutable. |
tifMPTCanEscrow |
0x00000008 |
8 | Make flag lsfMPTCanEscrow immutable. |
tifMPTCanTrade |
0x00000010 |
16 | Make flag lsfMPTCanTrade immutable. |
tifMPTCanTransfer |
0x00000020 |
32 | Make flag lsfMPTCanTransfer immutable. |
tifMPTCanClawback |
0x00000040 |
64 | Make flag lsfMPTCanClawback immutable. |
tifMPTCanHoldConfidentialBalance |
0x00000080 |
128 | Make flag lsfMPTCanHoldConfidentialBalance immutable. (XLS-96 Confidential MPT) |
tifMPTMetadata |
0x00010000 |
65536 | Make field MPTokenMetadata immutable. |
tifMPTTransferFee |
0x00020000 |
131072 | Make field TransferFee immutable. |
3.2. Failure Conditions¶
For both MPTokenIssuanceCreate and MPTokenIssuanceSet.
ImmutableFlagsis0, or contains a bit not defined above. (temINVALID_FLAG)ImmutableFlagsis present butfeatureDynamicMPTis disabled. (temDISABLED)tifMPTCanHoldConfidentialBalanceis set butfeatureConfidentialTransferorfeatureDynamicMPTis not enabled. (temDISABLED)
3.3. State Changes¶
- If the transaction succeeds, the created
MPTokenIssuanceobject'sImmutableFlagsfield is set to the value provided in the transaction. The corresponding flags or fields are now immutable. - If
ImmutableFlagsis omitted and the object'sImmutableFlagsfield is absent, then MPT issuance flags default to being able to be enabled once (and never disabled), and the metadata and transfer fee fields are mutable.
3.4. Example JSON¶
{
"TransactionType": "MPTokenIssuanceCreate",
"Account": "rIssuer...",
"AssetScale": "2",
"MaximumAmount": "100000000",
"ImmutableFlags": 4
}
3.5. Transaction Fee¶
Fee Structure: Standard
The base transaction fee for this transaction follows the standard XRP Ledger fee model (reference base fee of 10 drops, subject to standard load scaling). No additional fee multipliers or special fee structures apply.
4. Transaction: MPTokenIssuanceSet¶
This proposal extends the functionality of the MPTokenIssuanceSet transaction, allowing the issuer to modify MPTokenMetadata and TransferFee, enable MPT issuance flags, and permanently make any of these immutable via ImmutableFlags. The ImmutableFlags provided in this transaction are added to (bitwise ORed into) the current ledger object's ImmutableFlags; it is not a complete replacement.
For details on the original MPTokenIssuanceSet transaction see: The MPTokenIssuanceSet Transaction
4.1. Fields¶
The following are the new fields added to MPTokenIssuanceSet transaction. The original fields remain the same (see
MPTokenIssuanceSet fields).
| Field Name | Required? | JSON Type | Internal Type | Description |
|---|---|---|---|---|
MPTokenMetadata |
string |
BLOB |
New metadata to replace the existing value. The transaction will be rejected if lsifMPTMetadata has been set in ImmutableFlags. Setting an empty MPTokenMetadata removes the field. |
|
TransferFee |
number |
UINT16 |
New transfer fee value. The transaction will be rejected if lsifMPTTransferFee has been set in ImmutableFlags. Setting TransferFee to zero removes the field. |
|
ImmutableFlags |
number |
UINT32 |
To make specific fields or flags immutable. |
To declare which fields or flags are immutable.
Once a bit is set, the corresponding field or flag can never be set or modified again. The bit layout is the same as ImmutableFlags in MPTokenIssuanceCreate.
Whether it was made immutable at creation through MPTokenIssuanceCreate or afterward via MPTokenIssuanceSet. Submitting a bit that is already set is harmless and has no additional effect.
4.2. Flags¶
These flags are added to the Flags field of MPTokenIssuanceSet (see the original values in MPTokenIssuanceSet Flags). This proposal adds the following new Flags values to set MPT issuance flags. These flags are one-way: once set, they cannot be unset by MPTokenIssuanceSet.
| Flag Name | Hex Value | Decimal Value | Description |
|---|---|---|---|
tfMPTSetCanLock |
0x00000004 |
4 | Sets the lsfMPTCanLock flag. Enables the token to be locked both individually and globally. |
tfMPTSetRequireAuth |
0x00000008 |
8 | Sets the lsfMPTRequireAuth flag. Requires individual holders to be authorized. |
tfMPTSetCanEscrow |
0x00000010 |
16 | Sets the lsfMPTCanEscrow flag. Allows holders to place balances into escrow. |
tfMPTSetCanTrade |
0x00000020 |
32 | Sets the lsfMPTCanTrade flag. Allows holders to trade balances on the XRPL DEX. |
tfMPTSetCanTransfer |
0x00000040 |
64 | Sets the lsfMPTCanTransfer flag. Allows tokens to be transferred to non-issuer accounts. |
tfMPTSetCanClawback |
0x00000080 |
128 | Sets the lsfMPTCanClawback flag. Enables the issuer to claw back tokens via Clawback or AMMClawback transactions. |
tfMPTSetCanHoldConfidentialBalance |
0x00000100 |
256 | Sets the lsfMPTCanHoldConfidentialBalance flag. Enables the token to be held in a confidential balance. (XLS-96 Confidential MPT) |
Terminology:
We will call those flags tfMPTSet* as capability-setting flags.
Note:
- An MPT issuance flag can only be enabled by
MPTokenIssuanceSetif the correspondinglsifbit is not set in theMPTokenIssuanceobject'sImmutableFlags. - If an MPT issuance flag is already enabled, re-enabling it is valid and has no additional effect.
- A single
MPTokenIssuanceSettransaction may enable multiple MPT issuance flags, as long as none of them have been made immutable viaImmutableFlags.
4.3. Failure Conditions¶
MPTokenHolderis present together with capability-setting flags (e.g.tfMPTSetCanLock, etc.),MPTokenMetadata,TransferFee, orImmutableFlags. (temMALFORMED)tfMPTLockortfMPTUnlockis set together with a capability-setting flag,MPTokenMetadata,TransferFee, orImmutableFlags. (temMALFORMED)TransferFeeexceeds the limit, which is 50000. (temBAD_TRANSFER_FEE)MPTokenMetadatalength exceeds the limit, which is 1024. (temMALFORMED)- A capability-setting flag,
MPTokenMetadata,TransferFee, orImmutableFlagsis present butfeatureDynamicMPTis disabled. (temDISABLED) ImmutableFlagscontainstifMPTCanHoldConfidentialBalancebutfeatureConfidentialTransferis disabled. (temDISABLED)- Providing
tfMPTSetCanHoldConfidentialBalancebutfeatureConfidentialTransferis disabled. (temDISABLED) ImmutableFlagsis0, or contains a bit not defined. (temINVALID_FLAG)MPTokenIssuanceobject doesn't exist for the providedMPTokenIssuanceID. (tecOBJECT_NOT_FOUND)MPTokenIssuance's issuer is notAccount. (tecNO_PERMISSION)- A capability-setting flag is set, but the corresponding
lsifbit is already set inImmutableFlags. (tecNO_PERMISSION) MPTokenMetadatais present, butlsifMPTMetadatais set inImmutableFlags. (tecNO_PERMISSION)TransferFeeis present, butlsifMPTTransferFeeis set inImmutableFlags.(tecNO_PERMISSION)- A non-zero
TransferFeeis included,lsfMPTCanTransferis not already set on the ledger, and this same transaction does not also enable it viatfMPTSetCanTransfer. (tecNO_PERMISSION)
Note:
- Setting a capability-setting flag and its corresponding
tifbit inImmutableFlagsin the same transaction is allowed and atomic. Only the existing ledger object'sImmutableFlagsis checked when determining whether the capability is immutable. For example, submittingtfMPTSetCanLocktogether withtifMPTCanLockis allowed becauselsifMPTCanLockis not yet set on the ledger. After the transaction, the ledger object has bothlsfMPTCanLockandlsifMPTCanLockset. - The same rule applies to
MPTokenMetadataandTransferFee. Conditions 12 and 13 are also evaluated against the existing ledger object'sImmutableFlags, so a single transaction may update the field and make it immutable.
4.4. TransferFee Modification Rules¶
The ability to modify TransferFee depends on two conditions:
lsfMPTCanTransfer: must already be set on the ledger, or be enabled by this same transaction viatfMPTSetCanTransfer, in order to set a non-zeroTransferFee.lsifMPTTransferFee: must not be set inImmutableFlags, in order to modifyTransferFeeat all (zero or non-zero).
The rules break down as follows:
Case 1: lsfMPTCanTransfer not set, and not being enabled by this transaction:
- Setting
TransferFeeto zero: allowed (removes the field, if present), unlesslsifMPTTransferFeehas been set (makingTransferFeeimmutable). - Setting
TransferFeeto a non-zero value: always invalid, returnstecNO_PERMISSION.
Case 2: lsfMPTCanTransfer already set on the ledger, or being enabled by this same transaction via tfMPTSetCanTransfer:
- Setting
TransferFeeto any value (zero or non-zero): allowed, unlesslsifMPTTransferFeehas been set (makingTransferFeeimmutable). - An exception is from XLS-96 Confidential MPT, if
lsfMPTCanHoldConfidentialBalanceis set,TransferFeemust be 0. (See 6.4. Transfer Fee Compatibility)
Note:
- enabling
lsfMPTCanTransferand setting a non-zeroTransferFeein the same transaction is supported — there is no requirement to submit two separate transactions.
4.5. State Changes¶
If the transaction succeeds:
MPTokenMetadatais replaced with the provided value, or removed if an empty value is provided, unlesslsifMPTMetadatais already set inImmutableFlags.TransferFeeis replaced with the provided value, or removed if zero is provided, unlesslsifMPTTransferFeeis already set inImmutableFlags.- Each MPT issuance flag set via
Flagsis enabled on theMPTokenIssuanceobject, unless its correspondinglsifbit is already set inImmutableFlags. - Each bit set via
ImmutableFlagsis added to theMPTokenIssuanceobject's existingImmutableFlagsvalue; existing bits are never cleared.
4.6. Example JSON¶
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"Flags": 64, // tfMPTSetCanTransfer
"TransferFee": 200
}
4.7. Transaction Fee¶
Fee Structure: Standard
The base transaction fee for this transaction follows the standard XRP Ledger fee model (reference base fee of 10 drops, subject to standard load scaling). No additional fee multipliers or special fee structures apply.
5. Ledger Entry: MPTokenIssuance¶
This proposal adds a new optional field, ImmutableFlags to the MPTokenIssuance ledger object. The other fields remain the same (see MPTokenIssuance fields).
5.1. Fields¶
| Field Name | Required? | JSON Type | Internal Type | Description |
|---|---|---|---|---|
ImmutableFlags |
number |
UINT32 |
Defines which fields or flags are immutable. |
The field is type soeDEFAULT. So if it is present in the ledger object, it must not be 0.
Being absent is equivalent to being 0.
5.1.1. ImmutableFlags¶
On-ledger bits are prefixed lsif, and share the same numeric values as the corresponding tif bits. (See Section 3.1.1. for the transaction bits.) The value starts at 0x00000002 and 0x00000001 is reserved for consistency purposes.
| Flag Name | Hex Value | Decimal Value | Description |
|---|---|---|---|
lsifMPTCanLock |
0x00000002 |
2 | Indicate flag lsfMPTCanLock immutable. |
lsifMPTRequireAuth |
0x00000004 |
4 | Indicate flag lsfMPTRequireAuth immutable. |
lsifMPTCanEscrow |
0x00000008 |
8 | Indicate flag lsfMPTCanEscrow immutable. |
lsifMPTCanTrade |
0x00000010 |
16 | Indicate flag lsfMPTCanTrade immutable. |
lsifMPTCanTransfer |
0x00000020 |
32 | Indicate flag lsfMPTCanTransfer immutable. |
lsifMPTCanClawback |
0x00000040 |
64 | Indicate flag lsfMPTCanClawback immutable. |
lsifMPTCanHoldConfidentialBalance |
0x00000080 |
128 | Indicate flag lsfMPTCanHoldConfidentialBalance immutable. (XLS-96 Confidential MPT) |
lsifMPTMetadata |
0x00010000 |
65536 | Indicate field MPTokenMetadata immutable. |
lsifMPTTransferFee |
0x00020000 |
131072 | Indicate field TransferFee immutable. |
5.2. Invariants¶
Once a bit is set in ImmutableFlags, it is never cleared. A field or MPT issuance flag whose corresponding bit is set in ImmutableFlags can never be modified again by any MPTokenIssuanceSet transaction.
5.3. Example JSON¶
{
"LedgerEntryType": "MPTokenIssuance",
"Account": "rIssuer...",
"AssetScale": 2,
"MaximumAmount": "100000000",
"OutstandingAmount": "5000000",
"MPTokenMetadata": "575C5C",
"Flags": 66, // lsfMPTCanTransfer + lsfMPTCanEscrow
"TransferFee": 200,
"ImmutableFlags": 131072 // lsifMPTTransferFee
}
6. Examples¶
6.1. Example 1: Metadata mutable by default, then made immutable¶
6.1.1. MPTokenIssuanceCreate Transaction¶
{
"TransactionType": "MPTokenIssuanceCreate",
"Account": "rIssuer...",
"AssetScale": "2",
"MaximumAmount": "100000000",
"MPTokenMetadata": "464F4F"
}
- No
ImmutableFlagsis set, soMPTokenMetadataremains mutable by default.
6.1.2. MPTokenIssuanceSet Transaction¶
Sample 1 (successful):
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"MPTokenMetadata": "575C5C"
}
MPTokenMetadatawas never made immutable, so this updates the metadata from464F4Fto575C5C.
Sample 2 (successful): Alice now decides to make the metadata immutable at its current value.
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"ImmutableFlags": 65536 // tifMPTMetadata
}
MPTokenMetadata(currently575C5C) is now permanently immutable.
Sample 3 (rejected):
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"MPTokenMetadata": "AABBCC"
}
- This is rejected because
lsifMPTMetadatais now set inImmutableFlags.
6.2. Example 2: Making a capability immutable at creation¶
6.2.1. MPTokenIssuanceCreate Transaction¶
{
"TransactionType": "MPTokenIssuanceCreate",
"Account": "rIssuer...",
"AssetScale": "2",
"MaximumAmount": "100000000",
"ImmutableFlags": 4 // tifMPTRequireAuth
}
tifMPTRequireAuthis set, permanently preventinglsfMPTRequireAuthfrom ever being set. All other MPT issuance flags remain mutable by default.
6.2.2. MPTokenIssuanceSet Transaction¶
Sample 1 (successful):
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"Flags": 48 // tfMPTSetCanEscrow (0x00000010) + tfMPTSetCanTrade (0x00000020)
}
lsfMPTCanEscrowandlsfMPTCanTradewere never made immutable, so both are set by this transaction.
Sample 2 (rejected):
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"Flags": 8 // tfMPTSetRequireAuth
}
- This is rejected because
tifMPTRequireAuthwas set at creation, permanently preventinglsfMPTRequireAuthfrom being set.
6.3. Example 3: TransferFee and lsfMPTCanTransfer¶
6.3.1. MPTokenIssuanceCreate Transaction¶
{
"TransactionType": "MPTokenIssuanceCreate",
"Account": "rIssuer...",
"AssetScale": "2",
"MaximumAmount": "100000000",
"Flags": 2 // tfMPTCanLock
}
tfMPTCanTransferis not set, soTransferFeeMUST NOT be present.- No
ImmutableFlagsis set, soTransferFeeand all MPT issuance flags remain mutable by default.
6.3.2. MPTokenIssuanceSet Transactions¶
Sample 1 (rejected):
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"TransferFee": 200
}
- Rejected: a non-zero
TransferFeecannot be set unlesslsfMPTCanTransferis already set, or is being enabled by this same transaction.
Sample 2 (successful): enabling lsfMPTCanTransfer and setting a non-zero TransferFee together.
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"Flags": 64, // tfMPTSetCanTransfer
"TransferFee": 200
}
- Both effects apply atomically:
lsfMPTCanTransferis enabled, andTransferFeeis set to 200 in the same transaction.
Sample 3 (successful): Alice now permanently makes the transfer fee immutable at its current value.
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"ImmutableFlags": 131072 // tifMPTTransferFee
}
TransferFee(currently 200) can no longer be changed.
Sample 4 (rejected):
{
"TransactionType": "MPTokenIssuanceSet",
"Account": "rIssuer...",
"MPTokenIssuanceID": "000004C463C52827307480341125DA0577DEFC38405B0E3E",
"TransferFee": 0
}
- Rejected:
lsifMPTTransferFeeis now set inImmutableFlags, soTransferFeecan no longer be modified, even to remove it.
7. Rationale¶
Dynamic MPT gives issuers control over which parts of their token issuance can evolve after creation, while defaulting to maximum flexibility: MPTokenMetadata, TransferFee, and MPT issuance flags are mutable unless the issuer takes explicit action to make them immutable. This "mutable by default, immutable on demand" design lets issuers respond to changing business needs — adjusting metadata, tuning a transfer fee, or enabling a capability that wasn't needed at launch — without requiring every possible future need to be anticipated and pre-declared at issuance time.
Making fields or flags immutable via ImmutableFlags is available both at creation and at any later point via MPTokenIssuanceSet, letting issuers progressively make firmer commitments as their token's design stabilizes — for example, making metadata immutable once its final content is set, while leaving other fields open for longer.
MPT issuance flag mutability remains intentionally one-way: once enabled via MPTokenIssuanceSet, a flag cannot be disabled. This preserves the issuer's ability to opt into additional issuance behavior without ever retracting behavior that holders, integrations, or compliance workflows may have relied on. Making a flag immutable via ImmutableFlags (whether before or after it is enabled) makes that one-way guarantee permanent and auditable on-ledger.
8. Security Considerations¶
Only the issuer of the MPTokenIssuance can use MPTokenIssuanceSet to modify MPTokenMetadata or TransferFee, enable MPT issuance flags, or set ImmutableFlags.
ImmutableFlags bits are monotonic: once set, a bit is never cleared by any transaction, so a field or flag that has been made immutable remains immutable for the life of the issuance, regardless of who submits subsequent MPTokenIssuanceSet transactions.
MPT issuance flags are one-way and cannot be disabled through MPTokenIssuanceSet after being enabled, whether or not the corresponding ImmutableFlags bit is also set. Enabling a flag and immediately making it immutable via ImmutableFlags in the same transaction is supported and introduces no additional risk: the flag was already permanent once enabled, so making it immutable merely records that fact rather than changing what is achievable.