Wallet integration
Connect a dApp to QRDX Wallet: discovery, accounts, signing, transactions and errors.
QRDX Wallet is a standard EIP-1193 wallet with a QRDX namespace on top. Ethereum tooling works unchanged; post-quantum accounts and the native exchange use qrdx_* methods.
Find the wallet
Use EIP-6963. It works even when another wallet owns window.ethereum:
let qrdx
window.addEventListener('eip6963:announceProvider', (e) => {
if (e.detail.info.rdns === 'org.qrdx.wallet') qrdx = e.detail.provider
})
window.dispatchEvent(new Event('eip6963:requestProvider'))Also available: window.qrdx is always the QRDX provider (page scripts cannot overwrite it); window.ethereum is QRDX only if no other wallet took it first (window.ethereum.isQRDX).
Without the extension (the web wallet, the iPhone app), connect by QR code with QRDX Connect: you get the same provider over an encrypted relay.
Accounts
const [account] = await qrdx.request({ method: 'qrdx_requestAccounts' })
// { address: '0x…', pqAddress: '0xPQ…', pqAccountId: '0x…', pqPublicKey: '…', pqFingerprint: '…' }
const chain = await qrdx.request({ method: 'qrdx_chainInfo' })
// { id, name, chainId, isQrdx, isTestnet, nativeCurrency }| Method | |
|---|---|
eth_requestAccounts, eth_accounts | the shared 0x addresses, selected first; [] while locked or not connected |
qrdx_requestAccounts, qrdx_accounts | each account's address, pqAddress, pqAccountId, pqPublicKey, pqFingerprint |
eth_chainId, net_version, qrdx_chainInfo | the active network |
wallet_switchEthereumChain | networks in the wallet's registry only (unknown: 4902); asks the user |
wallet_getPermissions, wallet_requestPermissions, wallet_revokePermissions | EIP-2255; revoking disconnects the site |
Events: connect, disconnect, accountsChanged, chainChanged.
Signing
| Method | |
|---|---|
personal_sign(message, address) | EIP-191 with the classic key |
eth_signTypedData_v4(address, typedData) | EIP-712; refused if domain.chainId is not the active chain |
qrdx_signPQMessage(message, address?) | ML-DSA-65 over the QRDX message prefix; returns { signature, publicKey, address }. Verify with the public key (Keys and signing). |
eth_sign | disabled (4200): it signs arbitrary hashes and is a phishing vector |
Transactions
| Method | |
|---|---|
eth_sendTransaction(tx) | from the classic 0x key; EIP-1559 fields are folded into a legacy gasPrice. Contract deployment from dApps is not supported. |
qrdx_sendPQTransaction({ to, value, data?, gas? }) | from the post-quantum account, as a type-0x51 transaction; to may be any address form |
qrdx_sendExchangeTransaction({ op, params, gasLimit? }) | any exchange operation, signed with the account's post-quantum key and next exchange nonce; returns { txHash, nonce } |
wallet_watchAsset({ type: 'ERC20', options: { address, symbol, decimals } }) | adds a token after the user approves |
const { txHash } = await qrdx.request({
method: 'qrdx_sendExchangeTransaction',
params: [{ op: 'PLACE_ORDER', params: { pair: `QRDX:${token}`, side: 'buy', order_type: 'limit', price: '0.5', amount: '10' } }],
})
// poll exchange_getTransactionReceipt(txHash)Every request opens an approval showing the site's real origin, the call decoded ("Buy 0.5 qBTC at 85,000 qUSDC", "Swap 1,000 qUSDC for at least 0.0117 qBTC"), the maximum fee, and warnings (unknown tokens, swaps without a minimum, token approvals).
Several operations per block. The wallet tracks the exchange nonces it has submitted that no block has included, so a dApp can, say, place an order and cancel it before the next block. A TOKEN_DEPLOY's token address is known before inclusion (0x + blake2b-160 of "<sender>:<nonce>:<symbol>"), so a dApp can create the token's pool in the same block.
Reads without connecting
These pass through to the active network's node: eth_blockNumber, eth_call, eth_estimateGas, eth_gasPrice, eth_getBalance, eth_getBlockBy*, eth_getCode, eth_getLogs, eth_getStorageAt, eth_getTransaction*, eth_getTransactionCount, eth_feeHistory, qrdx_getAccountId, qrdx_getIntrinsicGas, and the exchange_get*, exchange_quote* and perp_get* reads.
Errors
| Code | |
|---|---|
4001 | the user rejected the request |
4100 | not authorized: the site is not connected, or that account is not shared |
4200 | method not supported |
4900 | the wallet is unavailable |
4902 | unknown chain id |
-32602 | invalid parameters |
Two accounts, one person
Each wallet account has a classic 0x address and a post-quantum 0xPQ address, which are two ledger accounts. eth_sendTransaction spends from the 0x one; qrdx_sendPQTransaction and every exchange operation from the post-quantum one (its ledger key is pqAccountId). Show users the 0xPQ address for anything they trade with.