DevelopmentIntermediate13 min read2026-07-16

Building on QuanChain: A Developer Quickstart Guide

TL;DR: The QuanChain SDK provides everything you need to generate ML-DSA key pairs, fund a testnet wallet, send quantum-safe transactions, and deploy smart contracts in under an hour. Install via npm, connect to the QuanChain testnet, generate your key pair with a single function call, and use the faucet to fund your development wallet. All wallets are quantum-safe at the protocol level: no extra configuration is required.

Setting Up Your QuanChain Development Environment

The QuanChain development environment requires Node.js 20 or later, the QuanChain SDK installed via npm, and a testnet RPC endpoint. Setup takes approximately ten minutes from a clean machine, and the testnet faucet provides sufficient tokens for development and testing without any registration.

QuanChain development follows a similar workflow to Ethereum development, with one core difference in the cryptographic primitives used for wallet generation and transaction signing. Rather than secp256k1 ECDSA key pairs, QuanChain wallets use ML-DSA-65 key pairs that comply with NIST FIPS 204. The QuanChain SDK abstracts this difference, exposing a wallet API that behaves like familiar Web3 tooling while using post-quantum signatures under the hood.

Install the QuanChain SDK and initialize a new project:

npm install @quanchain/sdk
# or with pnpm
pnpm add @quanchain/sdk

The SDK ships with full TypeScript type definitions. No additional @types package is required. Connect to the testnet by instantiating a client:

import { QuanChainClient } from '@quanchain/sdk';

const client = new QuanChainClient({
  rpcUrl: 'https://testnet-rpc.quanchain.ai',
  network: 'testnet',
});

For local development, the SDK also supports a local QuanChain node via Docker. See the QuanChain developer documentation for the Docker Compose configuration. Running a local node gives you deterministic block times and unlimited faucet access, which simplifies automated testing.

Generating Your First ML-DSA Key Pair

ML-DSA key pair generation on QuanChain uses the ML-DSA-65 parameter set from NIST FIPS 204 by default. The SDK wraps a production-quality implementation to produce a 1952-byte public key and a 4032-byte private key, both derived from a BIP-39-compatible seed phrase so that standard mnemonic backup flows work without modification.

The Wallet class handles key generation, address derivation, message signing, and transaction authorization. Wallet addresses are derived from a BLAKE3 hash of the ML-DSA-65 public key, producing a fixed-length 40-hex-character address regardless of the larger underlying key size.

import { Wallet } from '@quanchain/sdk';

// Generate a fresh wallet with a new random key pair
const wallet = Wallet.generate();
console.log('Address:  ', wallet.address);
console.log('Mnemonic: ', wallet.mnemonic);
// Public key is 1952 bytes (ML-DSA-65)
// Private key is 4032 bytes (ML-DSA-65)

// Restore a wallet from an existing mnemonic
const restored = Wallet.fromMnemonic(
  'quantum safe dragon twelve example phrase ...'
);

// Sign a message and verify the signature
const message = 'Hello, QuanChain';
const signature = await wallet.signMessage(message);
const valid = await Wallet.verifyMessage(message, signature, wallet.address);
console.log('Valid:', valid); // true

Each time you sign a transaction, the SDK automatically derives the next ML-DSA-65 child key and includes its commitment in the transaction payload. This is the SpendAndRotate mechanism: after the transaction confirms, only the next committed key can authorize future transactions from that address. The rotation is atomic with confirmation, so the public key that appears in any confirmed transaction already controls no funds by the time an adversary could collect and process it.

The ML-DSA-65 signature produced for each transaction is 3309 bytes. This is significantly larger than a 64-byte ECDSA signature, but QuanChain's block structure and fee model are calibrated for this size. You do not need to adjust fee parameters manually; the SDK fee estimator accounts for the actual post-signing byte count.

Funding Your Wallet From the Testnet Faucet

The QuanChain testnet faucet provides 100 testnet QCH per request, limited to one request per address per 24 hours. Testnet QCH has no monetary value and is used exclusively for development and testing. Access the faucet through the developer portal or the SDK CLI.

Via the QuanChain testnet portal: paste your wallet address and click Request Tokens. The faucet credits your address within a few seconds.

Via the SDK CLI:

npx qchan faucet --address 0xYourWalletAddress --network testnet

Check your balance programmatically:

const balance = await client.getBalance(wallet.address);
console.log('Balance:', balance.toString(), 'QCH');

For high-volume testing scenarios such as load tests or performance benchmarks, the standard faucet rate limit may be insufficient. The QuanChain developer program provides expanded testnet allocation for projects building production applications on QuanChain. Applications take approximately two business days to process.

Sending Your First Quantum-Safe Transaction

Sending a transaction on QuanChain requires a recipient address, an amount, and an optional data payload. The SDK handles ML-DSA-65 signing, SpendAndRotate key rotation, nonce management, and fee estimation automatically. A basic transfer confirms in two to four seconds on the testnet under normal conditions.

import { QuanChainClient, Wallet } from '@quanchain/sdk';

const client = new QuanChainClient({ network: 'testnet' });
const wallet = Wallet.fromMnemonic(process.env.MNEMONIC!);

