@rougechain/sdk
Build quantum-safe dApps on RougeChain
Transfers · DEX · NFTs · Social · Shielded Transactions · Bridge · Rollups · Dynamic Fees · Finality Proofs · WebSocket · Mail · Messenger
The official SDK for RougeChain — a post-quantum Layer 1 blockchain secured by ML-DSA-65 (CRYSTALS-Dilithium). All transaction signing happens client-side with NIST-approved post-quantum cryptography. Private keys never leave your application.
Works in the browser, Node.js 18+, and React Native.
Install
npm install @rougechain/sdk
30-Second Quickstart
import { RougeChain, Wallet } from "@rougechain/sdk";
const rc = new RougeChain("https://testnet.rougechain.io/api");
const wallet = Wallet.generate();
await rc.faucet(wallet);
await rc.transfer(wallet, { to: recipientPubKey, amount: 100 });
const { balance } = await rc.getBalance(wallet.publicKey);
Features
| Wallet | — | ML-DSA-65 keypair generation, import/export, client-side signing |
| Transfers | rc | Send XRGE or custom tokens, burn tokens |
| Token Creation | rc | Launch new tokens with on-chain logo support |
| Token Allowances | rc | ERC-20 style approve/transferFrom for DeFi composability |
| Staking | rc | Stake/unstake XRGE for validation |
| DEX | rc.dex | AMM pools, swaps with slippage protection, liquidity |
| NFTs | rc.nft | RC-721 collections, mint, batch mint, royalties, freeze |
| Shielded | rc.shielded | Private transfers with zk-STARK proofs, shield/unshield XRGE |
| Bridge | rc.bridge | ETH ↔ qETH, USDC ↔ qUSDC, XRGE bridge (Base mainnet; auto-claim deposits, withdrawal status + auto-refund) |
| Rollup | rc | zk-STARK batch proofs, rollup status, submit transfers |
| Social | rc.social | Posts, timeline feed, reposts, likes, follows, comments |
| Mail | rc.mail | On-chain encrypted email (@rouge.quant) |
| Messenger | rc.messenger | E2E encrypted messaging with self-destruct |
| Address Resolution | rc | O(1) rouge1↔pubkey resolution via on-chain index |
| Push Notifications | rc | PQC-signed push token registration (Expo) |
| Token Freeze | rc | Creator-only token freeze/pause |
| Mintable Tokens | rc | Ongoing token minting with supply cap enforcement |
| Dynamic Fees | rc | EIP-1559 base fee, priority tips, fee burning |
| Finality Proofs | rc | BFT finality certificates with ≥2/3 validator stake |
| WebSocket | rc | Real-time event streaming with topic subscriptions |
Wallet & Addresses
import { Wallet, pubkeyToAddress, isRougeAddress, formatAddress } from "@rougechain/sdk";
const wallet = Wallet.generate();
const address = await wallet.address();
const restored = Wallet.fromKeys(publicKey, privateKey);
const keys = wallet.toJSON();
wallet.verify();
const addr = await pubkeyToAddress(someHexPubKey);
const display = formatAddress(addr);
isRougeAddress("rouge1q8f3x7k2m4...");
Transfers & Tokens
await rc.transfer(wallet, { to: recipient, amount: 100 });
await rc.transfer(wallet, { to: recipient, amount: 50, token: "MYTOKEN" });
await rc.createToken(wallet, {
name: "My Token",
symbol: "MTK",
totalSupply: 1_000_000,
image: "https://example.com/logo.png",
});
await rc.updateTokenMetadata(wallet, {
symbol: "MTK",
image: "data:image/webp;base64,UklGR...",
description: "A community token",
website: "https://mytoken.io",
});
await rc.burn(wallet, 500);
await rc.createToken(wallet, {
name: "Inflation Token",
symbol: "INFT",
totalSupply: 100_000,
mintable: true,
maxSupply: 1_000_000,
});
await rc.mintTokens(wallet, { symbol: "INFT", amount: 50_000 });
Dynamic Fees (EIP-1559)
const fee = await rc.getFeeInfo();
console.log(`Base fee: ${fee.base_fee} XRGE`);
console.log(`Suggested total: ${fee.total_fee_suggestion} XRGE`);
console.log(`Total burned: ${fee.total_fees_burned} XRGE`);
Finality Proofs
const { proof } = await rc.getFinalityProof(42);
if (proof) {
console.log(`Block ${proof.height} finalized with ${proof.voting_stake}/${proof.total_stake} stake`);
console.log(`${proof.precommit_votes.length} precommit signatures`);
}
WebSocket Subscriptions
const ws = rc.connectWebSocket(["blocks", `account:${wallet.publicKey}`]);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === "new_block") {
console.log(`New block #${data.height}`);
}
};
DEX (rc.dex)
const pools = await rc.dex.getPools();
const pool = await rc.dex.getPool("XRGE-MTK");
const prices = await rc.dex.getPriceHistory("XRGE-MTK");
const stats = await rc.dex.getPoolStats("XRGE-MTK");
const events = await rc.dex.getPoolEvents("XRGE-MTK");
const quote = await rc.dex.quote({
poolId: "XRGE-MTK",
tokenIn: "XRGE",
tokenOut: "MTK",
amountIn: 100,
});
console.log(`You'll receive ${quote.amount_out} MTK`);
await rc.dex.swap(wallet, {
tokenIn: "XRGE",
tokenOut: "MTK",
amountIn: 100,
minAmountOut: quote.amount_out * 0.98,
});
await rc.dex.createPool(wallet, {
tokenA: "XRGE",
tokenB: "MTK",
amountA: 10_000,
amountB: 5_000,
});
await rc.dex.addLiquidity(wallet, {
poolId: "XRGE-MTK",
amountA: 1000,
amountB: 500,
});
await rc.dex.removeLiquidity(wallet, { poolId: "XRGE-MTK", lpAmount: 100 });
NFTs (rc.nft)
RC-721 standard with collections, royalties, freezing, and batch minting.
await rc.nft.createCollection(wallet, {
symbol: "ART",
name: "My Art Collection",
royaltyBps: 500,
maxSupply: 10_000,
});
await rc.nft.mint(wallet, {
collectionId: "abc123",
name: "Piece #1",
metadataUri: "https://example.com/nft/1.json",
attributes: { rarity: "legendary" },
});
await rc.nft.batchMint(wallet, {
collectionId: "abc123",
names: ["#1", "#2", "#3"],
});
await rc.nft.transfer(wallet, {
collectionId: "abc123",
tokenId: 1,
to: buyerPubKey,
salePrice: 100,
});
const myNfts = await rc.nft.getByOwner(wallet.publicKey);
Bridge (rc.bridge)
Bridge assets between Base mainnet and RougeChain L1. Supports ETH ↔ qETH, USDC ↔ qUSDC, and XRGE.
const config = await rc.bridge.getConfig();
await rc.bridge.claim({
evmTxHash: "0x...",
evmAddress: "0x...",
evmSignature: "0x...",
recipientPubkey: wallet.publicKey,
token: "ETH",
});
await rc.bridge.withdraw(wallet, {
amount: 500_000,
evmAddress: "0xYourAddress",
tokenSymbol: "qETH",
});
const pending = await rc.bridge.getWithdrawals();
const xrgeConfig = await rc.bridge.getXrgeConfig();
await rc.bridge.withdrawXrge(wallet, {
amount: 1000,
evmAddress: "0xYourAddress",
});
const xrgePending = await rc.bridge.getXrgeWithdrawals();
Rollup (zk-STARK Batch Proofs)
Submit transfers to the rollup accumulator for batched STARK proving. Transfers are collected into batches of up to 32 and proven with a single zk-STARK proof.
const status = await rc.getRollupStatus();
const result = await rc.submitRollupTransfer({
sender: wallet.publicKey,
receiver: recipientPubKey,
amount: 100,
fee: 1,
});
const batch = await rc.getRollupBatch(1);
Social (rc.social)
On-chain social layer with posts, threaded replies, reposts, likes, follows, and comments. All write operations require a wallet parameter for ML-DSA-65 signed requests.
Posts & Timeline
const { post } = await rc.social.createPost(wallet, "Hello RougeChain!");
await rc.social.createPost(wallet, "Great point!", post.id);
await rc.social.deletePost(wallet, post.id);
const result = await rc.social.getPost(post.id, wallet.publicKey);
const timeline = await rc.social.getGlobalTimeline(50, 0);
const feed = await rc.social.getFollowingFeed(wallet, 50, 0);
const { posts, total } = await rc.social.getUserPosts(userPubKey);
const replies = await rc.social.getPostReplies(post.id);
Likes, Reposts & Follows
await rc.social.toggleLike(wallet, postOrTrackId);
await rc.social.toggleRepost(wallet, post.id);
await rc.social.toggleFollow(wallet, artistPubKey);
const stats = await rc.social.getPostStats(post.id, wallet.publicKey);
const artistStats = await rc.social.getArtistStats(pubkey, wallet.publicKey);
const { comment } = await rc.social.postComment(wallet, trackId, "Fire track!");
const comments = await rc.social.getComments(trackId);
await rc.social.deleteComment(wallet, comment.id);
Play Counts
await rc.social.recordPlay(wallet, trackId);
const trackStats = await rc.social.getTrackStats(trackId, wallet.publicKey);
Mail (rc.mail)
On-chain encrypted email with @rouge.quant / @qwalla.mail addresses. All write operations require a wallet parameter for ML-DSA-65 request signing — requests are authenticated via /api/v2/ endpoints with anti-replay nonce protection.
Name Registry
Register a mail name so other users can send you encrypted email. Third-party apps (QWALLA, qRougee, etc.) should call these on wallet creation.
await rc.messenger.registerWallet(wallet, {
id: wallet.publicKey,
displayName: "Alice",
signingPublicKey: wallet.publicKey,
encryptionPublicKey: encPubKey,
});
await rc.mail.registerName(wallet, "alice", wallet.publicKey);
const resolved = await rc.mail.resolveName("alice");
const name = await rc.mail.reverseLookup(wallet.publicKey);
await rc.mail.releaseName(wallet, "alice");
Sending & Reading Mail
await rc.mail.send(wallet, {
from: wallet.publicKey,
to: recipientPubKey,
subject: "Hello",
body: "This is a test",
encrypted_subject: encryptedSubject,
encrypted_body: encryptedBody,
});
const inbox = await rc.mail.getInbox(wallet);
await rc.mail.move(wallet, messageId, "trash");
await rc.mail.markRead(wallet, messageId);
await rc.mail.delete(wallet, messageId);
Unread Counts
The SDK does not expose a dedicated unread-count endpoint. Derive unread totals client-side from inbox data:
const inbox = await rc.mail.getInbox(wallet);
const unreadMail = inbox.filter((m: any) => {
const label = m.label ?? {};
return !(label.is_read ?? label.isRead ?? true);
}).length;
const convos = await rc.messenger.getConversations(wallet);
const unreadChats = convos.reduce((sum: number, c: any) => {
return sum + (c.unread_count ?? c.unreadCount ?? 0);
}, 0);
Messenger (rc.messenger)
End-to-end encrypted messaging with media and self-destruct support. All operations use ML-DSA-65 signed requests via /api/v2/ endpoints with nonce-based anti-replay protection.
await rc.messenger.registerWallet(wallet, {
id: wallet.publicKey,
displayName: "Alice",
signingPublicKey: wallet.publicKey,
encryptionPublicKey: encPubKey,
});
const result = await rc.messenger.createConversation(wallet, [
wallet.publicKey,
recipientPubKey,
]);
const convos = await rc.messenger.getConversations(wallet);
await rc.messenger.sendMessage(wallet, conversationId, encryptedContent, {
selfDestruct: true,
destructAfterSeconds: 30,
});
const messages = await rc.messenger.getMessages(wallet, conversationId);
await rc.messenger.deleteMessage(wallet, messageId, conversationId);
await rc.messenger.deleteConversation(wallet, conversationId);
Shielded Transactions (rc.shielded)
Private value transfers using zk-STARK proofs. Shield XRGE into private notes, transfer privately, and unshield back to public balance.
import { createShieldedNote, computeCommitment, computeNullifier } from "@rougechain/sdk";
const { note } = await rc.shielded.shield(wallet, { amount: 100 });
const stats = await rc.shielded.getStats();
const { spent } = await rc.shielded.isNullifierSpent(note.nullifier);
await rc.shielded.transfer(wallet, {
nullifiers: [note.nullifier],
outputCommitments: [recipientCommitment],
proof: starkProofHex,
});
await rc.shielded.unshield(wallet, {
nullifiers: [note.nullifier],
amount: 100,
proof: starkProofHex,
});
const randomness = generateRandomness();
const commitment = computeCommitment(100, wallet.publicKey, randomness);
const nullifier = computeNullifier(randomness, commitment);
Low-Level Signing
For advanced use cases:
import { signTransaction, verifyTransaction, generateNonce } from "@rougechain/sdk";
const payload = {
type: "transfer" as const,
from: wallet.publicKey,
to: recipient,
amount: 100,
fee: 1,
token: "XRGE",
timestamp: Date.now(),
nonce: generateNonce(),
};
const signedTx = signTransaction(payload, wallet.privateKey, wallet.publicKey);
const valid = verifyTransaction(signedTx);
Address Resolution
Resolve between compact rouge1… addresses and full hex public keys using the on-chain persistent index.
const { publicKey } = await rc.resolveAddress("rouge1q8f3x7k2m4...");
const { address } = await rc.resolveAddress(hexPubKey);
Push Notifications
Register Expo push tokens for real-time notifications. Requires PQC signature — only the wallet owner can register.
await rc.registerPushToken(wallet, expoPushToken);
await rc.unregisterPushToken(wallet);
Nonce Management
const { nonce, next_nonce } = await rc.getNonce(wallet.publicKey);
Environment Support
| Browser | Works with any bundler (Vite, webpack, etc.) |
| Node.js 18+ | Works out of the box |
| Node.js < 18 | Pass a fetch polyfill: new RougeChain(url, { fetch }) |
| React Native | Install react-native-get-random-values before importing |
TypeScript
Written in TypeScript with full type declarations shipped. All interfaces are exported:
import type {
Block, Transaction, TokenMetadata, NftCollection,
NftToken, LiquidityPool, BalanceResponse, Validator,
BridgeConfig, MailMessage, MessengerMessage, WalletKeys,
PriceSnapshot, PoolEvent, PoolStats, SwapQuote,
ShieldParams, ShieldedTransferParams, UnshieldParams, ShieldedStats,
ShieldedNote,
RollupStatus, RollupBatchResult, RollupSubmitParams, RollupSubmitResult,
FeeInfo, FinalityProof, MintTokenParams, VoteMessage, WsSubscribeMessage,
SocialPost, PostStats, TrackStats, ArtistStats, SocialComment,
} from "@rougechain/sdk";
Security
- Post-quantum cryptography — All signatures use ML-DSA-65 (CRYSTALS-Dilithium), resistant to quantum computer attacks
- Client-side signing — Private keys never leave your application
- No key storage — The SDK does not store or transmit keys
- Signed API requests — All mail, messenger, and name registry operations use ML-DSA-65 signed requests with timestamp validation and nonce-based anti-replay protection
- Multi-recipient CEK encryption — Mail content is encrypted once with a random AES-256 key, KEM-wrapped individually per recipient via ML-KEM-768
- TOFU key verification — Public key fingerprints (SHA-256) are tracked for key change detection
Links
License
MIT © RougeChain