Concise overview of a Next.js (App Router) application showcasing XRPL MP Tokens (MPTokensV1) on Devnet. It lets an issuer:
- Create (or reuse) an MPToken issuance with authorization + clawback flags.
- Allow holders to opt-in (MPTokenAuthorize) via a XUMM (Xaman) QR signing flow – no holder secrets server-side.
- Authorize holders after opt-in (issuer-signed grant).
- Send and claw back MPToken units.
- Inspect balances and verify signed opt-in transactions.
- Parse simple payment memos ("PAY-CROWN|||") and assert pricing in whole XRP units.
The code hard‑enforces XRPL_NETWORK=devnet to avoid accidental use of main/test networks.
- Next.js 16 (App Router) + React 19
- TailwindCSS + Radix UI components
- XRPL JS SDK (
xrpl) - XUMM platform API (server-side only)
- TypeScript + ESLint
app/– Pages & API route handlers (/api/mpt/*) for issuance, opt-in QR, authorization, balances, send, clawback.lib/xrpl-logic.js– Core XRPL + MPT helpers (create issuance, authorize, send, clawback, balance, scaling).lib/xumm.ts– Minimal XUMM payload create/status helpers (QR + polling).components/ui/– Reusable UI primitives.docs/qr-optin-demo.md– Extended walkthrough of the opt‑in flow.
Copy .env.example to .env and fill:
XRPL_WS_URL=wss://clio.devnet.rippletest.net:51233
XRPL_NETWORK=devnet
ISSUER_SEED=YOUR_ISSUER_SEED
ISSUER_ADDRESS=rsameasderivedoptional
NETWORK_TIMEOUT_MS=20000
XUMM_API_KEY=your_xumm_api_key
XUMM_API_SECRET=your_xumm_api_secret
Notes:
ISSUER_ADDRESScan be omitted; derived fromISSUER_SEEDif not set.- Never commit real seeds or XUMM secrets.
- App will refuse to start if
XRPL_NETWORK!=devnet.
Requires Node 20+ (recommended) and pnpm.
pnpm install --legacy-peer-deps # install deps
pnpm install xrpl@latest --legacy-peer-deps #install xrpl dep
pnpm dev # start Next.js dev server (http://localhost:3000)Build & production start:
pnpm build
pnpm startOptional lint:
pnpm lint- Issuance: Server creates (or reuses) an issuance with required flags.
- Holder Opt-In: Frontend requests
/api/mpt/authorize/qrto produce a XUMM QR; holder signs MPTokenAuthorize. - Poll Status: Frontend polls
/api/mpt/authorize/status/:uuiduntilsigned=true&txidpresent. - Verify:
/api/mpt/authorize/verifycan assert the ledger transaction matches expected account + issuance id. - Grant: Issuer authorizes holder (unless already authorized).
- Send / Clawback: Payments (
Paymentwith MPT Amount) orClawbacksubmitted by issuer.
MPT_ASSET_SCALE (default 2) defines decimal scaling: human "1" -> ledger "100". Helpers: toMptLedgerValue, fromMptLedgerValue in lib/xrpl-logic.js.
- Seeds & API keys only loaded server-side; UI never exposes them.
- Logging uses
safeLogto avoid leaking sensitive data. - Network restricted to Devnet. Feature flag check
assertMPTokensV1ensures protocol support.
lib/xumm.ts exports:
createSignRequest(txjson, { expiresIn? })→ QR + uuid + redirect link.getPayloadStatus(uuid)→{ uuid, resolved, signed, txid?, account? }. Use only in route handlers / server code.
| Symptom | Hint |
|---|---|
XRPL_NETWORK must be 'devnet' |
Set XRPL_NETWORK=devnet in .env |
| Issuance creation fails | Ensure MPTokensV1 is active (Devnet normally enabled) |
tecNO_AUTH on Payment |
Holder not opt‑in or not authorized yet |
| XUMM status stuck unsigned | Holder hasn’t scanned or declined; regenerate QR |
xumm_not_found |
Expired or wrong payload uuid |
Hackathon / demo use. Add a formal license if this evolves beyond prototype.