Skip to content

Commit 192db8f

Browse files
committed
Merge pull request #3359 from XRPLF/mpt-metadata-schema
Add MPT Metadata Schema section to concept docs
2 parents 4319594 + bc0c698 commit 192db8f

File tree

9 files changed

+318
-31
lines changed

9 files changed

+318
-31
lines changed

_code-samples/batch/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Batch
22

33
Code samples showing how to create and submit a [Batch transaction](../../docs/concepts/transactions/batch-transactions.md).
4-
Both for simple and multi account batch transactions.
4+
Both for single and multi account batch transactions.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"dependencies": {
3+
"xrpl": "4.4.2"
4+
},
5+
"type": "module"
6+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* Single Account Batch Transaction Example
3+
*
4+
* This example demonstrates how to use the Batch transactions feature (XLS-56)
5+
* to create a single-account batch transaction.
6+
*/
7+
8+
import xrpl from "xrpl";
9+
import { BatchFlags } from "xrpl/dist/npm/models/transactions/batch.js";
10+
11+
const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233");
12+
await client.connect();
13+
14+
// Create and fund the sender wallet
15+
console.log("Funding new sender wallet from faucet...");
16+
const { wallet } = await client.fundWallet();
17+
console.log(`Wallet created: ${wallet.classicAddress}`);
18+
19+
// Create and fund recipient wallet
20+
console.log("\nFunding new recipient wallet from faucet...");
21+
const feeRecipient = (await client.fundWallet()).wallet;
22+
console.log(`Fee Recipient: ${feeRecipient.classicAddress}`);
23+
24+
25+
// Create inner transactions --------------------------------------------
26+
// REQUIRED: Inner transactions MUST have the tfInnerBatchTxn flag (0x40000000)
27+
// autofill() will automatically add Fee: "0" and SigningPubKey: ""
28+
29+
// Transaction 1: Create DEX offer (sell 100 XRP for 100 USD)
30+
const offerCreate = {
31+
TransactionType: "OfferCreate",
32+
Account: wallet.address,
33+
TakerPays: {
34+
currency: "USD",
35+
issuer: "rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc",
36+
value: "100"
37+
},
38+
TakerGets: xrpl.xrpToDrops(25 * 10 * 1.16),
39+
Flags: 0x40000000, // tfInnerBatchTxn - THIS IS REQUIRED
40+
};
41+
42+
// Transaction 2: Pay platform fee (10 XRP)
43+
const payment = {
44+
TransactionType: "Payment",
45+
Account: wallet.classicAddress,
46+
Destination: feeRecipient.classicAddress,
47+
Amount: xrpl.xrpToDrops(5), // 5 XRP
48+
Flags: 0x40000000,
49+
};
50+
51+
// Send Batch transaction --------------------------------------------
52+
const batchTx = {
53+
TransactionType: "Batch",
54+
Account: wallet.classicAddress,
55+
Flags: BatchFlags.tfAllOrNothing, // tfAllOrNothing: All inner transactions succeed or all fail
56+
RawTransactions: [{ RawTransaction: offerCreate }, { RawTransaction: payment }],
57+
};
58+
xrpl.validate(batchTx);
59+
60+
// Sign, submit, and wait for validation in one call
61+
console.log(
62+
"\nSigning and submitting batch transaction:\n",
63+
JSON.stringify(batchTx, null, 2)
64+
);
65+
const submitResponse = await client.submitAndWait(batchTx, {
66+
wallet: wallet,
67+
autofill: true, // Autofill handles Fee, Sequence, LastLedgerSequence, etc.
68+
});
69+
70+
// Check transaction results and disconnect --------------------------------
71+
if (submitResponse.result.meta.TransactionResult !== "tesSUCCESS") {
72+
const resultCode = response.result.meta.TransactionResult;
73+
console.warn(`Transaction failed with result code ${resultCode}.`);
74+
process.exit(1);
75+
}
76+
77+
console.log("\nBatch transaction submitted successfully:");
78+
console.log(JSON.stringify(submitResponse.result, null, 2));
79+
80+
await client.disconnect();

_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,26 @@ const { wallet } = await client.fundWallet()
1010

