Quantum Chainquantumcha.in ↗
py-quantum 0.3.0

Construct and sign Quantum transactions from Python.

py-quantum bundles the controlled native implementation for key generation, address derivation, signing, verification, and canonical transaction construction. Python never reimplements the cryptography.

Installation

Create a clean environment and install the private wheel.

Supported releases target CPython 3.10 through 3.14 on Linux x86-64 with glibc 2.34 or newer and Windows x86-64. Normal consumers do not need a compiler or system native dependency.

Linux

terminal
mkdir quantum-python-demo
cd quantum-python-demo
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install \
  --index-url "https://SDK_USERNAME:SDK_TOKEN@sdk.quantumapi.io/simple/" \
  py-quantum==0.3.0

Windows PowerShell

PowerShell
mkdir quantum-python-demo
cd quantum-python-demo
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install `
  --index-url "https://SDK_USERNAME:SDK_TOKEN@sdk.quantumapi.io/simple/" `
  py-quantum==0.3.0
Private index credentialsQuantum Chain issues a username and token for your organization. Substitute both placeholders. Keep the authenticated URL out of application source and support logs.
Copy-and-run quickstart

Send a real transaction on Quantum Sandbox.

Create send_quantum.py. This uses only the Python standard library for HTTP. The SDK remains RPC-free and owns only native keys and canonical transaction construction.

send_quantum.py
import json
import time
from urllib.request import Request, urlopen

from py_quantum import (
    LegacyTransactionRequest,
    Signer,
    build_and_sign_transaction,
    parse_quantum,
)

RPC = "https://rpc.sandbox.qustody.io"
FAUCET = "https://faucet.sandbox.qustody.io/fund"
CHAIN_ID = 20804
GENESIS = "0x3d9c6813ee3edd227abb5345aff55d6e2e76bc9db3a031d92d0e2bd54c8997e7"


def post_json(url, payload):
    request = Request(
        url,
        data=json.dumps(payload).encode("utf-8"),
        headers={"content-type": "application/json"},
        method="POST",
    )
    with urlopen(request, timeout=30) as response:
        return json.loads(response.read())


def rpc(method, params):
    response = post_json(RPC, {
        "jsonrpc": "2.0", "id": 1, "method": method, "params": params,
    })
    if "error" in response:
        raise RuntimeError(f"{method} failed: {response['error']}")
    return response["result"]


with Signer.generate() as sender, Signer.generate() as recipient:
    chain_id = int(rpc("qc_chainId", []), 16)
    genesis = rpc("qc_getBlockByNumber", ["0x0", False])["hash"].lower()
    if chain_id != CHAIN_ID or genesis != GENESIS:
        raise RuntimeError("refusing to sign for an unexpected network")

    funding = post_json(FAUCET, {"address": sender.address_hex})
    print("sender", sender.address_hex)
    print("faucet", funding)

    deadline = time.monotonic() + 120
    while int(rpc("qc_getBalance", [sender.address_hex, "latest"]), 16) == 0:
        if time.monotonic() > deadline:
            raise TimeoutError("faucet balance did not arrive")
        time.sleep(1)

    value = parse_quantum("0.01")
    call = {
        "from": sender.address_hex,
        "to": recipient.address_hex,
        "value": hex(value),
        "data": "0x",
    }
    nonce = int(rpc("qc_getTransactionCount", [sender.address_hex, "pending"]), 16)
    gas_limit = int(rpc("qc_estimateGas", [call]), 16)
    gas_price = int(rpc("qc_gasPrice", []), 16)

    result = build_and_sign_transaction(
        LegacyTransactionRequest(
            chain_id=CHAIN_ID,
            nonce=nonce,
            gas_limit=gas_limit,
            gas_price=gas_price,
            to=recipient.address_hex,
            value=value,
        ),
        sender,
    )

    node_hash = rpc("qc_sendRawTransaction", [result.raw_transaction]).lower()
    if node_hash != result.transaction_hash.lower():
        raise RuntimeError("node returned a different transaction hash")

    deadline = time.monotonic() + 120
    receipt = None
    while receipt is None:
        receipt = rpc("qc_getTransactionReceipt", [result.transaction_hash])
        if receipt is None and time.monotonic() > deadline:
            raise TimeoutError("transaction receipt did not arrive")
        if receipt is None:
            time.sleep(1)

    if int(receipt["status"], 16) != 1:
        raise RuntimeError("transaction execution failed")

    print("transaction", result.transaction_hash)
    print("block", int(receipt["blockNumber"], 16))
    print("status", int(receipt["status"], 16))
    print("explorer", f"https://explorer.sandbox.qustody.io/tx/{result.transaction_hash}")
