Skip to main content
This guide describes a flow that works today. Path B reuses the existing transaction endpoints (POST /v1/transactions with CONTRACT_CALL) and the Qustody approval + post-quantum signing pipeline.
You will compile a QRC-compatible Solidity contract, deploy it through Qustody, and call a function on the deployed contract.

Prerequisites

  • A Solidity toolchain (solc, Foundry, or Hardhat).
  • A Qustody tenant and an API key with transactions:create permission.
  • A vault account with native currency for gas.
  • An approval policy that requires approval for CONTRACT_CALL deployments.

Step 1 — Compile Solidity

solc --abi --bin contracts/MyToken.sol -o build/
You now have build/MyToken.bin (deployment bytecode) and build/MyToken.abi (ABI).

Step 2 — Prepare the deployment payload

DEPLOY_BYTECODE="0x$(cat build/MyToken.bin)"
If your constructor takes arguments, ABI-encode them and append to the bytecode. For example, with ethers.js:
import { Interface } from "ethers";
const iface = new Interface(abi);
const args  = iface.encodeDeploy(["My Token", "MTK", 18]);
const payload = "0x" + bytecode.slice(2) + args.slice(2);

Step 3 — Submit the deployment transaction

POST /v1/transactions
Idempotency-Key: deploy-mytoken-2026-04-27

{
  "operationType": "CONTRACT_CALL",
  "from": { "type": "vaultAccountId", "vaultAccountId": "vault_123" },
  "to": null,
  "data": "0x60806040…",
  "value": "0",
  "feeStrategy": "MEDIUM"
}
Qustody returns a transaction in PENDING_AUTHORIZATION.

Step 4 — Approve

An approver calls:
POST /v1/transactions/{id}/approve
The transaction enters PENDING_SIGNATURE.

Step 5 — Post-quantum signing

If your signer is configured to sign automatically (gRPC or remote modes), the transaction continues. Otherwise, fetch the signing payload and submit the signature:
GET  /v1/transactions/{id}/signing_payload
POST /v1/transactions/{id}/signature
See external signing.

Step 6 — Read the contract address

After confirmation:
GET /v1/transactions/{id}
The response includes receipt.contractAddress. Save it for later calls.

Step 7 — Call a function

ABI-encode the call data and submit a contract call:
const data = iface.encodeFunctionData("transfer", ["0x9a8e…", "1000000000000000000"]);
POST /v1/transactions
Idempotency-Key: transfer-2026-04-27-001

{
  "operationType": "CONTRACT_CALL",
  "from": { "type": "vaultAccountId", "vaultAccountId": "vault_123" },
  "to": "0x9a8e5e21f0c27d2c5c14b6e9bd8e4a0f9c9b4d12",
  "data": "0xa9059cbb000000000000000000000000…",
  "value": "0",
  "feeStrategy": "MEDIUM"
}

Step 8 — Verify state via RPC

Use Quantum Chain RPC to read state:
curl -X POST $RPC_URL -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x9a8e…","data":"0x70a08231000000000000000000000000…"},"latest"],"id":1}'

Caveats

  • The QVM removes the ecrecover precompile. Solidity that depends on it will revert. Use the post-quantum precompile at 0x0b instead — see QVM vs EVM.
  • BIP-32 / BIP-44 derivation is not applicable to post-quantum keys.
  • Treat upgrades as deployments — the upgrade transaction is its own privileged operation.