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 10 QRDX at 920 USDC", "Swap 1,000 USDC for at least 1.08 QRDX"), the result of a dry run against the latest block for EVM transactions, the maximum fee, and warnings (look-alike site names, 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.