Python client library for the Quantova Layer 1 blockchain, natively secured by Post-Quantum Cryptography (PQC). PyPI Version · License · Python 3.8+
qweb3.py is the Python developer-facing client for Quantova. It signs with NIST post-quantum signature schemes (Falcon, Dilithium, SPHINCS+), speaks the chain's q_* JSON-RPC namespace, encodes and decodes QVM (Solidity-on-PolkaVM) contract calls, resolves .q names, and reads the same data through the public REST gateway when you prefer HTTP. The API is synchronous, in the style of web3.py.
- Quantum-safe primitives — key generation, signing, and verification using Falcon, Dilithium, and SPHINCS+.
- Unified Q-RPC namespace — native support for the 27
q_*RPC methods covering blocks, balances, transaction counts, logs, fees, and extrinsic broadcast. - QVM contract layer — instantiate Solidity-on-QVM contracts and run
.call()/.send()with a standard Solidity ABI encoder/decoder (keccak-256 selectors) and human-readable event-log decoding. - QNS name service — resolve and register post-quantum
.qdomains through the on-chain QVM registry contract. - Dynamic fee & gas oracle — slow / standard / fast tiers derived from recent
q_feeHistoryactivity. - Batch portal — group many
q_*calls into a single JSON-RPC payload. - Unified event hooks — real-time block streaming plus per-transaction pending → receipt → confirmed tracking.
- REST fallback — every read can fall back to the Quantova public REST gateway (
/v1/...). - CLI toolbelt —
qweb3-cligenerates PQC wallets, inspects addresses, and calls a node from the terminal. - Hybrid addressing — handles both legacy hex (
0x...) and Quantova Base64 H160 addresses. - Sync API & type hints — synchronous client (no async/await), type-hinted throughout, pip-installable, Python 3.8+.
qweb3/ # the package
├── qweb3/
│ ├── __init__.py # QWeb3 facade + public exports
│ ├── abi.py # Solidity ABI encoder/decoder (keccak-256 selectors)
│ ├── batch.py # Batch request portal (one JSON-RPC payload, many calls)
│ ├── cli.py # qweb3-cli command-line toolbelt
│ ├── contract.py # QVM smart-contract class: call/send + event-log decoding
│ ├── crypto_backend.py # Pluggable post-quantum crypto backend interface
│ ├── errors.py # Structured SDK exceptions (Connection, Rpc, InvalidArg, Tx)
│ ├── fee.py # Dynamic fee & gas oracle
│ ├── hooks.py # Unified event hooks (blocks + transaction lifecycle)
│ ├── qns.py # QNS .q name registrar & resolver
│ ├── rest.py # REST gateway client (quantova-rest-api /v1 surface)
│ ├── rpc.py # Q-RPC client (q_* namespace)
│ ├── signer.py # Post-quantum sign/verify wrappers (Falcon, Dilithium, SPHINCS+)
│ ├── utils.py # Address derivation, hex, QTOV units, validation
│ ├── wallet.py # PQ account manager
│ └── _keccak.py # Pure-Python keccak-256 (overridable)
├── tests/ # Offline test suite
├── pyproject.toml # Packaging + qweb3-cli entry point
├── README.md
└── LICENSE- Python 3.8 or newer
requests(installed automatically)- Quantova's post-quantum crypto backend — required only for wallet / sign / verify; everything else (RPC, REST, ABI, contracts, QNS, fees, batch, hooks) works without it. See Post-quantum signing.
From the delivered package:
unzip qweb3-python.zip
cd qweb3py
pip install .For development (editable — picks up local edits without reinstalling):
pip install -e .If your environment blocks system installs (PEP 668), use a virtual env:
python3 -m venv .venv && source .venv/bin/activate
pip install .qweb3 ships with the Quantova stack, not public PyPI. Once it's published to your registry, pip install qweb3 works directly. Installing also adds the qweb3-cli command to your PATH.
The SDK depends only on requests. Post-quantum signing additionally requires Quantova's PQ crypto backend (registered once at startup — see Post-quantum signing).
No node and no crypto backend needed for these:
qweb3-cli address inspect QOuhXUELsRC0/zow/Vjwft8hNP8= # should report a valid PQ address
python3 tests/test_offline.py # 42 checks, runs fully offlinefrom qweb3 import QWeb3
# JSON-RPC primary; pass rest_url to enable the REST gateway as a readable fallback.
q = QWeb3("http://127.0.0.1:9944", rest_url="http://127.0.0.1:8080")
block = q.rpc.block_number()
fees = q.fees.estimate()
print({"block": block, "standard_tip": fees["tiers"]["standard"]})import qweb3
from qweb3 import crypto_backend, QuantumWallet, QuantumSigner
# Register Quantova's PQ backend once at startup (required before any signing).
crypto_backend.set_backend(backend)
wallet = QuantumWallet()
account = wallet.create("falcon") # 'falcon' | 'dilithium' | 'sphincsp'
print("Address: ", account.address)
print("Public key:", account.public_key)
message = "Quantova post-quantum extrinsic"
signature_hex = wallet.sign_transaction(message, account.address)
valid = QuantumSigner.verify(message, signature_hex, account.public_key, "falcon")
print("Verified:", valid)from qweb3 import QRpcClient
client = QRpcClient("http://127.0.0.1:9944")
block_number = client.block_number()
balance = client.get_balance("QOuhXUELsRC0/zow/Vjwft8hNP8=")
print({"block_number": block_number, "balance": balance})RPC methods are exposed with friendly snake_case names (
block_number(),get_balance(),get_transaction_receipt(), …), each mapping to itsq_*JSON-RPC method. Useclient.call("q_<method>", [params])for anything not wrapped explicitly.
from qweb3 import QWeb3
q = QWeb3("http://127.0.0.1:9944", rest_url="http://127.0.0.1:8080")
erc20_abi = [
{"type": "function", "name": "balanceOf", "stateMutability": "view",
"inputs": [{"name": "owner", "type": "address"}], "outputs": [{"type": "uint256"}]},
{"type": "function", "name": "transfer", "stateMutability": "nonpayable",
"inputs": [{"name": "to", "type": "address"}, {"name": "value", "type": "uint256"}],
"outputs": [{"type": "bool"}]},
{"type": "event", "name": "Transfer", "inputs": [
{"name": "from", "type": "address", "indexed": True},
{"name": "to", "type": "address", "indexed": True},
{"name": "value", "type": "uint256", "indexed": False}]},
]
token = q.contract(erc20_abi, "0xYourTokenAddress")
# Read (q_call, REST fallback) — decoded automatically:
bal = token.call("balanceOf", ["0xHolderAddress"])
# Write (built, post-quantum signed by the wallet, then broadcast):
account = q.wallet.create("falcon")
tx_hash = token.send("transfer", ["0xRecipient", 1000], from_address=account.address)
# Decode logs from a receipt into readable events:
receipt = q.rpc.get_transaction_receipt(tx_hash)
events = token.decode_logs(receipt["logs"]) # [{"name": "Transfer", "args": {...}}, ...]Write path note:
.send()builds a post-quantum-signed extrinsic via the wallet'sbuild_and_sign_contract_tx()(provided by Quantova's API layer). If your build doesn't expose it, usetoken.encode("transfer", [...])to get the calldata and submit a signed transaction yourself.
from qweb3 import abi
abi.function_selector("transfer(address,uint256)") # '0xa9059cbb'
abi.event_topic("Transfer(address,address,uint256)") # '0xddf2...3b3ef'
data = abi.encode_function_call(
{"name": "transfer", "inputs": [{"type": "address"}, {"type": "uint256"}]},
["0x1111111111111111111111111111111111111111", 1000],
)
value = abi.decode_parameters(["uint256"], "0x...")[0]from qweb3 import QWeb3
q = QWeb3("http://127.0.0.1:9944")
qns = q.qns("0xYourQnsRegistryAddress")
addr = qns.resolve("alice.q") # -> address or None
owner = qns.owner("alice.q")
name = qns.reverse("0xSomeAddress")
# Registration / records require a wallet:
# qns.register("alice.q", owner_address, from_address=owner_address)
# qns.set_addr("alice.q", target_address, from_address=owner_address)The default registry ABI follows the conventional resolver/registrar shape (namehash node keys). If your deployed registry uses different method names, pass a custom ABI:
q.qns(registry_address, abi=my_registry_abi).
fees = q.fees.estimate()
# {"baseFeePerGas": ..., "maxPriorityFeePerGas": ..., "maxFeePerGas": ...,
# "tiers": {"slow": ..., "standard": ..., "fast": ...}, "model": "quantova-dynamic-no-burn"}
tx_fee = q.fees.estimate_for_tx({"from": a, "to": b, "data": data})
# {"gas": ..., "effectiveGasPrice": ..., "fee": ..., "feeWei": ..., ...}from qweb3 import BatchRequest
batch = BatchRequest("http://127.0.0.1:9944")
(batch.add("q_blockNumber")
.add("q_getBalance", ["QOuhXUELsRC0/zow/Vjwft8hNP8=", "latest"])
.add("q_gasPrice"))
results = batch.execute() # list[BatchResult(success, result, error)] in insertion orderfrom qweb3 import QWeb3
q = QWeb3("http://127.0.0.1:9944")
q.hooks.start_blocks(on_block=lambda header: print("new head", header["number"]))
q.hooks.track(
tx_hash,
on_receipt=lambda r: print("mined in", r["blockNumber"]),
on_confirmed=lambda r: print("confirmed"),
on_failed=lambda r: print("reverted"),
)
# Or block until confirmed:
receipt = q.hooks.wait_for_receipt(tx_hash)Real-time tracking is polling-based (a background thread polls
q_getTransactionReceipt), so it works against any node with no extra dependencies.
from qweb3 import QRestClient
rest = QRestClient("http://127.0.0.1:8080")
balance = rest.get_balance("QOuhXUELsRC0/zow/Vjwft8hNP8=")
latest = rest.block_latest()
fees = rest.fees_estimate()After installing, the qweb3-cli command is available (or run python3 -m qweb3.cli):
# Wallets
qweb3-cli wallet new --scheme falcon
qweb3-cli wallet from-seed 0x<32-byte-seed> --scheme dilithium
qweb3-cli wallet from-mnemonic "word word word ..." --scheme sphincsp
# Addresses
qweb3-cli address inspect QOuhXUELsRC0/zow/Vjwft8hNP8=
qweb3-cli address from-pubkey 0x<publickey>
# Crypto
qweb3-cli sign "message" --seed 0x<seed> --scheme falcon
qweb3-cli verify "message" --sig 0x<sig> --pub 0x<pub> --scheme falcon
# Node (JSON-RPC, --url defaults to http://127.0.0.1:9944)
qweb3-cli rpc q_blockNumber
qweb3-cli block latest
qweb3-cli balance QOuhXUELsRC0/zow/Vjwft8hNP8=
qweb3-cli fees
# REST gateway (--rest defaults to http://127.0.0.1:8080)
qweb3-cli rest get /gas-price
qweb3-cli rest post /transactions/call '{"to":"0x...","data":"0x..."}'Add --json to any command for raw JSON output. (Wallet, sign, and verify commands need the PQ crypto backend installed.)
The contract, QNS, fee, and hook modules use JSON-RPC (q_*) as the primary transport and fall back to the REST gateway when a rest_client / rest_url is configured. Construct QWeb3(url, rest_url=...) to enable the fallback, or pass rest_client= directly to individual modules.
A note on hashing: QVM contract selectors use keccak-256 (standard Solidity ABI). This is independent of Quantova's transaction/state hashing (SHA3-256), which is applied in the signing layer.
Signing is post-quantum only (no ECDSA/secp256k1). The cryptography is provided by Quantova's native PQ backend, installed separately. You must register it once at startup before any wallet/sign/verify call — without it those calls raise a clear error telling you to set a backend (read-only RPC/REST work regardless).
import qweb3
from qweb3 import crypto_backend
# `backend` is an adapter exposing, for each scheme s in {falcon, dilithium, sphincsp}:
# s_pair_from_seed(seed: bytes) -> {"public_key": bytes}
# s_sign(seed: bytes, public_key: bytes, message: bytes) -> bytes
# s_verify(public_key: bytes, message: bytes, signature: bytes) -> bool
# These method names match Quantova's falcon-wasm module, so the adapter is thin.
crypto_backend.set_backend(backend)Addresses are Quantova Base64 H160 (first 20 bytes of the public key). Legacy 0x hex addresses are also accepted (useful for bridge-side addresses).
python3 tests/test_offline.pyThe suite runs fully offline using an injected HTTP session and a deterministic stub crypto backend.
qweb3.py is licensed under the Business Source License 1.1 (BUSL-1.1), © 2026 Quantova Inc. See LICENSE for the full text and parameters.
In short: the source is available — you may copy, modify, and make non-production use of it, with production use governed by the Additional Use Grant. On the Change Date, each version converts automatically to the Change License (Apache License, Version 2.0).
This summary is informational, is not legal advice, and does not modify the license; the LICENSE file governs. The BUSL parameters (Additional Use Grant, Change Date, Change License) are set by Quantova Inc — review them before publishing.