run
python send_quantum.py

Expected final output includes the sender address, transaction hash, block number, receipt status 1, and an explorer URL. HTTP 403 means your public egress address is not approved for the hosted Sandbox.

Package boundary

The Python SDK is intentionally RPC-free.

The SDK ownsYour application owns
Native key generation and address derivationRPC endpoint and network identity policy
Signing an exact 32-byte digestNonce and fee discovery
Native signature verificationGas estimation
Canonical transaction construction and serializationRaw transaction submission and retry policy
Local transaction hashReceipt polling, confirmations, and lifecycle

The quickstart shows one minimal caller implementation so a Python team can integrate end to end. Production adapters should use their established HTTP, observability, retry, and nonce coordination layers.

Native signer

Keys stay behind an opaque native handle.

Signer.generate()create a native signersigner.public_keycanonical 1,952-byte public keysigner.address20-byte addresssigner.address_hexlowercase 0x addresssigner.closedwhether the handle was releasedsigner.sign_digest(digest)3,293-byte signature over exactly 32 bytessigner.close()idempotently release the native handleverify_digest(key, digest, signature)boolean for a valid or invalid signature
sign.py
from py_quantum import Signer, verify_digest

digest = bytes.fromhex(
    "5b336411c12d54f162ef83653f01897ffb8b0987c666d34bed2920641832a4ef"
)

with Signer.generate() as signer:
    signature = signer.sign_digest(digest)
    assert verify_digest(signer.public_key, digest, signature)
    print(signer.address_hex)

Accepted binary inputs are bytes, bytearray, and contiguous one-dimensional memoryview values. Strings are never silently converted into signing data.

Transaction inputs

Supply every resolved chain field.

LegacyTransactionRequest(chain_id, nonce, gas_limit, gas_price, to=None, value=0, data=b"")
DynamicTransactionRequest(chain_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas, to=None, value=0, data=b"", access_list=())

Integer values are atomic units. A decimal string value is Quantum and may have at most 18 fractional digits. Omit to for contract creation. A string data value must be strict, even-length, and 0x-prefixed.

build_transaction(request)immutable normalized unsigned transactionsign_transaction(transaction, signer)sign a previously built transactionbuild_and_sign_transaction(request, signer)build and sign in one callserialize_transaction(transaction)canonical bytesserialize_transaction_hex(transaction)submission-ready 0x bytesdeserialize_transaction(raw)structured transactionget_transaction_signing_digest(unsigned)exact 32-byte digestattach_transaction_signature(...)signed transactionget_transaction_hash(signed)canonical local hash
Signing result

Return all adapter integration material.

AttributeTypeMeaning
unsigned_transactiontransactionNormalized unsigned fields
signed_transactiontransactionSame fields with signature and public key
signing_digestbytesExact 32-byte signer input
signaturebytesSignature embedded in the transaction
public_keybytesPublic key embedded in the transaction
raw_transaction_bytesbytesCanonical signed bytes
raw_transactionstr0x bytes for qc_sendRawTransaction
transaction_hashstrHash computed from the exact signed bytes
Errors and security

Reject malformed input before native signing.

The hierarchy includes QuantumError, InvalidArgumentError, ClosedSignerError, NativeLibraryError, UnsupportedPlatformError, InvalidTransactionError, SerializationError, and SigningError. An invalid signature normally returns False. Invalid key or digest lengths raise an argument error.