A clean TypeScript SDK for building Radiant (RXD) dapps — without reading internal Photonic/radiantjs source.
It wraps @radiant-core/radiantjs (the low‑level tx/script library) and gives you the primitives most web/app developers actually need: an ElectrumX client, ref‑safe UTXO selection that never burns tokens, Glyph token mint/transfer, WAVE name resolution, and SLIP‑0044 HD wallets.
- 🔌 ElectrumX WebSocket client — balance, UTXOs, broadcast, subscribe, auto‑reconnect with backoff
- 🛡️ Ref‑safe funding — token‑bearing UTXOs are never spent as RXD funding (no silent token burns)
- 🪙 Glyph tokens — mint FT/NFT and transfer via the commit/reveal pattern
- 🌊 WAVE names — resolve
alice→ address - 🔑 HD wallets —
m/44'/512'/0'/0/k(Radiant coin type 512, the v3.0.0+ default) - 📦 Dual ESM + CJS, tree‑shakeable, TypeScript types included
- 🔢 Photons as
BigInteverywhere internally, with RXD helpers at the edge
npm install @radiant-core/sdk
# radiantjs is a peer of the token/wallet APIs; install it too:
npm install @radiant-core/radiantjs
# On Node < 22 (no global WebSocket), also:
npm install wsUnits. The base unit is the photon.
1 RXD = 100,000,000 photons. All amounts inside the SDK areBigIntphotons. UserxdToPhotons()/photonsToRxd()only at your UI/input boundary — never do balance math in floating point.
import { ElectrumClient, photonsToRxd } from "@radiant-core/sdk";
const client = new ElectrumClient({ network: "mainnet" });
await client.connect();
const address = "1Yourradiantaddress...";
const { confirmed, unconfirmed } = await client.getBalance(address);
console.log(`Confirmed: ${photonsToRxd(confirmed)} RXD`);
const utxos = await client.listUnspent(address); // Utxo[] (photons as BigInt)import { HDWallet } from "@radiant-core/sdk";
// New wallet
const mnemonic = HDWallet.generateMnemonic();
const wallet = HDWallet.fromMnemonic(mnemonic, { network: "mainnet" });
// Address #0 at m/44'/512'/0'/0/0
const acct0 = wallet.deriveKey(0);
console.log(acct0.path); // m/44'/512'/0'/0/0
console.log(acct0.address); // base58 address
console.log(acct0.scriptHash); // ElectrumX scripthash
// Batch derive a gap of receive addresses
const receive = wallet.deriveRange(0, 20); // DerivedKey[]import { ElectrumClient, HDWallet, buildRxdTransfer, rxdToPhotons } from "@radiant-core/sdk";
const client = new ElectrumClient({ network: "mainnet" });
const wallet = HDWallet.fromMnemonic(mnemonic);
const me = wallet.deriveKey(0);
const utxos = await client.listUnspent(me.address);
const { hex, txid } = buildRxdTransfer({
address: me.address,
wif: me.wif,
to: "1Recipient...",
amount: rxdToPhotons("1.25"), // 1.25 RXD -> photons
utxos, // token‑bearing UTXOs are auto‑excluded
feeRate: 10_000n, // photons/byte (mainnet min‑relay)
});
await client.broadcastTx(hex);
console.log("sent", txid);The single most important safety property: funding is never gathered by a
value heuristic. A UTXO that carries a token ref (FT/NFT/dMint) looks like
plain value to a naive selector, but spending it as funding burns the token.
selectRxdFunding screens every candidate two ways — indexer‑reported refs
and a local script scan (isTokenBearing) — and throws TokenBurnGuardError
rather than risk a burn.
import { selectRxdFunding, filterFundingCandidates, isTokenBearing } from "@radiant-core/sdk";
const safe = filterFundingCandidates(allUtxos); // drops anything token‑bearing
const sel = selectRxdFunding(allUtxos, rxdToPhotons("0.5"), 10_000n);
console.log(sel.inputs, sel.fee, sel.change);
isTokenBearing(scriptHex); // true if the script has an OP_PUSHINPUTREF opcodeimport { mintFT, mintNFT, filterFundingCandidates } from "@radiant-core/sdk";
const funding = filterFundingCandidates(await client.listUnspent(me.address));
// Fungible token (FT amount == output photons)
const ft = await mintFT({
client,
address: me.address,
wif: me.wif,
ticker: "DEMO",
supply: 1_000_000n,
metadata: { name: "Demo Token", desc: "Minted with @radiant-core/sdk" },
fundingUtxos: funding,
});
console.log("FT ref:", ft.refDisplay, ft.commitTxid, ft.revealTxid);
// Non‑fungible token (singleton)
const nft = await mintNFT({
client,
address: me.address,
wif: me.wif,
metadata: { name: "My NFT", attrs: { rarity: "rare" } },
fundingUtxos: funding,
});import { transferToken } from "@radiant-core/sdk";
// `tokenUtxo` must include its on‑chain `script` (the token output script).
await transferToken({
client,
address: me.address,
wif: me.wif,
tokenUtxo, // the FT/NFT UTXO to move
toAddress: "1Recipient...",
fundingUtxos: funding, // covers the fee only; token value is conserved
});import { waveResolve, waveResolveAddress } from "@radiant-core/sdk";
const rec = await waveResolve("alice"); // "alice.rxd" works too (suffix stripped)
if (rec.registered) console.log(rec.address, rec.owner, rec.expires);
const addr = await waveResolveAddress("alice"); // string | nullconst status = await client.subscribe(me.address, (newStatus) => {
console.log("address changed:", newStatus); // re‑fetch UTXOs/balance here
});| Area | Exports |
|---|---|
| Client | ElectrumClient |
| Wallet | HDWallet, Keys |
| Funding | selectRxdFunding, filterFundingCandidates, isFundingSafe, estimateFee, sumValue |
| Tx | buildTx, buildRxdTransfer |
| Tokens | mintFT, mintNFT, transferToken, encodeGlyph, ftScript, nftScript, parseTokenRef |
| WAVE | waveResolve, waveResolveAddress, waveLabel |
| Script | scriptHash, addressToScriptHash, p2pkhScript, isTokenBearing, zeroRefs, packRef, unpackRef |
| Units | rxdToPhotons, photonsToRxd |
| Errors | RadiantSdkError, InsufficientFundsError, TokenBurnGuardError, ElectrumError, ValidationError |
See docs/API.md for the full reference (every export, options, and return types), and the hosted guide at radiantcore.org/docs/sdk.html.
- API reference — every export, with options and return types.
- Hosted guide — narrative walkthrough on radiantcore.org.
- Changelog · Contributing
- radiantjs is wrapped, not duplicated. All key/tx/script work delegates to
@radiant-core/radiantjs; the SDK adds BigInt photons, ref‑safety, the ElectrumX transport, and Glyph/WAVE conveniences. The raw library is available via theradiantjsexport if you need an escape hatch. - Fee rate. Mainnet min‑relay is
10,000photons/byte (since the V2 upgrade at block 410,000); testnet/regtest is1,000. These are exposed asMIN_RELAY_FEE_RATEand used as defaults — pin them, don't tune them.buildTxsets the fee from the measured signed transaction size (not an estimate), so token transactions carrying a CBOR envelope reliably clear the node's min‑relay floor. Funding selection reserves a little extra so a tightly‑funded wallet still has enough inputs; any surplus returns as change. - Endpoints. The public mainnet ElectrumX is
wss://electrumx.radiantcore.org:443(TLS on :443 only). WAVE resolution defaults tohttps://radiantcore.org/api. Both are overridable.
✅ Mint/transfer flows are regtest‑validated.
mintFT/mintNFT/transferTokenreproduce the proven Photonic‑Wallet on‑chain templates and have been run end‑to‑end against a realradiantdregtest node — commit + reveal accepted, outputs matchftScript/nftScriptand carry the ref, FT amount and singleton ref preserved across transfers. They still broadcast real, irreversible transactions, so smoke‑test your own flow on regtest/testnet before mainnet.
Requires Node 20.19+ or 22+ (require(ESM) support; radiantjs pulls in an
ESM-only dependency).
npm install
npm run build # tsup -> dist/ (ESM + CJS + d.ts)
npm run typecheck # tsc --noEmit
npm test # node --test against the built bundleMIT