QUANTUM CHAIN quantumcha.in ↗
DEVELOPER DOCUMENTATION · TYPESCRIPT SDK 2.0.1

Build directly on
Quantum Chain.

Construct, sign, submit, and track canonical Quantum transactions from Node.js. Deploy and call EVM smart contracts with native post-quantum signing—without recreating chain formats in application code.

32-bytecanonical signing digest
20804Quantum Sandbox chain ID
Node 22 / 24supported LTS runtimes
Network identity

Validate the chain before using it.

The SDK identifies known networks with both chain ID and genesis hash. This prevents a matching numeric ID from silently selecting the wrong chain.

FieldQuantum SandboxQuantum Mainnet
Network codequantum-sandboxquantum-mainnet
Chain / P2P ID2080420803
Genesis hash0x3d9c6813ee3edd227abb5345aff55d6e2e76bc9db3a031d92d0e2bd54c8997e70x3517886b1e875e8ab5229f4ced47be7020c81775d93abac25824ca113b7a0816
CurrencyQ, 18 decimalsQ, 18 decimals
Sandbox accessThe hosted RPC and faucet are source-allowlisted. HTTP 403 means your egress address needs approval; it is not an SDK or JSON-RPC failure.
Installation

Private npm packages, standard npm workflow.

Ask Quantum Chain for read access to the quantum_chain npm organization. Put the issued read-only token in your user or deployment secret configuration—never in the application source.

shell
mkdir quantum-sandbox-demo
cd quantum-sandbox-demo
npm init -y
npm install @quantum_chain/sdk @quantum_chain/pqc
npm install --save-dev tsx typescript @types/node

Example user-level npm configuration:

.npmrc
@quantum_chain:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
Five-minute quickstart

Fund a wallet and send a transaction.

This complete script validates the permanent Sandbox, funds a short-lived sender, transfers 0.01 Q, and waits for a successful receipt.

send.ts
import {
  QuantumClient,
  QuantumWallet,
  formatQ,
  networks,
  parseQ,
} from "@quantum_chain/sdk";

const client = new QuantumClient(networks.sandbox);
const sender = QuantumWallet.generate();
const recipient = QuantumWallet.generate();

try {
  const identity = await client.validateNetwork();
  console.log("network", identity.network, identity.chainId.toString());
  console.log("sender", sender.address);

  const funding = await client.sandbox!.fund(sender.address);
  console.log("balance", formatQ(funding.balance), "Q");

  const submitted = await sender.sendTransaction(client, {
    to: recipient.address,
    value: parseQ("0.01"),
  });
  console.log("transaction", submitted.hash);

  const receipt = await submitted.wait({
    confirmations: 1,
    timeoutMs: 120_000,
    pollIntervalMs: 1_000,
  });
  console.log("status", receipt.status);
  console.log("block", receipt.blockNumber.toString());
  console.log("explorer", client.getExplorerTxUrl(submitted.hash));
} finally {
  sender.destroy();
  recipient.destroy();
}
shell
npx tsx send.ts

What the SDK does

  1. Validates chain ID and genesis hash.
  2. Resolves pending nonce, fees, and gas estimate.
  3. Constructs the canonical unsigned Quantum transaction.
  4. Signs its explicit 32-byte digest through the native package.
  5. Serializes signature and public key into the chain transaction.
  6. Submits once and verifies the node returned the expected local hash.
  7. Waits for a successful receipt and requested confirmations.
Smart contracts

Compile and deploy a contract.

Quantum Chain executes EVM bytecode. The SDK handles the Quantum transaction. In this example solc compiles Solidity and viem is used only to encode ABI values—not for signing or RPC.

contracts/Counter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

contract Counter {
    uint256 public number;
    event NumberChanged(uint256 number);

    constructor(uint256 initialNumber) { number = initialNumber; }
    function increment() external {
        number += 1;
        emit NumberChanged(number);
    }
}

Install solc and viem, then compile the contract into an artifact containing its ABI and creation bytecode:

scripts/compile.mjs
import { mkdir, readFile, writeFile } from "node:fs/promises";
import solc from "solc";

const source = await readFile("contracts/Counter.sol", "utf8");
const input = {
  language: "Solidity",
  sources: { "Counter.sol": { content: source } },
  settings: { outputSelection: { "*": { "*": ["abi", "evm.bytecode.object"] } } },
};
const output = JSON.parse(solc.compile(JSON.stringify(input)));
const errors = (output.errors ?? []).filter((item) => item.severity === "error");
if (errors.length) throw new Error(errors.map((item) => item.formattedMessage).join("\n"));
const result = output.contracts["Counter.sol"].Counter;
await mkdir("artifacts", { recursive: true });
await writeFile("artifacts/Counter.json", JSON.stringify({
  abi: result.abi,
  bytecode: `0x${result.evm.bytecode.object}`,
}, null, 2));
shell
node scripts/compile.mjs

