RougeChain Documentation

Welcome to RougeChain — a post-quantum secure Layer 1 blockchain built with real NIST-approved cryptography.

What is RougeChain?

RougeChain is among the first blockchains designed from the ground up to be resistant to quantum computer attacks. It uses:

  • ML-DSA-65 (CRYSTALS-Dilithium) for digital signatures
  • ML-KEM-768 (CRYSTALS-Kyber) for key encapsulation
  • SHA-256 for hashing

All cryptographic primitives are NIST FIPS 204/203 compliant.

Key Features

FeatureDescription
Post-Quantum SecurityProtected against both classical and quantum attacks
Client-Side SigningPrivate keys never leave your browser
AMM/DEXUniswap V2-style liquidity pools and token swaps
Token BurningOfficial burn address with on-chain tracking
Proof of StakeEnergy-efficient consensus with validator staking
P2P NetworkDecentralized peer-to-peer block and transaction propagation
qETH BridgeBridge ETH from Base Sepolia to qETH on RougeChain
Custom TokensCreate your own tokens on the network
RC-721 NFTsNFT collections with royalties, batch minting, and freezing
Encrypted MessengerE2E encrypted messaging with PQC, media support, self-destruct
PQC MailEncrypted email with @rouge.quant addresses and threading
Browser ExtensionsChrome/Firefox wallet extensions with vault lock
PWA SupportInstallable progressive web app for mobile and desktop
Social LayerPosts, timeline, reposts, likes, follows, comments, tips
CLI WalletCommand-line wallet with full chain access and social commands
SDK@rougechain/sdk v1.0.0 npm package for building dApps
EIP-1559 Dynamic FeesBase fee auto-adjusts per block, fee burning for deflationary pressure
Token Mint AuthorityOngoing minting for custom tokens with supply cap enforcement
Validator SlashingSlash penalties for misbehavior, unbonding queue with 500-block delay
BFT Finality ProofsSerializable quorum certificates with ≥2/3 validator stake
WebSocket SubscriptionsTopic-based real-time event streaming (blocks, txs, accounts, tokens)
HD Wallet DerivationBIP-44-like PQC key derivation from master seed (HMAC-SHA256)
Open SourceApache 2.0 licensed node software

Network Info

NetworkAPI Endpoint
Testnethttps://testnet.rougechain.io/api
Devnet (local)http://127.0.0.1:5100/api

Tokens

XRGE

XRGE is the native token that powers transactions, secures the network through staking, and fuels the post-quantum economy.

RoleHow it works
Gas TokenEvery transaction pays fees in XRGE. Fees go to the block proposer.
Staking PrimitiveValidators must stake XRGE to propose blocks. More stake = more proposals = more rewards.
DeFi Base PairAMM liquidity pools trade against XRGE. It's the default quote currency on the built-in DEX.
Bridge AssetXRGE exists on both RougeChain (native) and Base (ERC-20) via the cross-chain bridge.

qETH

qETH is a bridged representation of ETH on RougeChain. It uses 6 decimal places and can be bridged in from Base Sepolia or withdrawn back.

PropertyValue
Decimals6
Bridge SourceBase Sepolia
Bridge ContractConfigured per-node via --bridge-custody-address

Fees

ActionFee
Transfer~0.1 XRGE (base fee, adjusts per block)
Token Creation100 XRGE
Pool Creation10 XRGE
Swap0.3% (to LPs)
Minimum Stake10,000 XRGE
Unbonding Period500 blocks

EIP-1559 Fee Model: The base fee adjusts ±12.5% per block based on transaction volume (target: 10 txs/block). The base fee portion is burned, and only the priority tip goes to validators. Check current fees via GET /api/fee.

Burn Address

Tokens can be permanently burned by sending to the official burn address:

XRGE_BURN_0x000000000000000000000000000000000000000000000000000000000000DEAD

Burned tokens are tracked on-chain and can be queried via the /api/burned endpoint.

Security

Client-Side Signing

RougeChain uses a secure v2 API where all transactions are signed client-side:

  1. Your wallet creates a transaction payload
  2. The payload is signed locally using ML-DSA-65
  3. Only the signature and public key are sent to the server
  4. Your private key never leaves your browser

This ensures maximum security even when interacting with untrusted nodes.

Getting Started

This guide will help you get up and running with RougeChain in minutes.

Prerequisites

  • A modern web browser (Chrome, Firefox, Edge, Safari)
  • For running a node: Rust toolchain (1.70+)

Quickest Path

  1. Visit rougechain.io
  2. Click "Wallet" in the sidebar
  3. Your wallet is automatically created and stored locally
  4. Use the faucet to get free testnet XRGE

That's it! You now have a quantum-secure wallet.

What's Next?

Quick Start

Get started with RougeChain in 5 minutes.

Step 1: Access the Web App

Visit rougechain.io or run locally:

git clone https://github.com/cyberdreadx/rougechain-node
cd rougechain-node
npm install
npm run dev

Open http://localhost:5173 in your browser.

Step 2: Create Your Wallet

Your wallet is created automatically when you first visit. It includes:

  • Address (rouge1...) — Your compact Bech32m address, share freely
  • Private Key (ML-DSA-65) — Never share this!
  • Encryption Key (ML-KEM-768) — For secure messaging

Keys are stored locally in your browser.

Step 3: Get Test Tokens

  1. Click Wallet in the sidebar
  2. Click Request from Faucet
  3. Receive 1,000 XRGE instantly

Step 4: Send Your First Transaction

  1. Click Send
  2. Enter recipient address
  3. Enter amount
  4. Click Send XRGE

Transaction is signed with your ML-DSA-65 key and broadcast to the network.

Step 5: View on Blockchain

  1. Click Blockchain in sidebar
  2. See your transaction in the latest block
  3. Verify the PQC signature

What's Next?

Troubleshooting

"Failed to fetch" Error

  • Check if you're connected to the right network (Testnet vs Devnet)
  • Ensure the node is running if using local devnet

"Insufficient balance"

  • Transaction requires amount + 0.1 XRGE fee
  • Use faucet to get more tokens

Wallet not loading

  • Clear browser cache
  • Check browser console for errors

Create a Wallet

Your RougeChain wallet is automatically created when you first visit the app. Here's what you need to know about it.

Wallet Components

ComponentAlgorithmPurpose
Signing KeyML-DSA-65Sign transactions, prove ownership
Encryption KeyML-KEM-768Encrypt/decrypt messages

Your Address

RougeChain uses compact Bech32m addresses with the rouge1 prefix, derived from your ML-DSA-65 public key:

address = bech32m("rouge", SHA-256(signing_public_key))

Example address:

rouge1q8f3x7k2m4n9pvj5dz6ywl2cg8hs0kw9...

Addresses are ~63 characters — much shorter than the raw 3,904-char hex public key. The wallet, extension, and explorer all display this format.

Note: API endpoints still use the raw hex public key internally. The rouge1 address is for display, sharing, and QR codes.

Backup Your Wallet

CRITICAL: Your private key is only stored locally. If you clear browser data or lose your device, you lose access to your funds.

Encrypted Backup (.pqcbackup)

All RougeChain wallets use password-protected encrypted backups. Plaintext key exports are disabled across all platforms.

How to Export

PlatformSteps
Web (rougechain.io)Wallet → Settings → Backup → Enter password → Download .pqcbackup file
Browser Extension (qRougee)Settings → Export Encrypted Backup → Enter password → Download
Mobile (Qwalla)Settings → Export Encrypted Backup → Enter password → Share/Save

How to Restore

  1. Go to Wallet page (or Settings in the extension)
  2. Click Restore or Import Wallet
  3. Select your .pqcbackup file
  4. Enter the password you used when exporting
  5. Your wallet keys are decrypted and restored

Seed Phrase (Mnemonic)

Wallets also support 24-word BIP-39 mnemonic backup (256-bit entropy for post-quantum security):

  1. Go to SettingsReveal Seed Phrase
  2. Write down all 24 words in order
  3. Store securely — anyone with your seed phrase has full access to your wallet

Tip: Use the seed phrase as your primary backup method. The .pqcbackup file is best for transferring between devices.

.pqcbackup File Format

The encrypted backup file uses industry-standard cryptography:

ComponentAlgorithmDetails
Key DerivationPBKDF2-SHA256600,000 iterations
EncryptionAES-256-GCM256-bit key, 96-bit IV
SaltRandom16 bytes per export
IVRandom12 bytes per export

File Structure

{
  "version": 1,
  "salt": "<hex-encoded 16-byte random salt>",
  "iv": "<hex-encoded 12-byte random IV>",
  "ciphertext": "<hex-encoded AES-256-GCM encrypted wallet data>",
  "algorithm": "PBKDF2-SHA256-AES-256-GCM"
}

How It Works

  1. Your password is stretched with PBKDF2-SHA256 (600,000 rounds) using a random salt
  2. The derived 256-bit key encrypts your wallet data with AES-256-GCM
  3. The salt, IV, and ciphertext are bundled into a .pqcbackup JSON file
  4. Without the correct password, the file cannot be decrypted

Warning: There is no password recovery. If you forget your backup password, use your seed phrase to restore instead.

Security Best Practices

  1. Never share your private key or seed phrase
  2. Backup your wallet immediately after creation
  3. Use a strong, unique password for your .pqcbackup file
  4. Store backups in multiple locations (password manager, USB, etc.)
  5. Never screenshot your seed phrase or keys
  6. Verify addresses before sending

Technical Details

Key Generation

// 24-word mnemonic (256-bit entropy — post-quantum safe)
const mnemonic = bip39.generateMnemonic(256);

// Derive 32-byte seed via HKDF-SHA256
const seed = hkdf(mnemonicToSeed(mnemonic), "rougechain-pqc-v1");

// Signing keypair (ML-DSA-65 / FIPS 204)
const signingKeypair = ml_dsa65.keygen(seed);

// Encryption keypair (ML-KEM-768 / FIPS 203)
const encryptionKeypair = ml_kem768.keygen(seed);

Key Storage

PlatformStorage
Web (rougechain.io)localStorage → encrypted via vault password
Browser Extension (qRougee)chrome.storage.local → AES-256-GCM vault
Mobile (Qwalla)expo-secure-store → device keychain

Get Test Tokens

The faucet distributes free XRGE tokens for testing on testnet.

Using the Web UI

  1. Go to the Wallet page
  2. Click Request from Faucet
  3. Receive 1,000 XRGE instantly

Using the API

curl -X POST https://testnet.rougechain.io/api/faucet \
  -H "Content-Type: application/json" \
  -d '{"publicKey": "your-public-key-here"}'

Response:

{
  "success": true,
  "amount": 1000,
  "txId": "abc123..."
}

Rate Limits

ConditionLimit
Per address1 request / hour
Per IP10 requests / hour
WhitelistedUnlimited

Whitelisting

For development or testing, addresses can be whitelisted:

# Start node with whitelist
./quantum-vault-daemon --mine \
  --faucet-whitelist "pubkey1,pubkey2,pubkey3"

# Or via environment variable
export QV_FAUCET_WHITELIST="pubkey1,pubkey2"
./quantum-vault-daemon --mine

Troubleshooting

"Rate limited"

Wait an hour or use a different address.

"Faucet disabled"

The node may not have faucet enabled. Check node configuration.

Transaction not appearing

  1. Check the block explorer for your tx
  2. Verify you're on the correct network
  3. Wait for the next block (1-2 seconds)

Running a Node

Run your own RougeChain node to participate in the network, validate transactions, and earn rewards.

The node software is open source under the Apache 2.0 license.

Node Types

TypeDescriptionRequires
Full NodeSyncs and validates all blocks--peers
Mining NodeProduces new blocks--mine
Public NodeAccepts external connections--host 0.0.0.0

Quick Start

# Clone the public repo
git clone https://github.com/cyberdreadx/rougechain-node
cd rougechain-node/core

# Build the daemon
cargo build --release -p quantum-vault-daemon

# Run a syncing node
./target/release/quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api"

# Run a mining node
./target/release/quantum-vault-daemon \
  --mine \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api"

Verify It's Working

curl http://127.0.0.1:5100/api/health

Expected response:

{
  "status": "ok",
  "chain_id": "rougechain-devnet-1",
  "height": 123
}

Next Steps

Installation

System Requirements

ResourceMinimumRecommended
CPU1 vCPU2+ vCPU
RAM512 MB1–2 GB
Disk5 GB SSD20 GB SSD
Network10 Mbps100 Mbps
OSLinux (Ubuntu 22.04+, Debian 12), macOS, WindowsUbuntu 22.04 LTS

The daemon is a single Rust binary with low overhead. A $5/month VPS (Hetzner CX22, DigitalOcean Basic Droplet, Vultr Cloud Compute) is more than enough for testnet.


Docker (Fastest)

Run a node with a single command — no Rust toolchain needed:

docker run -d \
  --name rougechain-node \
  -p 5100:5100 \
  -v qv-data:/data \
  rougechain/node \
  --mine --peers https://testnet.rougechain.io/api

Verify it's running:

curl http://127.0.0.1:5100/api/health

docker-compose

For a persistent setup, create a .env file:

API_PORT=5100
QV_PEERS=https://testnet.rougechain.io/api
CHAIN_ID=rougechain-devnet-1

Then start:

git clone https://github.com/cyberdreadx/rougechain-node
cd rougechain-node
docker compose up -d

View logs:

docker compose logs -f node

Build the image locally

git clone https://github.com/cyberdreadx/rougechain-node
cd rougechain-node
docker build -t rougechain/node .

Build from Source

Install and build manually if you prefer not to use Docker.

Prerequisites

Linux / macOS

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

# Install build dependencies (Ubuntu/Debian)
sudo apt update && sudo apt install -y build-essential pkg-config libssl-dev

# Install build dependencies (macOS)
xcode-select --install

Windows

  1. Install Rust
  2. Install Visual Studio Build Tools
  3. Ensure cargo is in your PATH

Compile

# Clone the repository
git clone https://github.com/cyberdreadx/rougechain-node
cd rougechain-node/core

# Build release binary
cargo build --release -p quantum-vault-daemon

# Binary location
./target/release/quantum-vault-daemon --help

Verify Installation

./target/release/quantum-vault-daemon --version

Expected output:

quantum-vault-daemon 0.1.0

First Run

Option A: Connect to Testnet

./target/release/quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api"

Option B: Start Local Devnet

./target/release/quantum-vault-daemon --mine --api-port 5100

Verify Node is Running

curl http://127.0.0.1:5100/api/health

Expected response:

{"status":"ok","chain_id":"rougechain-devnet-1","height":0}

Directory Structure

After first run, data is stored at:

OSDefault Location
Linux/macOS~/.quantum-vault/core-node/
WindowsC:\Users\<you>\.quantum-vault\core-node\

Updating

cd rougechain-node
git pull
cd core
cargo build --release -p quantum-vault-daemon

# Restart the node

Troubleshooting

"cargo not found"

source ~/.cargo/env
# or restart your terminal

"OpenSSL not found"

# Ubuntu/Debian
sudo apt install libssl-dev pkg-config

# Fedora/RHEL
sudo dnf install openssl-devel

# macOS
brew install openssl

"Address already in use"

Another process is using port 5100:

# Find and kill it
lsof -i :5100
kill -9 <PID>

# Or use a different port
./quantum-vault-daemon --api-port 5101

Build fails on Windows

Ensure you have Visual Studio Build Tools with C++ workload installed.


Environment Variables

All CLI flags can also be set via environment variables:

VariableCLI FlagDefaultDescription
QV_PEERS--peersComma-separated peer URLs
QV_PUBLIC_URL--public-urlThis node's public URL for peer discovery
QV_CORS_ORIGINSlocalhost onlyComma-separated allowed CORS origins
QV_API_KEYS--api-keysComma-separated API keys for authenticated access
QV_BRIDGE_CUSTODY_ADDRESS--bridge-custody-addressEVM custody address (enables bridge)
QV_BASE_SEPOLIA_RPC--base-sepolia-rpchttps://sepolia.base.orgBase Sepolia RPC URL

Common CLI-only flags:

FlagDefaultDescription
--host127.0.0.1Bind address. Use 0.0.0.0 for public nodes
--api-port5101REST API port
--mineoffEnable block production
--data-dir~/.quantum-vault/core-node/Chain data directory
--chain-idrougechain-devnet-1Network chain ID
--block-time-ms400Target block time in milliseconds

Running a Node on Windows

A step-by-step guide for building and running a RougeChain node on Windows.

Prerequisites

1. Install Rust

Download and run the installer from rustup.rs. Use the default options.

After installation, open a new PowerShell window and verify:

cargo --version
rustc --version

2. Install Visual Studio Build Tools

Download Visual Studio Build Tools and install the "Desktop development with C++" workload.

This provides the MSVC compiler and Windows SDK needed to build native Rust projects.

3. Install Git (if needed)

Download from git-scm.com or install via winget:

winget install Git.Git

Build the Node

Open PowerShell and run:

git clone https://github.com/cyberdreadx/rougechain-node.git
cd rougechain-node\core
cargo build --release -p quantum-vault-daemon

The first build takes 5-10 minutes. The binary will be at:

target\release\quantum-vault-daemon.exe

Run the Node

Connect to Testnet

.\target\release\quantum-vault-daemon.exe --api-port 5100 --peers "https://testnet.rougechain.io/api"

Run a Mining Node

.\target\release\quantum-vault-daemon.exe --mine --api-port 5100 --peers "https://testnet.rougechain.io/api"

Name Your Node

.\target\release\quantum-vault-daemon.exe --api-port 5100 --node-name "MyWindowsNode" --peers "https://testnet.rougechain.io/api"

Verify It's Working

In a separate PowerShell window:

Invoke-RestMethod http://127.0.0.1:5100/api/health

Expected output:

{
  "status": "ok",
  "chain_id": "rougechain-devnet-1",
  "height": 123
}

Visit http://localhost:5100 in your browser to see the built-in node dashboard.


Run as a Background Service

Option A: Task Scheduler

  1. Open Task Scheduler → Create Task
  2. General: Name it "RougeChain Node", check "Run whether user is logged on or not"
  3. Trigger: At startup
  4. Action: Start a program
    • Program: C:\path\to\quantum-vault-daemon.exe
    • Arguments: --mine --api-port 5100 --peers "https://testnet.rougechain.io/api"
    • Start in: C:\path\to\rougechain-node\core
  5. Settings: Check "Restart if task fails", set to every 1 minute

Option B: NSSM (Non-Sucking Service Manager)

# Install NSSM
winget install NSSM.NSSM

# Create the service
nssm install rougechain "C:\path\to\quantum-vault-daemon.exe"
nssm set rougechain AppParameters "--mine --api-port 5100 --peers ""https://testnet.rougechain.io/api"""
nssm set rougechain AppDirectory "C:\path\to\rougechain-node\core"

# Start
nssm start rougechain

Windows Firewall

If you want your node to be publicly accessible:

# Allow inbound connections to port 5100
New-NetFirewallRule -DisplayName "RougeChain Node" -Direction Inbound -Protocol TCP -LocalPort 5100 -Action Allow

Docker Alternative

If you prefer Docker, install Docker Desktop for Windows and run:

docker run -d --name rougechain-node -p 5100:5100 -v qv-data:/data rougechain/node --mine --peers https://testnet.rougechain.io/api

Troubleshooting

"cargo not found"

Close and reopen PowerShell. Rust adds itself to PATH but it takes a new session to pick up.

Build errors about MSVC

Make sure Visual Studio Build Tools is installed with the C++ desktop workload. Restart your terminal after installing.

"Access denied" or antivirus blocking

  • Run PowerShell as Administrator
  • Add an antivirus exclusion for the rougechain-node directory
  • Windows Defender may flag the newly built binary — allow it through

Node doesn't respond

  • Check Windows Firewall isn't blocking the port
  • Make sure you're using http://127.0.0.1:5100 (not localhost, which may resolve to IPv6)

Updating

cd rougechain-node
git pull
cd core
cargo build --release -p quantum-vault-daemon
# Restart the node

Docker

Run a RougeChain node without installing Rust or any build dependencies.

Requirements

Any VPS or machine with Docker installed. Minimum specs:

  • 1 vCPU, 512 MB RAM, 5 GB SSD, 10 Mbps
  • Recommended: 2 vCPU, 1–2 GB RAM, 20 GB SSD
  • A $5/month VPS is enough for testnet

Quick Start

docker run -d \
  --name rougechain-node \
  -p 5100:5100 \
  -v qv-data:/data \
  rougechain/node \
  --mine --peers https://testnet.rougechain.io/api

Your node will:

  • Sync with the testnet
  • Produce blocks (--mine)
  • Persist chain data to the qv-data Docker volume
  • Serve the REST API on port 5100

Verify:

curl http://127.0.0.1:5100/api/stats | python3 -m json.tool

docker-compose

For a persistent production setup, clone the repo and use docker-compose:

git clone https://github.com/cyberdreadx/rougechain-node
cd rougechain-node

Optionally create a .env file to override defaults:

API_PORT=5100
QV_PEERS=https://testnet.rougechain.io/api
QV_CORS_ORIGINS=https://yourdapp.com,https://rougechain.io
CHAIN_ID=rougechain-devnet-1

Start:

docker compose up -d

View logs:

docker compose logs -f node

Stop:

docker compose down

Building the Image

Build locally instead of pulling from the registry:

docker build -t rougechain/node .

The Dockerfile uses a multi-stage build:

  1. Builder stage — compiles the Rust daemon in a full Rust image
  2. Runtime stage — copies only the binary into a minimal Debian image (~50 MB)

Data Persistence

Chain data is stored at /data inside the container. Mount a volume to keep it across restarts:

# Named volume (recommended)
-v qv-data:/data

# Host directory
-v /srv/rougechain-data:/data

Data includes:

  • Block database
  • Validator state
  • Pool/DEX state
  • NFT collections
  • Node keys (node-keys.json)

Custom Configuration

Pass CLI flags after the image name:

docker run -d \
  -p 5100:5100 \
  -v qv-data:/data \
  rougechain/node \
  --mine \
  --peers https://testnet.rougechain.io/api \
  --chain-id rougechain-devnet-1 \
  --block-time-ms 400

Set CORS origins via environment variable:

docker run -d \
  -p 5100:5100 \
  -v qv-data:/data \
  -e QV_CORS_ORIGINS="https://yourdapp.com" \
  rougechain/node \
  --mine --peers https://testnet.rougechain.io/api

Becoming a Validator

Once your Docker node is running and synced:

  1. Open the Validators page in your browser
  2. Connect your wallet
  3. Stake XRGE to register as a validator
  4. Your node will begin participating in block production

Your node earns:

  • 20% of priority tips when selected as block proposer
  • A share of 70% of priority tips, weighted by your stake
  • A minimum tip floor of 0.1 XRGE/block is guaranteed from staking reserves

Health Checks

# Node health
curl http://127.0.0.1:5100/api/health

# Network stats (peers, height, mining status)
curl http://127.0.0.1:5100/api/stats

# Validator list
curl http://127.0.0.1:5100/api/validators

Updating

cd rougechain-node
git pull
docker compose build
docker compose up -d

Or if using docker run:

docker build -t rougechain/node .
docker stop rougechain-node
docker rm rougechain-node
docker run -d --name rougechain-node -p 5100:5100 -v qv-data:/data rougechain/node --mine --peers https://testnet.rougechain.io/api

The qv-data volume persists across container rebuilds, so your chain data is preserved.

Configuration

All node configuration is done via command-line flags or environment variables.

CLI Options

