{"id":703,"date":"2026-06-11T20:26:14","date_gmt":"2026-06-11T19:26:14","guid":{"rendered":"https:\/\/digitalguards.nl\/?p=703"},"modified":"2026-06-11T21:21:28","modified_gmt":"2026-06-11T20:21:28","slug":"qrl-connect-post-quantum-dapp-to-wallet-connection-and-how-to-add-it-to-your-dapp","status":"publish","type":"post","link":"https:\/\/digitalguards.nl\/index.php\/2026\/06\/11\/qrl-connect-post-quantum-dapp-to-wallet-connection-and-how-to-add-it-to-your-dapp\/","title":{"rendered":"QRL Connect: Post-Quantum dApp-to-Wallet Connection, and How to Add It to Your dApp"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">QRL Connect: Post-Quantum dApp-to-Wallet Connection, and How to Add It to Your dApp<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Connecting a decentralized application to a user&#8217;s wallet sounds simple and is not. The dApp runs in a browser, the wallet lives on a phone, and the two need to exchange signed transactions securely without trusting the network in between. For QRL 2.0 we built QRL Connect, an open-source SDK that solves this with a self-hosted, end-to-end encrypted, post-quantum channel. This post explains how it works and how you can drop it into your own dApp.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Why not WalletConnect?<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The obvious question is why not just use WalletConnect. Two reasons. First, WalletConnect v2 depends on third-party cloud infrastructure, and we wanted a transport we fully control and can self-host. Second, generic wallets do not understand QRL 2.0&#8217;s Q-prefixed addresses or its post-quantum signature scheme. Since we build both the dApp SDK and the wallet, a purpose-built protocol let us put post-quantum cryptography directly into the transport layer, instead of the classical key exchange WalletConnect relies on.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The cryptography<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Everything between the dApp and the wallet is end-to-end encrypted, and the relay server in the middle only ever sees ciphertext. The building blocks are all NIST-standardized post-quantum or well-established primitives:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>ML-KEM-768<\/strong> (FIPS 203, NIST Level 3): the key encapsulation mechanism that establishes a shared secret between the dApp and the wallet. This is the post-quantum stand-in for classical Diffie-Hellman.<\/li>\n\n\n<li><strong>HKDF-SHA-256<\/strong>: derives the actual encryption keys from that shared secret, bound to the full handshake transcript so both sides are provably talking about the same session.<\/li>\n\n\n<li><strong>AES-256-GCM<\/strong>: authenticated encryption applied to every JSON-RPC message. Any tampering is caught at the authentication tag.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The session key is tied to the entire handshake transcript (a label, the channel id, the public key, and the KEM ciphertext). That binding closes a class of attacks where a malicious peer tries to make two sessions agree on a key while disagreeing about who is who.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">The handshake<\/h5>\n\n\n\n<p class=\"wp-block-paragraph\">Establishing the channel takes three steps, modeled loosely on a TCP handshake:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SYN<\/strong>: the dApp sends its ML-KEM-768 public key to the wallet through the relay. In practice this public key travels inside the QR code itself, so the relay never even sees an uncommitted key.<\/li>\n\n\n<li><strong>SYNACK<\/strong>: the wallet encapsulates a shared secret to that public key and returns the KEM ciphertext.<\/li>\n\n\n<li><strong>ACK<\/strong>: the dApp decapsulates the secret, both sides derive their AES-256-GCM keys from the transcript, and the encrypted channel is open.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">From that point on, every request and response is an AES-256-GCM ciphertext passing through a relay that cannot read it.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Post-quantum signing, not Ethereum signing<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">QRL Connect speaks an EIP-1193 style provider interface, so it feels familiar to anyone who has used a browser wallet, but the signing methods are post-quantum native. The methods <code>qrl_signMessage<\/code> and <code>qrl_signTypedData<\/code> use SHAKE256 hashing and QRL 2.0&#8217;s ML-DSA-87 (Dilithium) signatures. They return a rich result that includes the signature, the public key, and the signer, and you can verify it locally with the <code>verifyMessage<\/code> and <code>verifyTypedData<\/code> helpers the package exports. The old Ethereum-flavored signing methods were removed in version 3.0.0.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Adding it to your dApp<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The SDK is published on npm as <code>@qrlwallet\/connect<\/code>. Install it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install @qrlwallet\/connect<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then create a connection, show the URI as a QR code (or open it as a deep link on mobile), and use it like any EIP-1193 provider:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { QRLConnect } from &#x27;@qrlwallet\/connect&#x27;;\n\nconst qrl = new QRLConnect({\n  dappMetadata: { name: &#x27;My QRL dApp&#x27;, url: &#x27;https:\/\/mydapp.com&#x27; },\n});\n\n\/\/ Get the pairing URI\nconst uri = await qrl.getConnectionURI();\n\nif (qrl.isMobile()) {\n  window.location.href = uri;   \/\/ open the wallet app via deep link\n} else {\n  \/\/ render `uri` as a QR code using any QR library\n}\n\nqrl.on(&#x27;connect&#x27;, ({ chainId }) =&gt; {\n  console.log(&#x27;Wallet connected on chain&#x27;, chainId);\n});\n\n\/\/ Use it like any EIP-1193 provider\nconst accounts = await qrl.request({ method: &#x27;qrl_requestAccounts&#x27; });\n\nconst txHash = await qrl.request({\n  method: &#x27;qrl_sendTransaction&#x27;,\n  params: [{ from: accounts[0], to: &#x27;0x...&#x27;, value: &#x27;0x2386F26FC10000&#x27; }], \/\/ 0.01 QRL\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That is the whole flow. Read-only calls such as <code>qrl_getBalance<\/code> or <code>qrl_blockNumber<\/code> are proxied automatically, while anything that needs approval, like sending a transaction or signing a message, prompts the user inside the wallet.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Sessions and reconnection<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Sessions persist in the browser&#8217;s local storage for seven days. When a user returns to your dApp, the SDK can reconnect to the existing channel without a fresh QR scan. Use <code>hasStoredSession()<\/code> on page load to decide whether to show a reconnect state, keep one <code>QRLConnect<\/code> instance for the page lifetime, and call <code>newConnection()<\/code> only when the user wants to pair a different wallet. Listen to the <code>statusChanged<\/code> event for UI transitions rather than reaching into internals.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Self-hosting the relay<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The relay is a lightweight message router that only forwards ciphertext, and it is part of our open-source backend. If you would rather run your own, point the SDK at it with a single config option:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const qrl = new QRLConnect({\n  dappMetadata: { name: &#x27;My dApp&#x27;, url: &#x27;https:\/\/mydapp.com&#x27; },\n  relayUrl: &#x27;https:\/\/my-relay-server.com&#x27;,\n});<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Try it and read the code<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The fastest way to see it work is the live example at <a href=\"https:\/\/zondscan.com\/dapp-example\">zondscan.com\/dapp-example<\/a>, which pairs with the production relay and the MyQRLWallet app out of the box. The SDK is open source and MIT licensed: install it from npm as <code>@qrlwallet\/connect<\/code> or read the source on GitHub. Contributions and questions are welcome.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>QRL Connect links dApps to the MyQRLWallet app over an end-to-end encrypted, post-quantum channel (ML-KEM-768 and AES-256-GCM). Here is how the cryptography works and how to integrate the open-source SDK.<\/p>\n","protected":false},"author":1,"featured_media":731,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"plain-container","ast-site-content-layout":"normal-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[],"class_list":["post-703","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/posts\/703","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/comments?post=703"}],"version-history":[{"count":2,"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/posts\/703\/revisions"}],"predecessor-version":[{"id":722,"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/posts\/703\/revisions\/722"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/media\/731"}],"wp:attachment":[{"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/media?parent=703"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/categories?post=703"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/digitalguards.nl\/index.php\/wp-json\/wp\/v2\/tags?post=703"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}