Deploy the compiled creation bytecode by omitting to:

deploy.ts
import { readFile, writeFile } from "node:fs/promises";
import {
  QuantumClient, QuantumWallet, hexToBytes, networks,
} from "@quantum_chain/sdk";
import { encodeAbiParameters } from "viem";

const artifact = JSON.parse(await readFile("artifacts/Counter.json", "utf8"));
const args = encodeAbiParameters([{ type: "uint256" }], [41n]);
const deploymentData = new Uint8Array([
  ...hexToBytes(artifact.bytecode),
  ...hexToBytes(args),
]);

const client = new QuantumClient(networks.sandbox);
const deployer = QuantumWallet.generate();

try {
  await client.validateNetwork();
  await client.sandbox!.fund(deployer.address);
  const tx = await deployer.sendTransaction(client, {
    data: deploymentData, // no `to`: contract creation
    value: 0n,
  });
  const receipt = await tx.wait({ timeoutMs: 180_000 });
  if (!receipt.contractAddress) throw new Error("missing contract address");
  const code = await client.getCode(receipt.contractAddress);
  if (!code.length) throw new Error("deployment produced no runtime code");
  const deployment = {
    network: "quantum-sandbox",
    chainId: "20804",
    transactionHash: tx.hash,
    blockNumber: receipt.blockNumber.toString(),
    contractAddress: receipt.contractAddress,
  };
  await writeFile("deployment.json", JSON.stringify(deployment, null, 2));
  console.log(deployment);
} finally {
  deployer.destroy();
}
Production deploymentsPin the exact compiler and settings, retain the artifact and transaction hash, validate deployed runtime bytecode, and replace the generated test wallet with an approved QuantumSigner.
Contract interaction

Read state, write state, verify the receipt.

interact.ts
import { readFile } from "node:fs/promises";
import {
  QuantumClient, QuantumContract, QuantumWallet, bytesToHex, networks,
} from "@quantum_chain/sdk";
import { decodeFunctionResult, encodeFunctionData } from "viem";

const artifact = JSON.parse(await readFile("artifacts/Counter.json", "utf8"));
const deployment = JSON.parse(await readFile("deployment.json", "utf8"));
const client = new QuantumClient(networks.sandbox);
const contract = new QuantumContract(deployment.contractAddress, client);
const writer = QuantumWallet.generate();

async function readNumber(): Promise<bigint> {
  const data = encodeFunctionData({ abi: artifact.abi, functionName: "number" });
  const output = await contract.call(data);
  return decodeFunctionResult({
    abi: artifact.abi,
    functionName: "number",
    data: bytesToHex(output),
  }) as bigint;
}

try {
  await client.validateNetwork();
  console.log("before", (await readNumber()).toString());
  await client.sandbox!.fund(writer.address);
  const data = encodeFunctionData({ abi: artifact.abi, functionName: "increment" });
  const submitted = await contract.send(writer, data);
  const receipt = await submitted.wait({ timeoutMs: 120_000 });
  console.log("hash", submitted.hash);
  console.log("status", receipt.status);
  console.log("after", (await readNumber()).toString());
} finally {
  writer.destroy();
}

contract.call() returns raw bytes. contract.send() returns a QuantumTransactionResponse with the transaction hash and wait(). Decode return data and events with the contract ABI.

Adapter integration

Construct and sign without RPC.

Applications that own nonce resolution, submission, and lifecycle handling can import the RPC-free subpaths. The result includes every artifact needed by an adapter.

scope-b.ts
import {
  buildAndSignTransaction, parseQ,
} from "@quantum_chain/sdk/transactions";
import { LocalPQCSigner } from "@quantum_chain/sdk/signers";

const signer = new LocalPQCSigner();
try {
  const result = await buildAndSignTransaction({
    type: "legacy",
    chainId: 20804n,
    nonce: 12n,
    gasLimit: 21_000n,
    gasPrice: 1_000_000_000n,
    to: "0x0000000000000000000000000000000000000001",
    value: parseQ("1"),
  }, signer);

  console.log(result.rawTransaction);     // submit through your RPC layer
  console.log(result.transactionHash);    // locally computed hash
  console.log(result.publicKey);          // embedded public key
  console.log(result.signature);          // embedded signature
  console.log(result.signingDigest);      // exact 32-byte digest
} finally {
  signer.destroy();
}
SDK reference

Public TypeScript surface.

Entry points

ImportPurposeRPC
@quantum_chain/sdkClient, wallet, contracts, transactions, networks, units, errorsOptional
@quantum_chain/sdk/transactionsBuild, sign, serialize, hashNever
@quantum_chain/sdk/signersQuantumSigner and native local signerNever
@quantum_chain/sdk/rpcClient, HTTP transport, RPC types and errorsYes

