mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-20 03:35:51 +00:00
Compare commits
1 Commits
issue-mpto
...
add-code-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6df042c7b |
@@ -1,40 +1,35 @@
|
||||
import {
|
||||
MPTokenIssuanceCreateFlags,
|
||||
Client,
|
||||
encodeMPTokenMetadata,
|
||||
decodeMPTokenMetadata
|
||||
} from 'xrpl'
|
||||
import { stringToHex, hexToString } from '@xrplf/isomorphic/dist/utils/index.js'
|
||||
import { MPTokenIssuanceCreateFlags, Client } from 'xrpl'
|
||||
|
||||
// Connect to network and get a wallet
|
||||
const client = new Client('wss://s.devnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
|
||||
console.log('=== Funding new wallet from faucet...===')
|
||||
const { wallet: issuer } = await client.fundWallet()
|
||||
console.log(`Issuer address: ${issuer.address}`)
|
||||
console.log('Funding new wallet from faucet...')
|
||||
const { wallet } = await client.fundWallet()
|
||||
|
||||
// Define metadata as JSON
|
||||
const mpt_metadata = {
|
||||
ticker: 'TBILL',
|
||||
name: 'T-Bill Yield Token',
|
||||
desc: 'A yield-bearing stablecoin backed by short-term U.S. Treasuries and money market instruments.',
|
||||
icon: 'https://example.org/tbill-icon.png',
|
||||
asset_class: 'rwa',
|
||||
asset_subclass: 'treasury',
|
||||
issuer_name: 'Example Yield Co.',
|
||||
uris: [
|
||||
t: 'TBILL',
|
||||
n: 'T-Bill Yield Token',
|
||||
d: 'A yield-bearing stablecoin backed by short-term U.S. Treasuries and money market instruments.',
|
||||
i: 'https://example.org/tbill-icon.png',
|
||||
ac: 'rwa',
|
||||
as: 'treasury',
|
||||
in: 'Example Yield Co.',
|
||||
us: [
|
||||
{
|
||||
uri: 'https://exampleyield.co/tbill',
|
||||
category: 'website',
|
||||
title: 'Product Page'
|
||||
u: 'https://exampleyield.co/tbill',
|
||||
c: 'website',
|
||||
t: 'Product Page'
|
||||
},
|
||||
{
|
||||
uri: 'https://exampleyield.co/docs',
|
||||
category: 'docs',
|
||||
title: 'Yield Token Docs'
|
||||
u: 'https://exampleyield.co/docs',
|
||||
c: 'docs',
|
||||
t: 'Yield Token Docs'
|
||||
}
|
||||
],
|
||||
additional_info: {
|
||||
ai: {
|
||||
interest_rate: '5.00%',
|
||||
interest_type: 'variable',
|
||||
yield_source: 'U.S. Treasury Bills',
|
||||
@@ -43,13 +38,8 @@ const mpt_metadata = {
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the metadata.
|
||||
// The encodeMPTokenMetadata function converts the JSON metadata object into
|
||||
// a compact, hex-encoded string, following the XLS-89 standard.
|
||||
// https://xls.xrpl.org/xls/XLS-0089-multi-purpose-token-metadata-schema.html
|
||||
console.log('\n=== Encoding metadata...===')
|
||||
const mpt_metadata_hex = encodeMPTokenMetadata(mpt_metadata)
|
||||
console.log("Encoded mpt_metadata_hex:", mpt_metadata_hex)
|
||||
// Convert JSON to a string (without excess whitespace), then string to hex
|
||||
const mpt_metadata_hex = stringToHex(JSON.stringify(mpt_metadata))
|
||||
|
||||
// Define the transaction, including other MPT parameters
|
||||
const mpt_issuance_create = {
|
||||
@@ -64,40 +54,32 @@ const mpt_issuance_create = {
|
||||
}
|
||||
|
||||
// Prepare, sign, and submit the transaction
|
||||
console.log('\n=== Sending MPTokenIssuanceCreate transaction...===')
|
||||
const submit_response = await client.submitAndWait(mpt_issuance_create,
|
||||
{ wallet, autofill: true }
|
||||
)
|
||||
console.log('Sending MPTokenIssuanceCreate transaction...')
|
||||
const submit_response = await client.submitAndWait(mpt_issuance_create, { wallet, autofill: true })
|
||||
|
||||
// Check transaction results
|
||||
console.log('\n=== Checking MPTokenIssuanceCreate results...===')
|
||||
// console.log(JSON.stringify(submit_response, null, 2))
|
||||
// Check transaction results and disconnect
|
||||
console.log(JSON.stringify(submit_response, null, 2))
|
||||
if (submit_response.result.meta.TransactionResult !== 'tesSUCCESS') {
|
||||
const result_code = submit_response.result.meta.TransactionResult
|
||||
const result_code = response.result.meta.TransactionResult
|
||||
console.warn(`Transaction failed with result code ${result_code}.`)
|
||||
await client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const issuance_id = submit_response.result.meta.mpt_issuance_id
|
||||
console.log(`- MPToken created successfully with issuance ID: ${issuance_id}`)
|
||||
// View the MPT issuance on the XRPL Explorer
|
||||
console.log(`\n- Explorer URL: https://devnet.xrpl.org/mpt/${issuance_id}`)
|
||||
console.log(`MPToken created successfully with issuance ID ${issuance_id}.`)
|
||||
|
||||
// Look up MPT Issuance entry in the validated ledger
|
||||
console.log('\n=== Confirming MPT Issuance metadata in the validated ledger... ===')
|
||||
console.log('Confirming MPT Issuance metadata in the validated ledger.')
|
||||
const ledger_entry_response = await client.request({
|
||||
"command": "ledger_entry",
|
||||
"mpt_issuance": issuance_id,
|
||||
"ledger_index": "validated"
|
||||
})
|
||||
|
||||
// Decode the metadata.
|
||||
// The decodeMPTokenMetadata function takes a hex-encoded string representing MPT metadata,
|
||||
// decodes it to a JSON object, and expands any compact field names to their full forms.
|
||||
// Decode the metadata
|
||||
const metadata_blob = ledger_entry_response.result.node.MPTokenMetadata
|
||||
const decoded_metadata = decodeMPTokenMetadata(metadata_blob)
|
||||
console.log('Decoded MPT metadata: ', decoded_metadata)
|
||||
const decoded_metadata = JSON.parse(hexToString(metadata_blob))
|
||||
console.log('Decoded metadata:', decoded_metadata)
|
||||
|
||||
|
||||
// Disconnect from the client
|
||||
client.disconnect()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"xrpl": "^4.4.3"
|
||||
"xrpl": "^4.4.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
xrpl-py==4.4.3
|
||||
xrpl-py==4.3.0
|
||||
|
||||
4
_code-samples/walk-owner-directory/README.md
Normal file
4
_code-samples/walk-owner-directory/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Walk Owner Directory
|
||||
Iterate over an account's owner directory and display how many ledger entries are in each page. In cases of highly active accounts, this can demonstrate the extent of "fragmentation" with skipped page numbers and non-full pages.
|
||||
|
||||
This code sample demonstrates the low-level structure of owner directories. If you don't need to see the breakdown by pages, you can use [`account_objects`](https://xrpl.org/docs/references/http-websocket-apis/public-api-methods/account-methods/account_objects) instead, since it provides a more convenient list of ledger entries attached to an account.
|
||||
@@ -0,0 +1,55 @@
|
||||
// iterate-owner-directory.js
|
||||
// Iterate over an account's owner directory and display how many ledger entries
|
||||
// are in each page. In cases of highly active accounts, it can demonstrate
|
||||
// the extent of "fragmentation" with skipped page numbers and non-full pages.
|
||||
|
||||
import xrpl from 'xrpl'
|
||||
|
||||
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
|
||||
const owner = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" // Testnet faucet
|
||||
// const owner = "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd" // TST issuer
|
||||
|
||||
// Set initial values for iterating
|
||||
let sub_index = 0 // Directory root
|
||||
let ledger_index = "validated"
|
||||
|
||||
// Query pages from the owner directory until they run out
|
||||
console.log("Page #\t\t\tEntry count")
|
||||
console.log("-----------------------------------")
|
||||
while (true) {
|
||||
// console.log(`Getting directory page ${sub_index}`)
|
||||
const resp = await client.request({
|
||||
"command": "ledger_entry",
|
||||
"directory": {
|
||||
"owner": owner,
|
||||
"sub_index": sub_index
|
||||
},
|
||||
"ledger_index": ledger_index
|
||||
})
|
||||
if (resp.error) {
|
||||
console.error("ledger_entry failed with error",resp.error)
|
||||
break
|
||||
}
|
||||
|
||||
// Consistently iterate the same ledger: query by index after the first
|
||||
if (ledger_index === "validated") {
|
||||
ledger_index = resp.result.ledger_index
|
||||
}
|
||||
|
||||
console.log(`${sub_index}\t\t\t${resp.result.node.Indexes.length}`)
|
||||
// console.log(`This page contains ${resp.result.node.Indexes.length} items.`)
|
||||
|
||||
// Continue onto another page if this one has more
|
||||
if (resp.result.node.hasOwnProperty("IndexNext")) {
|
||||
// The directory continues onto another page.
|
||||
// IndexNext is returned as hex but sub_index needs decimal
|
||||
sub_index = parseInt(resp.result.node.IndexNext, 16)
|
||||
} else {
|
||||
console.info("This is the last page of the directory")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
client.disconnect()
|
||||
5
_code-samples/walk-owner-directory/js/package.json
Normal file
5
_code-samples/walk-owner-directory/js/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"xrpl": "^4.4.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
# iterate-owner-directory.py
|
||||
# Iterate over an account's owner directory and display how many ledger entries
|
||||
# are in each page. In cases of highly active accounts, it can demonstrate
|
||||
# the extent of "fragmentation" with skipped page numbers and non-full pages.
|
||||
from xrpl.clients import JsonRpcClient
|
||||
from xrpl.models.requests import LedgerEntry
|
||||
from xrpl.clients import XRPLRequestFailureException
|
||||
|
||||
OWNER_ADDRESS = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet
|
||||
# OWNER_ADDRESS = "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd" # TST issuer
|
||||
|
||||
client = JsonRpcClient("https://s.altnet.rippletest.net:51234/")
|
||||
|
||||
# Set initial values for iterating
|
||||
sub_index = 0 # Directory root
|
||||
ledger_index = "validated"
|
||||
|
||||
# Query pages from the owner directory until they run out
|
||||
print("Page #\t\t\tEntry count")
|
||||
print("-----------------------------------")
|
||||
while True:
|
||||
# Construct the LedgerEntry request for the directory page
|
||||
directory_request = LedgerEntry(
|
||||
directory={
|
||||
"owner": OWNER_ADDRESS,
|
||||
"sub_index": sub_index
|
||||
},
|
||||
ledger_index=ledger_index
|
||||
)
|
||||
|
||||
# Send the request
|
||||
try:
|
||||
response = client.request(directory_request)
|
||||
except Exception as e:
|
||||
print(f"\nError: ledger_entry failed: {e}")
|
||||
break
|
||||
|
||||
# The 'ledger_index' is consistently set after the first successful query.
|
||||
# This ensures subsequent pages are read from the same ledger version.
|
||||
if ledger_index == "validated":
|
||||
ledger_index = response.result["ledger_index"]
|
||||
|
||||
# The entries are stored in the 'Indexes' field of the 'DirectoryNode'
|
||||
entry_count = len(response.result["node"]["Indexes"])
|
||||
print(f"{sub_index}\t\t\t{entry_count}")
|
||||
|
||||
# Check for the next page indicator
|
||||
if "IndexNext" in response.result["node"].keys():
|
||||
# The directory continues onto another page.
|
||||
# Convert IndexNext from hex to decimal for sub_index.
|
||||
hex_next = response.result["node"]["IndexNext"]
|
||||
sub_index = int(hex_next, 16)
|
||||
else:
|
||||
print("\nThis is the last page of the directory.")
|
||||
break
|
||||
1
_code-samples/walk-owner-directory/py/requirements.txt
Normal file
1
_code-samples/walk-owner-directory/py/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
xrpl-py==4.3.1
|
||||
@@ -1,149 +0,0 @@
|
||||
---
|
||||
seo:
|
||||
description: Issue a Multi-Purpose Token (MPT) with arbitrary metadata on the XRP Ledger.
|
||||
metadata:
|
||||
indexPage: true
|
||||
labels:
|
||||
- Multi-Purpose Token
|
||||
- MPT
|
||||
- Token Issuance
|
||||
---
|
||||
# Issue a Multi-Purpose Token (MPT)
|
||||
|
||||
A [Multi-Purpose Token (MPT)](../../../concepts/tokens/fungible-tokens/multi-purpose-tokens.md) lets you to quickly access powerful, built-in tokenization features on the XRP Ledger with minimal code.
|
||||
|
||||
This tutorial shows you how to issue an MPT with on-chain metadata such as the token's ticker, name, or description, encoded according to the MPT [metadata schema](../../../concepts/tokens/fungible-tokens/multi-purpose-tokens.md#metadata-schema) defined in [XLS-89](https://xls.xrpl.org/xls/XLS-0089-multi-purpose-token-metadata-schema.html).
|
||||
|
||||
## Goals
|
||||
|
||||
By the end of this tutorial, you will be able to:
|
||||
|
||||
- Issue a new MPT using the `MPTokenIssuanceCreate` transaction.
|
||||
- Encode or decode token metadata following MPT standards best practices.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To complete this tutorial, you should:
|
||||
|
||||
- Have a basic understanding of the XRP Ledger and token issuance.
|
||||
- Have an XRP Ledger client library set up in your development environment. This page provides examples for the following:
|
||||
- **JavaScript** with the [xrpl.js library](https://github.com/XRPLF/xrpl.js). See [Get Started Using JavaScript](../../javascript/build-apps/get-started.md) for setup steps.
|
||||
|
||||
## Source Code
|
||||
|
||||
You can find the complete source code for this tutorial's example in the [code samples section of this website's repository](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/issue-mpt-with-metadata).
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Install dependencies
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
From the code sample folder, use npm to install dependencies:
|
||||
|
||||
```bash
|
||||
npm install xrpl
|
||||
```
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 2. Set up client and fund issuer wallet
|
||||
|
||||
Import the client library, instantiate a client to connect to the XRPL, and fund a new wallet to act as the token issuer.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js" language="js" before="// Define metadata as JSON" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 3. Define and encode MPT metadata
|
||||
|
||||
Define your token's metadata as a JSON object:
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js" language="js" from="// Define metadata as JSON" before="// Encode the metadata" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
The metadata schema supports both long field names (e.g., `ticker`, `name`, `desc`) and compact short keys (e.g., `t`, `n`, `d`). To save space on the ledger, it’s recommended to use short key names. The MPT metadata field has a 1024-byte limit, so using compact keys allows you to include more information.
|
||||
|
||||
The SDK libraries provide utility functions to encode or decode the metadata for you, so you don't have to. If long field names are provided in the metadata JSON, the _encoding_ utility function automatically shortens them to their compact key equivalents before encoding. Similarly, when decoding, the _decoding_ utility function converts the shorthands back to the respective long names.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
Use the `encodeMPTokenMetadata()` function to encode metadata with `xrpl.js`.
|
||||
{% code-snippet file="/_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js" language="js" from="// Encode the metadata" before="// Define the transaction" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% /tabs %}
|
||||
|
||||
{% admonition type="warning" name="Warning" %}
|
||||
While the encoding utility formats the JSON for you correctly and replaces the full key names with shorthands, it does **not** validate the metadata content or size.
|
||||
{% /admonition %}
|
||||
|
||||
### 4. Prepare the MPTokenIssuanceCreate transaction
|
||||
|
||||
Create the transaction object, specifying the issuer, asset scale, maximum amount, transfer/trade flags, and the encoded metadata.
|
||||
|
||||
| Field | Value |
|
||||
|:------------------|:---------------------------------------------------------------------|
|
||||
| `TransactionType` | The type of transaction, in this case `MPTokenIssuanceCreate`. |
|
||||
| `Account` | The wallet address of the account that is issuing the MPT. |
|
||||
| `AssetScale` | The number of decimal places for the token. |
|
||||
| `MaximumAmount` | The maximum supply of the token to be issued. |
|
||||
| `TransferFee` | The transfer fee (if any) to charge for token transfers. |
|
||||
| `Flags` | Flags to control transfer/trade permissions. |
|
||||
| `MPTokenMetadata` | The hex-encoded metadata for the token. |
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js" language="js" from="// Define the transaction" before="// Prepare, sign, and submit the transaction" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 5. Submit the transaction
|
||||
|
||||
Sign and submit the transaction, then wait for validation.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js" language="js" from="// Prepare, sign, and submit the transaction" before="// Check transaction results" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 6. Check transaction result
|
||||
|
||||
Verify that the transaction succeeded and retrieve the MPT issuance ID.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js" language="js" from="// Check transaction results" before="// Look up MPT Issuance entry" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
{% admonition type="info" name="Note" %}
|
||||
A `tesSUCCESS` result indicates that the MPT issuance transaction was processed successfully and the token was created.
|
||||
{% /admonition %}
|
||||
|
||||
### 7. Confirm MPT issuance and decode metadata
|
||||
|
||||
Look up the MPT issuance entry in the validated ledger and decode the metadata to verify it matches your original input.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Javascript" %}
|
||||
{% code-snippet file="/_code-samples/issue-mpt-with-metadata/js/issue-mpt-with-metadata.js" language="js" from="// Look up MPT Issuance entry" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
The _decoding_ utility function converts the metadata shorthand key names back to the respective long names.
|
||||
|
||||
## See Also
|
||||
|
||||
- **Concepts**:
|
||||
- [Multi-Purpose Tokens (MPT)](../../../concepts/tokens/fungible-tokens/multi-purpose-tokens.md)
|
||||
- **References**:
|
||||
- [MPTokenIssuanceCreate Transaction](../../../references/protocol/transactions/types/mptokenissuancecreate.md)
|
||||
|
||||
{% raw-partial file="/docs/_snippets/common-links.md" /%}
|
||||
Reference in New Issue
Block a user