qorechain (Rust)
Rust SDK for QoreChain — network presets, denomination/address utilities, HD
account derivation (native / EVM / SVM), post-quantum (ML-DSA-87) signing, typed
messages for every custom chain module, the full transaction lifecycle (auto-gas,
error decoding, tracking, search), typed queries, WebSocket subscriptions, and
async read clients for the REST (LCD) and qor_* JSON-RPC surfaces.
This crate lives in the qorechain-sdk monorepo and mirrors the TypeScript,
Python, and Go SDK surfaces for the native chain.
Install
[]
= "0.2"
= { = "1", = ["macros", "rt-multi-thread"] }
Requires Rust 1.74+.
Modules
| Module | Purpose |
|---|---|
networks |
Network presets (testnet and mainnet, both live), get_network. |
denom |
to_base / from_base exact integer money math (no floats). |
address |
bech32 ⇄ hex conversion and validation. |
accounts |
BIP-39 mnemonics + HD derivation (native, EVM, SVM). |
pqc |
ML-DSA-87 (FIPS 204) keygen / sign / verify + hybrid extension. |
proto |
Generated prost types for every QoreChain custom module. |
msg |
Typed message composers (49 custom + standard Native) and to_any. |
query |
RestClient, JsonRpcClient, typed qor_* QorClient, and TypedQueryClient. |
client |
create_client / ClientBuilder composing the read clients + fees. |
tx |
bank_send, send_messages, build_hybrid_tx, broadcast, auto-gas, error decoding, tracking, and search. |
subscribe |
WebSocket new-block / tx subscriptions over the chain RPC /websocket. |
utils |
Hashing (sha256/keccak256/ripemd160), exact unit math, EVM/SVM address validators. |
ai |
AI pre-flight risk/anomaly scoring over the EVM precompiles (simulate_with_risk_score). |
cross_vm |
Unified cross-VM call helper over MsgCrossVMCall (single + atomic triple-VM). |
pqc_dx |
Quantum-safe DX: idempotent PQC-key registration + classical→hybrid migration. |
Typed messages and composers
Every QoreChain custom-module message (49 across amm, bridge, rdk, multilayer,
pqc, svm, lightnode, license, abstractaccount, crossvm, rlconsensus) plus the
common standard Native messages have typed composers under msg. Each returns a
prost message; the *_any variants pack it into a cosmrs::Any with the correct
type URL, ready for tx::send_messages or tx::build_hybrid_tx:
use msg;
use Coin;
let any = swap_exact_in_any;
assert_eq!;
The prost types are generated offline by scripts/codegen-rust.sh (buf +
protoc-gen-prost) and committed under src/proto, so cargo build needs no
protoc. Type URLs use the exact on-chain message names (e.g.
MsgRegisterPQCKey), which is what the chain's interface registry resolves.
Out of scope
Browser wallet adapters and EVM/SVM transaction adapters are intentionally not
bundled — Rust dApps use ethers-rs/alloy (EVM) and the Solana SDK (SVM)
directly. The ICS-20 IBC MsgTransfer is not bundled either (the underlying
proto crate omits IBC types); build it with ibc-proto and pack it via
msg::to_any using msg::cosmos::MSG_TRANSFER.
Quickstart
Create a client
use ClientBuilder;
async
Mainnet (chain id qorechain-vladi) is live; select it and override the
localhost defaults with your node URLs:
use ClientBuilder;
let client = new
.network
.rest
.evm_rpc
.build
.unwrap;
// client.network.chain_id == Some("qorechain-vladi".into())
Derive accounts
use ;
let mnemonic = generate_mnemonic.unwrap;
let native = derive_native_account.unwrap; // qor1...
let evm = derive_evm_account.unwrap; // 0x... (EIP-55)
let svm = derive_svm_account.unwrap; // base58 ed25519
Derivation paths: native m/44'/118'/0'/0/{i}, EVM m/44'/60'/0'/0/{i},
SVM m/44'/501'/{i}'/0'. Invalid mnemonics (wrong checksum) return an error.
Post-quantum signing
use ;
let kp = generate_pqc_keypair.unwrap; // ML-DSA-87: 2592 / 4896
let sig = pqc_sign.unwrap; // 4627-byte signature
assert!;
let ext =
build_hybrid_signature_extension.unwrap;
// serializes to {"algorithm_id":1,"pqc_signature":"<base64>","pqc_public_key":"<base64>"}
let _ = ext;
Denomination math
use ;
assert_eq!;
assert_eq!;
All amount math is exact integer arithmetic on decimal strings — never floating point — so conversions never drift for any magnitude.
Transactions
The tx module builds, signs, and broadcasts native transactions, and provides
end-to-end hybrid (classical secp256k1 + post-quantum ML-DSA-87) signing:
bank_sendbuilds and signs acosmos.bank.v1beta1.MsgSendinto a broadcast-readyTxRaw(SIGN_MODE_DIRECT).broadcastPOSTs signed bytes to the REST/cosmos/tx/v1beta1/txsendpoint (sync/async/block).fee_from_estimateturns an AI fee-oracle response into aFee.build_hybrid_txproduces a tx carrying the classical signature inTxRaw.signaturesPLUS an ML-DSA-87 signature in theTxBodyPQCHybridSignatureextension. The PQC half signsBE32(len(B0)) || B0 || BE32(len(A)) || A(body without the extension, then authInfo); the classical half signs the final body. The signer's PQC key must be registered on-chain (MsgRegisterPQCKeyV2) — or passinclude_pqc_public_keyto embed it for auto-registration.
Transaction proto encoding/signing is delegated to the cosmrs crate; no
proto/crypto primitives are reimplemented here.
Generic messages, auto-gas, tracking, and search
use ;
use Coin;
# async
Typed queries
TypedQueryClient runs the modules' gRPC Query services over the chain RPC
abci_query transport (no gRPC dependency) and returns the strongly typed prost
responses:
use TypedQueryClient;
# async
Chain v3.1.83 adds amm, license, and abstractaccount typed query methods,
the multilayer anchor / anchors state-anchor queries, and abstractaccount
register_authenticator_any / revoke_authenticator_any message composers.
Sidechains, paychains & rollups (v0.4.0)
The multilayer (sidechains/paychains) and rdk (rollup) modules have typed
composers under msg and typed reads on TypedQueryClient. Compose a write with
msg::multilayer::*_any / msg::rdk::*_any (the _any variants pack the message
into a cosmrs::Any) and sign it with tx::send_messages; read layer and rollup
state through the typed query client.
use ;
# async
See the multilayer and rollups guides.
AI pre-flight risk scoring (v0.5.0)
The ai module exposes QoreChain's on-chain AI risk/anomaly model over two EVM
precompiles, so you get an advisory verdict on a transaction before broadcasting
it. AiClient::simulate_with_risk_score bundles a gas estimate, the
aiRiskScore precompile (AI_RISK_SCORE_PRECOMPILE, 0x…0B01), and the
aiAnomalyCheck precompile (AI_ANOMALY_CHECK_PRECOMPILE, 0x…0B02) into one
Preflight.
use ;
# async
See the AI pre-flight guide.
Unified cross-VM calls (v0.5.0)
The cross_vm module wraps MsgCrossVMCall so you can route a single call — or
several atomically in one transaction (call_atomic) — across the EVM,
CosmWasm, and SVM VMs (VM_TYPES). A Payload::Raw(bytes) is sent as-is (the
EVM form: ABI-encoded calldata); a Payload::CosmWasm(json) is serialized to
compact UTF-8 JSON.
use ;
use json;
# async
A CrossVm is a struct literal carrying the signer's key material, chain id,
account number / sequence, fee, REST URL, and an optional QorClient for
get_message. See the cross-VM guide.
Quantum-safe DX (v0.5.0)
The pqc_dx module makes a dApp PQC-protected in one idempotent call: check
whether the signer's Dilithium key is registered, register it if not, then sign
hybrid (ML-DSA-87 + secp256k1).
use PqcDx;
use Any;
# async
migrate_pqc_key rotates an account's on-chain PQC key (MsgMigratePQCKey). See
the quantum-safe guide.
Unified eth-native wallet (v0.6.0)
One eth_secp256k1 key = ONE 20-byte identity rendered three ways — qor1…
(bech32), 0x… (EIP-55), and SVM base58 (the 20 bytes right-padded with 12 zero
bytes to 32). A deposit to any of the three lands in the same balance, and the
key spends on all lanes. unified::derive_unified_account uses the Ethereum HD
path (m/44'/60'/0'/0/{index}); unified::unified_account_from_seed builds one
directly from a 32-byte secret. unified::addresses_from_20 /
unified::qore_addresses convert between the three encodings. The legacy
coin-type-118 derive_native_account still works (additive).
Native-lane signing over the eth key: sign_eth::sign_classical_eth is a
classical secp256k1 signature over keccak256(SignDoc) with pubkey type
/cosmos.evm.crypto.v1.ethsecp256k1.PubKey; sign_eth::sign_hybrid_eth adds the
ML-DSA-87 post-quantum signature. Account parsing accepts eth_secp256k1 public keys.
use ;
use ;
#
See the unified-wallet guide.
WebSocket subscriptions
use SubscribeClient;
# async
Authenticator lanes (v0.7.0 / chain v3.1.85)
A linked external key — a Phantom ed25519 key, or a MetaMask secp256k1 key bound by address — spends from the ONE canonical PQC account through a relayer that submits the tx and pays the fee (its own hybrid-PQC signature satisfies the ante). The external key never produces an ML-DSA co-signature; its signature over the domain-separated, replay-bound sign-bytes is the authorization, under least-privilege, spending-limit and revocable terms enforced on-chain.
Three messages carry the lanes, each with a composer:
MsgExecuteEVM(/qorechain.abstractaccount.v1.MsgExecuteEVM) —msg::abstractaccount::execute_evmMsgExecuteCosmos(/qorechain.abstractaccount.v1.MsgExecuteCosmos) —msg::abstractaccount::execute_cosmosMsgRotatePQCKey(/qorechain.pqc.v1.MsgRotatePQCKey) —msg::pqc::rotate_pqc_key
Sign-bytes helpers (authenticator module) rebuild the exact digest the
chain re-derives: authenticator::evm_auth_sign_bytes / cosmos_auth_sign_bytes
(32-byte SHA-256 digests) and rotation_sign_bytes (the domain-separated string
both keys sign).
NONCE semantics:
MsgExecuteEVM.nonce= the account's current EVM nonce (the relayer is a different account than the owner, so its envelope does not bump the account's nonce — pass it as-is, do not+1).MsgExecuteCosmos.nonce= the per-authenticator sequence for(account, pubkey), a store counter distinct from the account's own sequence.
Permission taxonomy & errors. Query the on-chain permission schema with the
typed query client's abstractaccount_permission_schema() and compare an action
to it before submitting. tx::errors::decode_tx_error surfaces the lane
failures — codespace abstractaccount: 5 SpendingLimitExceeded, 6
SessionKeyExpired, 10 PermissionDenied, 11 AuthenticatorReplay; codespace
pqc: 21 HybridVerifyFailed.
Key rotation. authenticator::rotate_pqc_key_msg_from_mnemonic migrates a
legacy shake256(mnemonic) key to the canonical, address-bound key (dual-signs
over rotation_sign_bytes); authenticator::derive_pqc_legacy re-derives the
old key for the old-key half.
use ;
use execute_evm;
#
#
See the authenticators guide.
Development
Regenerate the committed prost types (maintainer only; needs buf and
protoc-gen-prost):