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. - Q-branded addressing — accounts are Bech32m
Q1...(letters + digits, no symbols); 24-word recovery phrase;QSEC1...private keys andQPUB1...public keys. Solidity/QVM contract addresses stay0x...hex. - 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 on requests and mnemonic (for 24-word recovery phrases). 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 Q1GZD3AGFY5U426V9NX6UNE06ZC4YVKNK3GU9L3C # 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) # "Q1..." (Bech32m)
print("Seed phrase:", account.mnemonic) # 24 recovery words
print("Private key:", account.private_key) # "QSEC1..." (Bech32m of the seed)
print("Public key: ", account.public_key) # "QPUB1..." (Bech32m)
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("Q1GZD3AGFY5U426V9NX6UNE06ZC4YVKNK3GU9L3C")
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}]},
]
# The token's own address is a Solidity/QVM contract address (0x hex):
token = q.contract(erc20_abi, "0xYourTokenContractAddress")
# Account arguments are passed as Q-branded addresses; the SDK converts them for the ABI:
bal = token.call("balanceOf", ["Q1HolderAccountAddress..."])
# Write (built, post-quantum signed by the wallet, then broadcast):
account = q.wallet.create("falcon")
tx_hash = token.send("transfer", ["Q1RecipientAccount...", 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"}]},
["Q1RecipientAccount...", 1000], # a Q1 account (or a 0x contract address) — both accepted
)
value = abi.decode_parameters(["uint256"], "0x...")[0] # "0x..." here is raw return data (hex)from qweb3 import QWeb3
q = QWeb3("http://127.0.0.1:9944")
qns = q.qns("0xYourQnsRegistryContractAddress") # the registry is a QVM contract (0x hex)
addr = qns.resolve("alice.q") # -> a "Q1..." account address, or None
owner = qns.owner("alice.q")
name = qns.reverse("Q1SomeAccountAddress...")
# 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", ["Q1GZD3AGFY5U426V9NX6UNE06ZC4YVKNK3GU9L3C", "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("Q1GZD3AGFY5U426V9NX6UNE06ZC4YVKNK3GU9L3C")
latest = rest.block_latest()
fees = rest.fees_estimate()After installing, the qweb3-cli command is available (or run python3 -m qweb3.cli):
# Wallets (wallet new prints a 24-word recovery phrase + Q1.../QSEC1.../QPUB1...)
qweb3-cli wallet new --scheme falcon
qweb3-cli wallet from-seed QSEC1... --scheme dilithium
qweb3-cli wallet from-mnemonic "word word word ..." --scheme sphincsp
# Addresses
qweb3-cli address inspect Q1GZD3AGFY5U426V9NX6UNE06ZC4YVKNK3GU9L3C
qweb3-cli address from-pubkey QPUB1...
# Crypto (a signature is raw hex bytes, not an address/key)
qweb3-cli sign "message" --seed QSEC1... --scheme falcon
qweb3-cli verify "message" --sig 0x<hex-signature> --pub QPUB1... --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 Q1GZD3AGFY5U426V9NX6UNE06ZC4YVKNK3GU9L3C
qweb3-cli fees
# REST gateway (--rest defaults to http://127.0.0.1:8080)
# ("to" is a Solidity/QVM contract address, "data" is calldata — both hex, not account addresses)
qweb3-cli rest get /gas-price
qweb3-cli rest post /transactions/call '{"to":"0xContractAddress","data":"0xCalldata"}'Address & key format. Account/wallet identity is always Q-branded Bech32m:
Q1...addresses,QSEC1...private keys,QPUB1...public keys, plus a 24-word recovery phrase — letters and digits only, no+ / _ = -symbols. The only0xhex you'll ever see is for Solidity/QVM contract addresses, transaction signatures, calldata, and selectors/hashes — none of which are account addresses. The SDK accepts aQ1...account anywhere a contract expects anaddressargument and converts it for you.
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)Account addresses are Bech32m Q1... — the 20-byte body is SHA3-256(publicKey)[0:20] with byte[0]=0x40, encoded with prefix q and shown upper-case. Private keys are QSEC1... and public keys QPUB1.... Solidity/QVM contract addresses remain 0x hex.
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.