Skip to content

Commit 3a548a5

Browse files
authored
Merge pull request #3233 from XRPLF/mpt_metadata_sample
Add sample code for XLS-89d MPT Metadata
2 parents 7b0ac8d + 9d8819c commit 3a548a5

File tree

9 files changed

+218
-3
lines changed

9 files changed

+218
-3
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Issue an MPT with Metadata
2+
3+
Shows how to issue a Multi-Purpose Token (MPT) with metadata encoded according to the XLS-89 schema.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Issue MPT with Metadata (JavaScript)
2+
3+
Creates a sample MPT issuance with metadata encoded as JSON according to the [XLS-89 standard](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0089-multi-purpose-token-metadata-schema).
4+
5+
Quick setup and usage:
6+
7+
```sh
8+
npm i
9+
node issue-mpt-with-metadata.js
10+
```
11+
12+
The script should output a validated transaction and end with a line such as the following:
13+
14+
```text
15+
MPToken created successfully with issuance ID 005073C721E14A7613BAAF5E0B1A253459832FF8D0D81278.
16+
```
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { stringToHex, hexToString } from '@xrplf/isomorphic/dist/utils/index.js'
2+
import { MPTokenIssuanceCreateFlags, Client } from 'xrpl'
3+
4+
// Connect to network and get a wallet
5+
const client = new Client('wss://s.devnet.rippletest.net:51233')
6+
await client.connect()
7+
8+
console.log('Funding new wallet from faucet...')
9+
const { wallet } = await client.fundWallet()
10+
11+
// Define metadata as JSON
12+
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: [
21+
{
22+
url: 'https://exampleyield.co/tbill',
23+
type: 'website',
24+
title: 'Product Page'
25+
},
26+
{
27+
url: 'https://exampleyield.co/docs',
28+
type: 'docs',
29+
title: 'Yield Token Docs'
30+
}
31+
],
32+
additional_info: {
33+
interest_rate: '5.00%',
34+
interest_type: 'variable',
35+
yield_source: 'U.S. Treasury Bills',
36+
maturity_date: '2045-06-30',
37+
cusip: '912796RX0'
38+
}
39+
}
40+
41+
// Convert JSON to a string (without excess whitespace), then string to hex
42+
const mpt_metadata_hex = stringToHex(JSON.stringify(mpt_metadata))
43+
44+
// Define the transaction, including other MPT parameters
45+
const mpt_issuance_create = {
46+
TransactionType: 'MPTokenIssuanceCreate',
47+
Account: wallet.address,
48+
AssetScale: 4,
49+
MaximumAmount: '50000000',
50+
TransferFee: 0,
51+
Flags: MPTokenIssuanceCreateFlags.tfMPTCanTransfer |
52+
MPTokenIssuanceCreateFlags.tfMPTCanTrade,
53+
MPTokenMetadata: mpt_metadata_hex
54+
}
55+
56+
// Prepare, sign, and submit the transaction
57+
console.log('Sending MPTokenIssuanceCreate transaction...')
58+
const submit_response = await client.submitAndWait(mpt_issuance_create, { wallet, autofill: true })
59+
60+
// Check transaction results and disconnect
61+
console.log(JSON.stringify(submit_response, null, 2))
62+
if (submit_response.result.meta.TransactionResult !== 'tesSUCCESS') {
63+
const result_code = response.result.meta.TransactionResult
64+
console.warn(`Transaction failed with result code ${result_code}.`)
65+
process.exit(1)
66+
}
67+
68+
const issuance_id = submit_response.result.meta.mpt_issuance_id
69+
console.log(`MPToken created successfully with issuance ID ${issuance_id}.`)
70+
71+
// Look up MPT Issuance entry in the validated ledger
72+
console.log('Confirming MPT Issuance metadata in the validated ledger.')
73+
const ledger_entry_response = await client.request({
74+
"command": "ledger_entry",
75+
"mpt_issuance": issuance_id,
76+
"ledger_index": "validated"
77+
})
78+
79+
// Decode the metadata
80+
const metadata_blob = ledger_entry_response.result.node.MPTokenMetadata
81+
const decoded_metadata = JSON.parse(hexToString(metadata_blob))
82+
console.log('Decoded metadata:', decoded_metadata)
83+
84+
85+
client.disconnect()
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.0"
4+
},
5+
"type": "module"
6+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Issue MPT with Metadata (Python)
2+
3+
Creates a sample MPT issuance with metadata encoded as JSON according to the [XLS-89 standard](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0089-multi-purpose-token-metadata-schema).
4+
5+
Quick setup and usage:
6+
7+
```sh
8+
python -m venv .venv
9+
source .venv/bin/activate
10+
pip install -r requirements.txt
11+
python issue-mpt-with-metadata.py
12+
```
13+
14+
The script should output a validated transaction and end with a line such as the following:
15+
16+
```text
17+
MPToken created successfully with issuance ID 0050773D6B8DF8C6BEA497016C8679728A217DE1C4D50AC5.
18+
```
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import json
2+
from xrpl.utils import str_to_hex, hex_to_str
3+
from xrpl.clients import JsonRpcClient
4+
from xrpl.wallet import generate_faucet_wallet
5+
from xrpl.transaction import submit_and_wait
6+
from xrpl.models import LedgerEntry, MPTokenIssuanceCreate, MPTokenIssuanceCreateFlag
7+
8+
# Set up client and get a wallet
9+
client = JsonRpcClient("https://s.devnet.rippletest.net:51234")
10+
print("Funding new wallet from faucet...")
11+
wallet = generate_faucet_wallet(client, debug=True)
12+
13+
# Define metadata as JSON
14+
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": [
23+
{
24+
"url": "https://exampleyield.co/tbill",
25+
"type": "website",
26+
"title": "Product Page"
27+
},
28+
{
29+
"url": "https://exampleyield.co/docs",
30+
"type": "docs",
31+
"title": "Yield Token Docs"
32+
}
33+
],
34+
"additional_info": {
35+
"interest_rate": "5.00%",
36+
"interest_type": "variable",
37+
"yield_source": "U.S. Treasury Bills",
38+
"maturity_date": "2045-06-30",
39+
"cusip": "912796RX0"
40+
}
41+
}
42+
43+
# Convert JSON to a string (without excess whitespace), then string to hex
44+
mpt_metadata_string = json.dumps(mpt_metadata, separators=(',', ':'))
45+
mpt_metadata_hex = str_to_hex(mpt_metadata_string)
46+
47+
# Define the transaction, including other MPT parameters
48+
mpt_issuance_create = MPTokenIssuanceCreate(
49+
account=wallet.address,
50+
asset_scale=4,
51+
maximum_amount="50000000",
52+
transfer_fee=0,
53+
flags=MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRANSFER |
54+
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRADE,
55+
mptoken_metadata=mpt_metadata_hex
56+
)
57+
58+
# Prepare, sign, and submit the transaction
59+
print("Sending MPTokenIssuanceCreate transaction...")
60+
response = submit_and_wait(mpt_issuance_create, client, wallet, autofill=True)
61+
print(json.dumps(response.result, indent=2))
62+
63+
# Check transaction results
64+
result_code = response.result["meta"]["TransactionResult"]
65+
if result_code != "tesSUCCESS":
66+
print(f"Transaction failed with result code {result_code}")
67+
exit(1)
68+
69+
issuance_id = response.result["meta"]["mpt_issuance_id"]
70+
print(f"MPToken successfully created with issuance ID {issuance_id}")
71+
72+
# Look up MPT Issuance entry in the validated ledger
73+
print("Confirming MPT Issuance metadata in the validated ledger.")
74+
ledger_entry_response = client.request(LedgerEntry(
75+
mpt_issuance=issuance_id,
76+
ledger_index="validated"
77+
))
78+
79+
# Decode the metadata
80+
metadata_blob = ledger_entry_response.result["node"]["MPTokenMetadata"]
81+
decoded_metadata = json.loads(hex_to_str(metadata_blob))
82+
print("Decoded metadata:", decoded_metadata)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
xrpl-py==4.3.0