1111
// Define metadata as JSON
1212
const mpt_metadata = {
13-
ticker: 'TBILL',
14-
name: 'T-Bill Yield Token',
15-
desc: 'A yield-bearing stablecoin backed by short-term U.S. Treasuries and money market instruments.',
16-
icon: 'https://example.org/tbill-icon.png',
17-
asset_class: 'rwa',
18-
asset_subclass: 'treasury',
19-
issuer_name: 'Example Yield Co.',
20-
urls: [
13+
t: 'TBILL',
14+
n: 'T-Bill Yield Token',
15+
d: 'A yield-bearing stablecoin backed by short-term U.S. Treasuries and money market instruments.',
16+
i: 'https://example.org/tbill-icon.png',
17+
ac: 'rwa',
18+
as: 'treasury',
19+
in: 'Example Yield Co.',
20+
us: [
2121
{
22-
url: 'https://exampleyield.co/tbill',
23-
type: 'website',
24-
title: 'Product Page'
22+
u: 'https://exampleyield.co/tbill',
23+
c: 'website',
24+
t: 'Product Page'
2525
},
2626
{
27-
url: 'https://exampleyield.co/docs',
28-
type: 'docs',
29-
title: 'Yield Token Docs'
27+
u: 'https://exampleyield.co/docs',
28+
c: 'docs',
29+
t: 'Yield Token Docs'
3030
}
3131
],
32-
additional_info: {
32+
ai: {
3333
interest_rate: '5.00%',
3434
interest_type: 'variable',
3535
yield_source: 'U.S. Treasury Bills',

_code-samples/issue-mpt-with-metadata/py/issue-mpt-with-metadata.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,26 @@
1212

1313
# Define metadata as JSON
1414
mpt_metadata = {
15-
"ticker": "TBILL",
16-
"name": "T-Bill Yield Token",
17-
"desc": "A yield-bearing stablecoin backed by short-term U.S. Treasuries and money market instruments.",
18-
"icon": "https://example.org/tbill-icon.png",
19-
"asset_class": "rwa",
20-
"asset_subclass": "treasury",
21-
"issuer_name": "Example Yield Co.",
22-
"urls": [
15+
"t": "TBILL",
16+
"n": "T-Bill Yield Token",
17+
"d": "A yield-bearing stablecoin backed by short-term U.S. Treasuries and money market instruments.",
18+
"i": "example.org/tbill-icon.png",
19+
"ac": "rwa",
20+
"as": "treasury",
21+
"in": "Example Yield Co.",
22+
"us": [
2323
{
24-
"url": "https://exampleyield.co/tbill",
25-
"type": "website",
26-
"title": "Product Page"
24+
"u": "exampleyield.co/tbill",
25+
"c": "website",
26+
"t": "Product Page"
2727
},
2828
{
29-
"url": "https://exampleyield.co/docs",
30-
"type": "docs",
31-
"title": "Yield Token Docs"
29+
"u": "exampleyield.co/docs",
30+
"c": "docs",
31+
"t": "Yield Token Docs"
3232
}
3333
],
34-
"additional_info": {
34+
"ai": {
3535
"interest_rate": "5.00%",
3636
"interest_type": "variable",
3737
"yield_source": "U.S. Treasury Bills",

docs/concepts/tokens/fungible-tokens/multi-purpose-tokens.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,94 @@ Every MPT issuance has a set of key properties defined in the ledger as an [MPTo
4545

4646
After the MPT is issued, the on-chain data cannot be changed. However, the proposed [XLS-94: Dynamic MPT standard](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0094-dynamic-MPT) {% not-enabled /%} would allow fields to be marked as mutable during creation, so that those fields can be changed later.
4747

48+
#### Metadata Schema
49+
50+
To fit within the 1024-byte limit, MPT metadata must use compressed JSON keys. The following table describes these keys and their corresponding fields:
51+
52+
| Field Name | Key | Type | Required? | Description |
53+
|:--------------- |:---- |:---------------- |---------- |-------------|
54+
| ticker | `t` | String | Yes | The ticker symbol used to represent the token. Must be uppercase letters (A-Z) and digits (0-9) only. A maximum of 6 characters is recommended. |
55+
| name | `n` | String | Yes | The display name of the token. Any UTF-8 string is permitted. |
56+
| desc | `d` | String | No | A short description of the token. Any UTF-8 string is permitted. |
57+
| icon | `i` | String | Yes | The URI to the token icon. Can be `hostname/path` (HTTPS is assumed), or full URI for other protocols. |
58+
| asset_class | `ac` | String | Yes️ | Categorizes tokens by their primary purpose and backing. See [Asset Class](#asset-class) for more details. |
59+
| asset_subclass | `as` | String | No | An optional subcategory that is only required if the `asset_class` is `rwa`. See [Asset Subclass](#asset-subclass) for more details. |
60+
| issuer_name | `in` | String | Yes | Name of the entity issuing the token. Any UTF-8 string is permitted. |
61+
| uris | `us` | Array | No | The list of related URIs such as website, documentation, and social media. See [URIs](#uris) for more details.|
62+
| additional_info | `ai` | Object or String | No | Freeform field for key token details like interest rate, maturity date, term, or other relevant info. Any valid JSON object or UTF-8 string is permitted. |
63+
64+
##### Asset Class
65+
66+
The `asset_class` field categorizes tokens by their primary purpose and backing. These categories help applications understand the nature of the token and its intended use case.
67+
68+
| Category | Definition |
69+
|----------|------------|
70+
| `rwa` | Tokens representing real-world assets (RWAs), which derive value from legally enforceable claims on physical or off-chain financial assets. |
71+
| `memes` | Community-driven tokens without intrinsic backing or utility claims, primarily driven by internet culture or speculation. |
72+
| `wrapped` | Tokens representing assets from other blockchains, typically backed 1:1 by bridges or custodians. |
73+
| `gaming` | Tokens used in games or virtual worlds, often representing in-game currency, assets, or rewards. |
74+
| `defi` | Tokens native to or used within DeFi protocols, including governance tokens, DEX tokens, and lending assets. |
75+
| `other` | Tokens that do not clearly fit into the defined categories. This may include experimental, test, or tokens with unique use cases not covered elsewhere. |
76+
77+
##### Asset Subclass
78+
79+
When `asset_class` is set to `rwa`, an `asset_subclass` can be specified to provide more granular categorization. This describes what type of real-world asset backs the token and what legal or regulatory framework might apply.
80+
81+
| Subclass | Definition |
82+
|----------|------------|
83+
| `stablecoin` | Tokens pegged to a stable value, typically fiat currencies like USD, which are backed by reserves like cash, treasuries, or crypto collateral. |
84+
| `commodity` | Tokens that represent physical commodities like gold, silver, or oil, often redeemable or legally linked to off-chain reserves. |
85+
| `real_estate` | Tokens representing ownership or claims on real estate, including fractionalized property shares or REIT-like instruments. |
86+
| `private_credit` | Tokens representing debt obligations from private entities, such as loans, invoices, or receivables. |
87+
| `equity` | Tokens representing ownership shares in companies, similar to traditional stock or equity instruments. |
88+
| `treasury` | Tokens backed by government debt instruments, such as U.S. Treasury bills or bonds. |
89+
| `other` | Tokens that do not fit into the predefined categories, including experimental, hybrid, or emerging real-world asset types. |
90+
91+
##### URIs
92+
93+
The `us` array contains a list of URI objects, each with a URI link, category, and human-readable title.
94+
95+
| Field Name | Key | Type | Required? | Description |
96+
|:---------- |:--- |:------ |:--------- |:-------------|
97+
| uri | `u` | String | Yes️ |`hostname/path` or full URI link to the related resource. |
98+
| category | `c` | String | Yes | The category of the link provided. Allowed values are: `website`, `social`, `docs`, `other`. |
99+
| title | `t` | String | Yes | Human-readable label for the link. |
100+
101+
#### Example JSON Metadata
102+
103+
The following example shows metadata for a treasury-backed token.
104+
105+
```json
106+
{
107+
"t": "TBILL",
108+
"n": "T-Bill Yield Token",
109+
"d": "A yield-bearing stablecoin backed by short-term U.S. Treasuries and money market instruments.",
110+
"i": "example.org/tbill-icon.png",
111+
"ac": "rwa",
112+
"as": "treasury",
113+
"in": "Example Yield Co.",
114+
"us": [
115+
{
116+
"u": "exampleyield.co/tbill",
117+
"c": "website",
118+
"t": "Product Page"
119+
},
120+
{
121+
"u": "exampleyield.co/docs",
122+
"c": "docs",
123+
"t": "Yield Token Docs"
124+
}
125+
],
126+
"ai": {
127+
"interest_rate": "5.00%",
128+
"interest_type": "variable",
129+
"yield_source": "U.S. Treasury Bills",
130+
"maturity_date": "2045-06-30",
131+
"cusip": "912796RX0"
132+
}
133+
}
134+
```
135+
48136
### Transferability Controls
49137

50138
MPTs can be configured with different levels of transferability controls by adjusting the following flags:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
seo:
3+
description: Batch multiple transactions together and execute them as a single unit.
4+
metadata:
5+
indexPage: true
6+
labels:
7+
- Batch
8+
- Transactions
9+
---
10+
# Use Batch Transactions
11+
12+
Batch multiple transactions together and execute them as a single unit.
13+
14+
{% child-pages /%}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
seo:
3+
description: Send a batch transaction from multiple accounts.
4+
metadata:
5+
indexPage: true
6+
labels:
7+
- Batch
8+
- Transactions
9+
---
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
seo:
3+
description: Send a batch transaction from a single account.
4+
metadata:
5+
indexPage: true
6+
labels:
7+
- Batch
8+
- Transactions
9+
---
10+
11+
# Send a Single-Account Batch Transaction
12+
13+
A [Batch transaction][] allows you to group multiple transactions together and execute them as a single atomic operation.
14+
This tutorial shows you how to create and send a batch transaction from a single account, and how to configure the batch transaction so that either all inner transactions succeed or none are submitted.
15+
16+
The example in this tutorial demonstrates a scenario where an account creates a DEX offer and pays a platform fee all in one batch transaction, ensuring the platform gets paid if and only if the offer is created.
17+
18+
## Goals
19+
20+
By the end of this tutorial, you will be able to:
21+
22+
- Create a batch transaction with multiple inner transactions from a single account.
23+
- Configure the batch transaction to ensure atomicity, so that either all inner transactions succeed or none are submitted.
24+
25+
## Prerequisites
26+
27+
To complete this tutorial, you should:
28+
29+
- Have a basic understanding of XRPL Ledger.
30+
- Have an XRP Ledger client library set up in your development environment:
31+
- JavaScript: [xrpl.js](https://github.com/XRPLF/xrpl.js)
32+
- Python: [xrpl-py](https://github.com/XRPLF/xrpl-py)
33+
34+
## Source Code
35+
36+
You can find the complete source code for this tutorial's examples in the [code samples section of this website's repository](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/batch/).
37+
38+
## Steps
39+
40+
### 1. Install dependencies
41+
42+
{% tabs %}
43+
{% tab label="Javascript" %}
44+
From the code sample folder, use npm to install dependencies:
45+
46+
```bash
47+
npm install xrpl
48+
```
49+
50+
{% /tab %}
51+
52+
{% tab label="Python" %}
53+
From the code sample folder, use pip to install dependencies:
54+
55+
```bash
56+
pip install xrpl-py
57+
```
58+
{% /tab %}
59+
{% /tabs %}
60+
61+
## Set up client and accounts
62+
63+
To get started, import the client library and instantiate a client to connect to the XRPL. For this tutorial you need a funded account for the transaction **sender**, and a second account to **receive** the platform fee.
64+
65+
{% tabs %}
66+
{% tab label="Javascript" %}
67+
{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="import xrpl" before="// Create inner transactions" /%}
68+
{% /tab %}
69+
70+
{% tab label="Python" %}
71+
{% /tab %}
72+
{% /tabs %}
73+
74+
## Prepare inner transactions
75+
76+
Next, prepare the inner transactions that will be included in the batch. The following code shows the two inner transactions we want to send: one for the DEX UI offer, and another for the platform fee.
77+
78+
{% tabs %}
79+
{% tab label="Javascript" %}
80+
{% code-snippet file="/_code-samples/batch/js/singleAccountBatch.js" language="js" from="// Transaction 1:" to="// Send Batch transaction" /%}
81+
{% /tab %}
82+
83+
{% tab label="Python" %}
84+
{% /tab %}
85+
{% /tabs %}
86+
87+
In the code above we prepare the [OfferCreate transaction][] transaction which creates a DEX offer to sell 100 USD for XRP, and the [Payment transaction][] which sends 5 XRP to the platform fee recipient.
88+
89+
90+
{% raw-partial file="/docs/_snippets/common-links.md" /%}

0 commit comments

Comments
 (0)