QuantumClient

getChainId()Promise<bigint> detectNetwork()detected identity validateNetwork()validated identity getBalance(address, block?)Promise<bigint> getTransactionCount(address, block?)Promise<bigint> getGasPrice()Promise<bigint> getMaxPriorityFeePerGas()Promise<bigint> estimateGas(call)Promise<bigint> call(call, block?)Promise<Uint8Array> getCode(address, block?)Promise<Uint8Array> getBlockNumber()Promise<bigint> getBlockByNumber(block, full?)block or null getBlockByHash(hash, full?)block or null getTransaction(hash)transaction or null getTransactionReceipt(hash)receipt or null prepareTransaction(from, request)unsigned transaction getSigningDigest(transaction)32-byte Uint8Array broadcastTransaction(signed)transaction response

Transaction request

TypeScript
interface TransactionRequest {
  to?: string;                       // omit for contract creation
  value?: bigint | string;           // bigint Qwei, decimal string Q
  data?: Uint8Array | `0x${string}`;
  gasLimit?: bigint;
  gasPrice?: bigint;
  maxFeePerGas?: bigint;
  maxPriorityFeePerGas?: bigint;
  nonce?: bigint;
  chainId?: bigint;
  type?: "legacy" | "dynamic";
  accessList?: readonly AccessTuple[];
}

Scope-B result

TypeScript
interface SignTransactionResult {
  unsignedTransaction: UnsignedQuantumTransaction;
  signedTransaction: SignedQuantumTransaction;
  signingDigest: Uint8Array;
  signature: Uint8Array;
  publicKey: Uint8Array;
  rawTransaction: `0x${string}`;
  rawTransactionBytes: Uint8Array;
  transactionHash: `0x${string}`;
}

Wallet, contract, utilities, errors

QuantumWallet exposes generate, fromPrivateKey, fromSigner, signTransaction, signPrepared, sendTransaction, anchorHash, resetNonce, and destroy. QuantumContract exposes call and send.

Utilities include parseQ, formatQ, bytesToHex, hexToBytes, toQuantity, fromQuantity, normalizeAddress, normalizeHash, and known networks.

Typed failures cover address/transaction validation, serialization, signing, chain/genesis mismatch, transport, JSON-RPC errors, rejection, revert, and timeout.

JSON-RPC reference

The chain-native qc namespace.

Current nodes also expose these calls under the eth compatibility alias. New applications should use qc. Hosted gateways may apply a smaller allowlist.

shell
curl --fail-with-body https://rpc.sandbox.qustody.io \
  -H 'content-type: application/json' \
  --data '{"jsonrpc":"2.0","id":1,"method":"qc_chainId","params":[]}'

Network, blocks, fees

qc_chainId qc_syncing qc_blockNumber qc_gasPrice qc_maxPriorityFeePerGas qc_feeHistory qc_getBlockByNumber qc_getBlockByHash qc_getHeaderByNumber qc_getHeaderByHash qc_getBlockReceipts

Account and state

qc_getBalance qc_getTransactionCount qc_getCode qc_getStorageAt qc_getProof qc_call qc_estimateGas qc_createAccessList

Transactions

qc_getTransactionByHash qc_getTransactionReceipt qc_getRawTransactionByHash qc_getTransactionByBlockHashAndIndex qc_getTransactionByBlockNumberAndIndex qc_getRawTransactionByBlockHashAndIndex qc_getRawTransactionByBlockNumberAndIndex qc_getBlockTransactionCountByHash qc_getBlockTransactionCountByNumber qc_sendRawTransaction

Logs and filters

qc_getLogs qc_newFilter qc_newBlockFilter qc_newPendingTransactionFilter qc_getFilterChanges qc_getFilterLogs qc_uninstallFilter

Standard utility namespaces

net_version net_listening net_peerCount web3_clientVersion web3_sha3

Submission ruleNever blindly retry qc_sendRawTransaction. Retain the locally computed hash, query it, and reconcile the pending nonce. The SDK submits once and verifies the returned hash.

Raw transport

TypeScript
import { HttpRpcTransport } from "@quantum_chain/sdk/rpc";

const rpc = new HttpRpcTransport("https://rpc.sandbox.qustody.io", {
  timeoutMs: 15_000,
  maxResponseBytes: 8 * 1024 * 1024,
});
const logs = await rpc.request<unknown[]>("qc_getLogs", [{
  fromBlock: "0x1",
  toBlock: "latest",
  address: CONTRACT_ADDRESS,
}]);

Quantities are minimal hexadecimal; binary values are even-length 0x bytes; addresses are exactly 20 bytes; Q and fees use Qwei. Administrative, debug, personal, miner, engine, and txpool APIs are operator surfaces and are not part of the public application contract.

Security model

Keep signing explicit and binary.