Skip to main content

Exchange & Integrator Guide

Everything an exchange, custodian, or payment integrator needs to list QOR and process deposits and withdrawals: choosing an interface, detecting deposits safely, and signing withdrawals.

note

This guide targets the qorechain-vladi mainnet (chain version v3.1.85). Rehearse the full flow on the qorechain-diana testnet first — endpoints for both networks are in Networks. If you run your own full node, keep it on the current chain version — an outdated node cannot decode newer transaction types and stops syncing.

Choosing an integration path

QoreChain is a single chain with one unified native-QOR balance exposed through three interfaces. The same private key controls the same funds under a Cosmos (qor1...), an EVM (0x...), and an SVM (base58) address — pick whichever interface fits your stack.

A) Cosmos (native)B) EVMC) SVM (Solana VM)
Addressqor1... (bech32)0x... (Ethereum)Solana base58 (same key)
Decimals (native QOR)6 (uqor)18 (wei-style)9 (lamports; 1 uqor = 1,000 lamports)
ToolingCosmos SDK / CosmJSStandard Ethereum (ethers/web3, MetaMask)@solana/web3.js
Withdrawal signingHybrid PQC required (ML-DSA-87 + secp256k1)Standard secp256k1 / EIP-155 — no PQCvia Cosmos tx or on-node submission
Memo / tag supportYes (shared address + memo)No (one address per user)No (one address per user)
Deposit detectionscan MsgSend eventsscan blocks via eth_getBlockByNumbergetBalance / getSignaturesForAddress
Best forCosmos-native platformsPlatforms with existing EVM integrationSolana-tooling platforms

Recommendation: if you already support EVM chains, Path B (EVM) is the least-effort integration — standard Ethereum tooling, and withdrawals do not require post-quantum signing (the EVM ante path is exempt). Path A (Cosmos) is the native route with memo-based shared deposit addresses. Path C (SVM) is a full native-QOR interface too — choose it if you specifically prefer Solana tooling.

The three interfaces are not mutually exclusive — funds sent to the 0x, qor1, or SVM form of the same key are the same balance.

Running your node

Production integrations should verify deposits against their own synced node, not a third-party endpoint. Follow Connecting to Mainnet — it covers the prebuilt binary bundle (with SHA-256 checksums), genesis, public peers, the fee floor (0.1uqor), and a fast bootstrap via the published chain-data snapshot. No license is required to run a non-validating full node.

Because QoreChain has instant finality (no reorgs), 1 confirmation is final; waiting 1–2 blocks gives comfortable operational margin.

Path A — Cosmos (native)

Base REST URL: https://api.qore.host (or http://localhost:1317 on your node).

Watching deposits

# latest height
curl -s https://rpc.qore.host/status | jq -r .result.sync_info.latest_block_height

# all txs in a height (deposit scanning)
curl -s "https://api.qore.host/cosmos/tx/v1beta1/txs/block/{HEIGHT}" | jq '.txs'

# incoming transfers to an address
curl -s "https://api.qore.host/cosmos/tx/v1beta1/txs?query=transfer.recipient='qor1...'&pagination.limit=50" | jq '.tx_responses[].txhash'

# balance (uqor — divide by 1e6 for QOR)
curl -s "https://api.qore.host/cosmos/bank/v1beta1/balances/qor1.../by_denom?denom=uqor" | jq -r .balance.amount

Anti-fake-deposit checklist

Credit a deposit only when all of the following hold:

  1. tx_response.code == 0 — the transaction succeeded; never credit a failed tx.
  2. The message is /cosmos.bank.v1beta1.MsgSend (or a MsgMultiSend output) — not a contract call or another module.
  3. The to_address equals your deposit address, and (with the shared-address model) the memo matches the user.
  4. The denom == "uqor" and the amount is the credited value (uqor → ÷ 10⁶ for QOR). Reject any other denom.
  5. The tx is in a committed block (height present and ≤ the latest committed height). Finality is instant — 1 confirmation is final; wait 1–2 blocks for margin.
  6. Recompute the amount from the transfer events (coin_received / coin_spent) and cross-check it against the message amount — never trust a single field or the memo alone.
  7. Verify the tx hash exists via GET /cosmos/tx/v1beta1/txs/{hash} against your own synced node.

Withdrawals — hybrid PQC signing

Mainnet enforces post-quantum signatures on cosmos transactions (allow_classical_fallback = false): every withdrawal needs a hybrid signature — ML-DSA-87 (Dilithium-5, FIPS-204) plus secp256k1. Deposits do not need this (you only watch the chain).

The signing library is @qorechain/wallet-adapter (npm), which pulls in @qorechain/pqc for the FIPS-204 primitives:

npm i @qorechain/wallet-adapter @qorechain/pqc @cosmjs/proto-signing cosmjs-types@0.9.0
# pin cosmjs-types to 0.9.x — 0.10 breaks the subpath imports the adapter uses

Signing is a two-step flow (mirroring qorechaind tx pqc cosign):

