Quantum Chain quantumcha.in ↗
DEVELOPER DOCUMENTATION · JAVASCRIPT 2.1.0 · PYTHON 0.3.0

Build directly on
Quantum Chain.

Construct and sign canonical Quantum transactions from JavaScript or Python. Validate networks, submit raw transactions, track receipts, and deploy or call QVM smart contracts without recreating chain formats or cryptography in application code.

32-bytecanonical signing digest
20804Quantum Sandbox chain ID
Node 22 / 24supported LTS runtimes
Choose your integration

Start with the language your application already uses.

Both paths use the same canonical Quantum transaction format and native signing implementation. JavaScript includes the complete RPC lifecycle. Python returns submission-ready raw bytes and the quickstart supplies a minimal RPC caller around it.

Start on SandboxEvery quickstart targets quantum-sandbox on chain ID 20804. Sandbox QUANTUM has no monetary value. Do not use tutorial keys on Mainnet.
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
CurrencyQuantum (QUANTUM), 18 decimalsQuantum (QUANTUM), 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.
Product boundaries

Quantum Chain and Qustody

They solve different problems and can be used independently.

Quantum Chain

The public blockchain and its native currency, Quantum (QUANTUM). Use this documentation for JSON-RPC, transaction construction, signing, smart contracts, Sandbox, and direct node integration.

Read the chain and QVM guide

Qustody

The custody and wallet operations platform. Use Qustody for managed wallets, approvals, policies, teams, vaults, and EnQlave-connected signing workflows.

Read Qustody docs
Direct integrationThe Quantum Chain SDK does not require Qustody. Read the complete product-boundary guide before choosing an integration path.
Permanent test network

Connect to Sandbox and fund a test wallet.

Quantum Sandbox is the Quantum Chain test network on chain ID 20804. Sandbox QUANTUM has no monetary value. The faucet works only on this test network and must never be configured for Mainnet.

sandbox.qustody.io names the environment; it is not the JSON-RPC URL. Applications must connect to the exact rpc.sandbox.qustody.io service shown below.

JSON-RPChttps://rpc.sandbox.qustody.io
Faucethttps://faucet.sandbox.qustody.io
Explorerhttps://explorer.sandbox.qustody.io

Step 1: prove that you reached Sandbox

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

The result must be 0x5144, which is decimal 20804. Then request block zero and confirm its hash:

terminal
curl --fail-with-body https://rpc.sandbox.qustody.io \
  -H 'content-type: application/json' \
  --data '{"jsonrpc":"2.0","id":2,"method":"qc_getBlockByNumber","params":["0x0",false]}'

result.hash must equal 0x3d9c6813ee3edd227abb5345aff55d6e2e76bc9db3a031d92d0e2bd54c8997e7.

Step 2: generate and fund a disposable test wallet

fund-sandbox.ts
import {
  QuantumClient, QuantumWallet, formatQuantum, networks,
} from "@quantum_chain/sdk";

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

try {
  const network = await client.validateNetwork();
  if (network.network !== "quantum-sandbox" || network.chainId !== 20804n) {
    throw new Error("refusing faucet use outside Quantum Sandbox");
  }
  console.log("address", wallet.address);
  const funding = await client.sandbox!.fund(wallet.address);
  console.log("faucet created transaction", funding.funded);
  console.log("balance", formatQuantum(funding.balance), "QUANTUM");
  console.log("faucet transaction", funding.transactionHash ?? "already funded");
  console.log("explorer", client.getExplorerAddressUrl(wallet.address));
} finally {
  wallet.destroy();
}
terminal
npx tsx fund-sandbox.ts

Step 3: call the test-only faucet directly

terminal
export SANDBOX_ADDRESS='0xREPLACE_WITH_YOUR_40_HEX_CHARACTER_ADDRESS'
curl --fail-with-body https://faucet.sandbox.qustody.io/fund \
  -H 'content-type: application/json' \
  --data "{\"address\":\"${SANDBOX_ADDRESS}\"}"

The faucet response includes the address, balance and target balance in atomic units, whether it funded the account, and an optional transaction hash. It is idempotent up to its configured target. Calling it repeatedly does not create unlimited funds.

Testnet onlyThe faucet accepts only Quantum Sandbox addresses and is available only in the Sandbox environment. Never reuse tutorial keys, Sandbox contract addresses, or Sandbox balances on Mainnet.
HTTP 403The Sandbox edge is source-allowlisted. If these commands return 403, ask Quantum Chain to approve the public egress IP of the machine making the request.
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 put it 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 QUANTUM, and waits for a successful receipt.