docs/references/protocol/ledger-data/ledger-entry-types/mptokenissuance.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ _(Requires the [MPTokensV1 amendment][] {% not-enabled /%}.)_
2121
"AssetScale": 2,
2222
"MaximumAmount": "100000000",
2323
"OutstandingAmount": "100",
24-
"TransferFee": 50000,
24+
"TransferFee": 50000,
2525
"MPTokenMetadata": "7B227469636B6572223A20225442494C4C222C20226E616D65223A2022542D42696C6C205969656C6420546F6B656E222C202264657363223A202241207969656C642D62656172696E6720737461626C65636F696E206261636B65642062792073686F72742D7465726D20552E532E205472656173757269657320616E64206D6F6E6579206D61726B657420696E737472756D656E74732E222C202269636F6E223A202268747470733A2F2F6578616D706C652E6F72672F7462696C6C2D69636F6E2E706E67222C202261737365745F636C617373223A2022727761222C202261737365745F737562636C617373223A20227472656173757279222C20226973737565725F6E616D65223A20224578616D706C65205969656C6420436F2E222C202275726C73223A205B7B2275726C223A202268747470733A2F2F6578616D706C657969656C642E636F2F7462696C6C222C202274797065223A202277656273697465222C20227469746C65223A202250726F647563742050616765227D2C207B2275726C223A202268747470733A2F2F6578616D706C657969656C642E636F2F646F6373222C202274797065223A2022646F6373222C20227469746C65223A20225969656C6420546F6B656E20446F6373227D5D2C20226164646974696F6E616C5F696E666F223A207B22696E7465726573745F72617465223A2022352E303025222C2022696E7465726573745F74797065223A20227661726961626C65222C20227969656C645F736F75726365223A2022552E532E2054726561737572792042696C6C73222C20226D617475726974795F64617465223A2022323034352D30362D3330222C20226375736970223A2022393132373936525830227D7D",
2626
"OwnerNode": "74"
2727
}
2828
```
2929

3030
{% admonition type="success" name="Tip" %}
31-
By convention, the metadata should decode to JSON data describing what the MPT represents. The [XLS-89d specification](https://github.com/XRPLF/XRPL-Standards/pull/293) defines a recommended format for metadata. For example, the above `MPTokenMetadata` field encodes the sample JSON from the XLS-89d spec, as a UTF-8 string with minimal whitespace.
31+
By convention, the metadata should decode to JSON data describing what the MPT represents. The [XLS-89 specification](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0089-multi-purpose-token-metadata-schema) defines a recommended format for metadata. The above `MPTokenMetadata` field encodes the sample JSON from the spec, as a UTF-8 string with minimal whitespace.
3232
{% /admonition %}
3333

3434
## MPTokenIssuance Fields

docs/references/protocol/transactions/types/mptokenissuancecreate.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ This example assumes that the issuer of the token is the signer of the transacti
4141
| `AssetScale` | Number | UInt8 | No | Where to put the decimal place when displaying amounts of this MPT. More formally, the asset scale is a non-negative integer (0, 1, 2, …) such that one standard unit equals 10^(-scale) of a corresponding fractional unit. For example, if a US Dollar Stablecoin has an asset scale of _2_, then 1 unit of that MPT would equal 0.01 US Dollars. This indicates to how many decimal places the MPT can be subdivided. If omitted, the default is 0, meaning that the MPT cannot be divided into smaller than 1 unit. |
4242
| `TransferFee` | Number | UInt16 | No | The value specifies the fee to charged by the issuer for secondary sales of the Token, if such sales are allowed. Valid values for this field are between 0 and 50,000 inclusive, allowing transfer rates of between 0.000% and 50.000% in increments of 0.001. The field _must not_ be present if the tfMPTCanTransfer flag is not set. If it is, the transaction should fail and a fee should be claimed. |
4343
| `MaximumAmount` | String - Number | UInt64 | No | The maximum asset amount of this token that can ever be issued, as a base-10 number encoded as a string. The current default maximum limit is 9,223,372,036,854,775,807 (2^63-1). _This limit may increase in the future. If an upper limit is required, you must specify this field._ |
44-
| `MPTokenMetadata` | String - Hexadecimal | Blob | No | Arbitrary metadata about this issuance. The limit for this field is 1024 bytes. By convention, the metadata should decode to JSON data describing what the MPT represents. The [XLS-89d specification](https://github.com/XRPLF/XRPL-Standards/pull/293) defines a recommended format for metadata. |
44+
| `MPTokenMetadata` | String - Hexadecimal | Blob | No | Arbitrary metadata about this issuance. The limit for this field is 1024 bytes. By convention, the metadata should decode to JSON data describing what the MPT represents. The [XLS-89 specification](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0089-multi-purpose-token-metadata-schema) defines a recommended format for metadata. |
45+
46+
{% admonition type="success" name="Tip" %}
47+
For an example of how to encode metadata for the `MPTokenMetadata` field, see {% repo-link path="_code-samples/issue-mpt-with-metadata/" %}Code Sample: Issue MPT with Metadata{% /repo-link %}.
48+
{% /admonition %}
4549

4650
## MPTokenIssuanceCreate Flags
4751

0 commit comments

Comments
 (0)