Step 1 — one-time per hot wallet: register its ML-DSA-87 key. This single registration transaction is classical-signed (bootstrap exemption): message /qorechain.pqc.v1.MsgRegisterPQCKeyV2 with {sender, public_key, algorithm_id: 1, key_type: "hybrid"}. Derive the ML-DSA key deterministically so it is recoverable from your existing secret — e.g. seed = SHAKE-256("qorechain:pqc:v1|" + address + "|" + mnemonic), then mldsa.keygen(seed) — and store the seed alongside your hot-wallet key.

Step 2 — every withdrawal after that: hybrid-sign the MsgSend. The adapter bakes the ML-DSA-87 signature into a tx-body extension before the normal secp256k1 signDirect, so your existing signer stays unchanged:

import { QoreChainSigner } from "@qorechain/wallet-adapter";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx.js";

// pqc = { publicKey, secretKey } from mldsa.keygen(seed)
// accountNumber + sequence from the auth query
const signer = new QoreChainSigner({ wallet, chainId: "qorechain-vladi",
address, pubkeySecp256k1, accountNumber, pqc });
const txBytes = await signer.signHybrid({
messages: [{ typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: MsgSend.encode(MsgSend.fromPartial({ fromAddress, toAddress,
amount: [{ denom: "uqor", amount: "1000000" }] })).finish() }],
fee: { amount: [{ denom: "uqor", amount: "40000" }], gasLimit: 400000n },
sequence });

Broadcast the signed bytes:

curl -s -X POST https://api.qore.host/cosmos/tx/v1beta1/txs \
-H 'Content-Type: application/json' \
-d '{"tx_bytes":"<base64-signed-tx>","mode":"BROADCAST_MODE_SYNC"}' | jq .tx_response.code
# 0 => accepted into the mempool
# code 8 "classical fallback not allowed" => step 1 not done yet for this account

Then poll GET /cosmos/tx/v1beta1/txs/{hash} until it appears in a block with code == 0.

For an HSM or a custom signer in another language, use the standalone qorechain-pqc FIPS-204 libraries (npm, PyPI, crates.io, Maven Central, Go) and assemble the same extension. The ML-DSA signature must be deterministic (FIPS-204 §3.4) — see Deterministic signing; the chain rejects hedged signatures.

Server-side alternative: @qorechain/chain-bridge

For a fully server-side hot-wallet worker (no browser wallet involved), @qorechain/chain-bridge (npm) wraps the whole flow — key derivation, PQC auto-registration on first use, hybrid signing, and broadcast — in one call. It is pure JavaScript (no native addons), suitable for serverless workers:

import { ChainBridge } from "@qorechain/chain-bridge";

const bridge = new ChainBridge({
cosmosRpc: "https://rpc.qore.host", // or your own node
chainId: "qorechain-vladi",
signerMnemonic: process.env.HOT_WALLET_MNEMONIC, // from your secrets manager
});

// One call: derives the canonical ML-DSA-87 key, auto-registers it if missing,
// hybrid-signs the MsgSend, and broadcasts. Amounts are in uqor (6 decimals).
const { txHash } = await bridge.sendTokens({
to: "qor1recipient...",
amountUqor: "1000000", // 1 QOR
});

chain-bridge (≥0.1.1) uses the same canonical address-bound PQC derivation as the rest of the stack — SHAKE-256("qorechain:pqc:v1|address|mnemonic") — so the key is recoverable from the mnemonic with qorechaind tx pqc recover-key. Accounts registered with older tooling are handled automatically (legacy-key fallback), and can be migrated once to the canonical key with MsgRotatePQCKey.

Path B — EVM

Standard Ethereum integration against https://evm.qore.host (chain ID 9801) or your own node's port 8545.

  • Decimals: native QOR is 18 decimals on the EVM rail (1 uqor = 10¹² wei). Getting this wrong mis-credits deposits by a factor of 10¹².
  • Deposits: scan blocks with eth_getBlockByNumber for native transfers to your addresses; confirm with eth_getTransactionReceipt (status == 0x1).
  • Withdrawals: standard secp256k1 / EIP-155 signing — no PQC required on the EVM ante path. Any Ethereum signing stack works unchanged.
  • Anti-fake-deposit: verify the receipt status, that the value moved is a native transfer (not an ERC-20 event you don't index), and confirm against your own node.
  • Address mapping: the 0x address and the qor1 address are two encodings of the same account — funds are shared. See EVM Development.

Path C — SVM (Solana-compatible)

As of v3.1.82 the SVM interface serves native QOR (see Native QOR on the SVM Interface):

  • Balances: getBalance returns lamports (÷ 10⁹ for QOR; 1 uqor = 1,000 lamports).
  • Deposits: getSignaturesForAddress gives the transaction history for an address; System Program transfers move native QOR.
  • Public endpoints (https://svm.qore.host, https://svm-testnet.qore.host) are read-only; submit transactions through your own node.

Flow summary

OperationPathSigning needed?
Deposit (user → platform)Watch your synced node for transfers to your address (+ memo on Cosmos)No — monitor only
Withdrawal (platform → user)Build the transfer, sign offline, broadcastCosmos: hybrid PQC · EVM: standard secp256k1
Balance / sweepREST / EVM / SVM balance query + transferSign only for the sweep