FlagEnv VariableDefaultDescription
--host-127.0.0.1Bind address for API/gRPC. Use 0.0.0.0 for public nodes
--port-4101gRPC port
--api-port-5101HTTP API port
--chain-id-rougechain-devnet-1Chain identifier
--block-time-ms-400Block production interval (ms)
--mine-falseEnable block production
--node-nameQV_NODE_NAME-Human-readable name shown on the network globe
--data-dir-~/.quantum-vault/core-nodeData storage directory
--peersQV_PEERS-Comma-separated peer URLs
--public-urlQV_PUBLIC_URL-This node's public URL for peer discovery
--api-keysQV_API_KEYS-Comma-separated API keys
--rate-limit-per-minute-0 (unlimited)Rate limit for API requests

Examples

Local Development Node

./quantum-vault-daemon --mine --api-port 5100

Syncing Node (No Mining)

./quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api"

Public Mining Node

./quantum-vault-daemon \
  --mine \
  --host 0.0.0.0 \
  --api-port 5100 \
  --node-name "MyNode" \
  --peers "https://testnet.rougechain.io/api" \
  --public-url "https://mynode.example.com"

Once running, visit http://localhost:5100 in your browser to see the built-in node dashboard with live stats, peer list, and block height.

Multiple Peers

./quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://node1.example.com,https://node2.example.com,https://node3.example.com"

Custom Data Directory

./quantum-vault-daemon \
  --mine \
  --api-port 5100 \
  --data-dir "/var/lib/rougechain"

Environment Variables

You can also use environment variables:

export QV_PEERS="https://testnet.rougechain.io/api"
export QV_PUBLIC_URL="https://mynode.example.com"
export QV_NODE_NAME="MyNode"
export QV_API_KEYS="key1,key2,key3"

./quantum-vault-daemon --mine --api-port 5100

Data Directory Structure

~/.quantum-vault/core-node/
├── chain.jsonl          # Block data (append-only)
├── tip.json             # Current chain tip
├── validators-db/       # Validator state (RocksDB)
└── messenger-db/        # Messenger data (RocksDB)

Running with Systemd (Linux)

Create /etc/systemd/system/rougechain.service:

[Unit]
Description=RougeChain Node
After=network.target

[Service]
Type=simple
User=rougechain
ExecStart=/opt/rougechain/quantum-vault-daemon --mine --host 0.0.0.0 --api-port 5100 --node-name "MyNode" --public-url "https://mynode.example.com" --peers "https://testnet.rougechain.io/api"
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl enable rougechain
sudo systemctl start rougechain
sudo journalctl -u rougechain -f  # View logs

Mining (Block Production)

RougeChain uses Proof of Stake, so "mining" refers to block production by validators rather than proof-of-work mining.

Enable Mining

Start your node with the --mine flag:

./quantum-vault-daemon --mine --api-port 5100

This tells the node to produce blocks at the configured interval (default: 400ms).

Requirements

RequirementValue
Staked XRGEMinimum 1,000 XRGE
Node uptimeMust be online to produce blocks
Network syncNode must be synced to chain tip

How Block Production Works

  1. Validator selection — Each block slot, the network selects a proposer based on stake weight and quantum entropy sourced from ANU QRNG (with local CSPRNG fallback)
  2. Block assembly — The selected validator collects pending transactions from the mempool
  3. Signing — The block is signed with the validator's ML-DSA-65 key
  4. Voting — Validators automatically submit prevote and precommit attestations for each block
  5. Propagation — The signed block is broadcast to all peers via POST /api/blocks/import
  6. Verification — Receiving nodes verify the signature and block validity before accepting

Block Timing

ParameterDefaultFlag
Block time400ms--block-time-ms
# Slower blocks (5 seconds)
./quantum-vault-daemon --mine --block-time-ms 5000

# Faster blocks (500ms, for local dev)
./quantum-vault-daemon --mine --block-time-ms 500

Mining with Peers

To mine as part of the network (not solo):

./quantum-vault-daemon \
  --mine \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api" \
  --public-url "https://mynode.example.com"

The --public-url flag is important — it lets other nodes discover and sync from you.

Monitoring

Check your validator status:

curl https://testnet.rougechain.io/api/validators

Check blocks produced:

curl https://testnet.rougechain.io/api/blocks?limit=10

Rewards

Validators earn transaction fees from blocks they produce. Fees are credited immediately when a block is finalized. See Staking Rewards for details.

Troubleshooting

Node not producing blocks

  • Ensure --mine flag is set
  • Verify you have enough XRGE staked (min 1,000)
  • Check that your node is synced: curl http://127.0.0.1:5100/api/health

Blocks not propagating

  • Ensure --public-url is set and accessible from the internet
  • Check firewall rules for your API port
  • Verify peer connections: curl http://127.0.0.1:5100/api/peers

Producing blocks but no rewards

  • Confirm your staking transaction was included in a block
  • Check validator status via the API

P2P Networking

RougeChain uses a peer-to-peer network for block propagation, transaction broadcasting, and peer discovery.

How It Works

┌─────────────┐     blocks/txs      ┌─────────────┐
│   Node A    │ ◄─────────────────► │   Node B    │
│  (mining)   │                     │  (syncing)  │
└─────────────┘                     └─────────────┘
       ▲                                   ▲
       │           peer discovery          │
       └───────────────┬───────────────────┘
                       │
                       ▼
               ┌─────────────┐
               │   Node C    │
               │ (new peer)  │
               └─────────────┘

Features

FeatureDescription
Block SyncNodes download and verify blocks from peers
Transaction BroadcastSubmitted txs propagate to all peers
Peer DiscoveryNodes share their peer lists with each other
Genesis ResetNew nodes automatically adopt the network's chain

Connecting to the Network

As a Syncing Node

./quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api"

As a Mining Node

./quantum-vault-daemon \
  --mine \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api"

Peer Discovery

Nodes automatically discover new peers every 30 seconds by:

  1. Querying known peers via GET /api/peers
  2. Adding any new peers to their list
  3. Optionally registering themselves via POST /api/peers/register

Enable Self-Registration

./quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api" \
  --public-url "https://mynode.example.com"

This tells other nodes how to reach you.

API Endpoints

EndpointMethodDescription
/api/peersGETList known peers
/api/peers/registerPOSTRegister as a peer
/api/blocks/importPOSTImport a block (peer-to-peer)
/api/tx/broadcastPOSTReceive a broadcasted transaction

Next Steps

Connecting to Peers

Connect your RougeChain node to the network to sync blocks and broadcast transactions.

Connect to Testnet

The simplest way to join the network:

./quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api"

Your node will:

  1. Download all blocks from the peer
  2. Verify each block's PQC signatures
  3. Build the local chain state
  4. Start receiving new blocks in real-time

⚠️ Important: Your node is syncing but INVISIBLE to the network.

Without --public-url, your node pulls blocks from peers but never registers itself. Other nodes won't know it exists, and it won't appear on the network globe.

To be discoverable, add --public-url with your node's reachable address:

./quantum-vault-daemon \
  --api-port 5100 \
  --node-name "MyNode" \
  --public-url "https://your-server.com:5100" \
  --peers "https://testnet.rougechain.io/api"

If running locally without a public IP, your node works fine for personal use — it just won't be visible to the rest of the network.

Connect to Multiple Peers

For better reliability, connect to multiple peers:

./quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io,https://node2.example.com,https://node3.example.com"

Or via environment variable:

export QV_PEERS="https://testnet.rougechain.io,https://node2.example.com"
./quantum-vault-daemon --api-port 5100

Verify Connection

Check that your node has peers:

curl http://127.0.0.1:5100/api/peers

Expected response:

{
  "peers": [
    "https://testnet.rougechain.io/api"
  ],
  "count": 1
}

Sync Status

Check if your node is synced:

curl http://127.0.0.1:5100/api/health
{
  "status": "ok",
  "chain_id": "rougechain-devnet-1",
  "height": 12345
}

Compare the height with the testnet to confirm you're in sync:

curl https://testnet.rougechain.io/api/health

Connection Flow

Your Node                          Peer Node
    │                                  │
    │── GET /api/health ──────────────►│
    │◄─── { height: 12345 } ──────────│
    │                                  │
    │── GET /api/blocks?from=0 ───────►│
    │◄─── [ block0, block1, ... ] ────│
    │                                  │
    │   (verify signatures, apply)     │
    │                                  │
    │── GET /api/peers ───────────────►│
    │◄─── { peers: [...] } ───────────│
    │                                  │
    │   (discover new peers)           │

Firewall Configuration

If running behind a firewall, ensure the API port is accessible:

PortProtocolPurpose
5100 (default)TCP/HTTPREST API and P2P
# Linux (ufw)
sudo ufw allow 5100/tcp

# Linux (firewalld)
sudo firewall-cmd --add-port=5100/tcp --permanent
sudo firewall-cmd --reload

Troubleshooting

"Connection refused"

  • Check that the peer URL is correct and reachable
  • Verify the peer node is running
  • Check for firewall restrictions

Node stuck syncing

  • The initial sync may take time on long chains
  • Monitor progress by watching the height increase via /api/health
  • Try connecting to a different peer

"Chain ID mismatch"

  • Ensure your --chain-id matches the network you're connecting to
  • Testnet uses rougechain-devnet-1

Peer Discovery

RougeChain nodes automatically discover other nodes on the network through a gossip-based peer exchange protocol.

How Discovery Works

1. Node A starts with seed peer(s) via --peers
2. Node A queries GET /api/peers on each known peer
3. Each peer returns its own list of known peers
4. Node A adds any new peers to its local list
5. If --public-url is set, Node A registers itself on peers
6. Repeat every 30 seconds

Over time, all nodes in the network discover each other, forming a mesh.

Discovery Interval

Peer discovery runs automatically every 30 seconds. No configuration is needed.

Seed Peers

The first peer(s) you connect to act as seeds for discovery. From them, your node learns about the rest of the network.

# Single seed
./quantum-vault-daemon --peers "https://testnet.rougechain.io/api"

# Multiple seeds for redundancy
./quantum-vault-daemon --peers "https://testnet.rougechain.io,https://backup.example.com"

Self-Registration

To make your node discoverable by others, set --public-url:

./quantum-vault-daemon \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api" \
  --public-url "https://mynode.example.com"

Your node will call POST /api/peers/register on all known peers, announcing its presence. Other nodes will then include your URL in their peer lists.

Discovery API

Get Known Peers

curl http://127.0.0.1:5100/api/peers
{
  "peers": [
    "https://testnet.rougechain.io/api",
    "https://node2.example.com/api",
    "https://node3.example.com/api"
  ],
  "count": 3
}

Register as a Peer

curl -X POST https://testnet.rougechain.io/api/peers/register \
  -H "Content-Type: application/json" \
  -d '{"peerUrl": "https://mynode.example.com"}'

Discovery Diagram

         Seed Node
        ┌─────────┐
        │  Node A  │ ◄── your --peers target
        └────┬────┘
             │
     GET /api/peers
             │
    ┌────────┼────────┐
    ▼        ▼        ▼
┌───────┐┌───────┐┌───────┐
│Node B ││Node C ││Node D │  ◄── discovered automatically
└───────┘└───────┘└───────┘
    │         │        │
    └─────────┼────────┘
              │
    More peers discovered
       from B, C, D...

Privacy Considerations

  • Your node's IP/URL is shared with all peers when using --public-url
  • Without --public-url, your node connects outward but is not discoverable by others
  • Consider using a reverse proxy or domain name instead of exposing raw IP addresses

Running a Public Node

Run a RougeChain node that is publicly accessible and participates fully in the network.

Requirements

RequirementDetails
ServerVPS or dedicated server with a public IP
Domain (recommended)Point a domain to your server
SSL certificateRequired for HTTPS (use Let's Encrypt)
Open portAPI port accessible from the internet

Setup

1. Build and Install

git clone https://github.com/cyberdreadx/rougechain-node
cd rougechain-node/core
cargo build --release -p quantum-vault-daemon
sudo cp target/release/quantum-vault-daemon /usr/local/bin/

2. Configure Reverse Proxy (nginx)

server {
    listen 443 ssl;
    server_name mynode.rougechain.example.com;

    ssl_certificate /etc/letsencrypt/live/mynode.rougechain.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mynode.rougechain.example.com/privkey.pem;

    client_max_body_size 50M;

    location / {
        proxy_pass http://127.0.0.1:5100;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Set client_max_body_size to at least 50M to support messenger media uploads.

3. Start the Node

./quantum-vault-daemon \
  --mine \
  --host 127.0.0.1 \
  --api-port 5100 \
  --node-name "MyNode" \
  --peers "https://testnet.rougechain.io/api" \
  --public-url "https://mynode.rougechain.example.com"

Why --public-url is required: Without it, your node syncs blocks but never tells other nodes "I exist." It stays invisible — it won't appear on the network globe, other nodes can't sync from it, and it won't receive direct block broadcasts. The --public-url must be the URL that other nodes on the internet can reach.

Once running, visit http://127.0.0.1:5100 in your browser to see the built-in node dashboard with live block height, peers, mining status, and fees.

4. Run as a Service

Create /etc/systemd/system/rougechain.service:

[Unit]
Description=RougeChain Node
After=network.target

[Service]
Type=simple
User=rougechain
ExecStart=/usr/local/bin/quantum-vault-daemon --mine --host 127.0.0.1 --api-port 5100 --peers "https://testnet.rougechain.io/api" --public-url "https://mynode.rougechain.example.com"
Restart=always
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
sudo useradd -r -s /bin/false rougechain
sudo systemctl enable rougechain
sudo systemctl start rougechain

Verify Your Node

Health Check

curl https://mynode.rougechain.example.com/api/health

Check Peers Can See You

curl https://testnet.rougechain.io/api/peers
# Your node's URL should appear in the list

Security Hardening

API Keys

Restrict write access with API keys:

./quantum-vault-daemon \
  --mine \
  --api-port 5100 \
  --api-keys "secret-key-1,secret-key-2"

Rate Limiting

The node has built-in rate limiting (disabled by default). Enable it for public-facing nodes:

./quantum-vault-daemon --rate-limit-per-minute 60

Firewall

Only expose the necessary port:

sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 443/tcp
sudo ufw enable

Monitoring

Check Logs

sudo journalctl -u rougechain -f

Health Endpoint

Set up a monitoring tool (e.g., UptimeRobot) to poll:

https://mynode.rougechain.example.com/api/health

Metrics to Watch

MetricHow to Check
Block heightGET /api/health — compare with testnet
Peer countGET /api/peers — should be > 0
Validator statusGET /api/validators — check your stake

Staking & Validators

RougeChain uses Proof of Stake (PoS) for consensus. Validators stake XRGE tokens to participate in block production and earn rewards.

How It Works

  1. Stake tokens - Lock XRGE to become a validator
  2. Propose blocks - Selected validators propose new blocks
  3. Earn rewards - Collect transaction fees from blocks you produce
  4. Unstake - Wait for unbonding period to withdraw

Requirements

RequirementValue
Minimum stake10,000 XRGE
Unbonding period~7 days
SlashingNot implemented (testnet)

Become a Validator

Via Web UI

  1. Go to the Validators page
  2. Click Stake
  3. Enter amount (min 10,000 XRGE)
  4. Confirm transaction
import { RougeChain, Wallet } from '@rougechain/sdk';

const rc = new RougeChain('https://testnet.rougechain.io/api');
const wallet = Wallet.fromKeys(publicKey, privateKey);

await rc.stake(wallet, { amount: 10000 });

Via API (v2 Signed Request)

All write operations require ML-DSA-65 client-side signing. See Staking API for the full request format.

# The payload must be signed client-side with your ML-DSA-65 private key.
# Use the SDK or build the signed request manually:
curl -X POST https://testnet.rougechain.io/api/v2/stake \
  -H "Content-Type: application/json" \
  -d '{
    "payload": {
      "amount": 10000,
      "from": "your-public-key-hex",
      "timestamp": 1706745600000,
      "nonce": "random-hex"
    },
    "signature": "ml-dsa65-signature-hex",
    "public_key": "your-public-key-hex"
  }'

Check Your Stake

curl "https://testnet.rougechain.io/api/validators?publicKey=your-public-key"

Response:

{
  "validators": [
    {
      "publicKey": "your-public-key",
      "stake": 10000.0,
      "status": "active",
      "blocksProposed": 42
    }
  ]
}

Unstake

Via SDK

await rc.unstake(wallet, { amount: 5000 });

Via API (v2 Signed Request)

curl -X POST https://testnet.rougechain.io/api/v2/unstake \
  -H "Content-Type: application/json" \
  -d '{
    "payload": {
      "amount": 5000,
      "from": "your-public-key-hex",
      "timestamp": 1706745600000,
      "nonce": "random-hex"
    },
    "signature": "ml-dsa65-signature-hex",
    "public_key": "your-public-key-hex"
  }'

Validator Selection

Block proposers are selected using:

  1. Stake weight - Higher stake = higher probability
  2. Quantum entropy - Unpredictable randomness
  3. Round-robin fallback - Ensures all validators participate

Rewards

Validators earn from an EIP-1559-inspired fee model:

ComponentDistribution
Base fee50% burned, 50% to block proposer
Priority fee (tip)100% to block proposer
Minimum tip0.1 XRGE per block

Fees are credited immediately when a block is finalized. See Validator Economics for detailed reward calculations.

PQC Security

All validator operations use ML-DSA-65 signatures:

  • Block proposals are signed
  • Stake/unstake transactions are signed
  • Signatures are verified by all nodes

This ensures quantum-resistant security for the entire consensus process.

Becoming a Validator

Validators produce blocks and earn transaction fees on RougeChain. This guide walks you through the full process.

Prerequisites

RequirementDetails
XRGE balanceAt least 10,000 XRGE (+ fees)
WalletA RougeChain wallet with signing keys
Node (optional)Running a node earns you more blocks

Step 1: Get XRGE

If you're on testnet, use the faucet:

  1. Visit rougechain.io
  2. Go to Wallet and click Request from Faucet
  3. Repeat until you have at least 10,000 XRGE

Step 2: Stake Tokens

Via Web UI

  1. Navigate to the Validators page
  2. Click Stake
  3. Enter your stake amount (minimum 10,000 XRGE)
  4. Confirm the transaction
  5. Your wallet signs the stake transaction with ML-DSA-65

Via v2 API (Client-Side Signing)

curl -X POST https://testnet.rougechain.io/api/v2/stake \
  -H "Content-Type: application/json" \
  -d '{
    "publicKey": "your-public-key-hex",
    "amount": 1000,
    "nonce": 1706745600000,
    "signature": "your-ml-dsa65-signature-hex"
  }'

Step 3: Verify Your Validator Status

curl "https://testnet.rougechain.io/api/validators"

Look for your public key in the response:

{
  "validators": [
    {
      "publicKey": "your-public-key",
      "stake": 1000.0,
      "status": "active",
      "blocksProposed": 0
    }
  ]
}

While staking alone makes you a validator, running a node ensures you're online to produce blocks when selected:

./quantum-vault-daemon \
  --mine \
  --api-port 5100 \
  --peers "https://testnet.rougechain.io/api" \
  --public-url "https://mynode.example.com"

Increasing Your Stake

You can add more XRGE to increase your block proposal probability:

# Stake additional 500 XRGE
curl -X POST https://testnet.rougechain.io/api/v2/stake \
  -H "Content-Type: application/json" \
  -d '{
    "publicKey": "your-public-key-hex",
    "amount": 500,
    "nonce": 1706745600001,
    "signature": "your-signature-hex"
  }'

Your total stake accumulates.

Validator Selection Algorithm

Proposer selection uses three factors:

  1. Stake weight — Higher stake gives proportionally higher probability
  2. Quantum entropy — Sourced from ANU QRNG (quantum vacuum fluctuations); falls back to local CSPRNG if unavailable
  3. Block context — Previous block hash and height are mixed into the seed for deterministic verifiability

This means even validators with the minimum stake will produce blocks, just less frequently. The entropy source ("quantum" or "local") is visible on the validator dashboard.

Leaving the Validator Set

See Unstaking — after unstaking below the minimum, you're removed from the active validator set.

Staking Rewards

Validators earn rewards for producing blocks on RougeChain.

Reward Sources

SourceDescription
Transaction feesAll fees from transactions in your block
Base block rewardFixed reward per block (if configured)

How Rewards Work

  1. A validator is selected to propose a block
  2. The validator assembles pending transactions
  3. All transaction fees in that block go to the proposer
  4. Rewards are credited immediately upon block finalization

Fee Structure

Transaction TypeFee
Transfer0.1 XRGE
Token creation100 XRGE
Pool creation10 XRGE
Swap0.3% (to LPs, not validators)
Stake/Unstake0.1 XRGE

Validators earn the flat fees (transfer, token creation, pool creation, stake/unstake). Swap fees go to liquidity providers.

Estimated Returns

Rewards depend on:

  • Your stake relative to total staked — determines how often you're selected
  • Network activity — more transactions = more fees per block
  • Number of validators — fewer validators means more blocks per validator

Example

ScenarioValue
Your stake10,000 XRGE
Total staked100,000 XRGE
Your share10%
Blocks per day~86,400 (1s block time)
Your blocks per day~8,640
Avg fee per block0.5 XRGE
Daily earnings~4,320 XRGE

These are approximate — actual returns vary with network conditions.

Compounding

Rewards are added to your balance, not your stake. To compound:

  1. Periodically stake your accumulated rewards
  2. This increases your proposer probability
  3. Leading to more blocks and more rewards

Checking Rewards

Via Web UI

Go to the Validators page to see your validator stats including blocks proposed.

Via API

# Check your balance (includes accumulated rewards)
curl "https://testnet.rougechain.io/api/balance/your-public-key"

# Check blocks proposed
curl "https://testnet.rougechain.io/api/validators"

Tax Considerations

Staking rewards may be taxable income in your jurisdiction. Keep records of:

  • Amount staked
  • Rewards received (block by block)
  • Token price at time of receipt
  • Unstaking transactions

RougeChain does not provide tax advice. Consult a tax professional.

Bridge

RougeChain supports bridging assets between Base Sepolia (EVM) and the RougeChain L1 network. The bridge uses a lock-and-mint / burn-and-release model with a dedicated smart contract.

Supported Assets

EVM AssetRougeChain AssetDecimalsDirection
ETHqETH6 (L1 units)Both ways
USDCqUSDC6Both ways
XRGEXRGE18 (EVM) / whole units (L1)Both ways

How It Works

Deposit (EVM → RougeChain)

  1. User deposits ETH, USDC, or XRGE into the RougeBridge smart contract on Base Sepolia
  2. User calls the claim endpoint on RougeChain with the EVM transaction hash
  3. The node verifies the deposit on-chain and mints the wrapped token (qETH, qUSDC, or XRGE) on L1

Withdrawal (RougeChain → EVM)

  1. User submits a signed bridge_withdraw transaction on RougeChain, burning the wrapped token
  2. The withdrawal is recorded in the pending withdrawals store
  3. The bridge relayer polls for pending withdrawals and releases the corresponding asset from the contract on EVM

Security

  • Client-side signing — Private keys never leave the browser. Withdraw transactions are signed locally using ML-DSA-65
  • RougeBridge contract — Pausable, with guardian role for emergencies, timelock on large withdrawals
  • Relayer authentication — The relayer uses a BRIDGE_RELAYER_SECRET for API authentication
  • Replay protection — Claimed transaction hashes are persisted to prevent double-claims
  • EVM signature verification — ETH claims require an EVM personal_sign to prove deposit ownership

Architecture