// Send 1 QCH to a recipient
const tx = await client.sendTransaction({
  from: wallet,
  to: '0xRecipientAddress',
  value: '1000000000000000000', // 1 QCH in wei-equivalent units
});

console.log('Hash:            ', tx.hash);
console.log('Next key commit: ', tx.nextKeyCommitment);

// Wait for confirmation
const receipt = await tx.wait();
console.log('Block:    ', receipt.blockNumber);
console.log('Gas used: ', receipt.gasUsed.toString());

The transaction receipt includes a nextKeyCommitment field that records the hash of the next ML-DSA-65 public key on-chain. This commitment is what enables SpendAndRotate: the network will only accept future transactions from this address signed with the committed next key, preventing replay attacks and ensuring key rotation integrity even if an attacker derives the spent key's private key later.

Transaction fees on QuanChain are denominated in QCH and paid by the sender. Because ML-DSA-65 signatures are roughly 51 times larger than ECDSA signatures by byte count, the fee model weights byte size more heavily than simple gas unit counts. The SDK abstracts this: client.estimateGas() returns an accurate estimate that accounts for the full signed transaction size before you broadcast.

Understanding the Three-Channel Architecture for Developers

QuanChain separates blockchain communication into three distinct channels: the transaction channel for value transfers and smart contract calls, the consensus channel for validator block proposals and voting, and the key channel for public key commitments and rotation records. Each channel uses independent ML-DSA key pairs, limiting the blast radius of any single key compromise to one channel.

As an application developer, you interact primarily with the transaction channel and the key channel. The transaction channel is the standard interface: sending transactions, calling contracts, querying balances, and reading event logs all go through the transaction channel via the standard SDK client.

The key channel is where active public key commitments live. When a SpendAndRotate transaction confirms, the new public key commitment is registered in the key channel. Smart contracts, bridge validators, and DApp backends query the key channel to resolve the currently active public key for any address. This is essential for any contract that needs to verify off-chain ML-DSA signatures, such as permit-style gasless transactions or cross-chain message attestations:

// Resolve the current active public key for a wallet address
const keyRecord = await client.keyChannel.getCurrentKey(wallet.address);
console.log('Active public key:    ', keyRecord.publicKey);
console.log('Registered at block:  ', keyRecord.blockHeight);
console.log('Previous commitment:  ', keyRecord.previousCommitment);

The consensus channel uses ML-DSA-87 (NIST security level 5, equivalent to AES-256 against quantum adversaries) for all validator messages. This is a deliberate choice to provide the highest available ML-DSA security for protocol-level operations where compromise would affect the entire network. Application developers running their own validator nodes should consult the QuanChain technology documentation for consensus channel key management requirements.

Deploying a Smart Contract on QuanChain

Smart contract deployment on QuanChain uses Solidity 0.8.x with a QuanChain-specific Hardhat plugin that adds ML-DSA signature verification precompiles to the compiler and testing environment. The deployment process is near-identical to Ethereum, with the deployer's ML-DSA signature triggering automatic key rotation on the deployer address at confirmation.

Install the Hardhat plugin:

npm install --save-dev @quanchain/hardhat-plugin hardhat

Configure hardhat.config.ts:

import '@quanchain/hardhat-plugin';
import { HardhatUserConfig } from 'hardhat/config';

const config: HardhatUserConfig = {
  solidity: '0.8.24',
  networks: {
    quanchain_testnet: {
      url: 'https://testnet-rpc.quanchain.ai',
      accounts: [process.env.DEPLOYER_MNEMONIC!],
    },
  },
};
export default config;

QuanChain exposes an MLDSA_VERIFY precompile at address 0x09 for on-chain ML-DSA signature verification. Any contract that verifies off-chain-signed messages, such as a registry that accepts signed attestations or a multisig that verifies ML-DSA quorum signatures, should call this precompile rather than implementing lattice arithmetic in Solidity.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IMLDSA {
    function verify(
        bytes32 messageHash,
        bytes calldata signature,
        bytes calldata publicKey
    ) external view returns (bool);
}

contract QuantumSafeRegistry {
    IMLDSA constant MLDSA = IMLDSA(address(0x09));
    mapping(address => bytes32) public registeredHash;

    function register(
        bytes32 dataHash,
        bytes calldata mldsaSig,
        bytes calldata publicKey
    ) external {
        require(
            MLDSA.verify(dataHash, mldsaSig, publicKey),
            'Invalid ML-DSA signature'
        );
        registeredHash[msg.sender] = dataHash;
    }
}

Deploy to the testnet:

npx hardhat run scripts/deploy.ts --network quanchain_testnet

The deployment receipt includes a nextKeyCommitment field just like any other transaction. If you plan to interact with the deployed contract from the same wallet immediately after deployment, retrieve the committed next public key from the receipt or the key channel before attempting further transactions. Attempting to sign with the pre-deployment key after the deployment transaction confirms will be rejected by the network.

For a complete end-to-end example including tests, deployment scripts, and a sample DApp frontend, see the QuanChain developer documentation. The docs include a Foundry integration guide for developers who prefer the Foundry toolchain over Hardhat.

Frequently Asked Questions