Quantum Chainquantumcha.in ↗
Solidity and QVM

Deploy and interact with a smart contract.

Quantum Chain executes Solidity bytecode in the QVM. Use normal Solidity and ABI tooling for compilation and calldata. Use the Quantum SDK for account keys, transaction signing, canonical serialization, submission, and receipts.

Step 1

Create a small TypeScript project.

terminal
mkdir quantum-counter
cd quantum-counter
npm init -y
npm install @quantum_chain/sdk @quantum_chain/pqc viem solc
npm install --save-dev tsx typescript @types/node
mkdir contracts scripts artifacts

solc compiles Solidity. viem is used only to encode and decode ABI values. Neither library signs the Quantum transaction.

Step 2

Create the contract.

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 setNumber(uint256 nextNumber) external {
        number = nextNumber;
        emit NumberChanged(nextNumber);
    }

    function increment() external {
        number += 1;
        emit NumberChanged(number);
    }
}
Step 3

Compile 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 failures = (output.errors ?? []).filter((item) => item.severity === "error");
if (failures.length) {
  throw new Error(failures.map((item) => item.formattedMessage).join("\n"));
}
const compiled = output.contracts["Counter.sol"].Counter;
await mkdir("artifacts", { recursive: true });
await writeFile("artifacts/Counter.json", JSON.stringify({
  abi: compiled.abi,
  bytecode: `0x${compiled.evm.bytecode.object}`,
}, null, 2));
console.log("wrote artifacts/Counter.json");
run
node scripts/compile.mjs
Step 4

Fund a disposable deployer and submit the bytecode.

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

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

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

try {
  await client.validateNetwork();
  await client.sandbox!.fund(deployer.address);

  const submitted = await deployer.sendTransaction(client, {
    data: deploymentData,
    value: 0n,
  });
  const receipt = await submitted.wait({ confirmations: 1, timeoutMs: 180_000 });
  if (!receipt.contractAddress) throw new Error("missing contract address");

  const code = await client.getCode(receipt.contractAddress);
  if (code.length === 0) throw new Error("deployment produced no runtime code");

  const deployment = {
    network: "quantum-sandbox",
    chainId: "20804",
    deployer: deployer.address,
    publicKey: bytesToHex(deployer.publicKey),
    transactionHash: submitted.hash,
    blockNumber: receipt.blockNumber.toString(),
    contractAddress: receipt.contractAddress,
  };
  await writeFile("deployment.json", JSON.stringify(deployment, null, 2));
  console.log(deployment);
} finally {
  deployer.destroy();
}
run
npx tsx scripts/deploy.ts

Contract creation is a normal transaction with creation bytecode in data and no to. The successful receipt contains contractAddress.

Step 5

Read state, send a write, and verify it.

scripts/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({ confirmations: 1, timeoutMs: 120_000 });

  console.log("transaction", submitted.hash);
  console.log("status", receipt.status);
  console.log("after", (await readNumber()).toString());
} finally {
  writer.destroy();
}
run
npx tsx scripts/interact.ts

contract.call() performs read-only execution and returns raw bytes. contract.send() constructs a state-changing transaction and returns a response with its hash and wait(). Decode results and logs with the ABI.

Porting contracts

Audit signature verification, not ordinary business logic.

Most contracts that use storage, arithmetic, access control by msg.sender, events, token balances, and normal external calls follow familiar EVM patterns. The critical exception is code that verifies an off-chain signature.

Production checklist

Promote an artifact, not a tutorial wallet.