Direct Mint FXRP
Overview
This guide walks you through minting FAssets FXRP using direct minting: a single XRPL payment to the FXRP Core Vault with a memo that encodes the Flare-side recipient. An executor then finalizes the mint on Flare, and the recipient receives FXRP.
The complete runnable example is available in the flare-viem-starter repository.
How it differs from standard minting
Compared to the standard collateral reservation flow, direct minting is a single-step XRPL payment:
- No collateral reservation — the minter does not call
reserveCollateraland there is no agent selection. - A single XRPL payment to the Core Vault address replaces the agent payment.
- The recipient (and optionally the executor) is encoded in the XRPL memo or destination tag.
- An executor calls
executeDirectMintingon Flare to finalize and receives a flat fee.
This guide demonstrates the 32-byte memo format, where the recipient is a Flare address and any executor can finalize.
Prerequisites
- Flare smart account controlled by your XRPL wallet.
- Testnet XRP on the XRP Ledger Testnet usage. Get testnet XRP from the XRP Testnet Faucet.
Direct Minting Memo
The memo is a packed 32-byte PaymentReference:
| Bytes | Value | Meaning |
|---|---|---|
| 0-7 | 4642505266410018 | Direct minting prefix. |
| 8-11 | 00000000 | Zero padding to fill the 32-byte memo. |
| 12-31 | 20-byte recipient | EVM address of the Flare smart account receiving FXRP tokens. |
Because no executor is encoded, anyone can finalize this minting on Flare. For executor-restricted minting, use the 48-byte memo format described in Memo Field.
Direct Minting Script
import { Client, Wallet } from "xrpl";
import type { Address } from "viem";
import { sendXrplPayment } from "./utils/xrpl";
import { getPersonalAccountAddress } from "./utils/smart-accounts";
import {
getContractAddressByName,
getDirectMintingPaymentAddress,
} from "./utils/flare-contract-registry";
import {
computeDirectMintingPaymentAmountXrp,
getFxrpBalance,
waitForDirectMintingExecuted,
} from "./utils/fassets";
// 1. Direct minting prefix
const DIRECT_MINTING_PREFIX = "4642505266410018";
// 2. Build the 32-byte direct-minting PaymentReference memo:
// [8-byte prefix][4-byte zero padding][20-byte recipient address]
function buildDirectMintingMemo(recipientAddress: Address): string {
return (
DIRECT_MINTING_PREFIX + "00000000" + recipientAddress.slice(2).toLowerCase()
);
}
// 3. Send the XRPL payment to the Core Vault with the direct-minting memo
async function sendDirectMintPayment({
coreVaultXrplAddress,
recipientAddress,
amountXrp,
xrplClient,
xrplWallet,
}: {
coreVaultXrplAddress: string;
recipientAddress: Address;
amountXrp: number;
xrplClient: Client;
xrplWallet: Wallet;
}) {
const memoData = buildDirectMintingMemo(recipientAddress);
console.log("Direct minting memo (32 bytes):", memoData, "\n");
const transaction = await sendXrplPayment({
destination: coreVaultXrplAddress,
amount: amountXrp,
memos: [{ Memo: { MemoData: memoData } }],
wallet: xrplWallet,
client: xrplClient,
});
console.log(
"Direct mint XRPL transaction hash:",
transaction.result.hash,
"\n",
);
return transaction;
}
async function main() {
// 4. Net FXRP amount to mint in XRP. Minting + executor fees are added on top
// by computeDirectMintingPaymentAmountXrp to form the XRPL payment amount.
const fxrpMintAmount = 10;
// 5. Connect to the XRPL Testnet and load the sender wallet from XRPL_SEED.
const xrplClient = new Client(process.env.XRPL_TESTNET_RPC_URL!);
const xrplWallet = Wallet.fromSeed(process.env.XRPL_SEED!);
// 6. Resolve the Flare smart-account recipient and the AssetManagerFXRP address.
const [personalAccountAddress, assetManagerAddress] = await Promise.all([
getPersonalAccountAddress(xrplWallet.address),
getContractAddressByName("AssetManagerFXRP"),
]);
console.log("Personal account address:", personalAccountAddress, "\n");
// 7. Read the Core Vault XRPL payment address, the recipient's initial FXRP
// balance, and the gross XRP amount (net mint + minting fee + executor fee).
const [coreVaultXrplAddress, initialBalance, paymentAmountXrp] =
await Promise.all([
getDirectMintingPaymentAddress(assetManagerAddress),
getFxrpBalance(personalAccountAddress),
computeDirectMintingPaymentAmountXrp({
netMintAmountXrp: fxrpMintAmount,
}),
]);
console.log("Core Vault XRPL address:", coreVaultXrplAddress, "\n");
console.log("AssetManagerFXRP address:", assetManagerAddress, "\n");
console.log("Initial FXRP balance:", initialBalance, "\n");
console.log("Payment amount (XRP, net mint + fees):", paymentAmountXrp, "\n");
// 8. Send the XRPL payment that triggers direct minting on Flare.
await sendDirectMintPayment({
coreVaultXrplAddress,
recipientAddress: personalAccountAddress,
amountXrp: paymentAmountXrp,
xrplClient,
xrplWallet,
});
// 9. Wait for the DirectMintingExecuted event from AssetManagerFXRP.
const mintEvent = await waitForDirectMintingExecuted({
assetManagerAddress,
targetAddress: personalAccountAddress,
});
console.log("DirectMintingExecuted event:", mintEvent, "\n");
console.log("Minted amount (UBA):", mintEvent.args.mintedAmountUBA, "\n");
console.log("Minting fee (UBA):", mintEvent.args.mintingFeeUBA, "\n");
console.log("Executor fee (UBA):", mintEvent.args.executorFeeUBA, "\n");
// 10. Read the final FXRP balance and log the delta.
const finalBalance = await getFxrpBalance(personalAccountAddress);
console.log("Final FXRP balance:", finalBalance, "\n");
console.log("FXRP minted:", finalBalance - initialBalance, "\n");
}
void main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Code Breakdown
- Define the direct minting prefix constant
DIRECT_MINTING_PREFIX. buildDirectMintingMemoconcatenates the prefix, four zero bytes, and the recipient address (without the0xprefix, lowercased) into a 32-byte hex string ready to be passed asMemoDataon the XRPL payment.sendDirectMintPaymentbuilds the memo for the recipient and submits an XRPLPaymenttransaction to the Core Vault address withmemos: [{ Memo: { MemoData } }], returning the resulting transaction.- Set the net FXRP amount to mint in XRP. Minting and executor fees are added to the net amount to form the XRPL payment amount.
- Connect to the XRPL Testnet using
XRPL_TESTNET_RPC_URLand load the sender wallet fromXRPL_SEED. - Resolve the recipient — the Flare
PersonalAccountfor the XRPL wallet — viagetPersonalAccountAddress, and look upAssetManagerFXRPthrough the Flare Contract Registry usinggetContractAddressByName. - Read three values in parallel:
getDirectMintingPaymentAddress— the Core Vault XRPL address that receives the payment.getFxrpBalance— the recipient's FXRP balance before minting.computeDirectMintingPaymentAmountXrp— the gross XRP amount equal to the net mint amount plus the minting fee and the executor fee, as quoted by theAssetManagerFXRPcontract.
- Send the XRPL payment to the Core Vault with the recipient memo via the
sendDirectMintPaymentfunction. - Wait for the
DirectMintingExecutedevent onAssetManagerFXRPusingwaitForDirectMintingExecutedfunction. The event'smintedAmountUBA,mintingFeeUBA, andexecutorFeeUBAdescribe how the underlying payment was split. - Read the final FXRP balance and log the delta.
Important Notes
- Fees are deducted from the underlying payment. The XRP sent to the Core Vault must cover the net mint amount plus the minting fee and the executor fee.
- If the payment is below the minimum minting fee, no FXRP is minted, and the entire payment is forfeited to the fee receiver.
- Rate limits may delay execution. When hourly, daily, or large-minting thresholds are hit, the minting emits
DirectMintingDelayedinstead ofDirectMintingExecuted, with anexecutionAllowedAttimestamp. - For repeat minters, prefer the destination-tag flow via
IMintingTagManager— it avoids constructing a memo per payment and lets you set a preferred executor.
To continue your FAssets development journey, you can:
- Mint with tag with
mintWithTag. - Read the protocol details in Direct Minting.
- Redeem FAssets with
redeemAmountorredeemWithTag.