@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 +21 -0
- package/README.md +247 -0
- package/dist/index.d.mts +409 -0
- package/dist/index.d.ts +409 -0
- package/dist/index.js +1288 -0
- package/dist/index.mjs +1236 -0
- package/package.json +70 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|