Base Sepolia (EVM)              RougeChain L1
┌──────────────────┐           ┌──────────────────┐
│  RougeBridge.sol │           │  Node Daemon     │
│  - depositETH() │──relayer──│  - /bridge/claim  │
│  - depositERC20()│           │  - /bridge/withdraw│
│  - releaseETH() │◀─relayer──│  - withdraw store │
│  - releaseERC20()│           │                  │
│  BridgeVault.sol │           │                  │
│  - deposit()     │──relayer──│  - /bridge/xrge/* │
│  - release()     │◀─relayer──│                  │
└──────────────────┘           └──────────────────┘

ETH Bridge (qETH)

Bridge ETH from Base Sepolia to RougeChain as qETH, and back.

Deposit (ETH → qETH)

Step 1: Send ETH to the Bridge

Connect your MetaMask (or other EVM wallet) to Base Sepolia and send ETH to the custody/bridge contract address shown on the Bridge page.

Step 2: Claim qETH

  1. Go to the Bridge page and select the Bridge In tab
  2. Paste the EVM transaction hash
  3. Sign the claim message with your EVM wallet (proves you made the deposit)
  4. Click Claim

The node verifies:

  • The transaction exists on Base Sepolia
  • It was sent to the correct custody address
  • The EVM signature matches the sender
  • The transaction has sufficient confirmations
  • It hasn't been claimed before

On success, qETH is minted to your RougeChain wallet.

Conversion Rate

1 ETH = 1,000,000 qETH units (6 decimal precision)

For example, depositing 0.01 ETH gives you 10,000 qETH units.

Withdraw (qETH → ETH)

  1. Go to the Bridge page and select the Bridge Out tab
  2. Select ETH as the token
  3. Enter the amount of qETH to bridge out
  4. Enter the Base Sepolia address to receive ETH
  5. Click Bridge Out

The transaction is signed client-side (your private key never leaves the browser), then:

  • qETH is burned on RougeChain
  • A pending withdrawal is created
  • The bridge relayer picks it up and sends ETH to your EVM address

A 0.1 XRGE fee is charged for the withdrawal transaction.

Fees

OperationFee
Deposit (ETH → qETH)Gas on Base Sepolia only
Withdraw (qETH → ETH)0.1 XRGE

USDC Bridge (qUSDC)

Bridge USDC from Base Sepolia to RougeChain as qUSDC, and back.

Overview

qUSDC is a wrapped representation of USDC on RougeChain. It maintains a 1:1 peg with USDC locked in the RougeBridge contract on Base Sepolia.

USDC Contract on Base Sepolia: 0x036CbD53842c5426634e7929541eC2318f3dCF7e

Deposit (USDC → qUSDC)

  1. Approve the RougeBridge contract to spend your USDC
  2. Call depositERC20(usdc_address, amount, rougechainPubkey) on the contract
  3. Go to the Bridge page, select USDC as the token, switch to Bridge In
  4. Paste the EVM transaction hash and claim

The node parses the ERC-20 Transfer event from the transaction receipt to determine the amount. USDC uses 6 decimals, and qUSDC uses the same 6-decimal precision.

Withdraw (qUSDC → USDC)

  1. Go to Bridge page, select USDC, switch to Bridge Out
  2. Enter amount and destination EVM address
  3. Click Bridge Out qUSDC

The relayer calls releaseERC20() on the RougeBridge contract to send USDC back to your EVM address.

Fees

OperationFee
DepositGas on Base Sepolia
Withdraw0.1 XRGE

XRGE Bridge

Bridge XRGE tokens between Base (EVM) and RougeChain L1 using the BridgeVault contract.

Overview

Unlike qETH/qUSDC (which are wrapped assets), XRGE is the native token of RougeChain. The XRGE bridge allows moving XRGE between its ERC-20 representation on Base and the L1 network.

XRGE Token on Base: 0x147120faEC9277ec02d957584CFCD92B56A24317

Deposit (Base XRGE → L1 XRGE)

  1. Approve the BridgeVault contract to spend your XRGE
  2. Call deposit(amount, rougechainPubkey) on the BridgeVault
  3. The vault locks your XRGE and emits a BridgeDeposit event
  4. Call the /api/bridge/xrge/claim endpoint with the transaction hash
  5. The node verifies the receipt and credits XRGE on L1

Withdraw (L1 XRGE → Base XRGE)

  1. Go to the Bridge page and use the XRGE Bridge Out tab
  2. Enter the amount and your Base EVM address
  3. Submit the signed withdrawal
  4. The relayer calls release() on the BridgeVault to unlock your XRGE on Base

BridgeVault Contract

The BridgeVault is a lock-and-release contract:

  • deposit(amount, rougechainPubkey) — Lock XRGE, emit event for relayer
  • release(to, amount, l1TxId) — Owner (relayer) releases XRGE back to user
  • vaultBalance() — View how much XRGE the vault holds
  • emergencyWithdraw(token) — Admin-only emergency recovery

Liquidity in the vault = total XRGE locked by depositors minus released amounts.

RougeBridge Contract

The RougeBridge.sol contract is a multi-asset bridge contract deployed on Base Sepolia that handles ETH and ERC-20 deposits/releases with enhanced security features.

Features

  • Multi-token support — ETH and any ERC-20 token (USDC, etc.)
  • Pausable — Guardian can pause all operations in emergencies
  • Timelock — Large withdrawals require a delay period before execution
  • Guardian role — Separate from owner; can pause but cannot withdraw
  • Replay protection — Processed L1 transaction IDs are tracked to prevent double-releases
  • Owner = multisig — Deploy with a Gnosis Safe as owner for production

Key Functions

Deposits

function depositETH(string calldata rougechainPubkey) external payable;
function depositERC20(address token, uint256 amount, string calldata rougechainPubkey) external;

Users call these to lock assets in the contract. Events are emitted for the relayer to pick up.

Releases (Owner Only)

function releaseETH(address to, uint256 amount, bytes32 l1TxId) external;
function releaseERC20(address token, address to, uint256 amount, bytes32 l1TxId) external;

The relayer (owner) calls these to release assets when a user burns their wrapped tokens on L1. If the amount exceeds largeWithdrawalThreshold, a timelock is queued instead.

Timelock

function executeTimelock(uint256 requestId) external;  // Owner, after delay
function cancelTimelock(uint256 requestId) external;    // Guardian

Large releases are queued with a configurable delay (default 24 hours). The guardian can cancel suspicious requests during the delay window.

Admin

function pause() external;                              // Guardian or Owner
function unpause() external;                             // Owner only
function setGuardian(address newGuardian) external;      // Owner
function setSupportedToken(address token, bool) external;// Owner
function setLargeWithdrawalThreshold(uint256) external;  // Owner

Events

EventDescription
BridgeDepositETHETH deposited for bridging
BridgeDepositERC20ERC-20 deposited for bridging
BridgeReleaseETHETH released to user
BridgeReleaseERC20ERC-20 released to user
TimelockQueuedLarge release queued with delay
TimelockExecutedQueued release executed
TimelockCancelledQueued release cancelled by guardian

Deployment

The contract is deployed on Base Sepolia (chain ID 84532). For mainnet, deploy with a Gnosis Safe multisig as the owner.

Bridge Relayer

The bridge relayer is an off-chain process that monitors pending withdrawals on RougeChain and executes the corresponding releases on Base Sepolia.

How It Works

  1. Polls the RougeChain node for pending ETH and XRGE withdrawals
  2. For each pending withdrawal, sends the corresponding asset on Base Sepolia
  3. Marks the withdrawal as fulfilled on the node

Running the Relayer

# Required environment variables
export CORE_API_URL="http://localhost:5101"
export BRIDGE_CUSTODY_PRIVATE_KEY="0x..."   # EVM private key for the bridge wallet
export BRIDGE_RELAYER_SECRET="your-secret"  # Shared secret for API authentication
export BASE_SEPOLIA_RPC="https://sepolia.base.org"

# Optional
export XRGE_BRIDGE_VAULT="0x..."            # BridgeVault contract address
export ROUGE_BRIDGE_ADDRESS="0x..."         # RougeBridge contract address
export USDC_ADDRESS="0x036CbD53842c5426634e7929541eC2318f3dCF7e"
export POLL_INTERVAL_MS="5000"

# Run
npx tsx scripts/bridge-relayer.ts

Authentication

The relayer authenticates with the node using the BRIDGE_RELAYER_SECRET environment variable. This is sent as the x-bridge-relayer-secret HTTP header when marking withdrawals as fulfilled.

Set the same secret on both the relayer and the node:

# On the node
export BRIDGE_RELAYER_SECRET="your-secret"

# On the relayer
export BRIDGE_RELAYER_SECRET="your-secret"

Contract Mode vs Legacy Mode

  • With ROUGE_BRIDGE_ADDRESS — The relayer calls releaseETH() / releaseERC20() on the RougeBridge contract
  • Without it — Falls back to raw ETH transfers from the custody wallet (legacy mode)
  • With XRGE_BRIDGE_VAULT — Enables XRGE bridge support via the BridgeVault contract

Security Considerations

  • The relayer's EVM private key should be stored securely (not in code)
  • Use a dedicated wallet with limited funds for the relayer
  • For production, the RougeBridge contract owner should be a multisig
  • The BRIDGE_RELAYER_SECRET should be a strong random string

DEX & AMM

RougeChain includes a built-in decentralized exchange (DEX) powered by an Automated Market Maker (AMM) using the constant product formula (x * y = k).

Overview

  • Create pools for any token pair
  • Swap between tokens with slippage protection
  • Provide liquidity and earn fees from trades
  • Multi-hop routing for tokens without direct pools

All operations are signed client-side with ML-DSA-65 — your private key never leaves the browser.

Fee Structure

OperationFee
Create Pool100 XRGE
Swap0.3% of input + 1 XRGE tx fee
Add Liquidity1 XRGE
Remove Liquidity1 XRGE

The 0.3% swap fee is distributed to liquidity providers proportional to their share of the pool.

Pool Mechanics

Pools use the Constant Product Market Maker model:

reserve_a × reserve_b = k (constant)

When a user swaps token A for token B:

  1. Token A is added to the pool
  2. Token B is removed, maintaining the constant product
  3. 0.3% fee is taken from the input amount
  4. Price impact increases with larger trades relative to pool size

LP Tokens

When you provide liquidity, you receive LP tokens representing your share of the pool. When you remove liquidity, you burn LP tokens and receive your proportional share of both tokens in the pool.

Liquidity Pools

Creating a Pool

To create a new trading pool:

  1. Go to the Swap page
  2. Click Create Pool
  3. Select two tokens (e.g., XRGE / qETH)
  4. Enter the initial amounts for each token
  5. Click Create Pool

The initial token ratio sets the starting price. A 100 XRGE fee is charged for pool creation.

Adding Liquidity

  1. Go to the pool page
  2. Click Add Liquidity
  3. Enter the amount of one token — the other amount auto-calculates based on the current ratio
  4. Confirm the transaction

You receive LP tokens proportional to your contribution.

Removing Liquidity

  1. Go to the pool page
  2. Click Remove Liquidity
  3. Enter the LP token amount to withdraw
  4. You receive your proportional share of both tokens

Impermanent Loss

If the price ratio changes significantly from when you deposited, you may experience impermanent loss compared to simply holding the tokens. This is standard AMM behavior.

Pool Stats

Each pool shows:

  • Current reserves for both tokens
  • Total LP tokens issued
  • Your LP share percentage
  • 24h volume and fees earned
  • Price history chart

Swaps

Making a Swap

  1. Go to the Swap page
  2. Select the input token and output token
  3. Enter the amount to swap
  4. Review the quote (output amount, price impact, minimum received)
  5. Click Swap

Slippage Protection

Set your maximum slippage tolerance to protect against price movements during your transaction. If the actual output would be less than your minimum, the transaction is rejected.

Default slippage: 0.5%

Price Impact

Price impact shows how much your trade will move the pool price. Large trades relative to pool size have higher price impact.

Price ImpactSeverity
< 1%Low
1-5%Medium
> 5%High (warning shown)

Multi-Hop Routing

If no direct pool exists between your tokens, the router automatically finds a path through XRGE:

TOKEN_A → XRGE → TOKEN_B

This uses two swaps internally but is handled in a single transaction.

Fees

  • 0.3% of the input amount goes to liquidity providers
  • 1 XRGE base transaction fee

Token Creation

Create custom tokens on RougeChain. Tokens can be traded on the built-in AMM/DEX.

Overview

PropertyValue
Creation fee100 XRGE
Max supplySet at creation (immutable)
DecimalsConfigurable
TradingVia AMM liquidity pools

Create a Token

Via Web UI

  1. Navigate to the Token Explorer page
  2. Click Create Token
  3. Fill in token details:
    • Name — Full name (e.g., "My Token")
    • Symbol — Ticker symbol (e.g., "MTK")
    • Total Supply — Maximum supply
    • Logo — Upload an image or paste a URL (optional)
  4. Confirm and sign the transaction

Uploaded logos are compressed to WebP (max 256×256, ≤100 KB) and stored on-chain as base64 data URIs. They display across the wallet, swap, pools, and explorer.

Via SDK

import { RougeChain, Wallet } from "@rougechain/sdk";

const rc = new RougeChain("https://testnet.rougechain.io/api");
const wallet = Wallet.generate();

await rc.createToken(wallet, {
  name: "My Token",
  symbol: "MTK",
  totalSupply: 1_000_000,
  image: "https://example.com/logo.png", // or a data:image/webp;base64,... URI
});

Via v2 API

curl -X POST https://testnet.rougechain.io/api/v2/token/create \
  -H "Content-Type: application/json" \
  -d '{
    "publicKey": "your-public-key-hex",
    "payload": {
      "name": "My Token",
      "symbol": "MTK",
      "totalSupply": 1000000,
      "decimals": 8,
      "image": "https://example.com/logo.png"
    },
    "nonce": 1706745600000,
    "signature": "your-ml-dsa65-signature-hex"
  }'

Response

{
  "success": true,
  "txId": "abc123...",
  "token": {
    "symbol": "MTK",
    "name": "My Token",
    "totalSupply": 1000000,
    "decimals": 8,
    "creator": "your-public-key"
  }
}

After Creation

Once created, the entire supply is credited to the creator's wallet. You can then:

  1. Transfer tokens to other wallets
  2. Create a liquidity pool to enable trading
  3. Burn tokens by sending to the burn address

Creating a Liquidity Pool

To make your token tradeable on the DEX:

curl -X POST https://testnet.rougechain.io/api/v2/pool/create \
  -H "Content-Type: application/json" \
  -d '{
    "publicKey": "your-public-key-hex",
    "payload": {
      "tokenA": "XRGE",
      "tokenB": "MTK",
      "amountA": 1000,
      "amountB": 10000
    },
    "nonce": 1706745600001,
    "signature": "your-signature-hex"
  }'

This creates an XRGE/MTK pool with an initial price of 0.1 XRGE per MTK.

Pool creation costs 10 XRGE.

Token Burning

Send tokens to the burn address to permanently remove them from circulation:

XRGE_BURN_0x000000000000000000000000000000000000000000000000000000000000DEAD

Burned amounts are tracked on-chain and queryable via GET /api/burned.

Listing on the DEX

Tokens are automatically listed on the DEX once a liquidity pool is created. Users can then:

  • Swap between your token and XRGE
  • Add/remove liquidity
  • View price charts and pool stats

Token Metadata

Every token has on-chain metadata that the creator can manage:

FieldDescription
imageLogo URL or base64 data URI
descriptionToken description
websiteProject website
twitterX/Twitter handle
discordDiscord invite link

Updating Metadata

Only the original creator can update metadata:

await rc.updateTokenMetadata(wallet, {
  symbol: "MTK",
  image: "data:image/webp;base64,UklGR...",
  description: "A community token for...",
  website: "https://mytoken.io",
  twitter: "@mytoken",
  discord: "discord.gg/mytoken",
});

Logo images can be:

  • URLshttps://..., ipfs://...
  • Data URIsdata:image/webp;base64,... (stored directly on-chain, persists forever)

The web UI provides an Upload button that compresses images to WebP and stores them as base64 on-chain.

Token Standards

RougeChain tokens are native protocol-level assets (not smart contract tokens). This means:

  • No ERC-20 compatibility (different chain architecture)
  • Transfers are first-class transactions
  • All token operations are signed with ML-DSA-65
  • Quantum-resistant by default

NFTs

RougeChain supports on-chain NFTs with collections, minting, transfers, and marketplace features — all secured by post-quantum cryptography.

Features

  • Collections — Create NFT collections with configurable max supply, royalties, and metadata
  • Minting — Mint single or batch NFTs with metadata URIs and custom attributes
  • Transfers — Send NFTs to any RougeChain address with optional sale price tracking
  • Burning — Permanently destroy NFTs
  • Locking — Lock individual NFTs to prevent transfers
  • Freezing — Freeze entire collections to prevent further minting

Fee Structure

OperationFee (XRGE)
Create Collection50
Mint NFT5
Batch Mint5 per NFT
Transfer1
Burn0.1
Lock/Unlock0.1
Freeze Collection0.1

Security

All NFT operations use the v2 signed transaction API — transactions are signed client-side with ML-DSA-65 and verified on-chain. Only collection creators can mint, and only token owners can transfer or burn.

NFT Collections

Creating a Collection

  1. Go to the NFT Explorer page
  2. Click Create Collection
  3. Fill in:
    • Symbol — Short identifier (e.g., "ROGUE")
    • Name — Full collection name
    • Max Supply — Maximum number of NFTs (optional, 0 = unlimited)
    • Royalty — Royalty percentage in basis points (e.g., 500 = 5%)
    • Image — Collection cover image URL
    • Description — Collection description
  4. Click Create

The collection ID is generated from creator_pubkey:SYMBOL.

Collection Properties

PropertyDescription
idUnique identifier (creator:symbol)
symbolShort token symbol
nameFull collection name
creatorCreator's public key
max_supplyMax tokens (0 = unlimited)
royalty_bpsRoyalty in basis points
frozenWhether minting is frozen
total_mintedNumber of tokens minted

Freezing a Collection

Collection creators can freeze their collections to permanently prevent further minting. This is irreversible and signals to holders that the supply is final.

Browsing Collections

The NFT Explorer page shows all collections with their stats, floor price, and total items. Click any collection to view its individual tokens.

Minting & Trading NFTs

Minting

Single Mint

  1. Open a collection page
  2. Click Mint NFT
  3. Enter the token name and optional metadata URI / attributes
  4. Click Mint

Only the collection creator can mint new tokens.

Batch Mint

Mint multiple NFTs at once:

  1. Open a collection page
  2. Click Batch Mint
  3. Provide a list of names (and optional URIs/attributes for each)
  4. Click Mint All

Fee is 5 XRGE per NFT in the batch.

Token Properties

Each NFT has:

PropertyDescription
token_idSequential ID within the collection
nameToken name
ownerCurrent owner's address (rouge1...)
creatorOriginal minter
metadata_uriLink to off-chain metadata (IPFS, HTTP)
attributesOn-chain key-value attributes
lockedWhether the token is locked (non-transferable)
created_atTimestamp of minting

Transferring

  1. Open the NFT detail page
  2. Click Transfer
  3. Enter the recipient's RougeChain address (rouge1...)
  4. Optionally set a sale price (for marketplace tracking)
  5. Confirm

Locked NFTs cannot be transferred until unlocked by the owner.

Burning

Permanently destroy an NFT:

  1. Open the NFT detail page
  2. Click Burn
  3. Confirm

Only the current owner can burn. This action is irreversible.

Locking

Lock an NFT to prevent it from being transferred:

  1. Open the NFT detail page
  2. Toggle Lock

Only the owner can lock/unlock their NFTs.

Block Explorer

RougeChain includes a built-in block explorer at rougechain.io for browsing blocks, transactions, addresses, tokens, and NFTs.

Features

  • Block details — View block height, hash, timestamp, proposer, and all transactions
  • Transaction details — View transaction type, sender, recipient, amount, fee, and status
  • Address pages — View any address's balances, token holdings, and transaction history
  • Token list — Browse all tokens created on RougeChain
  • NFT Explorer — Browse NFT collections and individual tokens
  • Global search — Search by address, transaction hash, or block height

The explorer is integrated into the main RougeChain web app:

PageURLDescription
Blockchain/blockchainBlock list with live updates
Block Detail/block/:heightIndividual block
Transaction/tx/:hashIndividual transaction
Address/address/:pubkeyAddress details
Transactions/transactionsAll transactions list
NFT Explorer/nft-explorerNFT collections browser
Tokens/tokensAll tokens list

Block Explorer — Blocks

Block List

The Blockchain page shows all blocks in reverse chronological order with:

  • Block height (index)
  • Block hash (truncated)
  • Timestamp
  • Number of transactions
  • Proposer address

Click any block to view its full details.

Block Detail Page

URL: /block/:height

Shows:

FieldDescription
HeightBlock number in the chain
HashSHA-256 hash of the block
Previous HashHash of the parent block
TimestampWhen the block was created
ProposerPublic key of the block proposer
Transaction CountNumber of transactions in the block

Below the header, all transactions in the block are listed with their type, sender, recipient, amount, and fee.

API

GET /api/blocks              — List all blocks (paginated)
GET /api/block/:height       — Get a specific block by height
GET /api/blocks/summary      — Block summary for charts

Block Explorer — Transactions

Transaction List

The Transactions page shows all transactions across the network with:

  • Transaction hash
  • Type (transfer, swap, stake, bridge, NFT, etc.)
  • Sender and recipient
  • Amount
  • Block number
  • Timestamp

Transaction Detail Page

URL: /tx/:hash

Shows the full transaction details:

FieldDescription
HashTransaction hash (SHA-256)
TypeTransaction type
FromSender public key
ToRecipient public key
AmountTransaction amount
FeeTransaction fee (XRGE)
TokenToken symbol (if applicable)
BlockBlock height containing this tx
TimestampWhen the transaction was processed
SignaturePQC signature (ML-DSA-65)

Transaction Types

TypeDescription
transferToken transfer between addresses
create_tokenNew token creation
stakeStaking tokens
unstakeUnstaking tokens
create_poolCreating a liquidity pool
add_liquidityAdding liquidity to a pool
remove_liquidityRemoving liquidity from a pool
swapToken swap via AMM
bridge_mintMinting bridged tokens (qETH, qUSDC)
bridge_withdrawBurning bridged tokens for withdrawal
nft_create_collectionCreating an NFT collection
nft_mintMinting an NFT
nft_transferTransferring an NFT
nft_burnBurning an NFT

API

GET /api/txs                                    — List recent transactions
GET /api/tx/:hash                               — Get transaction by hash
GET /api/address/:public_key/transactions       — Get transactions for an address

Block Explorer — Addresses

Address Detail Page

URL: /address/:pubkey

Shows comprehensive information for any RougeChain address. Addresses are displayed in the compact rouge1... Bech32m format throughout the UI, derived from the SHA-256 hash of the raw ML-DSA-65 public key.

Balances

  • XRGE balance
  • All token balances (qETH, qUSDC, custom tokens)
  • Staked amount (if any)

Transaction History

Paginated list of all transactions where this address is the sender or recipient, including:

  • Transfers
  • Swaps
  • Staking operations
  • Bridge operations
  • NFT operations

NFTs Owned

List of all NFTs currently owned by this address, grouped by collection.

API

GET /api/balance/:public_key                    — XRGE balance
GET /api/balance/:public_key/:token_symbol      — Token balance
GET /api/address/:public_key/transactions       — Transaction history (paginated)
GET /api/nft/owner/:pubkey                      — NFTs owned

Contracts

The Contracts Explorer lets you browse all WASM smart contracts deployed on RougeChain.

Contracts List (/contracts)

The contracts page displays:

  • Total Contracts — Number of deployed contracts
  • Total WASM Size — Combined bytecode size
  • Search — Filter by contract address or deployer
  • Sort — By newest, oldest, or size

Each contract card shows its address, deployer, WASM size, and deploy block. Click to view details.

Contract Detail (/contract/:addr)

The contract detail page has three sections:

Contract Information

  • Contract address (hex)
  • Deployer identity
  • Deploy block and transaction
  • WASM bytecode size

State Viewer

Live key-value table showing all data in the contract's persistent storage. Updates after each contract call.

Execute Contract

Interactive form to call contract methods directly from the browser:

FieldDescription
MethodFunction name (e.g. get_count, transfer)
ArgumentsJSON object with method parameters
CallerCaller identity (optional)
Gas LimitMax fuel units (default: 100,000)

Results display success/failure status, gas used, and return data.

Transaction Labels

Contract transactions appear throughout the explorer with distinct labels:

LabelDescription
Contract DeployWASM bytecode deployment
Contract CallMethod execution

The transaction detail page shows a Contract Details card for these transactions with the contract address, method name, gas used, and WASM size.

API Reference

The RougeChain node exposes a REST API on the configured --api-port (default: 5101).

Important: All write operations (POST/PUT/DELETE) require v2 signed requests using ML-DSA-65 client-side signing. Legacy unsigned v1 write endpoints return 410 GONE in production. Private keys never leave your application.

Base URL

http://127.0.0.1:5100/api

For the public testnet:

https://testnet.rougechain.io/api

Address Format

RougeChain uses Bech32m addresses with the rouge1 prefix (e.g., rouge1q8f3x7k2m4n9p...). These are derived from the SHA-256 hash of the raw ML-DSA-65 public key.

All API endpoints expect raw hex public keys, not rouge1 addresses:

✅ /api/balance/d67d8da279755a...
❌ /api/balance/rouge1q8f3x7k2m4n9p...

Tip: Use GET /api/resolve/:input to convert between rouge1… addresses and hex public keys.

v2 Signed Request Format

All write endpoints use a standard signed-request envelope:

{
  "payload": {
    "...endpoint-specific fields...",
    "from": "your-signing-public-key-hex",
    "timestamp": 1706745600000,
    "nonce": "random-hex-string"
  },
  "signature": "ml-dsa65-signature-of-payload-hex",
  "public_key": "your-signing-public-key-hex"
}

The payload is JSON-serialized with keys sorted alphabetically, then signed with your ML-DSA-65 private key. The server verifies the signature, checks the timestamp (must be within 60 seconds), and rejects replayed nonces.

Endpoints Overview

System

EndpointMethodDescription
/api/healthGETNode health check
/api/statsGETNetwork statistics
/api/wsGETWebSocket for real-time events
/api/price/xrgeGETCurrent XRGE price

Blockchain

EndpointMethodDescription
/api/blocksGETGet all blocks
/api/blocks/summaryGETBlock summary for charts
/api/block/:heightGETGet block by height
/api/txsGETGet transactions
/api/tx/:hashGETGet transaction by hash
/api/tx/:hash/receiptGETGet transaction receipt
/api/eventsGETGet all events

Wallet & Accounts

EndpointMethodDescription
/api/balance/:publicKeyGETGet XRGE balance
/api/balance/:publicKey/:tokenGETGet token balance
/api/account/:pubkey/nonceGETGet account nonce
/api/resolve/:inputGETResolve address ↔ public key
/api/address/:pubkey/transactionsGETGet address transactions
/api/v2/transferPOSTTransfer tokens (signed)
/api/v2/faucetPOSTRequest testnet tokens (signed)

Tokens

EndpointMethodDescription
/api/tokensGETList all tokens
/api/token/:symbol/metadataGETGet token metadata
/api/token/:symbol/holdersGETGet token holders
/api/token/:symbol/transactionsGETGet token transactions
/api/burn-addressGETGet official burn address
/api/burnedGETGet burned token stats
/api/v2/token/createPOSTCreate token (signed)
/api/v2/token/metadata/updatePOSTUpdate token metadata (signed)
/api/v2/token/metadata/claimPOSTClaim metadata ownership (signed)

Token Allowances (ERC-20 Style)

EndpointMethodDescription
/api/v2/token/approvePOSTApprove spender allowance (signed)
/api/v2/token/transfer-fromPOSTTransfer using allowance (signed)
/api/v2/token/freezePOSTFreeze/unfreeze token transfers (signed)
/api/token/allowanceGETCheck specific allowance
/api/token/allowancesGETList allowances by owner/spender
/api/allowances/:pubkeyGETList all allowances for a key
/api/locks/:pubkeyGETGet token locks

Staking & Validators

EndpointMethodDescription
/api/validatorsGETList validators
/api/validators/statsGETValidator vote stats
/api/selectionGETProposer selection
/api/finalityGETFinality status
/api/votesGETVote quorum info
/api/v2/stakePOSTStake tokens (signed)
/api/v2/unstakePOSTUnstake tokens (signed)

Token Staking Pools

EndpointMethodDescription
/api/staking/poolsGETList all staking pools
/api/staking/pool/:pool_idGETGet staking pool details
/api/staking/stakes/:pubkeyGETGet stakes by owner
/api/staking/pool/:pool_id/stakesGETGet stakes in a pool

AMM/DEX

EndpointMethodDescription
/api/poolsGETList liquidity pools
/api/pool/:pool_idGETGet pool details
/api/pool/:pool_id/pricesGETGet price history
/api/pool/:pool_id/eventsGETGet pool events
/api/pool/:pool_id/statsGETGet pool statistics
/api/swap/quotePOSTGet swap quote
/api/v2/pool/createPOSTCreate liquidity pool (signed)
/api/v2/pool/add-liquidityPOSTAdd liquidity (signed)
/api/v2/pool/remove-liquidityPOSTRemove liquidity (signed)
/api/v2/swap/executePOSTExecute swap (signed)

NFTs

EndpointMethodDescription
/api/nft/collectionsGETList collections
/api/nft/collection/:idGETGet collection
/api/nft/collection/:id/tokensGETGet collection tokens
/api/nft/token/:coll/:idGETGet specific NFT
/api/nft/owner/:pubkeyGETGet NFTs by owner
/api/v2/nft/collection/createPOSTCreate collection (signed)
/api/v2/nft/mintPOSTMint NFT (signed)
/api/v2/nft/batch-mintPOSTBatch mint (signed)
/api/v2/nft/transferPOSTTransfer NFT (signed)
/api/v2/nft/burnPOSTBurn NFT (signed)
/api/v2/nft/lockPOSTLock/unlock NFT (signed)
/api/v2/nft/freeze-collectionPOSTFreeze collection (signed)

Smart Contracts

EndpointMethodDescription
/api/v2/contract/deployPOSTDeploy WASM contract (signed)
/api/v2/contract/callPOSTCall contract method (signed)
/api/contract/:addressGETGet contract metadata
/api/contract/:address/stateGETGet contract state
/api/contract/:address/eventsGETGet contract events

Bridge (ETH/USDC + XRGE)

EndpointMethodDescription
/api/bridge/configGETETH/USDC bridge config
/api/bridge/claimPOSTClaim bridge deposit
/api/bridge/withdrawPOSTWithdraw to EVM
/api/bridge/withdrawalsGETList pending withdrawals
/api/bridge/withdrawals/:txIdDELETEFulfill withdrawal
/api/bridge/xrge/configGETXRGE bridge config
/api/bridge/xrge/claimPOSTClaim XRGE deposit
/api/bridge/xrge/withdrawPOSTWithdraw XRGE to EVM
/api/bridge/xrge/withdrawalsGETList XRGE withdrawals
/api/bridge/xrge/withdrawals/:txIdDELETEFulfill XRGE withdrawal

Shielded Transactions

EndpointMethodDescription
/api/v2/shielded/shieldPOSTShield tokens (signed)
/api/v2/shielded/transferPOSTPrivate transfer (signed)
/api/v2/shielded/unshieldPOSTUnshield tokens (signed)
/api/shielded/statsGETShielded pool statistics
/api/shielded/nullifier/:hashGETCheck nullifier

Rollup (Phase 3)

EndpointMethodDescription
/api/v2/rollup/statusGETRollup status
/api/v2/rollup/batch/:idGETGet rollup batch
/api/v2/rollup/submitPOSTSubmit rollup transfer (signed)

Governance

EndpointMethodDescription
/api/governance/proposalsGETList all proposals
/api/governance/proposals/:tokenGETProposals by token
/api/governance/proposal/:idGETGet proposal details
/api/governance/proposal/:id/votesGETGet proposal votes

Social

EndpointMethodDescription
/api/social/track/:trackId/statsGETGet track stats (plays, likes, comments)
/api/social/track/:trackId/commentsGETGet track comments
/api/social/artist/:pubkey/statsGETGet artist stats (followers, following)
/api/social/user/:pubkey/likesGETGet user's liked track/post IDs
/api/social/user/:pubkey/followingGETGet user's followed artists
/api/social/user/:pubkey/postsGETGet user's posts
/api/social/post/:postIdGETGet a single post with stats
/api/social/post/:postId/statsGETGet post stats (likes, reposts, replies)
/api/social/post/:postId/repliesGETGet replies to a post
/api/social/timelineGETGlobal timeline (newest first)
/api/v2/social/playPOSTRecord a play (signed)
/api/v2/social/likePOSTToggle like (signed)
/api/v2/social/commentPOSTPost a comment (signed)
/api/v2/social/comment/deletePOSTDelete a comment (signed)
/api/v2/social/followPOSTToggle follow (signed)
/api/v2/social/postPOSTCreate a post (signed)
/api/v2/social/post/deletePOSTDelete a post (signed)
/api/v2/social/repostPOSTToggle repost (signed)
/api/v2/social/feedPOSTGet following feed (signed)

Messenger

EndpointMethodDescription
/api/messenger/walletsGETList messenger wallets
/api/v2/messenger/wallets/registerPOSTRegister wallet (signed)
/api/v2/messenger/conversationsPOSTCreate conversation (signed)
/api/v2/messenger/conversations/listPOSTList conversations (signed)
/api/v2/messenger/conversations/deletePOSTDelete conversation (signed)
/api/v2/messenger/messagesPOSTSend message (signed)
/api/v2/messenger/messages/listPOSTList messages (signed)
/api/v2/messenger/messages/readPOSTMark as read (signed)
/api/v2/messenger/messages/deletePOSTDelete message (signed)

Mail

EndpointMethodDescription
/api/v2/names/registerPOSTRegister mail name (signed)
/api/v2/names/releasePOSTRelease a name (signed)
/api/names/resolve/:nameGETResolve name → wallet
/api/names/reverse/:walletIdGETReverse lookup → name
/api/v2/mail/sendPOSTSend encrypted mail (signed)
/api/v2/mail/folderPOSTGet inbox/sent/trash (signed)
/api/v2/mail/messagePOSTGet single mail item (signed)
/api/v2/mail/readPOSTMark as read (signed)
/api/v2/mail/movePOSTMove to folder (signed)
/api/v2/mail/deletePOSTDelete permanently (signed)

Push Notifications

EndpointMethodDescription
/api/push/registerPOSTRegister push token (PQC-signed)
/api/push/unregisterPOSTUnregister push token (PQC-signed)

P2P

EndpointMethodDescription
/api/peersGETList known peers
/api/peers/registerPOSTRegister as peer
/api/blocks/importPOSTImport block from peer
/api/tx/broadcastPOSTReceive broadcasted tx

Authentication

Some endpoints require an API key (if configured on the node):

curl -H "X-API-Key: your-api-key" https://testnet.rougechain.io/api/stats

Rate Limiting

Rate limiting is disabled by default (--rate-limit-per-minute 0). When enabled, the node supports tiered limits:

  • Tier 1 (Validators): Proven via X-Validator-Key header + signature
  • Tier 2 (Registered peers): Recognized by IP
  • Tier 3 (Public): Separate limits for read and write endpoints

Health & Stats API

System endpoints for monitoring node status and network statistics.

Health Check

GET /api/health

Returns the node's current status.

Response

{
  "status": "ok",
  "chain_id": "rougechain-devnet-1",
  "height": 12345
}
FieldTypeDescription
statusstring"ok" if the node is healthy
chain_idstringThe chain identifier
heightnumberCurrent block height

Use Cases

  • Monitoring node uptime
  • Checking sync status (compare height with peers)
  • Load balancer health checks

Network Statistics

GET /api/stats

Returns network-wide statistics.

Response

{
  "blockHeight": 12345,
  "totalTransactions": 98765,
  "totalWallets": 432,
  "totalValidators": 15,
  "totalStaked": 150000.0,
  "totalBurned": 5000.0,
  "totalPools": 8,
  "chainId": "rougechain-devnet-1"
}
FieldTypeDescription
blockHeightnumberCurrent block height
totalTransactionsnumberTotal transactions processed
totalWalletsnumberUnique wallets on the network
totalValidatorsnumberActive validators
totalStakednumberTotal XRGE staked
totalBurnednumberTotal XRGE burned
totalPoolsnumberNumber of AMM liquidity pools
chainIdstringChain identifier

Burn Stats

GET /api/burned

Get burned token statistics.

Response

{
  "totalBurned": 5000.0,
  "burnAddress": "XRGE_BURN_0x000000000000000000000000000000000000000000000000000000000000DEAD"
}

Examples

Monitoring Script

#!/bin/bash
while true; do
  HEIGHT=$(curl -s http://127.0.0.1:5100/api/health | jq '.height')
  echo "$(date): Block height = $HEIGHT"
  sleep 10
done

Compare with Testnet

LOCAL=$(curl -s http://127.0.0.1:5100/api/health | jq '.height')
TESTNET=$(curl -s https://testnet.rougechain.io/api/health | jq '.height')
echo "Local: $LOCAL, Testnet: $TESTNET, Behind: $((TESTNET - LOCAL))"

Blocks API

Endpoints for querying block data on RougeChain.

Get Blocks

GET /api/blocks?limit=50&from_height=0

Query Parameters

ParameterTypeDefaultDescription
limitnumber50Maximum blocks to return (max 100, up to 1000 for sync)
from_heightnumber-Start from this block height (used for P2P sync)
offsetnumber0Pagination offset

Response

{
  "blocks": [
    {
      "version": 1,
      "header": {
        "version": 1,
        "chainId": "rougechain-devnet-1",
        "height": 42,
        "time": 1706745600000,
        "prevHash": "abc123...",
        "txHash": "def456...",
        "proposerPubKey": "ghi789..."
      },
      "txs": [...],
      "proposerSig": "...",
      "hash": "xyz..."
    }
  ],
  "total": 12345
}

Block Fields

FieldTypeDescription
versionnumberBlock format version
header.heightnumberBlock number
header.timenumberTimestamp (ms since epoch)
header.prevHashstringHash of previous block
header.txHashstringMerkle root of transactions
header.proposerPubKeystringValidator who proposed this block
txsarrayTransactions included in this block
proposerSigstringML-DSA-65 signature by the proposer
hashstringSHA-256 hash of this block

Block Summary

GET /api/blocks/summary

Returns a lightweight summary of recent blocks, suitable for charts and dashboards.

Response

{
  "blocks": [
    {
      "height": 42,
      "time": 1706745600000,
      "txCount": 5,
      "proposer": "abc123..."
    }
  ]
}

Import Block (P2P)

Used by peer nodes to propagate blocks.

POST /api/blocks/import
Content-Type: application/json

See Peers API for details.


Block Verification

Every block is verified by receiving nodes:

  1. Hash check — Recompute the block hash and compare
  2. Signature check — Verify ML-DSA-65 signature against the proposer's public key
  3. Height check — Must extend the current chain tip by exactly 1
  4. Previous hash — Must reference the current tip's hash
  5. Transaction validity — All transactions must have valid signatures and sufficient balances

Transactions API

Transfer Tokens (v2)

Send XRGE or custom tokens to another address using client-side ML-DSA-65 signing.

POST /api/v2/transfer
Content-Type: application/json

Request Body

{
  "payload": {
    "toPubKeyHex": "recipient-public-key-hex",
    "amount": 100.0,
    "fee": 0.1,
    "token": "XRGE",
    "from": "sender-public-key-hex",
    "timestamp": 1706745600000,
    "nonce": "random-hex-string"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "sender-public-key-hex"
}
FieldTypeRequiredDescription
toPubKeyHexstringYesRecipient's public key (hex) or rouge1 address
amountnumberYesAmount to send
feenumberNoTransaction fee (default: 0.1)
tokenstringNoToken symbol (default: "XRGE")

Security: Private keys never leave your application. The transaction is signed client-side using ML-DSA-65 and the server verifies the signature before processing.

Response

{
  "success": true,
  "txId": "abc123..."
}

Error Response

{
  "success": false,
  "error": "insufficient balance: have 50.0000 XRGE, need 100.1000 XRGE"
}

Get Transactions

Retrieve recent transactions.

GET /api/txs?limit=50&offset=0

Query Parameters

ParameterTypeDefaultDescription
limitnumber50Max transactions to return
offsetnumber0Pagination offset

Response

{
  "txs": [
    {
      "version": 1,
      "txType": "transfer",
      "fromPubKey": "abc...",
      "nonce": 1234567890,
      "payload": {
        "toPubKeyHex": "def...",
        "amount": 100
      },
      "fee": 0.1,
      "sig": "ghi...",
      "blockHeight": 42,
      "blockTime": 1706745600000
    }
  ],
  "total": 150
}

Get Transaction by Hash

GET /api/tx/:hash

Response

{
  "success": true,
  "tx": {
    "version": 1,
    "txType": "transfer",
    "fromPubKey": "abc...",
    "nonce": 1234567890,
    "payload": {
      "toPubKeyHex": "def...",
      "amount": 100
    },
    "fee": 0.1,
    "sig": "ghi...",
    "blockHeight": 42,
    "blockTime": 1706745600000
  }
}

Get Transaction Receipt

GET /api/tx/:hash/receipt

Returns execution receipt for contract calls and other complex transactions.


Request Faucet (v2)

Get free testnet XRGE tokens.

POST /api/v2/faucet
Content-Type: application/json

Request Body

{
  "payload": {
    "from": "your-public-key-hex",
    "timestamp": 1706745600000,
    "nonce": "random-hex-string"
  },
  "signature": "your-signature-hex",
  "public_key": "your-public-key-hex"
}

Response

{
  "success": true,
  "amount": 1000,
  "txId": "abc123..."
}

Rate Limit

The faucet has additional rate limiting:

  • 1 request per address per hour
  • Whitelisted addresses bypass rate limits

Transaction Types

TypeDescription
transferStandard XRGE or token transfer
faucetFaucet distribution
stakeStake tokens to become validator
unstakeUnstake tokens
create_tokenCreate custom token
burnBurn tokens permanently
shieldShield tokens (make private)
unshieldUnshield tokens (make public)
contract_deployDeploy WASM smart contract
contract_callCall smart contract method

Wallet API

Endpoints for balance queries, transfers, and token management.

Note: Wallets are created client-side using ML-DSA-65 + ML-KEM-768 key generation. Private keys never leave your application. See Create a Wallet for details.

Get Balance

GET /api/balance/:publicKey

Path Parameters

ParameterTypeDescription
publicKeystringThe wallet's ML-DSA-65 public key (hex)

Response

{
  "balance": 1500.5,
  "publicKey": "abc123...",
  "tokens": {
    "XRGE": 1500.5,
    "qETH": 0.5
  }
}

Transfer Tokens (v2)

POST /api/v2/transfer
Content-Type: application/json

Request Body

{
  "payload": {
    "toPubKeyHex": "recipient-public-key-hex",
    "amount": 100,
    "fee": 0.1,
    "token": "XRGE",
    "from": "sender-public-key-hex",
    "timestamp": 1706745600000,
    "nonce": "random-hex-string"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "sender-public-key-hex"
}

The transaction is signed client-side using ML-DSA-65. The server verifies the signature before processing.

Response

{
  "success": true,
  "txId": "abc123..."
}

Request Faucet (v2)

POST /api/v2/faucet
Content-Type: application/json

Request Body

{
  "payload": {
    "from": "your-public-key-hex",
    "timestamp": 1706745600000,
    "nonce": "random-hex-string"
  },
  "signature": "your-signature-hex",
  "public_key": "your-public-key-hex"
}

Response

{
  "success": true,
  "amount": 1000,
  "txId": "abc123..."
}

See Get Test Tokens for details on rate limits.


Burn Address

GET /api/burn-address

Response

{
  "burnAddress": "XRGE_BURN_0x000000000000000000000000000000000000000000000000000000000000DEAD"
}

Send tokens to this address to permanently burn them. Burned amounts are tracked on-chain.


Address Resolution

Resolve between compact rouge1… bech32 addresses and full hex public keys.

GET /api/resolve/:input

Input can be either a rouge1… address or a hex public key. The endpoint auto-detects the format.

Response

{
  "success": true,
  "address": "rouge1q8f3x7k2m4...",
  "publicKey": "a1b2c3d4e5f6...",
  "balance": 1000.5
}

Account Nonce

Get the current and next sequential nonce for a wallet. Used for replay protection in v2 signed transactions.

GET /api/account/:publicKey/nonce

Response

{
  "success": true,
  "publicKey": "a1b2c3d4...",
  "nonce": 5,
  "next_nonce": 6
}

Staking API

Endpoints for validator staking operations. All write operations use v2 signed requests.

List Validators

GET /api/validators

Query Parameters

ParameterTypeDescription
publicKeystring(Optional) Filter by specific validator

Response

{
  "validators": [
    {
      "publicKey": "abc123...",
      "stake": 10000.0,
      "status": "active",
      "blocksProposed": 142
    },
    {
      "publicKey": "def456...",
      "stake": 5000.0,
      "status": "active",
      "blocksProposed": 71
    }
  ],
  "totalStaked": 15000.0
}

Validator Fields

FieldTypeDescription
publicKeystringValidator's ML-DSA-65 public key
stakenumberAmount of XRGE staked
statusstringactive or unbonding
blocksProposednumberTotal blocks produced

Stake Tokens

POST /api/v2/stake
Content-Type: application/json

Request Body

{
  "payload": {
    "amount": 10000,
    "from": "your-public-key-hex",
    "timestamp": 1706745600000,
    "nonce": "random-hex-string"
  },
  "signature": "your-ml-dsa65-signature-hex",
  "public_key": "your-public-key-hex"
}

Response

{
  "success": true,
  "txId": "abc123...",
  "stake": 10000.0,
  "status": "active"
}

Requirements

RequirementValue
Minimum stake10,000 XRGE
Fee0.1 XRGE

Unstake Tokens

POST /api/v2/unstake
Content-Type: application/json

Request Body

{
  "payload": {
    "amount": 5000,
    "from": "your-public-key-hex",
    "timestamp": 1706745600000,
    "nonce": "random-hex-string"
  },
  "signature": "your-ml-dsa65-signature-hex",
  "public_key": "your-public-key-hex"
}

Response

{
  "success": true,
  "txId": "abc123...",
  "remainingStake": 5000.0
}

Unbonding

After unstaking, tokens enter an unbonding period (~7 days on testnet) before they become available in your balance.


Error Responses

ErrorCause
"insufficient balance"Not enough XRGE to stake
"below minimum stake"Amount is less than 10,000 XRGE
"validator not found"Trying to unstake but not a validator
"invalid signature"ML-DSA-65 signature verification failed

Peers API

Endpoints for P2P peer management and discovery.

List Peers

Get all known peers.

GET /api/peers

Response

{
  "peers": [
    "https://testnet.rougechain.io/api",
    "https://node2.example.com/api"
  ],
  "peer_details": [
    { "url": "https://testnet.rougechain.io/api", "node_name": "RougeChain Primary" },
    { "url": "https://node2.example.com/api", "node_name": "My Cool Node" }
  ],
  "count": 2
}

Register Peer

Register this node with another peer (enables discovery).

POST /api/peers/register
Content-Type: application/json

Request Body

{
  "peerUrl": "https://mynode.example.com",
  "nodeName": "My Validator Node"
}

The nodeName field is optional. If provided, it will be displayed on the network globe at rougechain.io/blockchain.

Response

{
  "success": true,
  "message": "Peer registered"
}

If the peer is already known:

{
  "success": true,
  "message": "Peer already known"
}

Import Block (P2P)

Receive a block from a peer (used for block propagation).

POST /api/blocks/import
Content-Type: application/json

Request Body

The full block object:

{
  "version": 1,
  "header": {
    "version": 1,
    "chainId": "rougechain-devnet-1",
    "height": 42,
    "time": 1706745600000,
    "prevHash": "abc123...",
    "txHash": "def456...",
    "proposerPubKey": "..."
  },
  "txs": [...],
  "proposerSig": "...",
  "hash": "..."
}

Response

{
  "success": true
}

Error Response

{
  "success": false,
  "error": "Block height 42 doesn't extend tip height 40"
}

Broadcast Transaction (P2P)

Receive a transaction broadcast from a peer.

POST /api/tx/broadcast
Content-Type: application/json

Request Body

The full transaction object.

Response

{
  "success": true
}

Peer Discovery Flow

1. Node A starts with --peers "https://seed.example.com"

2. Node A calls GET /api/peers on seed node
   → Receives list of other peers

3. Node A adds new peers to its list

4. If Node A has --public-url, it calls POST /api/peers/register
   on all known peers to announce itself

5. Every 30 seconds, repeat steps 2-4

This creates a mesh network where all nodes eventually discover each other.

Messenger API

Endpoints for the end-to-end encrypted PQC messenger.

All messages are encrypted client-side using ML-KEM-768 key encapsulation and AES-GCM. The server only stores encrypted blobs — it cannot read message contents.

v2 API (March 2026): All write operations now use /api/v2/ endpoints that require ML-DSA-65 signed requests with timestamp validation and nonce-based anti-replay protection. Legacy unsigned endpoints return HTTP 410 (Gone) in production.

Signed Request Format

All v2 write endpoints accept a signed request body:

{
  "payload": {
    "action": "register_wallet",
    "from": "ml-dsa65-public-key-hex",
    "timestamp": 1710100000000,
    "nonce": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
    "...": "operation-specific fields"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "ml-dsa65-public-key-hex"
}

The server verifies: (1) the ML-DSA-65 signature, (2) the timestamp is within a 5-minute window, (3) the from field matches the signing key, and (4) the nonce has not been used before.

Register Messenger Wallet

POST /api/v2/messenger/wallets/register
Content-Type: application/json

Register your wallet's encryption public key so others can send you encrypted messages and mail. This is required before receiving mail from other apps. Requires a signed request.

Request Body (signed)

{
  "payload": {
    "id": "wallet-uuid-or-public-key",
    "displayName": "Alice",
    "signingPublicKey": "ml-dsa65-signing-public-key-hex",
    "encryptionPublicKey": "ml-kem768-encryption-public-key-hex",
    "from": "ml-dsa65-signing-public-key-hex",
    "timestamp": 1710100000000,
    "nonce": "random-hex-nonce"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "ml-dsa65-signing-public-key-hex"
}
FieldTypeDescription
idstringUnique wallet identifier
displayNamestringHuman-readable name (unique, case-insensitive)
signingPublicKeystringML-DSA-65 public key (hex)
encryptionPublicKeystringML-KEM-768 public key (hex) — needed for E2E encryption

Response

{
  "success": true
}

List Messenger Wallets

GET /api/messenger/wallets

Returns all registered messenger wallets.

Response

{
  "success": true,
  "wallets": [
    {
      "id": "wallet-uuid",
      "display_name": "Alice",
      "signing_public_key": "abc123...",
      "encryption_public_key": "def456..."
    }
  ]
}

Send Message

POST /api/v2/messenger/messages
Content-Type: application/json

Requires a signed request. The sender is authenticated via the ML-DSA-65 signature.

Request Body (signed)

The payload includes the message fields plus from, timestamp, and nonce:

{
  "payload": {
    "conversationId": "conv-uuid",
    "senderPublicKey": "sender-pub-hex",
    "recipientPublicKey": "recipient-pub-hex",
    "senderEncrypted": "base64-encrypted-for-sender",
    "recipientEncrypted": "base64-encrypted-for-recipient",
    "senderDisplayName": "Alice",
    "recipientDisplayName": "Bob",
    "selfDestruct": false,
    "destructSeconds": 0,
    "from": "sender-pub-hex",
    "timestamp": 1710100000000,
    "nonce": "random-hex-nonce"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "sender-pub-hex"
}

Messages are encrypted twice — once for the sender (so they can read their sent messages) and once for the recipient. The server stores both ciphertexts.

Self-Destruct Messages

Set selfDestruct: true and destructSeconds to a value. After the recipient reads the message, it is marked as read and hidden from the UI.

Response

{
  "success": true,
  "messageId": "msg-uuid"
}

Get Messages

POST /api/v2/messenger/messages/list
Content-Type: application/json

Requires a signed request. The server verifies the caller is a participant in the conversation.

Request Body (signed)

{
  "payload": {
    "conversationId": "conv-uuid",
    "from": "your-pub-hex",
    "timestamp": 1710100000000,
    "nonce": "random-hex-nonce"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "your-pub-hex"
}

Response

{
  "messages": [
    {
      "id": "msg-uuid",
      "conversationId": "conv-uuid",
      "senderPublicKey": "abc...",
      "senderDisplayName": "Alice",
      "encrypted": "base64-ciphertext",
      "timestamp": 1706745600000,
      "selfDestruct": false,
      "mediaType": null
    }
  ]
}

Mark Message as Read

POST /api/v2/messenger/messages/read
Content-Type: application/json

Used for self-destruct messages. Requires a signed request with messageId and conversationId in the payload.

Delete Message

POST /api/v2/messenger/messages/delete
Content-Type: application/json

Requires a signed request with messageId and conversationId in the payload. The caller must be a conversation participant.

Delete Conversation

POST /api/v2/messenger/conversations/delete
Content-Type: application/json

Requires a signed request with conversationId in the payload. The caller must be a conversation participant.


Get Conversations

POST /api/v2/messenger/conversations/list
Content-Type: application/json

Returns all conversations for a wallet. Requires a signed request.

Response

{
  "conversations": [
    {
      "conversationId": "conv-uuid",
      "participants": [
        {
          "publicKey": "abc...",
          "displayName": "Alice"
        },
        {
          "publicKey": "def...",
          "displayName": "Bob"
        }
      ],
      "lastMessage": "2024-01-31T12:00:00Z",
      "unreadCount": 2
    }
  ]
}

Media Messages

The messenger supports image and video attachments. Media is encrypted and sent as base64 within the message payload.

LimitValue
Max upload size50 MB (before compression)
Compressed target~1.5 MB
Image formatConverted to WebP client-side
Video formatConverted to WebM (VP9) client-side

The client automatically compresses large media before encryption and sending.


Encryption Flow

Sender                                    Recipient
  │                                          │
  │ 1. Generate shared secret via ML-KEM-768 │
  │ 2. Derive AES-256 key via HKDF           │
  │ 3. Encrypt message with AES-GCM          │
  │ 4. Encrypt for self (sender copy)        │
  │ 5. Encrypt for recipient                 │
  │                                          │
  │── POST /messages (both ciphertexts) ────►│
  │                                          │
  │                    6. Decapsulate shared secret
  │                    7. Derive same AES key
  │                    8. Decrypt message

Wallet Blocking

Users can block wallets client-side. Blocked wallets are filtered from conversations and contacts. The block list is stored in browser localStorage under pqc_blocked_wallets.

This is a client-side feature — the server is not involved. Blocked users can still send messages, but they won't appear in the blocking user's UI.


SDK Usage

The @rougechain/sdk provides a high-level API for messenger operations. All write operations now require a wallet parameter for ML-DSA-65 request signing:

import { RougeChain, Wallet } from "@rougechain/sdk";

const rc = new RougeChain("https://testnet.rougechain.io/api");
const wallet = Wallet.generate();

// Register wallet (signed request, required for receiving messages and mail)
await rc.messenger.registerWallet(wallet, {
  id: wallet.publicKey,
  displayName: "Alice",
  signingPublicKey: wallet.publicKey,
  encryptionPublicKey: encPubKey,
});

// Conversations (signed requests)
const convos = await rc.messenger.getConversations(wallet);
await rc.messenger.createConversation(wallet, [wallet.publicKey, recipientPubKey]);

// Messages (signed requests)
const msgs = await rc.messenger.getMessages(wallet, conversationId);
await rc.messenger.sendMessage(wallet, conversationId, encryptedContent, {
  selfDestruct: true,
  destructAfterSeconds: 30,
});
await rc.messenger.markRead(wallet, messageId);
await rc.messenger.deleteMessage(wallet, messageId);

TypeScript types: MessengerWallet, MessengerConversation, MessengerMessage

Mail API

Endpoints for the PQC-encrypted mail system. Mail uses ML-KEM-768 encryption with a Content Encryption Key (CEK) pattern for multi-recipient support, ML-DSA-65 unified signatures, and name registry with atomic registration.

v2 API (March 2026): All write operations now use /api/v2/ endpoints that require ML-DSA-65 signed requests with timestamp validation and nonce-based anti-replay protection. Legacy unsigned endpoints return HTTP 410 (Gone) in production.

Signed Request Format

All v2 write endpoints accept a signed request body:

{
  "payload": {
    "name": "alice",
    "walletId": "wallet-uuid",
    "from": "ml-dsa65-public-key-hex",
    "timestamp": 1710100000000,
    "nonce": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "ml-dsa65-public-key-hex"
}

The server verifies: (1) the ML-DSA-65 signature, (2) the timestamp is within a 5-minute window, (3) the from field matches the signing key, (4) the nonce has not been used before, and (5) the caller is authorized for the operation.

Name Registry

Important for third-party apps: Before a user can receive mail, two things must be registered on the node:

  1. Their wallet (via /api/v2/messenger/wallets/register) — provides the encryption key
  2. Their mail name (via /api/v2/names/register) — maps a human-readable name to the wallet

Register a Name

POST /api/v2/names/register
Content-Type: application/json

Register a human-readable email address (e.g., alice@rouge.quant). Requires a signed request. Registration uses atomic compare-and-swap to prevent race conditions.

Request (signed):

{
  "payload": {
    "name": "alice",
    "walletId": "wallet-uuid-or-public-key",
    "from": "signing-public-key-hex",
    "timestamp": 1710100000000,
    "nonce": "random-hex-nonce"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "signing-public-key-hex"
}

Response:

{
  "success": true,
  "entry": {
    "name": "alice",
    "wallet_id": "wallet-uuid-or-public-key",
    "registered_at": "2026-03-11T00:00:00Z"
  }
}

Error:

{
  "success": false,
  "error": "Name already taken"
}

Resolve a Name

GET /api/names/resolve/:name

Returns the name entry and associated wallet (with encryption keys).

Response:

{
  "success": true,
  "entry": {
    "name": "alice",
    "wallet_id": "wallet-uuid",
    "registered_at": "2026-03-11T00:00:00Z"
  },
  "wallet": {
    "id": "wallet-uuid",
    "display_name": "Alice",
    "signing_public_key": "abc123...",
    "encryption_public_key": "def456..."
  }
}

Reverse Lookup (Wallet ID → Name)

GET /api/names/reverse/:walletId

Response:

{
  "success": true,
  "name": "alice"
}

Release a Name

POST /api/v2/names/release
Content-Type: application/json

Requires a signed request. The caller must own the name (verified via signing key).

{
  "payload": {
    "name": "alice",
    "from": "signing-public-key-hex",
    "timestamp": 1710100000000,
    "nonce": "random-hex-nonce"
  },
  "signature": "ml-dsa65-signature-hex",
  "public_key": "signing-public-key-hex"
}

Send Mail

POST /api/v2/mail/send
Content-Type: application/json

Requires a signed request. The sender is authenticated via ML-DSA-65 signature verification.

Request Body

{
  "from": "alice",
  "fromPublicKey": "sender-pub-hex",
  "to": "bob",
  "toPublicKey": "recipient-pub-hex",
  "senderEncrypted": "base64-encrypted-for-sender",
  "recipientEncrypted": "base64-encrypted-for-recipient",
  "replyToId": null
}

The senderEncrypted and recipientEncrypted fields contain the encrypted subject and body. Both sender and recipient get their own copy, just like the messenger.

Set replyToId to the ID of the mail being replied to, enabling threading.

Response

{
  "success": true,
  "id": "mail-uuid"
}

Get Inbox

POST /api/v2/mail/folder
Content-Type: application/json

Requires a signed request with "folder": "inbox" in the payload. The caller's wallet is resolved from the signing key.

Response

{
  "mail": [
    {
      "id": "mail-uuid",
      "from": "alice",
      "fromPublicKey": "abc...",
      "to": "bob",
      "toPublicKey": "def...",
      "encrypted": "base64-ciphertext",
      "timestamp": 1706745600000,
      "read": false,
      "replyToId": null
    }
  ]
}

Get Sent Mail

POST /api/v2/mail/folder
Content-Type: application/json

Same as inbox but with "folder": "sent" in the payload. Requires a signed request.


Get Single Mail

POST /api/v2/mail/message
Content-Type: application/json

Requires a signed request with messageId in the payload.


Mark as Read

POST /api/v2/mail/read
Content-Type: application/json

Requires a signed request with messageId in the payload.


Move to Folder

POST /api/v2/mail/move
Content-Type: application/json

Requires a signed request with messageId and folder in the payload. Valid folders: inbox, sent, trash, starred, drafts.


Get Trash

POST /api/v2/mail/folder
Content-Type: application/json

Same as inbox/sent but with "folder": "trash" in the payload. Requires a signed request.


Delete Mail

POST /api/v2/mail/delete
Content-Type: application/json

Permanently deletes a mail item. Requires a signed request with messageId in the payload. The caller must be a participant (sender or recipient).


Email Domains

DomainPlatform
@rouge.quantWebsite and browser extensions
@qwalla.mailQWalla mobile app (future)

Threading

Mail threading is handled client-side by following the replyToId chain. When viewing a mail, the client:

  1. Fetches both inbox and sent mail
  2. Walks the replyToId chain to build the thread
  3. Displays messages in chronological order
  4. Collapses older messages, expands the latest two

Encryption

Mail uses a Content Encryption Key (CEK) pattern for efficient multi-recipient support:

  1. Generate a random 256-bit AES key (the CEK)
  2. Encrypt subject, body, and attachment with the CEK via AES-256-GCM
  3. For each recipient (and the sender): encapsulate a shared secret via ML-KEM-768, derive a wrapping key via HKDF, and encrypt the CEK with AES-GCM
  4. Sign the concatenation of all encrypted parts (subject + body + attachment) with ML-DSA-65 (unified signature)
  5. Server stores the encrypted content with per-recipient wrapped keys

Attachments

Mail supports encrypted file attachments up to 2 MB. Attachments are encrypted client-side using the same ML-KEM-768 scheme as the message body.

Sending with Attachment

Include the attachmentEncrypted and hasAttachment fields in the send request:

{
  "fromWalletId": "sender-wallet-id",
  "toWalletIds": ["recipient-wallet-id"],
  "subjectEncrypted": "ml-kem-encrypted-subject",
  "bodyEncrypted": "ml-kem-encrypted-body",
  "attachmentEncrypted": "ml-kem-encrypted-attachment-json",
  "signature": "ml-dsa-65-signature",
  "replyToId": null,
  "hasAttachment": true
}

Attachment Payload

The attachmentEncrypted field contains an ML-KEM encrypted JSON string:

{
  "name": "photo.jpg",
  "type": "image/jpeg",
  "data": "base64-encoded-file-data",
  "size": 102400
}
FieldTypeDescription
namestringOriginal filename
typestringMIME type
datastringBase64-encoded file content
sizenumberFile size in bytes

Reading Attachments

When fetching mail (inbox/sent/trash), messages with attachments will include the attachment_encrypted field. The client must:

  1. Decrypt using the recipient's ML-KEM-768 private key
  2. Parse the JSON to extract the attachment metadata and data
  3. Display inline (images) or offer download (other types)

SDK Usage

The @rougechain/sdk provides a high-level API for name registry and mail operations. All write operations now require a wallet parameter for ML-DSA-65 request signing:

import { RougeChain, Wallet } from "@rougechain/sdk";

const rc = new RougeChain("https://testnet.rougechain.io/api");
const wallet = Wallet.generate();

// Register wallet + name (signed requests)
await rc.messenger.registerWallet(wallet, {
  id: wallet.publicKey,
  displayName: "Alice",
  signingPublicKey: wallet.publicKey,
  encryptionPublicKey: encPubKey,
});
await rc.mail.registerName(wallet, "alice", wallet.publicKey);

// Resolve a recipient before sending (public, no signing needed)
const recipient = await rc.mail.resolveName("bob");
// recipient.wallet.encryption_public_key → use for ML-KEM encryption

// Reverse lookup (public, no signing needed)
const name = await rc.mail.reverseLookup(wallet.publicKey); // "alice"

// Send, read, manage mail (all signed requests)
await rc.mail.send(wallet, { from, to, subject, body, encrypted_subject, encrypted_body });
const inbox = await rc.mail.getInbox(wallet);
await rc.mail.markRead(wallet, messageId);
await rc.mail.move(wallet, messageId, "trash");
await rc.mail.delete(wallet, messageId);

TypeScript types: NameEntry, ResolvedName, MailMessage, SendMailParams

Push Notifications API

Register and unregister Expo push tokens for real-time mobile notifications. Both endpoints require ML-DSA-65 signed payloads — only the wallet owner can register or unregister their token.

Register Push Token

POST /api/push/register
Content-Type: application/json

Request (SignedTransactionRequest)

{
  "payload": {
    "type": "push_register",
    "from": "wallet-public-key",
    "pushToken": "ExponentPushToken[xxx]",
    "platform": "expo",
    "timestamp": 1234567890123,
    "nonce": "random-hex"
  },
  "signature": "ml-dsa-65-signature-hex",
  "public_key": "wallet-public-key"
}

Response

{
  "success": true
}

Unregister Push Token

POST /api/push/unregister
Content-Type: application/json

Request (SignedTransactionRequest)

{
  "payload": {
    "type": "push_unregister",
    "from": "wallet-public-key",
    "timestamp": 1234567890123,
    "nonce": "random-hex"
  },
  "signature": "ml-dsa-65-signature-hex",
  "public_key": "wallet-public-key"
}

Response

{
  "success": true
}

Security

Push token registration is secured with the same SignedTransactionRequest pattern used by v2 API endpoints:

  1. The payload is signed client-side with the wallet's ML-DSA-65 private key
  2. The server verifies the signature against the public_key
  3. Timestamp must be within 5 minutes to prevent replay attacks
  4. Only the wallet owner can register a push token for their public key

This prevents spoofing — an attacker cannot register a rogue push token for someone else's wallet.


SDK Usage

import { RougeChain } from "@rougechain/sdk";

const rc = new RougeChain("https://testnet.rougechain.io/api");

// Register
await rc.registerPushToken(
  publicKey,
  privateKey,
  "ExponentPushToken[xxx]"
);

// Unregister
await rc.unregisterPushToken(publicKey, privateKey);

API Reference — Bridge

ETH/USDC Bridge

Get Bridge Config

GET /api/bridge/config

Returns bridge status, custody address, chain ID, and supported tokens.

Response:

{
  "enabled": true,
  "custodyAddress": "0x...",
  "chainId": 84532,
  "supportedTokens": ["ETH", "USDC"]
}

Claim Bridge Deposit

POST /api/bridge/claim

Claim wrapped tokens (qETH or qUSDC) after depositing on Base Sepolia.

Body:

{
  "evmTxHash": "0x...",
  "evmAddress": "0x...",
  "evmSignature": "0x...",
  "recipientRougechainPubkey": "abc123...",
  "token": "ETH"
}

The token field can be "ETH" (default) or "USDC". The node verifies the EVM transaction, checks the signature, and mints the corresponding wrapped token.

Bridge Withdraw

POST /api/bridge/withdraw

Burn wrapped tokens and create a pending withdrawal for the relayer.

Body (signed):

{
  "fromPublicKey": "abc123...",
  "amountUnits": 10000,
  "evmAddress": "0x...",
  "signature": "...",
  "payload": { "type": "bridge_withdraw", "..." }
}

List Pending Withdrawals

GET /api/bridge/withdrawals

Returns all pending ETH/USDC withdrawals waiting for the relayer.

Fulfill Withdrawal

DELETE /api/bridge/withdrawals/:txId

Mark a withdrawal as fulfilled. Requires x-bridge-relayer-secret header or a PQC-signed body.


XRGE Bridge

Get XRGE Bridge Config

GET /api/bridge/xrge/config

Response:

{
  "enabled": true,
  "vaultAddress": "0x...",
  "tokenAddress": "0x147120faEC9277ec02d957584CFCD92B56A24317",
  "chainId": 84532
}

Claim XRGE Deposit

POST /api/bridge/xrge/claim

Body:

{
  "evmTxHash": "0x...",
  "evmAddress": "0x...",
  "amount": "1000000000000000000",
  "recipientRougechainPubkey": "abc123..."
}

XRGE Withdraw

POST /api/bridge/xrge/withdraw

Body (signed):

{
  "fromPublicKey": "abc123...",
  "amount": 100,
  "evmAddress": "0x...",
  "signature": "...",
  "payload": { "..." }
}

List XRGE Withdrawals

GET /api/bridge/xrge/withdrawals

Fulfill XRGE Withdrawal

DELETE /api/bridge/xrge/withdrawals/:txId

API Reference — NFTs

All write operations use the v2 signed transaction API (client-side signing).

Read Endpoints

List Collections

GET /api/nft/collections

Get Collection

GET /api/nft/collection/:id

Get Collection Tokens

GET /api/nft/collection/:id/tokens

Get Token

GET /api/nft/token/:collection_id/:token_id

Get NFTs by Owner

GET /api/nft/owner/:pubkey

Write Endpoints (v2 Signed)

All write endpoints accept a signed transaction body:

{
  "payload": { "type": "nft_mint", "..." },
  "signature": "...",
  "public_key": "..."
}

Create Collection

POST /api/v2/nft/collection/create

Payload fields: symbol, name, maxSupply, royaltyBps, image, description Fee: 50 XRGE

Mint NFT

POST /api/v2/nft/mint

Payload fields: collectionId, name, metadataUri, attributes Fee: 5 XRGE

Batch Mint

POST /api/v2/nft/batch-mint

Payload fields: collectionId, names, uris, batchAttributes Fee: 5 XRGE per NFT

Transfer NFT

POST /api/v2/nft/transfer

Payload fields: collectionId, tokenId, to, salePrice Fee: 1 XRGE

Burn NFT

POST /api/v2/nft/burn

Payload fields: collectionId, tokenId Fee: 0.1 XRGE

Lock/Unlock NFT

POST /api/v2/nft/lock

Payload fields: collectionId, tokenId, locked Fee: 0.1 XRGE

Freeze Collection

POST /api/v2/nft/freeze-collection

Payload fields: collectionId, frozen Fee: 0.1 XRGE

API Reference — DEX / AMM

Read Endpoints

List Pools

GET /api/pools

Returns all liquidity pools with reserves, LP supply, and token info.

Response:

{
  "success": true,
  "pools": [
    {
      "pool_id": "XRGE-MTK",
      "token_a": "MTK",
      "token_b": "XRGE",
      "reserve_a": 50000,
      "reserve_b": 100000,
      "total_lp_supply": 70710,
      "fee_rate": 0.003
    }
  ]
}

Get Pool

GET /api/pool/:pool_id

Pool ID format: TOKEN_A-TOKEN_B (alphabetically sorted).

Response:

{
  "success": true,
  "pool": {
    "pool_id": "XRGE-MTK",
    "token_a": "MTK",
    "token_b": "XRGE",
    "reserve_a": 50000,
    "reserve_b": 100000,
    "total_lp_supply": 70710,
    "fee_rate": 0.003
  }
}

Get Pool Events

GET /api/pool/:pool_id/events

Returns swap, add_liquidity, and remove_liquidity events for a pool.

Response:

{
  "success": true,
  "events": [
    {
      "id": "evt_abc123",
      "pool_id": "XRGE-MTK",
      "event_type": "Swap",
      "user_pub_key": "abc123...",
      "timestamp": 1710000000,
      "block_height": 12345,
      "tx_hash": "tx_abc...",
      "token_in": "XRGE",
      "token_out": "MTK",
      "amount_in": 1000,
      "amount_out": 490,
      "reserve_a_after": 50490,
      "reserve_b_after": 99000
    }
  ]
}

Get Pool Price History

GET /api/pool/:pool_id/prices

Returns up to 500 price snapshots in chronological order. A snapshot is recorded after every swap, pool creation, and liquidity change.

Response:

{
  "success": true,
  "prices": [
    {
      "pool_id": "XRGE-MTK",
      "timestamp": 1710000000,
      "block_height": 12345,
      "reserve_a": 50000,
      "reserve_b": 100000,
      "price_a_in_b": 2.0,
      "price_b_in_a": 0.5
    }
  ]
}
FieldDescription
price_a_in_bHow many token_b for 1 token_a
price_b_in_aHow many token_a for 1 token_b

Get Pool Stats

GET /api/pool/:pool_id/stats

Response:

{
  "success": true,
  "stats": {
    "pool_id": "XRGE-MTK",
    "total_swaps": 142,
    "total_volume_a": 500000,
    "total_volume_b": 1000000,
    "swap_count_24h": 12,
    "volume_24h_a": 25000,
    "volume_24h_b": 50000
  }
}

Get Swap Quote

POST /api/swap/quote

Body:

{
  "token_in": "XRGE",
  "token_out": "qETH",
  "amount_in": 1000
}

Response:

{
  "success": true,
  "amount_out": 95,
  "price_impact": 0.5,
  "path": ["XRGE", "qETH"],
  "pools": ["XRGE-qETH"]
}

Get All Events

GET /api/events

Returns all DEX events across all pools.

Write Endpoints (v2 Signed)

Create Pool

POST /api/v2/pool/create

Payload fields: token_a, token_b, amount_a, amount_b Fee: 100 XRGE

Add Liquidity

POST /api/v2/pool/add-liquidity

Payload fields: pool_id, amount_a, amount_b Fee: 1 XRGE

Remove Liquidity

POST /api/v2/pool/remove-liquidity

Payload fields: pool_id, lp_amount Fee: 1 XRGE

Execute Swap

POST /api/v2/swap/execute

Payload fields: token_in, token_out, amount_in, min_amount_out Fee: 0.3% of input + 1 XRGE

SDK Usage

import { RougeChain, Wallet } from '@rougechain/sdk';
import type { PriceSnapshot, PoolEvent, PoolStats } from '@rougechain/sdk';

const rc = new RougeChain('https://testnet.rougechain.io/api');

// Read
const pools = await rc.dex.getPools();
const pool = await rc.dex.getPool('XRGE-MTK');
const prices: PriceSnapshot[] = await rc.dex.getPriceHistory('XRGE-MTK');
const stats: PoolStats = await rc.dex.getPoolStats('XRGE-MTK');
const events: PoolEvent[] = await rc.dex.getPoolEvents('XRGE-MTK');

// Quote & swap
const quote = await rc.dex.quote({ poolId: 'XRGE-MTK', tokenIn: 'XRGE', tokenOut: 'MTK', amountIn: 100 });
await rc.dex.swap(wallet, { tokenIn: 'XRGE', tokenOut: 'MTK', amountIn: 100, minAmountOut: 95 });

Smart Contracts API

Deploy Contract

Deploy a WASM smart contract to RougeChain.

POST /api/v2/contract/deploy

FieldTypeRequiredDescription
wasmstringBase64-encoded WASM bytecode
deployerstringDeployer's public key (hex)
noncenumberNonce for deterministic address (default: 0)

Response:

{
  "success": true,
  "address": "a1b2c3d4e5f6...",
  "wasmSize": 12345,
  "txHash": "4520936071b9..."
}

Contract deploy fee: wasmSize × 0.000001 XRGE

Call Contract

Execute a method on a deployed contract (mutating — creates an on-chain tx).

POST /api/v2/contract/call

FieldTypeRequiredDescription
contractAddrstringContract address (hex)
methodstringMethod name to call
callerstringCaller's public key
argsobjectJSON arguments
gasLimitnumberMax fuel (default: 10,000,000)

Response:

{
  "success": true,
  "returnData": { ... },
  "gasUsed": 1500,
  "events": [],
  "txHash": "b226a36688f0...",
  "error": null
}

Contract call fee: gasUsed × 0.000001 XRGE

Get Contract Metadata

GET /api/contract/:addr

Returns contract metadata: address, deployer, code hash, creation timestamp, WASM size.

{
  "success": true,
  "contract": {
    "address": "86fe93e2...",
    "deployer": "test-deployer",
    "code_hash": "a1b2c3...",
    "wasm_size": 711,
    "created_at": 1774401569985
  }
}

Read Contract Storage

Full State Dump

GET /api/contract/:addr/state

Returns all key-value pairs in the contract's persistent storage.

{
  "success": true,
  "state": {
    "count": "42",
    "owner": "alice"
  },
  "count": 2
}

Single Key Lookup

GET /api/contract/:addr/state?key=<hex_key>

Reads a single key from storage.

{
  "success": true,
  "key": "636f756e74",
  "value": "3432",
  "valueUtf8": "42"
}

Get Contract Events

GET /api/contract/:addr/events?limit=50

Returns indexed events emitted by the contract.

{
  "success": true,
  "events": [
    {
      "contract_addr": "86fe93e2...",
      "topic": "transfer",
      "data": "{\"from\":\"alice\",\"to\":\"bob\",\"amount\":100}",
      "block_height": 620,
      "tx_hash": "c3d4e5f6..."
    }
  ],
  "count": 1
}

List All Contracts

GET /api/contracts

Returns all deployed contracts with their metadata.

{
  "success": true,
  "contracts": [
    {
      "address": "86fe93e2...",
      "deployer": "test-deployer",
      "code_hash": "a1b2c3...",
      "wasm_size": 711,
      "created_at": 1774401569985
    }
  ],
  "count": 1
}

Architecture

An overview of RougeChain's system architecture.

High-Level Architecture

┌───────────────────────────────────────────────────────────┐
│                      Clients                               │
│                                                           │
│  ┌──────────┐  ┌──────────────────┐  ┌────────────────┐  │
│  │ Website  │  │ Browser Extension│  │ @rougechain/sdk│  │
│  │ React    │  │ Chrome / Firefox │  │ npm package    │  │
│  └────┬─────┘  └────────┬─────────┘  └───────┬────────┘  │
│       │                 │                     │           │
│       │   Client-side ML-DSA-65 signing       │           │
│       │   Client-side ML-KEM-768 encryption   │           │
│       └────────────┬────┴─────────────────────┘           │
└────────────────────┼──────────────────────────────────────┘
                     │ HTTPS REST API
                     ▼
┌───────────────────────────────────────────────────────────┐
│                   Core Node (Rust)                        │
│                                                           │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │ REST API │  │Blockchain│  │Validator │  │Messenger │ │
│  │ (Actix)  │  │ Engine   │  │ / PoS    │  │ Server   │ │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬─────┘ │
│       │              │              │              │       │
│  ┌────┴──────────────┴──────────────┴──────────────┴────┐ │
│  │                    Storage Layer                      │ │
│  │  chain.jsonl │ validators-db (RocksDB) │ messenger-db│ │
│  └──────────────────────────────────────────────────────┘ │
└────────────────────┬──────────────────────────────────────┘
                     │ P2P (HTTP)
                     ▼
┌───────────────────────────────────────────────────────────┐
│                    Peer Nodes                             │
│         Block sync │ TX broadcast │ Peer discovery        │
└───────────────────────────────────────────────────────────┘

Components

Core Node (Rust)

The backend is a single Rust binary (quantum-vault-daemon) that includes:

ModuleResponsibility
REST APIHTTP endpoints via Actix-web
Blockchain EngineBlock production, transaction processing, state management
Validator / PoSStake tracking, proposer selection, rewards
Messenger ServerStores encrypted messages and wallet registrations
Mail ServerStores encrypted mail, name registry
P2P LayerPeer discovery, block/tx propagation
AMM/DEXLiquidity pools, swap execution, price calculation
BridgeqETH bridge from Base Sepolia

Frontend (React + TypeScript)

The website at rougechain.io is a single-page application built with:

TechnologyPurpose
React 18UI framework
TypeScriptType safety
ViteBuild tool
Tailwind CSSStyling
shadcn/uiComponent library
@noble/post-quantumPQC cryptography (ML-DSA-65, ML-KEM-768)

The frontend is a PWA (Progressive Web App) and can be installed on mobile and desktop.

Browser Extensions

Two Chrome extensions provide wallet functionality:

ExtensionDescription
RougeChain WalletPrimary browser extension
rougechain-walletSecondary extension

Both inject a window.rougechain provider (similar to MetaMask's window.ethereum) for dApp integration.

SDK (@rougechain/sdk)

The npm package @rougechain/sdk provides a programmatic interface for interacting with RougeChain from Node.js or browser applications.

Cryptography Stack

┌─────────────────────────────────────────┐
│            Application Layer            │
│  Transactions │ Messages │ Mail │ Auth  │
└─────────────┬───────────────────────────┘
              │
┌─────────────▼───────────────────────────┐
│         Cryptographic Primitives        │
│                                         │
│  ML-DSA-65 (FIPS 204)                  │
│  └─ Signing: txs, blocks, stakes       │
│                                         │
│  ML-KEM-768 (FIPS 203)                 │
│  └─ Key encapsulation: messenger, mail │
│                                         │
│  AES-256-GCM                           │
│  └─ Symmetric encryption of content    │
│                                         │
│  HKDF (SHA-256)                        │
│  └─ Key derivation from shared secrets │
│                                         │
│  SHA-256                               │
│  └─ Block hashes, tx hashes, Merkle   │
└─────────────────────────────────────────┘

Data Flow

Transaction Flow

1. User creates transaction in browser
2. Transaction payload is constructed
3. ML-DSA-65 signs the payload client-side
4. Signed transaction is sent to node via REST API
5. Node verifies signature
6. Transaction enters mempool
7. Validator includes it in next block
8. Block is signed and propagated to peers

Message Flow

1. Sender looks up recipient's ML-KEM-768 public key
2. ML-KEM-768 encapsulation generates shared secret
3. HKDF derives AES-256 key from shared secret
4. Message is encrypted with AES-GCM (for both sender and recipient)
5. Encrypted blobs are sent to server
6. Recipient fetches encrypted blob
7. ML-KEM-768 decapsulation recovers shared secret
8. Message is decrypted client-side

Mail Flow

1. User registers a name (e.g., alice@rouge.quant) via Name Registry
2. Sender composes email, encrypts subject + body with PQC
3. Encrypted mail is stored on the server
4. Recipient fetches and decrypts client-side
5. Thread history is reconstructed via replyToId chain

Storage

Node Storage

StoreFormatContent
chain.jsonlAppend-only JSON linesBlock data
tip.jsonJSONCurrent chain tip reference
validators-db/RocksDBValidator stakes and state
messenger-db/RocksDBEncrypted messages and wallets

Client Storage

StoreLocationContent
Wallet keyslocalStorageEncrypted ML-DSA-65 and ML-KEM-768 keys
Block listlocalStorageBlocked wallet addresses
Mail settingslocalStorageEmail signature preferences
Display namelocalStorageUser's messenger display name

Security Model

PrincipleImplementation
Keys never leave clientAll signing/encryption happens in-browser
Server is untrustedServer only stores encrypted data
Quantum-resistantNIST-approved PQC algorithms throughout
No seed phrasesKeys are stored directly (backup via file export)
Dual encryptionMessages encrypted for both sender and recipient

Post-Quantum Cryptography

RougeChain uses NIST-approved post-quantum cryptographic algorithms to protect against both classical and quantum computer attacks.

Why Post-Quantum?

Quantum computers threaten current cryptography:

AlgorithmQuantum Threat
RSABroken by Shor's algorithm
ECDSABroken by Shor's algorithm
SHA-256Weakened (Grover's algorithm)
ML-DSASecure
ML-KEMSecure

Algorithms Used

ML-DSA-65 (Digital Signatures)

Formerly: CRYSTALS-Dilithium
Standard: FIPS 204
Security Level: NIST Level 3 (192-bit classical equivalent)

Used for:

  • Transaction signatures
  • Block proposal signatures
  • Validator attestations

Key sizes:

ComponentSize
Public key~1,952 bytes
Private key~4,032 bytes
Signature~3,309 bytes

ML-KEM-768 (Key Encapsulation)

Formerly: CRYSTALS-Kyber
Standard: FIPS 203
Security Level: NIST Level 3

Used for:

  • Messenger encryption
  • PQC Mail encryption
  • Future: Encrypted transactions

Key sizes:

ComponentSize
Public key~1,184 bytes
Private key~2,400 bytes
Ciphertext~1,088 bytes

SHA-256 (Hashing)

Used for:

  • Block hashes
  • Transaction hashes
  • Merkle trees

While Grover's algorithm reduces SHA-256 security to ~128-bit equivalent against quantum computers, this is still considered secure.

Implementation

RougeChain uses the following libraries:

ComponentLibrary
Backend (Rust)pqcrypto crate
Frontend (JS)@noble/post-quantum

All cryptographic operations happen locally - private keys never leave your device.

Key Generation

#![allow(unused)]
fn main() {
// Rust example
use pqcrypto_dilithium::dilithium3::*;

let (pk, sk) = keypair();
let signature = sign(message, &sk);
let valid = verify(message, &signature, &pk);
}
// TypeScript example
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa';

const { publicKey, secretKey } = ml_dsa65.keygen();
const signature = ml_dsa65.sign(message, secretKey);
const valid = ml_dsa65.verify(signature, message, publicKey);

Security Considerations

  1. Key storage - Private keys are stored in localStorage when no vault password is set (for PWA persistence), or encrypted with AES-256-GCM (PBKDF2, 600K iterations) when the user configures a vault passphrase. Active session keys are held in sessionStorage.
  2. Entropy - Keys use cryptographically secure random number generators
  3. Side channels - Library implementations are designed to be constant-time
  4. Hybrid approach - Consider adding classical signatures for defense-in-depth

zk-STARKs (Zero-Knowledge Proofs)

RougeChain includes a zk-STARK proof system for privacy-preserving transaction verification. STARKs are quantum-resistant by design — they rely only on hash functions, not elliptic curves.

How It Works

The STARK module can prove that a balance transfer is valid (value is conserved, sender has sufficient funds) without revealing the actual balances or transfer amount. The verifier only sees the final balances.

PropertyValue
Librarywinterfell (Meta)
Hash functionBlake3-256
Quantum resistance✅ Hash-based (no EC)
Proof typeBalance transfer (value conservation)

Usage

#![allow(unused)]
fn main() {
use quantum_vault_crypto::stark::{
    prove_balance_transfer, verify_balance_transfer, BalanceTransferInputs,
};
use winterfell::math::{fields::f128::BaseElement, FieldElement};

// Prover (knows private balances)
let proof = prove_balance_transfer(1000, 500, 250).unwrap();

// Verifier (only sees final balances)
let public_inputs = BalanceTransferInputs {
    total_value: BaseElement::from(1500u64),
    final_sender_balance: BaseElement::from(750u64),
    final_receiver_balance: BaseElement::from(750u64),
};
verify_balance_transfer(proof, public_inputs).unwrap();
}

zk-STARK Rollup Batch Proofs (Phase 3)

The rollup system batches multiple transfers into a single STARK proof, dramatically reducing on-chain verification costs.

Rollup AIR (5-Column Trace)

sender_before | sender_after | receiver_after | amount | running_hash

Constraints:

  1. Value conservation: sender_after = sender_before - amount
  2. Running hash accumulation: hash[i+1] = hash[i] + sender_before[i] * amount[i]
  3. Cross-check redundancy

Boundary Assertions: running_hash transitions from pre_state_root to post_state_root

State Root

SHA-256 Merkle tree from sorted account balances with domain separation:

  • Leaf: SHA-256("ROUGECHAIN_STATE_V1" || address || balance_le_bytes)
  • Node: SHA-256("ROUGECHAIN_NODE_V1" || left || right)

Rollup API

# Check rollup status
curl https://testnet.rougechain.io/api/v2/rollup/status

# Submit transfer to rollup batch
curl -X POST https://testnet.rougechain.io/api/v2/rollup/submit \
  -H "Content-Type: application/json" \
  -d '{"sender":"pubkey1","receiver":"pubkey2","amount":100,"fee":1}'

# Get completed batch result
curl https://testnet.rougechain.io/api/v2/rollup/batch/1

Bridge Verification (STARK Bridge)

Deposits from Base are cryptographically verified before minting:

  1. EVM Receipt Verification — tx status, recipient, sender, confirmations
  2. SHA-256 Commitment NullifiersSHA-256("ROUGECHAIN_BRIDGE_V1" || tx_hash || address || amount) prevents double-claims
  3. BridgeDeposit Event Verification — for XRGE vault deposits (log parsing)

Future Roadmap

  • zk-STARK proof system (Phase 1: balance transfer AIR)
  • zk-STARK Phase 2: shielded transactions on-chain
  • zk-STARK Phase 3: ZK-rollup layer
  • STARK bridge deposit verification
  • Fully trustless STARK bridge (Base light client)
  • SLH-DSA (SPHINCS+) as alternative signature scheme
  • Hybrid classical+PQC mode
  • Hardware wallet support
  • Threshold signatures for multi-sig
  • WASM-compiled STARK prover for browser (core/wasm-prover/, served as stark-prover.wasm)

PQC Messaging & Mail

RougeChain includes two built-in communication systems — both fully end-to-end encrypted with post-quantum cryptography.

Overview

FeatureMessengerMail
PurposeReal-time chatAsync email
EncryptionML-KEM-768 + AES-GCMML-KEM-768 + AES-GCM
AddressesWallet public keys@rouge.quant / @qwalla.mail names
MediaImages, videos (auto-compressed)Text + attachments (up to 2 MB)
Self-destruct✅ Configurable timer
FoldersInbox, Sent, Trash
ThreadingConversationsReply chains
Server seesEncrypted blobs onlyEncrypted blobs only

Why Post-Quantum?

Classical encrypted messengers (Signal, WhatsApp) use algorithms like X25519 and Ed25519. A sufficiently powerful quantum computer could break these with Shor's algorithm.

RougeChain's messaging uses ML-KEM-768 (CRYSTALS-Kyber, FIPS 203) for key encapsulation — resistant to both classical and quantum attacks. Messages encrypted today remain secure even if quantum computers arrive in the future.

How It Works

Messenger Encryption

Alice                                       Bob
  │                                          │
  │ 1. Look up Bob's ML-KEM-768 public key   │
  │ 2. Encapsulate → shared secret           │
  │ 3. HKDF → AES-256 key                   │
  │ 4. AES-GCM encrypt (for Bob)            │
  │ 5. AES-GCM encrypt (for self)           │
  │                                          │
  │── Signed request to /api/v2/... ────────►│
  │                                          │
  │                  6. Decapsulate → same shared secret
  │                  7. HKDF → same AES-256 key
  │                  8. AES-GCM decrypt

Key principle: The server stores two encrypted blobs per message — one for the sender, one for the recipient. It never has the keys to decrypt either.

Mail Encryption (CEK Pattern)

Mail uses a Content Encryption Key (CEK) pattern for efficient multi-recipient support:

  1. Generate a random 256-bit AES key (the CEK)
  2. Encrypt all mail content (subject, body, attachment) once with the CEK via AES-256-GCM
  3. For each recipient (and the sender): KEM-wrap the CEK using their ML-KEM-768 public key
  4. Sign the concatenation of all encrypted parts with ML-DSA-65 (unified signature)

This design encrypts content only once regardless of recipient count.

Messenger

The real-time messenger is built into the RougeChain web app and browser extension.

Features

  • E2E encrypted conversations between any two wallets
  • Media sharing — images (auto-converted to WebP) and videos (VP9 WebM), compressed client-side
  • Self-destruct messages — set a timer; message disappears after being read
  • Display names — register a name that shows in conversations
  • Contact blocking — client-side block list stored in localStorage
  • Conversation management — create, view, and delete conversations

Getting Started

  1. Go to Messenger in the sidebar
  2. Your wallet is automatically registered
  3. Enter a recipient's public key to start a conversation
  4. Messages are encrypted before leaving your browser

API Endpoints

See Messenger API Reference for full endpoint documentation.

PQC Mail

Mail adds traditional email features on top of the same PQC encryption layer.

@rouge.quant Addresses

Instead of sharing long public keys, register a human-readable name:

ActionResult
Register "alice"You get alice@rouge.quant
Send to "bob"System looks up Bob's public key automatically

Names are unique and first-come-first-served.

Mail Features

  • Subject lines — encrypted along with the body
  • Folders — Inbox, Sent, Trash with move/delete operations
  • Threading — reply chains built client-side via replyToId
  • Mark as read — track read status per message

Email Domains

DomainPlatform
@rouge.quantWebsite and browser extension
@qwalla.mailQWalla mobile app

Both domains resolve against the same on-chain name registry — the domain is a client-side display choice only.

Getting Started

  1. Go to Mail in the sidebar
  2. Click Register Name and choose your @rouge.quant address
  3. Send encrypted mail to any registered name
  4. All encryption/decryption happens in your browser

API Endpoints

See Mail API Reference for full endpoint documentation.

Authenticated Requests

All mail, messenger, and name registry write operations require ML-DSA-65 signed requests via /api/v2/ endpoints. Each request includes:

  • from — Sender's ML-DSA-65 public key
  • timestamp — Millisecond-precision timestamp (valid within a 5-minute window)
  • nonce — 16 bytes of random hex (prevents replay attacks)
  • signature — ML-DSA-65 signature over the canonical JSON payload

The server verifies the signature, validates the timestamp, confirms the sender owns the wallet, and rejects duplicate nonces. Legacy unsigned endpoints return HTTP 410 (Gone) in production.

Trust-on-First-Use (TOFU)

The messenger tracks public key fingerprints (SHA-256 hash) for contacts:

  • On first interaction, the contact's key fingerprint is stored locally
  • On subsequent interactions, the fingerprint is compared — a "Key Changed" warning appears if it differs
  • The shortened fingerprint is shown in the chat header for manual verification

Security Properties

PropertyDetails
Quantum-resistantML-KEM-768 key encapsulation (FIPS 203)
Forward secrecyEach message uses a fresh encapsulation
Zero-knowledge serverServer stores only ciphertext — cannot read messages
Client-side cryptoAll encryption/decryption in the browser via WebAssembly
Dual ciphertextSender and recipient each get their own encrypted copy
Signed requestsAll API calls authenticated with ML-DSA-65 signatures
Anti-replayNonce + timestamp prevents request replay attacks
TOFUKey fingerprint tracking with change detection
Unified signaturesMail signed over all encrypted parts (subject + body + attachment)
CEK multi-recipientEfficient per-recipient key wrapping without re-encryption
Atomic name registryCompare-and-swap prevents race conditions on name claims
Persistent or vaulted keysPrivate keys in localStorage (no password) or AES-256-GCM encrypted blob (with vault passphrase); active session keys in sessionStorage

Notifications & Unread Badges

Browser Extension

The browser extension tracks unread counts for both Chat and Mail tabs:

FeatureDetails
Tab badgesChat and Mail tabs display a numeric badge when unread items exist
TooltipsHovering a badged tab shows "3 unread messages" or "2 unread emails"
Extension icon badgeThe combined unread total (chat + mail) is shown on the browser toolbar icon via chrome.action.setBadgeText
System notificationsNative OS notifications for new messages, new mail, received/sent tokens, contract events, staking, and balance changes
Badge clearingViewing a conversation or inbox marks items as read server-side and updates the badge immediately

Notifications are powered by two channels:

  • WebSocket — Real-time transaction and balance events via wss://testnet.rougechain.io/api/ws
  • Polling — Unread messenger and mail counts checked every 15 seconds via signed /api/v2/ endpoints

QWalla Mobile App

QWalla provides the same unread badge experience:

FeatureDetails
Tab badgesExpo Router tabBarBadge on Chat and Mail tabs
Initial pollOn app launch, actual unread counts are fetched from the server so badges are accurate from the start
Real-time updatesWebSocket events increment the badge for new messages and mail
Push notificationsExpo push notifications for messages, mail, and transfers (requires registerPushToken)
Badge clearingNavigating to the Chat or Mail tab clears the respective unread count

Deriving Unread Counts

The server does not expose a dedicated "unread total" endpoint. Clients derive counts from existing data:

  • Chat: Sum unread_count from each conversation returned by POST /api/v2/messenger/conversations/list
  • Mail: Count inbox items where the label's is_read field is false from POST /api/v2/mail/folder

SDK Usage

All write operations now require a wallet parameter for ML-DSA-65 request signing:

import { RougeChain, Wallet } from '@rougechain/sdk';

const rc = new RougeChain('https://testnet.rougechain.io/api');
const wallet = Wallet.generate();

// Step 1: Register wallet (signed request)
await rc.messenger.registerWallet(wallet, {
  id: wallet.publicKey,
  displayName: 'Alice',
  signingPublicKey: wallet.publicKey,
  encryptionPublicKey: encPubKey,
});

// Step 2: Register a mail name (signed request)
await rc.mail.registerName(wallet, 'alice', wallet.publicKey);

// Resolve a recipient's name → wallet + encryption key (public, no signing needed)
const bob = await rc.mail.resolveName('bob');
// bob.wallet.encryption_public_key → use for ML-KEM encryption

// Reverse lookup (public, no signing needed)
const name = await rc.mail.reverseLookup(wallet.publicKey); // "alice"

For the full messenger and mail APIs, see the SDK documentation.

Social Layer

RougeChain includes a built-in social layer for posts, likes, reposts, follows, comments, and play tracking. Social data is stored server-side in sled with ML-DSA-65 signed writes — tips settle on-chain via rc.transfer().

Features

FeatureDescription
PostsStandalone text posts (max 4000 chars) with threaded replies via replyToId
TimelineGlobal timeline (newest first) and personalized following feed
LikesToggle likes on posts or tracks — reuses the same endpoint
RepostsToggle reposts on any post
CommentsTrack-level comments with pagination
FollowsFollow/unfollow any user; follower and following counts
Play countsRecord plays on tracks (debounced per session)
TipsSend XRGE tips to creators via rc.transfer() — settles on-chain

API Endpoints

Read (unsigned GET):

  • GET /api/social/timeline — Global timeline
  • GET /api/social/post/:postId — Single post with stats
  • GET /api/social/post/:postId/stats — Post engagement stats (likes, reposts, replies)
  • GET /api/social/post/:postId/replies — Threaded replies
  • GET /api/social/user/:pubkey/posts — User's posts
  • GET /api/social/track/:trackId/stats — Track stats (plays, likes, comments)
  • GET /api/social/track/:trackId/comments — Track comments
  • GET /api/social/artist/:pubkey/stats — Artist stats (followers, following)
  • GET /api/social/user/:pubkey/likes — User's liked IDs
  • GET /api/social/user/:pubkey/following — User's followed artists

Write (v2 signed POST):

  • POST /api/v2/social/post — Create a post
  • POST /api/v2/social/post/delete — Delete your post
  • POST /api/v2/social/like — Toggle like
  • POST /api/v2/social/repost — Toggle repost
  • POST /api/v2/social/comment — Post a comment
  • POST /api/v2/social/comment/delete — Delete your comment
  • POST /api/v2/social/follow — Toggle follow
  • POST /api/v2/social/play — Record a play
  • POST /api/v2/social/feed — Get following feed (authenticated)

SDK Usage

import { RougeChain, Wallet } from '@rougechain/sdk';

const rc = new RougeChain('https://testnet.rougechain.io/api');
const wallet = Wallet.generate();

// Create a post
const { post } = await rc.social.createPost(wallet, "Hello RougeChain!");

// Reply
await rc.social.createPost(wallet, "Great post!", post.id);

// Like, repost, follow
await rc.social.toggleLike(wallet, post.id);
await rc.social.toggleRepost(wallet, post.id);
await rc.social.toggleFollow(wallet, artistPubKey);

// Read timeline
const timeline = await rc.social.getGlobalTimeline();
const feed = await rc.social.getFollowingFeed(wallet);

CLI Usage

# Post
rougechain post "Hello from the CLI!"

# Reply
rougechain post "Nice post!" --reply-to <post-id>

# Timeline
rougechain timeline --limit 20

# Your feed (posts from people you follow)
rougechain feed

# Like / repost
rougechain like <post-or-track-id>
rougechain repost <post-id>

# Get a post
rougechain get-post <post-id>

WASM Smart Contracts

RougeChain includes a built-in WASM smart contract engine powered by wasmi — the same pure-Rust WASM interpreter used by Parity/Substrate.

Overview

Contracts are written in Rust (or any language that compiles to WASM), compiled to .wasm, and deployed on-chain. Execution is fuel-metered in a sandbox with host functions for chain interaction.

Architecture

Your Contract (Rust) → cargo build --target wasm32
  → .wasm bytecode
    → Deploy via API
      → Execute in wasmi sandbox
        → Host functions bridge to chain state

Host Functions

Contracts can call these host functions to interact with the chain:

FunctionDescription
host_log(ptr, len)Debug logging
host_get_caller(buf, len)Get caller's public key
host_get_self_addr(buf, len)Get contract's own address
host_get_block_height()Current block height
host_get_block_time()Current block timestamp
host_get_balance(addr, len)Check XRGE balance
host_transfer(to, len, amount)Transfer XRGE from contract
host_storage_read(key, klen, val, vlen)Read persistent storage
host_storage_write(key, klen, val, vlen)Write persistent storage
host_storage_delete(key, klen)Delete from storage
host_emit_event(topic, tlen, data, dlen)Emit indexed event
host_sha256(data, dlen, out)Compute SHA-256 hash
host_set_return(data, dlen)Set return value
host_call_contract(addr, alen, method, mlen, args, argslen, gas)Cross-contract call (returns call_id)
host_get_call_result(call_id, buf, len)Read sub-call result
host_pqc_verify(pk, pklen, msg, msglen, sig, siglen)ML-DSA-65 signature verify
host_pqc_pubkey_to_address(pk, pklen, out, outlen)Derive rouge1... address
host_pqc_hash_pubkey(pk, pklen, out)SHA-256 of public key

Gas Metering & Fees

Every WASM instruction costs 1 fuel unit. The default limit is 10,000,000 fuel per call (≈10M instructions). If a contract runs out of fuel, execution halts and all state changes are reverted.

Fee Schedule

OperationFee Formula
Contract Deploywasm_size_bytes × 0.000001 XRGE
Contract Callgas_used × 0.000001 XRGE

Fees are automatically calculated and included in the on-chain transaction. They appear in the transaction detail view on the explorer.

API

Deploy a Contract

POST /api/v2/contract/deploy
{
  "wasm": "<base64-encoded WASM bytecode>",
  "deployer": "<public key hex>",
  "nonce": 0
}

Call a Contract Method

POST /api/v2/contract/call
{
  "contractAddr": "<contract address>",
  "method": "my_method",
  "caller": "<public key>",
  "args": { "key": "value" },
  "gasLimit": 10000000
}

Query Contract State

GET /api/contract/{addr}                # metadata
GET /api/contract/{addr}/state          # full state dump (all keys)
GET /api/contract/{addr}/state?key=x    # single key lookup
GET /api/contract/{addr}/events         # event log
GET /api/contracts                      # list all contracts

ERC-20 Token Standard

RougeChain includes a reference ERC-20 token contract at contracts/erc20_template/. This implements the standard fungible token interface:

MethodArgsDescription
init{name, symbol, decimals, total_supply, owner}Initialize token, mint supply to owner
name / symbol / decimals / total_supply{}Token metadata queries
balance_of{account}Get account balance
transfer{to, amount}Transfer tokens (caller → to)
approve{spender, amount}Set allowance
allowance{owner, spender}Get allowance
transfer_from{from, to, amount}Transfer using allowance

Storage Layout

  • meta:name / meta:symbol / meta:decimals / meta:total_supply — Token metadata
  • bal:{account} — Account balances
  • allow:{owner}:{spender} — Allowances

Build & Deploy

cd contracts/erc20_template
cargo build --release --target wasm32-unknown-unknown
# Deploy the .wasm from target/wasm32-unknown-unknown/release/

Explorer Integration

Deployed contracts are visible in the RougeChain explorer:

  • Contracts Explorer (/contracts) — List all deployed contracts with search/sort
  • Contract Detail (/contract/{addr}) — Contract info, live state viewer, interactive call UI
  • Transaction Detail — Contract txs show: contract address, method, gas used, WASM size

SDK

import { RougeChain } from '@rougechain/sdk';

const rc = new RougeChain({ baseUrl: 'https://rougechain.io' });

// Deploy
const deploy = await rc.deployContract({
  wasm: base64WasmBytes,
  deployer: wallet.publicKey,
});

// Call
const result = await rc.callContract({
  contractAddr: deploy.address,
  method: 'increment',
  caller: wallet.publicKey,
});

// Query
const meta = await rc.getContract(deploy.address);
const events = await rc.getContractEvents(deploy.address);
const allState = await rc.getContractState(deploy.address);         // full dump
const single = await rc.getContractState(deploy.address, '636f756e74'); // single key

MCP Server (AI Agents)

The RougeChain MCP server exposes smart contract operations as tools for AI agents:

ToolDescription
list_contractsList all deployed contracts
get_contractGet contract metadata
get_contract_stateRead state (single key or full dump)
get_contract_eventsGet contract event log
deploy_contractDeploy WASM bytecode
call_contractExecute a contract method

Security

WASM smart contracts maintain RougeChain's post-quantum security guarantees:

  • All contract interactions are ML-DSA-65 signed transactions
  • WASM execution is pure computation — no classical crypto involved
  • Contract addresses are derived deterministically via SHA-256
  • Execution is sandboxed with no host OS access

Cross-Contract Calls

Contracts can call other contracts using host functions. Calls are queued during execution and processed recursively by the runtime after the primary call completes.

Host Functions

FunctionReturnsDescription
host_call_contract(addr, alen, method, mlen, args, argslen, gas)call_id (i32)Queue a call to another contract
host_get_call_result(call_id, buf, len)bytes writtenRead the result of a sub-call

Behavior

  • Max depth: 8 nested calls (prevents infinite recursion)
  • State merging: storage writes, events, and balance deltas from sub-calls are merged atomically
  • Gas: sub-calls consume gas from the parent's remaining budget
  • Failure: if a sub-call fails, it returns -2 from host_get_call_result; the parent can handle it gracefully

Example (Rust)

#![allow(unused)]
fn main() {
extern "C" {
    fn host_call_contract(
        addr: *const u8, alen: u32,
        method: *const u8, mlen: u32,
        args: *const u8, argslen: u32,
        gas: u64,
    ) -> i32;
    fn host_get_call_result(call_id: i32, buf: *mut u8, len: u32) -> i32;
}

#[no_mangle]
pub extern "C" fn call_other() {
    let addr = b"contract_abc123...";
    let method = b"get_value";
    let args = b"{}";
    let call_id = unsafe {
        host_call_contract(
            addr.as_ptr(), addr.len() as u32,
            method.as_ptr(), method.len() as u32,
            args.as_ptr(), args.len() as u32,
            1_000_000,
        )
    };
    // Result is available after host processes the call queue
    let mut buf = [0u8; 1024];
    let n = unsafe { host_get_call_result(call_id, buf.as_mut_ptr(), buf.len() as u32) };
    // n > 0: success, n bytes of return data
    // n == -2: sub-call failed
}
}

PQC Precompiles

Native post-quantum cryptographic operations available as host functions — no need to implement ML-DSA in WASM.

FunctionReturnsDescription
host_pqc_verify(pk, pklen, msg, msglen, sig, siglen)1 valid, 0 invalid, -1 errorML-DSA-65 signature verification
host_pqc_pubkey_to_address(pk, pklen, out, outlen)bytes writtenDerive rouge1... bech32m address from raw pubkey
host_pqc_hash_pubkey(pk, pklen, out)32 on successSHA-256 hash of public key

Key Sizes

  • Public Key: 1,952 bytes (ML-DSA-65)
  • Signature: 3,309 bytes (ML-DSA-65)
  • Address: ~63 bytes (rouge1... bech32m string)
  • Pubkey Hash: 32 bytes (SHA-256)

Use Cases

  • On-chain auth: Verify a user's PQC signature inside a contract
  • Address derivation: Convert a pubkey to a rouge1... address for permission checks
  • Identity checks: Compare pubkey hashes for compact storage

EIP-1559 Dynamic Fees

RougeChain uses an EIP-1559-like fee model with base fee adjustment and fee burning:

How It Works

  1. Base fee adjusts ±12.5% per block based on block fullness (target: 10 txs/block)
  2. Floor: minimum base fee of 0.001 XRGE
  3. Fee burning: base fee portion is burned (deflationary)
  4. Priority fee (tip): 20% to block proposer, 70% to validators (stake-weighted), 10% to treasury

API

GET /api/fee-info
{
  "baseFee": 0.0377,
  "suggestedPriorityFee": 0.0038,
  "suggestedTotalFee": 0.0415,
  "totalBurned": 68.16,
  "targetTxsPerBlock": 10,
  "maxChangePercent": 12.5,
  "blockHeight": 618
}

Multi-Signature Wallets

RougeChain supports M-of-N multi-signature wallets with PQC-safe ML-DSA-65 aggregate verification.

Overview

A multi-sig wallet requires M out of N co-signers to approve a transaction before it executes. This enables:

  • Shared treasury management — DAOs, teams, and organizations
  • Secure cold storage — 2-of-3 schemes (e.g. hardware + phone + backup)
  • Escrow — Buyer + seller + arbitrator

Transaction Types

Create a Wallet

{
  "tx_type": "multisig_create",
  "payload": {
    "multisig_signers": ["pubkey_hex_1", "pubkey_hex_2", "pubkey_hex_3"],
    "multisig_threshold": 2,
    "multisig_label": "Team Treasury"
  }
}
  • Minimum 2 signers
  • Threshold must be ≥ 1 and ≤ number of signers
  • Wallet ID is auto-generated if not provided

Submit a Proposal

{
  "tx_type": "multisig_submit",
  "payload": {
    "multisig_wallet_id": "ms-abc123...",
    "multisig_proposal_tx_type": "transfer",
    "multisig_proposal_payload": {
      "to_pub_key_hex": "recipient_pubkey_hex",
      "amount": 1000
    },
    "multisig_proposal_fee": 0.1
  }
}
  • Only signers of the wallet can submit proposals
  • The submitter's signature counts as the first approval

Approve a Proposal

{
  "tx_type": "multisig_approve",
  "payload": {
    "multisig_proposal_id": "mp-def456..."
  }
}
  • Only signers who haven't already approved can approve
  • When threshold is reached, the proposal auto-executes
  • For transfer proposals: funds move from the wallet creator's balance

API Endpoints

MethodEndpointDescription
GET/api/multisig/walletsList all multi-sig wallets
GET/api/multisig/wallet/:idGet wallet details
GET/api/multisig/wallet/:id/proposalsList proposals for a wallet
GET/api/multisig/wallets/:pubkeyFind wallets where pubkey is a signer

Response Format

{
  "success": true,
  "wallet": {
    "wallet_id": "ms-abc123...",
    "creator": "pubkey_hex",
    "signers": ["pubkey1", "pubkey2", "pubkey3"],
    "threshold": 2,
    "created_at_height": 500,
    "label": "Team Treasury"
  }
}

SDK

import { RougeChain } from '@rougechain/sdk';

const rc = new RougeChain({ baseUrl: 'https://rougechain.io' });

// Create a 2-of-3 wallet
await rc.sendTransaction({
  txType: 'multisig_create',
  payload: {
    multisig_signers: [keyA, keyB, keyC],
    multisig_threshold: 2,
    multisig_label: 'Team Treasury',
  },
});

// Submit a transfer proposal
await rc.sendTransaction({
  txType: 'multisig_submit',
  payload: {
    multisig_wallet_id: 'ms-abc123...',
    multisig_proposal_tx_type: 'transfer',
    multisig_proposal_payload: { to_pub_key_hex: recipient, amount: 1000 },
  },
});

// Co-signer approves
await rc.sendTransaction({
  txType: 'multisig_approve',
  payload: { multisig_proposal_id: 'mp-def456...' },
});

// Query wallets
const wallets = await rc.get('/api/multisig/wallets');
const myWallets = await rc.get(`/api/multisig/wallets/${myPubKey}`);

MCP Server (AI Agent Integration)

RougeChain is the first blockchain with native MCP (Model Context Protocol) integration. AI agents like Claude, ChatGPT, and custom agents can interact with the blockchain using standardized MCP tools.

What is MCP?

The Model Context Protocol is an open standard for AI agents to interact with external services. RougeChain's MCP server exposes 29 blockchain tools that any MCP-compatible agent can use.

Setup

cd mcp-server
npm install
npm run build

Claude Desktop

Add to ~/.config/claude/claude_desktop_config.json:

{
  "mcpServers": {
    "rougechain": {
      "command": "node",
      "args": ["/path/to/mcp-server/dist/index.js"],
      "env": {
        "ROUGECHAIN_URL": "https://rougechain.io"
      }
    }
  }
}

Available Tools (29)

CategoryTools
Chainget_chain_stats, get_block, get_latest_blocks
Walletget_balance, get_transaction
Tokenslist_tokens, get_token, get_token_holders
DeFilist_pools, get_swap_quote
NFTslist_nft_collections, get_nft_collection
Validatorslist_validators
Contractslist_contracts, get_contract, get_contract_state, get_contract_events, deploy_contract, call_contract
Socialget_global_timeline, get_post, get_user_posts, get_post_replies, get_track_stats, get_artist_stats
Mail & Messagingresolve_name, reverse_lookup_name, list_messenger_wallets
Otherlist_proposals, get_fee_info

Architecture

AI Agent (Claude / GPT / Custom)
    ↕ stdio (MCP protocol)
RougeChain MCP Server
    ↕ HTTPS
RougeChain Node API
    ↕ PQC-signed transactions
RougeChain L1 (ML-DSA + ML-KEM)

All operations maintain post-quantum security guarantees.

Resources

The MCP server also exposes a rougechain://info resource with static context about RougeChain's technology stack, features, and API endpoints.

Browser Extensions

RougeChain provides browser extensions that serve as quantum-safe wallets, similar to how MetaMask works for Ethereum — but using post-quantum cryptography.

Available Extensions

ExtensionDescriptionStore
RougeChain WalletPrimary browser extensionChrome Web Store

Features

  • Wallet — View balances, send/receive XRGE, claim faucet, custom token support
  • Encrypted Messenger — E2E encrypted chat using ML-KEM-768 + ML-DSA-65
  • PQC Mail — Encrypted email with @rouge.quant addresses
  • Vault Lock — AES-256-GCM encryption with PBKDF2 key derivation and auto-lock timer
  • Cross-browser — Chrome, Edge, Brave, Opera, Arc, Firefox (Manifest V3)

Installation

From Chrome Web Store

  1. Visit the extension page on the Chrome Web Store
  2. Click Add to Chrome
  3. The extension icon appears in your toolbar

From Source

cd browser-extension
npm install
npm run build
  1. Open chrome://extensions (or edge://extensions, brave://extensions)
  2. Enable Developer mode
  3. Click Load unpacked
  4. Select the browser-extension/dist folder

Firefox

cd browser-extension
npm install
npm run build
  1. Open about:debugging#/runtime/this-firefox
  2. Click Load Temporary Add-on
  3. Select browser-extension/dist/manifest.json

DApp Integration

The extension injects a window.rougechain provider object, similar to MetaMask's window.ethereum. DApps can use this to interact with the user's wallet.

Detecting the Extension

if (window.rougechain) {
  console.log('RougeChain wallet detected');
  const address = await window.rougechain.getAddress();
}

Provider API

MethodDescription
getAddress()Get the user's public key
signTransaction(tx)Sign a transaction with ML-DSA-65
getBalance()Get the wallet's balance
getNetwork()Get the current network (testnet/devnet)

Security

FeatureImplementation
Vault encryptionAES-256-GCM with PBKDF2-derived key
Auto-lockConfigurable timer via background service worker
Key storagechrome.storage.local (encrypted)
SigningML-DSA-65 (FIPS 204) — quantum-resistant
EncryptionML-KEM-768 (FIPS 203) — quantum-resistant

Permissions

The extensions request minimal permissions:

PermissionPurpose
storageStore encrypted wallet data
alarmsAuto-lock timer
notificationsTransaction alerts
Host permissionsConnect to RougeChain nodes (rougechain.io, testnet.rougechain.io, localhost)

Why Not MetaMask?

MetaMask and all EVM wallets use secp256k1 / ECDSA cryptography. RougeChain uses ML-DSA-65 / ML-KEM-768 (post-quantum). The key formats, signature schemes, and transaction structures are fundamentally incompatible. RougeChain's extensions are purpose-built for quantum-safe operations.

See PQC Cryptography for details on the algorithms used.

SDK (@rougechain/sdk)

The official JavaScript/TypeScript SDK for building on RougeChain.

Installation

npm install @rougechain/sdk

Quick Start

import { RougeChain, Wallet } from '@rougechain/sdk';

const rc = new RougeChain('https://testnet.rougechain.io/api');

// Generate a new post-quantum wallet
const wallet = Wallet.generate();
console.log('Public key:', wallet.publicKey);

// Request testnet tokens
await rc.faucet(wallet);

// Check balance
const balance = await rc.getBalance(wallet.publicKey);
console.log('Balance:', balance);

// Transfer tokens
await rc.transfer(wallet, { to: recipientPubKey, amount: 100 });

Wallet

All signing happens client-side. Private keys never leave your application.

import { Wallet } from '@rougechain/sdk';

// Generate new keypair
const wallet = Wallet.generate();

// Restore from saved keys
const restored = Wallet.fromKeys(publicKey, privateKey);

// Export for storage
const keys = wallet.toJSON(); // { publicKey, privateKey }

// Verify keypair integrity
const valid = wallet.verify(); // true

Client

import { RougeChain } from '@rougechain/sdk';

// Basic connection
const rc = new RougeChain('https://testnet.rougechain.io/api');

// With API key
const rc = new RougeChain('https://testnet.rougechain.io/api', {
  apiKey: 'your-api-key',
});

Available Methods

Queries

await rc.getHealth();
await rc.getStats();
await rc.getBlocks({ limit: 10 });
await rc.getBalance(publicKey);
await rc.getValidators();
await rc.getTokens();
await rc.getBurnedTokens();

Transactions

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.burn(wallet, 500, 1, 'XRGE');
await rc.faucet(wallet);

Staking

await rc.stake(wallet, { amount: 1000 });
await rc.unstake(wallet, { amount: 500 });

DEX (rc.dex)

// Pool queries
await rc.dex.getPools();
await rc.dex.getPool('XRGE-MTK');
await rc.dex.getPriceHistory('XRGE-MTK');  // PriceSnapshot[] — for building charts
await rc.dex.getPoolStats('XRGE-MTK');     // volume, swap counts (total + 24h)
await rc.dex.getPoolEvents('XRGE-MTK');    // swap/add/remove event history

// Quote & swap
await rc.dex.quote({ poolId: 'XRGE-MTK', tokenIn: 'XRGE', tokenOut: 'MTK', amountIn: 100 });
await rc.dex.swap(wallet, { tokenIn: 'XRGE', tokenOut: 'MTK', amountIn: 100, minAmountOut: 95 });

// Liquidity
await rc.dex.createPool(wallet, { tokenA: 'XRGE', tokenB: 'MTK', amountA: 10000, amountB: 5000 });
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)

await rc.nft.createCollection(wallet, { symbol: 'ART', name: 'My Art', royaltyBps: 500, maxSupply: 10000 });
await rc.nft.mint(wallet, { collectionId: 'abc123', name: 'Piece #1', metadataUri: '...' });
await rc.nft.batchMint(wallet, { collectionId: 'abc123', names: ['#1', '#2'], uris: ['...', '...'] });
await rc.nft.transfer(wallet, { collectionId: 'abc123', tokenId: 1, to: buyerPubKey, salePrice: 100 });
await rc.nft.getCollections();
await rc.nft.getByOwner(wallet.publicKey);

Bridge (rc.bridge)

await rc.bridge.getConfig();
await rc.bridge.withdraw(wallet, { amount: 500000, evmAddress: '0x...' });
await rc.bridge.claim({ evmTxHash: '0x...', evmAddress: '0x...', evmSignature: '0x...', recipientPubkey: wallet.publicKey });
await rc.bridge.getWithdrawals();

Mail Name Registry (rc.mail)

Register human-readable names and resolve recipients before sending encrypted mail. Third-party apps must call these for cross-app mail to work.

All write operations require a wallet parameter for ML-DSA-65 signed requests via /api/v2/ endpoints with anti-replay nonce protection.

// Register wallet on the node first (signed request)
await rc.messenger.registerWallet(wallet, {
  id: wallet.publicKey,
  displayName: "Alice",
  signingPublicKey: wallet.publicKey,
  encryptionPublicKey: encPubKey,
});

// Register a mail name (signed request)
await rc.mail.registerName(wallet, "alice", wallet.publicKey);

// Resolve a name → wallet info (public, no signing needed)
const resolved = await rc.mail.resolveName("bob");
// { entry: { name, wallet_id }, wallet: { id, encryption_public_key, ... } }

// Reverse lookup: wallet ID → name (public, no signing needed)
const name = await rc.mail.reverseLookup(wallet.publicKey); // "alice"

// Release a name (signed request)
await rc.mail.releaseName(wallet, "alice");

// Send mail (signed request, multi-recipient CEK encryption)
await rc.mail.send(wallet, { from, to, subject, body, encrypted_subject, encrypted_body });

// Read inbox / sent (signed requests)
const inbox = await rc.mail.getInbox(wallet);
const sent = await rc.mail.getSent(wallet);

// Manage mail (signed requests)
await rc.mail.markRead(wallet, messageId);
await rc.mail.move(wallet, messageId, "trash");
await rc.mail.delete(wallet, messageId);

Messenger (rc.messenger)

All operations use ML-DSA-65 signed requests with nonce-based anti-replay protection.

await rc.messenger.getWallets();
await rc.messenger.registerWallet(wallet, { id, displayName, signingPublicKey, encryptionPublicKey });
await rc.messenger.getConversations(wallet);
await rc.messenger.createConversation(wallet, [pubKeyA, pubKeyB]);
await rc.messenger.getMessages(wallet, conversationId);
await rc.messenger.sendMessage(wallet, conversationId, encryptedContent, { selfDestruct: true, destructAfterSeconds: 30 });
await rc.messenger.deleteMessage(wallet, messageId, conversationId);
await rc.messenger.deleteConversation(wallet, conversationId);

Push Notifications

// Register for push notifications (PQC-signed)
await rc.registerPushToken(publicKey, privateKey, 'ExponentPushToken[xxx]');

// Unregister
await rc.unregisterPushToken(publicKey, privateKey);

Address Resolution

// Resolve rouge1… address to public key, or vice versa
const result = await rc.resolveAddress('rouge1q8f3x...');
// → { address, publicKey, balance }

Account Nonce

const nonce = await rc.getNonce(publicKey);
// → { nonce, next_nonce }

Token Allowances

// ERC-20 style approve/transferFrom
await rc.approveAllowance(wallet, { spender, token, amount });
await rc.transferFrom(wallet, { owner, to, token, amount });
await rc.freezeToken(wallet, { token, frozen: true });

Multi-Sig Wallets

// Create a 2-of-3 multi-sig wallet
await rc.sendTransaction(wallet, {
  txType: 'multisig_create',
  payload: {
    multisig_signers: [keyA, keyB, keyC],
    multisig_threshold: 2,
    multisig_label: 'Team Treasury',
  },
});

// Submit a transfer proposal
await rc.sendTransaction(wallet, {
  txType: 'multisig_submit',
  payload: {
    multisig_wallet_id: 'ms-abc123...',
    multisig_proposal_tx_type: 'transfer',
    multisig_proposal_payload: { to_pub_key_hex: recipient, amount: 1000 },
  },
});

// Co-signer approves
await rc.sendTransaction(coSignerWallet, {
  txType: 'multisig_approve',
  payload: { multisig_proposal_id: 'mp-def456...' },
});

// Query wallets + proposals
const wallets = await rc.get('/multisig/wallets');
const myWallets = await rc.get(`/multisig/wallets/${myPubKey}`);
const proposals = await rc.get(`/multisig/wallet/${walletId}/proposals`);

Smart Contracts (rc.shielded)

Contract helpers are accessed via the rc.shielded sub-client:

// Deploy WASM contract
const deploy = await rc.shielded.deployContract({
  wasm: base64WasmBytes,
  deployer: wallet.publicKey,
});

// Call contract method
const result = await rc.shielded.callContract({
  contractAddr: deploy.address,
  method: 'increment',
  caller: wallet.publicKey,
  args: { key: 'value' },
  gasLimit: 10_000_000,
});

// Query contract
const meta = await rc.shielded.getContract(deploy.address);
const state = await rc.shielded.getContractState(deploy.address);
const events = await rc.shielded.getContractEvents(deploy.address);

EIP-1559 Fee Info

const feeInfo = await rc.getFeeInfo();
// → { base_fee, total_fee_suggestion, total_fees_burned, block_height }

Environment Support

EnvironmentRequirements
BrowserAny bundler (Vite, webpack)
Node.js 18+Works out of the box
Node.js < 18Provide node-fetch polyfill
React NativeInstall react-native-get-random-values

TypeScript

Full type declarations are included:

import type {
  Block, Transaction, Validator, LiquidityPool, NftCollection,
  NameEntry, ResolvedName, MailMessage, SendMailParams,
  PriceSnapshot, PoolStats, SwapQuote,
} from '@rougechain/sdk';

Security

  • All signatures use ML-DSA-65 (FIPS 204), resistant to quantum attacks
  • Private keys never leave your application
  • The SDK does not store keys — persistence is your responsibility
  • All mail, messenger, and name registry operations use ML-DSA-65 signed requests with timestamp validation and nonce-based anti-replay protection
  • Mail uses a CEK pattern for efficient multi-recipient encryption via ML-KEM-768
  • TOFU key fingerprint tracking for contact key-change detection

Source

The SDK source code is in the sdk/ directory of the quantum-vault repository.

Troubleshooting

Common issues and how to fix them.


Node Issues

Node won't start

"Address already in use"

Another process is using the port. Find it and stop it, or use a different port:

# Linux/macOS
lsof -i :5100
kill -9 <PID>

# Windows (PowerShell)
Get-Process -Id (Get-NetTCPConnection -LocalPort 5100).OwningProcess
Stop-Process -Id <PID>

# Or just use a different port
./quantum-vault-daemon --api-port 5102

"cargo not found"

Rust isn't in your PATH. Fix with:

source ~/.cargo/env
# Or restart your terminal

"OpenSSL not found" (build error)

Install OpenSSL development headers:

# Ubuntu/Debian
sudo apt install libssl-dev pkg-config

# Fedora/RHEL
sudo dnf install openssl-devel

# macOS
brew install openssl

# Windows — install Visual Studio Build Tools with C++ workload

Node won't sync

CheckHow
Node is runningcurl http://127.0.0.1:5100/api/health
Peers are correctMake sure --peers includes /api: --peers "https://testnet.rougechain.io/api"
Firewall isn't blockingEnsure port 5100 (or your --api-port) is open
Testnet is reachablecurl https://testnet.rougechain.io/api/health

Peers value of 0

Your node isn't connected to anyone. Check:

  • You passed --peers with the correct URL (including /api)
  • Your internet connection is working
  • The testnet node is online: curl https://testnet.rougechain.io/api/stats

Height stuck at 0

If your node's chain height isn't advancing:

  • Ensure --peers is set (solo nodes without peers don't receive blocks)
  • If you're mining solo (--mine without --peers), blocks are only produced locally
  • Check logs for sync errors

Blocks not propagating

If you're mining but other nodes don't see your blocks:

  1. Set --public-url — Without this, your node is invisible to the network. Other nodes can't sync from you.
  2. Check your firewall — Your API port must be reachable from the internet
  3. Verify with peers API:
    curl https://testnet.rougechain.io/api/peers
    # Your node's URL should appear in the list
    

Transaction Issues

"Insufficient balance"

Transaction amount + fee must be less than your balance. The fee is 0.1 XRGE per transfer.

Required: amount + 0.1 XRGE

Use the faucet to get more tokens:

  • Website: Go to the Wallet page and click "Request Faucet"
  • API: POST /api/v2/faucet

"Transaction rejected"

Common causes:

CauseFix
Wrong signatureEnsure you're signing with the correct private key
Duplicate transactionWait a moment and retry — the previous tx may still be processing
Node not syncedCheck GET /api/health — your node's height should match the network
Stale nonceRefresh your wallet state and retry

"Failed to fetch" in the web app

  • Check you're connected to the right network (Testnet vs local Devnet)
  • If using local devnet, make sure the daemon is running
  • Check browser console (F12) for the actual error
  • Verify CORS: the node only allows specific origins by default

Wallet Issues

Wallet not loading

  • Clear browser cache and localStorage
  • Check the browser console for errors (F12 → Console)
  • Try a different browser
  • If using the extension, check it's enabled and not suspended

Lost private key

There is no recovery mechanism. Private keys are stored locally in your browser. If you clear browser data, the keys are gone.

Best practice: Always export and safely store your keys after creating a wallet.

Extension not connecting

  • Check the extension is enabled in your browser
  • Ensure you're on a supported page (the extension injects on pages that need it)
  • Try disabling and re-enabling the extension
  • Check if another wallet extension is conflicting

Staking Issues

Can't stake — "insufficient balance"

You need at least 1,000 XRGE plus the transaction fee. Use the faucet multiple times if needed.

Staked but not producing blocks

  • Ensure --mine flag is set on your node
  • Your node must be synced (height matches the network)
  • Validator selection is stake-weighted — with minimum stake, you'll produce blocks less frequently
  • Check your validator status: GET /api/validators

Unstaked but balance not returned

After unstaking, the tokens are returned to your wallet address. Check your balance:

curl "https://testnet.rougechain.io/api/balance/YOUR_PUBLIC_KEY"

Bridge Issues

Bridge deposit not credited

  1. Confirm the EVM transaction was confirmed on Base Sepolia
  2. Wait up to 2 minutes — the bridge relayer polls periodically
  3. Check the bridge config: GET /api/bridge/config
  4. Verify the custody address matches your bridge target

Withdrawal pending

Withdrawals require the bridge relayer to process them. Check status:

curl "https://testnet.rougechain.io/api/bridge/withdrawals"

DEX Issues

Swap failed — "slippage exceeded"

The price moved between your quote and execution. Increase your slippage tolerance or retry.

Pool creation failed

  • Both tokens must exist on the network
  • You need sufficient balance of both tokens
  • Pool creation fee is 10 XRGE

Platform-Specific

Windows: Build fails

  1. Install Visual Studio Build Tools with the C++ desktop workload
  2. Install Rust — make sure cargo is in your PATH
  3. Restart your terminal after installation

Windows: Permission denied

Run PowerShell as Administrator, or check that your antivirus isn't blocking the daemon.

macOS: "developer cannot be verified"

xattr -d com.apple.quarantine ./target/release/quantum-vault-daemon

Still Stuck?

  • Check the GitHub Issues
  • Visit the built-in node dashboard at http://localhost:5100 for live diagnostics
  • Review daemon logs (stderr output) for detailed error messages

Changelog

All notable changes to RougeChain.


Testnet v0.2.4 — March 2026

Added

  • Social layer — On-chain social features with plays, likes, comments, follows, and tips. Data is stored server-side in sled with ML-DSA-65 signed writes; tips settle on-chain via rc.transfer()
  • Standalone posts — Create, delete, and fetch posts (max 4000 chars) with threaded replies via replyToId. Global timeline and personalized following feed endpoints
  • Reposts — Toggle repost on any post; repost counts aggregated per post with viewer state
  • Post stats — Aggregate endpoint returns likes, reposts, reply count, and viewer's liked/reposted state for any post
  • Following feed — Authenticated endpoint returns posts from users the viewer follows, sorted newest-first
  • qRougee social integration — TrackDetail shows play counts, like button, tip modal, and comments. TrackCard badges show plays/likes. ArtistProfile shows followers and follow button. Library includes a "Liked" tab. Home page sorts discovery by popularity
  • SDK v1.0.0rc.social namespace with 19 methods: createPost, deletePost, toggleRepost, getPost, getPostStats, getPostReplies, getUserPosts, getGlobalTimeline, getFollowingFeed, plus existing play/like/comment/follow methods
  • WASM STARK prover — Browser-side STARK proof generation via core/wasm-prover/ compiled to WebAssembly. Unshield and shielded transfer operations now generate real winterfell STARK proofs client-side without relying on a trusted server
  • Groq-powered Quantum Bot — Messenger AI bot proxied through the node using Groq's llama-3.1-8b-instant model with a comprehensive RougeChain knowledge base
  • Mail & messenger unread badges — Browser extension and QWalla app show unread count badges on both Chat and Mail tabs with hover tooltips (e.g. "3 unread emails"). The browser extension icon badge displays the combined unread total
  • Native browser notifications — Browser extension fires system notifications for new messages, new mail, received/sent tokens, contract deployments, staking events, and balance changes via WebSocket and periodic polling
  • Mail reply pre-fill — Clicking "Reply" in the browser extension auto-populates the recipient, subject (with "Re:"), and quoted original message body
  • Mail attachments (extension) — Browser extension mail compose and read views now support file attachments with upload, preview, and download, matching the website's feature set
  • Initial unread count polling (QWalla) — App fetches actual unread chat and mail counts from the server on launch so tab badges are accurate immediately, not just after a real-time event arrives

Fixed

  • Custom token transfersv2_transfer handler now correctly sets token_symbol on TxPayload (previously only set token_name), which the balance engine actually reads. Without this, all custom token transfers silently moved XRGE instead of the selected token
  • Browser extension token send — Added token picker dropdown to the send form so users can send any held token, not just XRGE. Upgraded from legacy v1 /tx/submit (which sent the private key to the server) to v2 signed endpoint with client-side ML-DSA-65 signing
  • Browser extension import token — Added MetaMask-style "Import Custom Token" flow: enter a symbol, validates on-chain, persists to tracked list. Replaced the "All Tokens on Chain" dump with a curated held + imported view
  • Qwalla custom token balance validation — Send screen now fetches and validates the correct token balance when sending a custom token, instead of always checking XRGE balance
  • Website send dialog pre-selection — Clicking "Send" from a token's detail view now pre-selects that token instead of always defaulting to XRGE
  • Swap pre-fill from Trade button — "Trade on this Pool" now passes tokenIn/tokenOut URL params to the swap page, correctly pre-filling both sides of the swap
  • PWA wallet persistence — Wallet private keys now persist in localStorage when no vault password is set, preventing wallet loss on PWA/tab restart. Password-protected vaults continue to use encrypted storage only
  • Browser extension key regeneration — Removed aggressive version-based key regeneration that was replacing existing valid keys and causing loss of on-chain identity (faucet funds)
  • Quantum Bot registration — Bot wallets use unique per-browser IDs and register as non-discoverable to prevent display name conflicts
  • Browser extension messenger — All 8 messenger endpoints migrated from legacy unsigned v1 to ML-DSA-65 signed v2 endpoints, fixing "Registration failed" errors in production
  • Browser extension mail decryption — Ported v2 multi-recipient CEK encryption/decryption to the extension, fixing "[Unable to decrypt]" on mail sent from the website
  • QWalla mail encryption — Added v2 CEK encryption/decryption for cross-client mail compatibility with the website and browser extension
  • QWalla message signing — Messages now include ML-DSA-65 content signatures; fixed ml_dsa65.sign argument order that caused "secretKey expected Uint8Array of length 4032" errors
  • Message signature display — Three-state indicator: green check (verified), red X (failed), grey shield (no signature) — prevents false negatives on unsigned legacy messages
  • Shielded note badge — Browser extension wallet tab now shows shielded notes for the current wallet only (per-wallet getActiveNotes) instead of the global chain stat
  • Unread badge persistence — Badge clears correctly after viewing messages; lastKnownUnread persisted to chrome.storage.local to survive service worker restarts
  • QWalla @qwalla.mail domain — Mail addresses now display as @qwalla.mail throughout the app instead of @rouge.quant
  • QWalla mail name resolution — Mail list and detail views resolve and display registered names instead of raw wallet IDs

Changed

  • Validator economics — Base fee burn reduced from 100% to 50%; remaining 50% flows into the tip pool for validator rewards. A 0.1 XRGE/block minimum tip floor is guaranteed from the staking reserves allocation
  • Minimum stake — Enforced at 10,000 XRGE (previously unenforced). Staking requests below this threshold are rejected
  • Entropy prefetch — ANU QRNG entropy is now fetched in a background thread and cached, eliminating per-block blocking HTTP calls that could stall block production for up to 20 seconds
  • Service worker cache bumped to rougechain-v2 to invalidate stale assets on existing PWA installs
  • Session-only private keys policy updated: localStorage used for unprotected wallets, sessionStorage-only when vault passphrase is configured

Testnet v0.2.3 — March 2026

Security Hardening

  • Signed API requests — All 16 mail, messenger, and name registry endpoints now require ML-DSA-65 signed requests via /api/v2/ routes. Legacy unsigned endpoints return HTTP 410 (Gone)
  • Anti-replay nonces — Each signed request includes a cryptographically random nonce; duplicates within the timestamp window are rejected server-side
  • Multi-recipient CEK encryption — Mail content encrypted once with a random AES-256 CEK, KEM-wrapped individually per recipient via ML-KEM-768
  • Unified mail signatures — Single ML-DSA-65 signature over concatenation of all encrypted parts (subject + body + attachment) prevents partial content substitution
  • TOFU key verification — Public key fingerprints (SHA-256) tracked on first use with key-change warnings displayed in messenger UI
  • Atomic name registration — Name registry uses sled compare-and-swap (CAS) to prevent TOCTOU race conditions during name claims
  • Sled messenger storage — Messenger data migrated from JSON file to sled embedded database with per-record atomic operations and automatic migration from legacy format
  • Server-side input validation — Length limits enforced on all fields (display names: 50 chars, message content: 2 MB, mail subject: 10 KB, mail body: 512 KB, attachments: 3 MB, max 50 recipients)
  • Session-only private keys — Web app stores private keys in sessionStorage (cleared on tab close) instead of localStorage; encrypted wallet blob persists in localStorage (superseded in v0.2.4: localStorage used when no vault password is set for PWA persistence)
  • Legacy decryption removal — Pre-v2 mail and messenger decryption fallbacks removed to reduce attack surface

Changed

  • Messenger, mail, and name registry SDK methods now require a wallet parameter for request signing
  • WHITEPAPER.md updated to v1.7 with full security hardening documentation

Testnet v0.2.2 — March 2026

Added

  • SDK v0.8.4 — Name registry methods: rc.mail.registerName(), rc.mail.resolveName(), rc.mail.reverseLookup(), rc.mail.releaseName()
  • SDK typesNameEntry, ResolvedName exported for TypeScript consumers
  • Browser Extension — BIP-39 seed phrase support: generate, view, and import 24-word mnemonic phrases

Fixed

  • API docs corrected: name registry endpoints now show actual routes (/names/resolve/:name, /names/reverse/:walletId) instead of non-existent query-param URLs
  • Blockchain explorer chain validation no longer fails on descending block order from API
  • Tamper detection demo works correctly with real blocks from the API
  • SDK SwapQuoteParams now includes required tokenOut field

Testnet v0.2.1 — March 2026

Added

  • Mail Attachments — Encrypted file attachments (up to 2 MB) via ML-KEM-768
  • Push Notifications — PQC-signed Expo push token registration (/api/push/register)
  • Address Resolution — Convert between rouge1… and hex via /api/resolve/:input
  • Account Nonce APIGET /api/account/:pubkey/nonce for replay protection
  • SDK v0.8.2registerPushToken(), unregisterPushToken(), resolveAddress(), getNonce()

Fixed

  • Auto-migration of stale timestamp-based nonces to sequential nonces on node startup

Testnet v0.2.0 — March 2026

Added

  • PQC Mail — Encrypted email with @rouge.quant addresses, threading, and folder management
  • RC-721 NFTs — Collections, batch minting, royalties, transferring, burning, and freezing
  • AMM/DEX — Uniswap V2-style liquidity pools, swaps, and price charts
  • XRGE Bridge — Bidirectional bridge for XRGE between RougeChain and Base (ERC-20)
  • Shielded Transactions — Private transfers using STARK proofs with on-chain nullifiers
  • Token Staking Pools — Stake custom tokens with configurable reward rates
  • Governance — On-chain proposal creation and weighted voting
  • Token Locking — Time-locked and vesting token locks
  • Token Allowances — Approve and spend-from delegation
  • ZK Rollup (Phase 3) — Batch transaction accumulation with proof submission
  • Tiered Rate Limiting — Validators and peers get separate rate limit tiers
  • Network Globe — 3D visualization of connected nodes on the blockchain page
  • SDK@rougechain/sdk npm package for building dApps
  • Docker Support — One-command node deployment with docker run
  • Node Dashboard — Built-in web dashboard at http://localhost:5100 when running a node
  • Name Registry — Register human-readable names for wallets (alice@rouge.quant)
  • Browser Extensions — Chrome/Firefox wallet extensions with vault lock

Changed

  • Default block time reduced from 1000ms to 400ms
  • --peers URL now requires /api suffix (e.g., https://testnet.rougechain.io/api)
  • v1 endpoints that accept private keys are disabled by default (use --dev to enable)
  • Secure v2 API with client-side signing is the default for all write operations

Security

  • All signatures use ML-DSA-65 (FIPS 204)
  • All key encapsulation uses ML-KEM-768 (FIPS 203)
  • Client-side signing — private keys never leave your browser
  • Validator-proven rate limiting with PQC signature verification

Testnet v0.1.0 — February 2026

Added

  • Core blockchain — Proof of Stake L1 with PQC cryptography
  • Wallet — Generate ML-DSA-65 keypairs, send/receive XRGE
  • Faucet — Request Testnet tokens
  • Validator staking — Stake XRGE to become a block proposer
  • Encrypted Messenger — E2E encrypted messaging with ML-KEM-768 and AES-GCM
  • Self-destruct messages — Messages that auto-delete after being read
  • Token creation — Create custom tokens with metadata
  • Token burning — Official burn address with on-chain tracking
  • P2P networking — Peer discovery, block propagation, and automatic sync
  • ETH Bridge (qETH) — Bridge ETH from Base Sepolia with 6-decimal precision
  • Block Explorer — Browse blocks, transactions, and addresses
  • gRPC API — Chain, wallet, validator, and messenger services
  • REST API — Full HTTP API for all blockchain operations