@qrlwallet/connect 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DigitalGuards
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,247 @@
1
+ # @qrlwallet/connect
2
+
3
+ Connect your dApp to QRL Wallet. Users scan a QR code (desktop) or tap a button (mobile) to pair their wallet, then approve transactions right from the app.
4
+
5
+ ## How it works
6
+
7
+ 1. Your dApp generates a connection URI and shows it as a QR code
8
+ 2. User scans with the QRL Wallet app (or taps a deep link on mobile)
9
+ 3. An encrypted channel is established through our relay server
10
+ 4. Your dApp sends JSON-RPC requests, the wallet prompts for approval
11
+ 5. Signed results come back to your dApp
12
+
13
+ All communication is end-to-end encrypted (ECIES). The relay server never sees your data.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @qrlwallet/connect
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```typescript
24
+ import { QRLConnect } from '@qrlwallet/connect';
25
+
26
+ const qrl = new QRLConnect({
27
+ dappMetadata: {
28
+ name: 'My QRL dApp',
29
+ url: 'https://mydapp.com',
30
+ },
31
+ });
32
+
33
+ // Get the connection URI
34
+ const uri = await qrl.getConnectionURI();
35
+
36
+ // Desktop: render as QR code (use any QR library)
37
+ // Mobile: redirect to open the wallet app
38
+ if (qrl.isMobile()) {
39
+ window.location.href = uri;
40
+ } else {
41
+ // render uri as QR code
42
+ }
43
+
44
+ // Listen for connection
45
+ qrl.on('connect', ({ chainId }) => {
46
+ console.log('Wallet connected on chain', chainId);
47
+ });
48
+
49
+ qrl.on('accountsChanged', (accounts) => {
50
+ console.log('Connected accounts:', accounts);
51
+ });
52
+
53
+ qrl.on('statusChanged', (status) => {
54
+ console.log('Connection status:', status);
55
+ });
56
+
57
+ // Use as an EIP-1193 provider
58
+ const accounts = await qrl.request({ method: 'qrl_requestAccounts' });
59
+
60
+ const txHash = await qrl.request({
61
+ method: 'qrl_sendTransaction',
62
+ params: [{
63
+ from: accounts[0],
64
+ to: '0x...',
65
+ value: '0x2386F26FC10000', // 0.01 QRL
66
+ }],
67
+ });
68
+ ```
69
+
70
+ ## Configuration
71
+
72
+ ```typescript
73
+ const qrl = new QRLConnect({
74
+ // Required
75
+ dappMetadata: {
76
+ name: 'QuantaPool',
77
+ url: 'https://quantapool.com',
78
+ icon: 'https://quantapool.com/icon.png', // optional
79
+ },
80
+
81
+ // Optional
82
+ relayUrl: 'https://qrlwallet.com', // default relay
83
+ chainId: '0x0', // QRL chain ID
84
+ autoReconnect: true, // reconnect on page load (default: true)
85
+ debug: false, // console logging
86
+ });
87
+ ```
88
+
89
+ ## API
90
+
91
+ ### `QRLConnect`
92
+
93
+ The main class. Creates a connection manager and EIP-1193 provider.
94
+
95
+ | Method | Description |
96
+ |--------|-------------|
97
+ | `getConnectionURI()` | Returns the `qrlconnect://` URI for QR codes or deep links |
98
+ | `request({ method, params })` | Send a JSON-RPC request to the wallet |
99
+ | `isMobile()` | Check if the user is on a mobile browser |
100
+ | `getAppStoreUrl()` | Get the app store link for QRL Wallet |
101
+ | `isConnected()` | Whether the wallet is connected |
102
+ | `getAccounts()` | Get connected accounts |
103
+ | `getStatus()` | Current connection status |
104
+ | `hasStoredSession()` | Check if a reconnectable session exists in local storage |
105
+ | `newConnection()` | Reset current pairing and generate a new channel/URI |
106
+ | `disconnect()` | End the session |
107
+ | `getChannelId()` | Get the current relay channel ID (useful for debugging) |
108
+
109
+ ### Events
110
+
111
+ | Event | Payload | Description |
112
+ |-------|---------|-------------|
113
+ | `connect` | `{ chainId }` | Wallet connected |
114
+ | `disconnect` | `{ code, message }` | Wallet disconnected |
115
+ | `accountsChanged` | `string[]` | Account list changed |
116
+ | `chainChanged` | `string` | Chain switched |
117
+ | `statusChanged` | `ConnectionStatus` | Intermediate lifecycle states (see values below) |
118
+ | `connection_lost` | — | Emitted after 5 failed reconnection attempts |
119
+
120
+ #### `ConnectionStatus` values
121
+
122
+ | Value | Meaning |
123
+ |-------|---------|
124
+ | `disconnected` | No active connection |
125
+ | `connecting` | Connecting to relay server |
126
+ | `waiting` | QR displayed, waiting for wallet to scan |
127
+ | `key_exchange` | ECIES key exchange in progress |
128
+ | `connected` | Wallet connected, ready for requests |
129
+ | `reconnecting` | Attempting to restore a previous session |
130
+
131
+ ### Supported RPC methods
132
+
133
+ **Require user approval:**
134
+ `qrl_requestAccounts`, `qrl_sendTransaction`, `qrl_signTransaction`, `qrl_sign`, `personal_sign`, `qrl_signTypedData_v4`, `wallet_addQrlChain`, `wallet_switchQrlChain`
135
+
136
+ **Auto-proxied (no approval needed):**
137
+ `qrl_getBalance`, `qrl_call`, `qrl_estimateGas`, `qrl_blockNumber`, `qrl_chainId`, `qrl_getTransactionReceipt`, and 30+ more read-only methods.
138
+
139
+ ## Sessions
140
+
141
+ Sessions persist in `localStorage` for 7 days. When a user returns to your dApp, the SDK can automatically reconnect without requiring a new QR scan.
142
+
143
+ Recommended lifecycle:
144
+
145
+ - Use `hasStoredSession()` on page load to decide whether to show reconnect state/UI
146
+ - Keep a single `QRLConnect` instance for the page lifetime
147
+ - Use `newConnection()` when the user explicitly wants to pair a different wallet
148
+ - Use `statusChanged` for UI state transitions instead of relying on internals
149
+
150
+ ## How the relay works
151
+
152
+ The relay is a lightweight Socket.IO server that routes encrypted messages between your dApp and the wallet. It runs at `wss://qrlwallet.com/relay`.
153
+
154
+ - Messages are ECIES-encrypted end-to-end (the relay sees only ciphertext)
155
+ - Max 2 participants per channel
156
+ - Messages are buffered for up to 5 minutes if the wallet is temporarily offline (e.g., app backgrounded)
157
+ - Channels auto-expire after 30 minutes of inactivity
158
+
159
+ ## Self-hosting the relay
160
+
161
+ The relay is part of [myqrlwallet-backend](https://github.com/DigitalGuards/myqrlwallet-backend). To use your own:
162
+
163
+ ```typescript
164
+ const qrl = new QRLConnect({
165
+ dappMetadata: { name: 'My dApp', url: 'https://mydapp.com' },
166
+ relayUrl: 'https://my-relay-server.com',
167
+ });
168
+ ```
169
+
170
+ ## Bundler setup
171
+
172
+ The SDK depends on Node's `Buffer` via `eciesjs`. If you're using Vite, add the polyfill plugin:
173
+
174
+ ```bash
175
+ npm install vite-plugin-node-polyfills
176
+ ```
177
+
178
+ ```js
179
+ // vite.config.js
180
+ import { nodePolyfills } from 'vite-plugin-node-polyfills';
181
+
182
+ export default defineConfig({
183
+ plugins: [
184
+ nodePolyfills({
185
+ include: ['buffer'],
186
+ globals: { Buffer: true },
187
+ }),
188
+ ],
189
+ define: { global: 'globalThis' },
190
+ });
191
+ ```
192
+
193
+ For webpack 5, add `buffer` to `resolve.fallback` and the `ProvidePlugin` for `Buffer`.
194
+
195
+ ## Development
196
+
197
+ ```bash
198
+ # Build (CJS + ESM + .d.ts)
199
+ npm run build
200
+
201
+ # Watch mode (rebuilds on change)
202
+ npm run dev
203
+
204
+ # Type-check
205
+ npm run typecheck
206
+
207
+ # Unit tests (vitest)
208
+ npm test
209
+
210
+ # Lint
211
+ npm run lint
212
+
213
+ # E2E test (starts a local relay, simulates dApp + wallet handshake)
214
+ node test-e2e.js
215
+ ```
216
+
217
+ ## Running the example dApp
218
+
219
+ The `example/` directory contains a Vite test dApp with QR code generation, transaction sending, message signing, and read-only RPC calls.
220
+
221
+ > **Want to try it without any setup?** The same example is hosted live at [zondscan.com/dapp-example](https://zondscan.com/dapp-example) — it pairs with the production relay and MyQRLWallet mobile app out of the box.
222
+
223
+ ```bash
224
+ # 1. Build the SDK first (the example links to it locally)
225
+ npm run build
226
+
227
+ # 2. Install example dependencies
228
+ cd example && npm install
229
+
230
+ # 3. Start the dev server (opens http://localhost:5174)
231
+ npm run dev
232
+ ```
233
+
234
+ The example connects to the production relay at `wss://qrlwallet.com/relay` by default. To use a local relay, start `myqrlwallet-backend` and change `RELAY_URL` in `example/main.js`.
235
+
236
+ ## Security
237
+
238
+ - Private keys and seeds never leave the wallet
239
+ - All relay traffic is ECIES-encrypted (Elliptic Curve Integrated Encryption Scheme)
240
+ - Key exchange uses a 3-step SYN/SYNACK/ACK handshake
241
+ - PIN or biometric authentication required for every transaction
242
+ - dApp URL is displayed to the user before connecting
243
+ - Unknown RPC methods are rejected with `-32601`
244
+
245
+ ## License
246
+
247
+ MIT
@@ -0,0 +1,409 @@
1
+ import EventEmitter from 'eventemitter3';
2
+
3
+ /** dApp metadata shown to user in approval UI */
4
+ interface DAppMetadata {
5
+ name: string;
6
+ url: string;
7
+ icon?: string;
8
+ }
9
+ /** Stored session for reconnection */
10
+ interface DAppSession {
11
+ channelId: string;
12
+ privateKey: string;
13
+ otherPublicKey: string | null;
14
+ dappMetadata: DAppMetadata;
15
+ connectedAccounts: string[];
16
+ chainId: string;
17
+ createdAt: number;
18
+ lastActivity: number;
19
+ }
20
+ /** Pending JSON-RPC request awaiting wallet response */
21
+ interface PendingRequest {
22
+ id: string | number;
23
+ method: string;
24
+ params?: unknown[];
25
+ resolve: (result: unknown) => void;
26
+ reject: (error: Error) => void;
27
+ timestamp: number;
28
+ }
29
+ /** Key exchange message types */
30
+ declare enum KeyExchangeMessageType {
31
+ SYN = "key_handshake_SYN",
32
+ SYNACK = "key_handshake_SYNACK",
33
+ ACK = "key_handshake_ACK"
34
+ }
35
+ /** Message types for the relay protocol */
36
+ declare enum MessageType {
37
+ KEY_EXCHANGE = "key_exchange",
38
+ JSONRPC = "jsonrpc",
39
+ WALLET_INFO = "wallet_info",
40
+ ORIGINATOR_INFO = "originator_info",
41
+ TERMINATE = "terminate",
42
+ PING = "ping",
43
+ READY = "ready"
44
+ }
45
+ /** Wire message format sent through the relay */
46
+ interface RelayMessage {
47
+ id: string;
48
+ clientType: 'dapp' | 'wallet';
49
+ message: string | object;
50
+ seq?: number;
51
+ }
52
+ /** Connection state */
53
+ declare enum ConnectionStatus {
54
+ DISCONNECTED = "disconnected",
55
+ CONNECTING = "connecting",
56
+ WAITING = "waiting",
57
+ KEY_EXCHANGE = "key_exchange",
58
+ CONNECTED = "connected",
59
+ RECONNECTING = "reconnecting"
60
+ }
61
+ /** EIP-1193 provider events */
62
+ interface ProviderEvents {
63
+ connect: (info: {
64
+ chainId: string;
65
+ }) => void;
66
+ disconnect: (error: {
67
+ code: number;
68
+ message: string;
69
+ }) => void;
70
+ chainChanged: (chainId: string) => void;
71
+ accountsChanged: (accounts: string[]) => void;
72
+ message: (message: {
73
+ type: string;
74
+ data: unknown;
75
+ }) => void;
76
+ statusChanged: (status: ConnectionStatus) => void;
77
+ }
78
+ /** JSON-RPC request */
79
+ interface JsonRpcRequest {
80
+ jsonrpc?: string;
81
+ id?: string | number;
82
+ method: string;
83
+ params?: unknown[];
84
+ }
85
+ /** JSON-RPC response */
86
+ interface JsonRpcResponse {
87
+ jsonrpc: string;
88
+ id: string | number;
89
+ result?: unknown;
90
+ error?: {
91
+ code: number;
92
+ message: string;
93
+ data?: unknown;
94
+ };
95
+ }
96
+ /** QRL Connect configuration */
97
+ interface QRLConnectOptions {
98
+ dappMetadata: DAppMetadata;
99
+ relayUrl?: string;
100
+ chainId?: string;
101
+ /** Auto-reconnect to stored session on init */
102
+ autoReconnect?: boolean;
103
+ /** Session storage key prefix */
104
+ storageKey?: string;
105
+ /** Enable debug logging */
106
+ debug?: boolean;
107
+ }
108
+
109
+ /**
110
+ * QRL Connect EIP-1193 Provider.
111
+ * Bridges JSON-RPC requests from dApp to QRL Wallet via the relay.
112
+ */
113
+
114
+ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
115
+ private connectionManager;
116
+ private pendingRequests;
117
+ private options;
118
+ readonly isQRLConnect = true;
119
+ constructor(options: QRLConnectOptions);
120
+ private setupConnectionListeners;
121
+ /**
122
+ * Generate a connection URI for QR code display or deep link redirect.
123
+ */
124
+ getConnectionURI(): Promise<string>;
125
+ /**
126
+ * Check if the current browser is mobile.
127
+ */
128
+ isMobile(): boolean;
129
+ /**
130
+ * Get the app store URL for the QRL Wallet app.
131
+ */
132
+ getAppStoreUrl(): string;
133
+ /**
134
+ * EIP-1193 request method.
135
+ */
136
+ request(args: {
137
+ method: string;
138
+ params?: unknown[];
139
+ }): Promise<unknown>;
140
+ /**
141
+ * Get the current connection status.
142
+ */
143
+ getStatus(): ConnectionStatus;
144
+ /**
145
+ * Get connected accounts.
146
+ */
147
+ getAccounts(): string[];
148
+ /**
149
+ * Get the channel ID for this connection.
150
+ */
151
+ getChannelId(): string;
152
+ /**
153
+ * Check if connected and keys exchanged.
154
+ */
155
+ isConnected(): boolean;
156
+ /**
157
+ * Check if a stored session exists that can be reconnected.
158
+ */
159
+ hasStoredSession(): boolean;
160
+ /**
161
+ * Reset the connection and start a fresh pairing with a new channel.
162
+ * Use this when the user wants to create a new connection instead of
163
+ * reconnecting to an existing session.
164
+ */
165
+ newConnection(): Promise<string>;
166
+ /**
167
+ * Disconnect from wallet and clean up.
168
+ */
169
+ disconnect(): void;
170
+ }
171
+
172
+ /**
173
+ * Connection Manager - Orchestrates socket connection, key exchange,
174
+ * and encrypted message routing. Manages session persistence and reconnection.
175
+ */
176
+
177
+ interface ConnectionManagerEvents {
178
+ status_changed: (status: ConnectionStatus) => void;
179
+ accounts_changed: (accounts: string[]) => void;
180
+ chain_changed: (chainId: string) => void;
181
+ jsonrpc_response: (response: JsonRpcResponse) => void;
182
+ wallet_info: (info: {
183
+ accounts: string[];
184
+ chainId: string;
185
+ }) => void;
186
+ connection_lost: () => void;
187
+ error: (error: Error) => void;
188
+ }
189
+ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
190
+ private socketClient;
191
+ private ecies;
192
+ private keyExchange;
193
+ private status;
194
+ private channelId;
195
+ private dappMetadata;
196
+ private chainId;
197
+ private relayUrl;
198
+ private connectedAccounts;
199
+ private storageKey;
200
+ private unresponsiveTimer;
201
+ private synRetryTimer;
202
+ private failedReconnects;
203
+ private synSent;
204
+ private synAttempts;
205
+ private walletPresent;
206
+ private static MAX_RECONNECT_FAILURES;
207
+ private static SYN_RETRY_MS;
208
+ private static MAX_SYN_ATTEMPTS;
209
+ constructor(options: {
210
+ dappMetadata: DAppMetadata;
211
+ relayUrl?: string;
212
+ chainId?: string;
213
+ storageKey?: string;
214
+ });
215
+ private setupSocketListeners;
216
+ private setupKeyExchangeListeners;
217
+ /**
218
+ * Generate the connection URI and start listening for wallet connection.
219
+ */
220
+ getConnectionURI(retryOnConflict?: boolean): Promise<string>;
221
+ /**
222
+ * Reconnect to an existing session.
223
+ */
224
+ reconnect(): Promise<boolean>;
225
+ /**
226
+ * Send a JSON-RPC request to the wallet.
227
+ */
228
+ sendJsonRpc(request: JsonRpcRequest): void;
229
+ /**
230
+ * Handle an incoming relay message.
231
+ */
232
+ private handleRelayMessage;
233
+ private handleKeyExchangeMessage;
234
+ private handleDecryptedMessage;
235
+ /** Send a plaintext (unencrypted) message through the relay. */
236
+ private sendPlaintext;
237
+ /** Send an encrypted message through the relay. */
238
+ private sendEncrypted;
239
+ private startUnresponsiveTimer;
240
+ private clearUnresponsiveTimer;
241
+ private sendSYN;
242
+ private scheduleSynRetry;
243
+ private clearSynRetryTimer;
244
+ private setStatus;
245
+ private isDappParticipantConflictError;
246
+ private areArraysEqual;
247
+ getStatus(): ConnectionStatus;
248
+ getAccounts(): string[];
249
+ getChainId(): string;
250
+ getChannelId(): string;
251
+ /** Check if a stored session exists (without loading/restoring it). */
252
+ hasStoredSession(): boolean;
253
+ /**
254
+ * Reset the connection and create a new channel for a fresh pairing.
255
+ * Disconnects any existing session, generates a new channel ID and keys.
256
+ */
257
+ resetForNewChannel(): void;
258
+ /** Disconnect and clean up. */
259
+ disconnect(): void;
260
+ private saveSession;
261
+ private loadSession;
262
+ private clearSession;
263
+ }
264
+
265
+ /**
266
+ * ECIES encryption/decryption client.
267
+ * Adapted from MetaMask SDK's ECIES.ts pattern.
268
+ */
269
+ declare class ECIESClient {
270
+ private privateKey;
271
+ constructor(existingPrivateKey?: string);
272
+ /** Get public key as hex string */
273
+ getPublicKey(): string;
274
+ /** Get private key as hex string (for session persistence) */
275
+ getPrivateKeyHex(): string;
276
+ /** Encrypt a string message for the given public key. Returns base64. */
277
+ encrypt(data: string, otherPublicKey: string): string;
278
+ /** Decrypt a base64-encoded message using our private key. */
279
+ decrypt(encryptedBase64: string): string;
280
+ }
281
+
282
+ /**
283
+ * Key Exchange - 3-step SYN/SYNACK/ACK handshake protocol.
284
+ * Adapted from MetaMask SDK's KeyExchange.ts.
285
+ */
286
+
287
+ interface KeyExchangeEvents {
288
+ keys_exchanged: () => void;
289
+ step_change: (step: KeyExchangeMessageType) => void;
290
+ }
291
+ declare class KeyExchange extends EventEmitter<KeyExchangeEvents> {
292
+ private ecies;
293
+ private otherPublicKey;
294
+ private keysExchanged;
295
+ private awaitingSynAck;
296
+ private awaitingAck;
297
+ private step;
298
+ private isOriginator;
299
+ constructor(ecies: ECIESClient, isOriginator: boolean, otherPublicKey?: string);
300
+ /** Get the SYN message to start the handshake (originator/dApp side). */
301
+ createSYN(): object;
302
+ /**
303
+ * Process an incoming key exchange message.
304
+ * Returns a response message to send back, or null if handshake is complete.
305
+ */
306
+ onMessage(message: {
307
+ type: KeyExchangeMessageType;
308
+ pubkey?: string;
309
+ v?: number;
310
+ }): object | null;
311
+ /** Encrypt a message for the counterparty. */
312
+ encryptMessage(data: string): string;
313
+ /** Decrypt a message from the counterparty. */
314
+ decryptMessage(encryptedBase64: string): string;
315
+ /** Reset key exchange state for a fresh handshake (e.g. new QR code). */
316
+ reset(): void;
317
+ areKeysExchanged(): boolean;
318
+ getOtherPublicKey(): string | null;
319
+ getCurrentStep(): KeyExchangeMessageType;
320
+ }
321
+
322
+ /**
323
+ * Socket.IO client wrapper for relay communication.
324
+ */
325
+
326
+ interface SocketClientEvents {
327
+ message: (data: RelayMessage) => void;
328
+ connected: () => void;
329
+ disconnected: (reason: string) => void;
330
+ reconnected: () => void;
331
+ participants_changed: (data: {
332
+ event: string;
333
+ clientType?: string;
334
+ }) => void;
335
+ error: (err: Error) => void;
336
+ }
337
+ declare class SocketClient extends EventEmitter<SocketClientEvents> {
338
+ private socket;
339
+ private relayUrl;
340
+ private channelId;
341
+ private clientType;
342
+ private seq;
343
+ private pendingJoin;
344
+ constructor(relayUrl: string, clientType: 'dapp' | 'wallet');
345
+ /**
346
+ * Connect to the relay server.
347
+ */
348
+ connect(): void;
349
+ /**
350
+ * Join a relay channel.
351
+ */
352
+ joinChannel(channelId: string): Promise<{
353
+ bufferedMessages: unknown[];
354
+ }>;
355
+ private joinChannelNow;
356
+ /**
357
+ * Send a message through the relay.
358
+ */
359
+ sendMessage(data: RelayMessage): Promise<{
360
+ success: boolean;
361
+ buffered: boolean;
362
+ }>;
363
+ /**
364
+ * Leave the current channel.
365
+ */
366
+ leaveChannel(): void;
367
+ /**
368
+ * Disconnect from the relay.
369
+ */
370
+ disconnect(): void;
371
+ isConnected(): boolean;
372
+ getChannelId(): string | null;
373
+ }
374
+
375
+ interface QRConnectParams {
376
+ channelId: string;
377
+ pubKey: string;
378
+ name: string;
379
+ url: string;
380
+ icon?: string;
381
+ chainId: string;
382
+ relayUrl: string;
383
+ }
384
+ /**
385
+ * Generate a qrlconnect:// URI for QR code or deep link.
386
+ */
387
+ declare function generateConnectionURI(params: QRConnectParams): string;
388
+ /**
389
+ * Parse a qrlconnect:// URI back into its components.
390
+ */
391
+ declare function parseConnectionURI(uri: string): QRConnectParams | null;
392
+
393
+ /**
394
+ * Detect if the current browser is on a mobile device.
395
+ */
396
+ declare function isMobileBrowser(): boolean;
397
+ /**
398
+ * Check if the QRL Wallet app is likely installed by attempting a deep link.
399
+ * Falls back to app store URL after timeout.
400
+ */
401
+ declare function getAppStoreUrl(): string;
402
+
403
+ declare const DEFAULT_RELAY_URL = "https://qrlwallet.com";
404
+ /** RPC methods that require user approval in the wallet */
405
+ declare const RESTRICTED_METHODS: Set<string>;
406
+ /** RPC methods that can be auto-proxied without approval */
407
+ declare const UNRESTRICTED_METHODS: Set<string>;
408
+
409
+ export { ConnectionManager, ConnectionStatus, type DAppMetadata, type DAppSession, DEFAULT_RELAY_URL, ECIESClient, type JsonRpcRequest, type JsonRpcResponse, KeyExchange, KeyExchangeMessageType, MessageType, type PendingRequest, type ProviderEvents, QRLConnectProvider as QRLConnect, type QRLConnectOptions, QRLConnectProvider, RESTRICTED_METHODS, type RelayMessage, SocketClient, UNRESTRICTED_METHODS, generateConnectionURI, getAppStoreUrl, isMobileBrowser, parseConnectionURI };