send.ts
import {
  QuantumClient,
  QuantumWallet,
  formatQuantum,
  networks,
  parseQuantum,
} 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", formatQuantum(funding.balance), "QUANTUM");

  const submitted = await sender.sendTransaction(client, {
    to: recipient.address,
    value: parseQuantum("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. 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, parseQuantum,
} 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: parseQuantum("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();
}
Python SDK 0.3.0

Install the native Python SDK.

py-quantum generates native Quantum keys, derives addresses, signs an exact 32-byte digest, verifies signatures, and constructs canonical RPC-free transactions. It does not implement cryptography in Python.

Exact scopeThe Python SDK does not call RPC, discover nonces or fees, estimate gas, broadcast, or wait for receipts. Those lifecycle operations remain caller-owned.

Step 1: get your private index credentials

Quantum Chain issues an SDK username and token for your organization. Keep them in a password manager or CI secret store. Do not commit the authenticated URL.

Step 2: create a virtual environment

Linux
mkdir quantum-python-demo
cd quantum-python-demo
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
Windows PowerShell
mkdir quantum-python-demo
cd quantum-python-demo
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip

Step 3: install the private wheel

terminal
python -m pip install \
  --index-url "https://SDK_USERNAME:SDK_TOKEN@sdk.quantumapi.io/simple/" \
  py-quantum==0.3.0

Step 4: generate, sign and verify

sign.py
from py_quantum import (
    ADDRESS_SIZE, DIGEST_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE,
    Signer, verify_digest,
)

# Tutorial fixture only. Transaction digests must come from the canonical
# Quantum Chain transaction implementation.
digest = bytes.fromhex(
    "5b336411c12d54f162ef83653f01897ffb8b0987c666d34bed2920641832a4ef"
)

assert len(digest) == DIGEST_SIZE
with Signer.generate() as signer:
    public_key = signer.public_key
    signature = signer.sign_digest(digest)
    assert len(bytes.fromhex(signer.address_hex[2:])) == ADDRESS_SIZE
    assert len(public_key) == PUBLIC_KEY_SIZE
    assert len(signature) == SIGNATURE_SIZE
    assert verify_digest(public_key, digest, signature)
    print("address", signer.address_hex)
    print("public key bytes", len(public_key))
    print("signature bytes", len(signature))
    print("verified", True)
terminal
python sign.py

Python API

Signer.generate()new opaque native signer signer.public_key1,952-byte public key signer.address20-byte address signer.address_hexlowercase 0x address signer.sign_digest(digest)3,293-byte signature signer.close()release native key handle verify_digest(key, digest, signature)boolean build_transaction(request)unsigned transaction sign_transaction(transaction, signer)complete signing result build_and_sign_transaction(request, signer)build and sign without RPC

Build a submission-ready Sandbox transaction

First obtain the pending nonce, gas estimate, and gas price from RPC. Supply those resolved fields to the Python SDK:

build_transaction.py
from py_quantum import (
    LegacyTransactionRequest, Signer, build_and_sign_transaction,
)

with Signer.generate() as signer:
    result = build_and_sign_transaction(
        LegacyTransactionRequest(
            chain_id=20804,
            nonce=12,                 # qc_getTransactionCount
            gas_limit=21_000,         # qc_estimateGas
            gas_price=1_000_000_000,  # qc_gasPrice
            to="0x0000000000000000000000000000000000000001",
            value="1.0",
        ),
        signer,
    )

print(result.raw_transaction)   # qc_sendRawTransaction parameter
print(result.transaction_hash)  # computed locally
print(result.public_key)
print(result.signature)
print(result.signing_digest)
Complete Python transactionThe Python SDK quickstart adds network validation, faucet funding, nonce and fee discovery, raw submission, hash comparison, and successful receipt polling around this RPC-free SDK result.

The result also contains raw_transaction_bytes, unsigned_transaction, and signed_transaction. Do not blindly retry submission if an HTTP response is lost; query the local hash and reconcile the pending nonce.

Supported wheels: Linux x86-64 with glibc 2.34 or newer and Windows x86-64, for CPython 3.10-3.14. Normal users need no compiler, CMake, or system native library.

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 atomic units, decimal string Quantum
  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 parseQuantum, formatQuantum, 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

Click a method for its inputs, output and request

Every entry below expands into a plain-language description and a copyable JSON-RPC request. Replace values marked REPLACE_... before running the command.

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. Quantum values and fees use atomic units. 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.