@qrlwallet/connect 3.3.0 → 5.0.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/README.md +140 -52
- package/dist/index.d.mts +275 -79
- package/dist/index.d.ts +275 -79
- package/dist/index.js +2973 -1173
- package/dist/index.mjs +2944 -1172
- package/package.json +14 -6
package/README.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
+
## Version 5 migration
|
|
6
|
+
|
|
7
|
+
Version 5 requires QIP-55 addresses: `Q` followed by 128 hexadecimal characters.
|
|
8
|
+
Account validation and message-signature verification use the complete 64-byte
|
|
9
|
+
address. Applications using legacy 20-byte addresses must remain on version 4
|
|
10
|
+
until their network and wallet support QIP-55.
|
|
11
|
+
|
|
12
|
+
Wallet typed-data requests are disabled pending a versioned 64-byte address
|
|
13
|
+
encoding. The local verifier remains available for address-free typed data.
|
|
14
|
+
The private v3 network is a test network; its availability does not establish
|
|
15
|
+
compatibility with legacy deployments or contracts.
|
|
16
|
+
|
|
5
17
|
## How it works
|
|
6
18
|
|
|
7
19
|
1. Your dApp generates a connection URI and shows it as a QR code
|
|
@@ -10,7 +22,10 @@ Connect your dApp to QRL Wallet. Users scan a QR code (desktop) or tap a button
|
|
|
10
22
|
4. Your dApp sends JSON-RPC requests, the wallet prompts for approval
|
|
11
23
|
5. Signed results come back to your dApp
|
|
12
24
|
|
|
13
|
-
All
|
|
25
|
+
All application traffic is end-to-end encrypted with ML-KEM-768 (FIPS 203)
|
|
26
|
+
key encapsulation and AES-256-GCM. PQP3 adds a fresh 32-byte pairing
|
|
27
|
+
capability to every QR/deep link. The relay serves the dApp public key, but it
|
|
28
|
+
never receives that capability or application plaintext.
|
|
14
29
|
|
|
15
30
|
## Install
|
|
16
31
|
|
|
@@ -18,6 +33,10 @@ All communication is end-to-end encrypted with ML-KEM-768 (FIPS 203) key encapsu
|
|
|
18
33
|
npm install @qrlwallet/connect
|
|
19
34
|
```
|
|
20
35
|
|
|
36
|
+
Package installation and source builds require Node.js 20.19.0 or newer because
|
|
37
|
+
the direct cryptography dependencies use that engine floor. Browser execution
|
|
38
|
+
requires Web Crypto.
|
|
39
|
+
|
|
21
40
|
Want a ready-made pairing dialog (QR code, deep link, copy-code fallback) instead of building your own? Add the framework-free [`@qrlwallet/connect-ui`](ui/) web component.
|
|
22
41
|
|
|
23
42
|
## Quick start
|
|
@@ -68,11 +87,13 @@ const accounts = await qrl.request({ method: 'qrl_requestAccounts' });
|
|
|
68
87
|
|
|
69
88
|
const txHash = await qrl.request({
|
|
70
89
|
method: 'qrl_sendTransaction',
|
|
71
|
-
params: [
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
params: [
|
|
91
|
+
{
|
|
92
|
+
from: accounts[0],
|
|
93
|
+
to: 'Q22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222',
|
|
94
|
+
value: '0x2386F26FC10000', // 0.01 QRL
|
|
95
|
+
},
|
|
96
|
+
],
|
|
76
97
|
});
|
|
77
98
|
```
|
|
78
99
|
|
|
@@ -85,75 +106,133 @@ const qrl = new QRLConnect({
|
|
|
85
106
|
name: 'QuantaPool',
|
|
86
107
|
url: 'https://quantapool.com',
|
|
87
108
|
icon: 'https://quantapool.com/icon.png', // optional
|
|
109
|
+
redirectUrl: 'https://quantapool.com/return', // optional
|
|
88
110
|
},
|
|
89
111
|
|
|
90
112
|
// Optional
|
|
91
|
-
relayUrl: 'https://qrlwallet.com',
|
|
92
|
-
chainId: '0x0',
|
|
93
|
-
autoReconnect: true,
|
|
94
|
-
walletRedirectOnRequest: true,
|
|
95
|
-
|
|
96
|
-
debug: false,
|
|
113
|
+
relayUrl: 'https://qrlwallet.com', // default relay
|
|
114
|
+
chainId: '0x0', // QRL chain ID
|
|
115
|
+
autoReconnect: true, // reconnect on page load (default: true)
|
|
116
|
+
walletRedirectOnRequest: true, // mobile: deep-link the wallet app awake
|
|
117
|
+
// for approval requests (default: true)
|
|
118
|
+
debug: false, // console logging
|
|
97
119
|
});
|
|
98
120
|
```
|
|
99
121
|
|
|
122
|
+
`dappMetadata.url` and optional `redirectUrl` are canonicalized
|
|
123
|
+
credential-free HTTP(S) URLs. HTTPS is required except on an explicit
|
|
124
|
+
`localhost`, `.localhost`, `127.0.0.1`, or `[::1]` development hostname. A
|
|
125
|
+
custom app scheme is not accepted as a return target.
|
|
126
|
+
|
|
100
127
|
## API
|
|
101
128
|
|
|
102
129
|
### `QRLConnect`
|
|
103
130
|
|
|
104
131
|
The main class. Creates a connection manager and EIP-1193 provider.
|
|
105
132
|
|
|
106
|
-
| Method
|
|
107
|
-
|
|
108
|
-
| `getConnectionURI()`
|
|
109
|
-
| `request({ method, params })` | Send a JSON-RPC request to the wallet
|
|
110
|
-
| `isMobile()`
|
|
111
|
-
| `getAppStoreUrl()`
|
|
112
|
-
| `isConnected()`
|
|
113
|
-
| `isPaired()`
|
|
114
|
-
| `isWalletPresent()`
|
|
115
|
-
| `getAccounts()`
|
|
116
|
-
| `getStatus()`
|
|
117
|
-
| `hasStoredSession()`
|
|
118
|
-
| `newConnection()`
|
|
119
|
-
| `disconnect()`
|
|
120
|
-
| `getChannelId()`
|
|
133
|
+
| Method | Description |
|
|
134
|
+
| ----------------------------- | ------------------------------------------------------------------- |
|
|
135
|
+
| `getConnectionURI()` | Retires any prior pairing and returns a fresh `qrlconnect://` URI |
|
|
136
|
+
| `request({ method, params })` | Send a JSON-RPC request to the wallet |
|
|
137
|
+
| `isMobile()` | Check if the user is on a mobile browser |
|
|
138
|
+
| `getAppStoreUrl()` | Get the app store link for QRL Wallet |
|
|
139
|
+
| `isConnected()` | Whether the wallet is connected |
|
|
140
|
+
| `isPaired()` | Whether a pairing exists, even while the wallet app is backgrounded |
|
|
141
|
+
| `isWalletPresent()` | Whether the wallet is currently live in the relay channel |
|
|
142
|
+
| `getAccounts()` | Get connected accounts |
|
|
143
|
+
| `getStatus()` | Current connection status |
|
|
144
|
+
| `hasStoredSession()` | Check if a reconnectable session exists in local storage |
|
|
145
|
+
| `newConnection()` | Reset current pairing and generate a new channel/URI |
|
|
146
|
+
| `disconnect()` | End the session |
|
|
147
|
+
| `getChannelId()` | Get the current relay channel ID (useful for debugging) |
|
|
121
148
|
|
|
122
149
|
### Events
|
|
123
150
|
|
|
124
|
-
| Event
|
|
125
|
-
|
|
126
|
-
| `connect`
|
|
127
|
-
| `disconnect`
|
|
128
|
-
| `accountsChanged` | `string[]`
|
|
129
|
-
| `chainChanged`
|
|
130
|
-
| `statusChanged`
|
|
131
|
-
| `connection_lost` | (none)
|
|
151
|
+
| Event | Payload | Description |
|
|
152
|
+
| ----------------- | ------------------- | ------------------------------------------------ |
|
|
153
|
+
| `connect` | `{ chainId }` | Wallet connected |
|
|
154
|
+
| `disconnect` | `{ code, message }` | Wallet disconnected |
|
|
155
|
+
| `accountsChanged` | `string[]` | Account list changed |
|
|
156
|
+
| `chainChanged` | `string` | Chain switched |
|
|
157
|
+
| `statusChanged` | `ConnectionStatus` | Intermediate lifecycle states (see values below) |
|
|
158
|
+
| `connection_lost` | (none) | Emitted after 5 failed reconnection attempts |
|
|
132
159
|
|
|
133
160
|
#### `ConnectionStatus` values
|
|
134
161
|
|
|
135
|
-
| Value
|
|
136
|
-
|
|
137
|
-
| `disconnected` | No active connection
|
|
138
|
-
| `connecting`
|
|
139
|
-
| `waiting`
|
|
140
|
-
| `key_exchange` | Post-quantum key exchange in progress
|
|
141
|
-
| `connected`
|
|
142
|
-
| `reconnecting` | Attempting to restore a previous session
|
|
162
|
+
| Value | Meaning |
|
|
163
|
+
| -------------- | ------------------------------------------------------------------------------------------------ |
|
|
164
|
+
| `disconnected` | No active connection |
|
|
165
|
+
| `connecting` | Connecting to relay server |
|
|
166
|
+
| `waiting` | Waiting for the wallet: a first scan, or (when `isPaired()`) a backgrounded wallet app to return |
|
|
167
|
+
| `key_exchange` | Post-quantum key exchange in progress |
|
|
168
|
+
| `connected` | Wallet connected, ready for requests |
|
|
169
|
+
| `reconnecting` | Attempting to restore a previous session |
|
|
143
170
|
|
|
144
171
|
### Supported RPC methods
|
|
145
172
|
|
|
146
173
|
**Require user approval:**
|
|
147
|
-
`qrl_requestAccounts`, `qrl_sendTransaction`, `qrl_signTransaction`, `qrl_signMessage`, `qrl_signTypedData`, `
|
|
148
|
-
|
|
149
|
-
`qrl_signMessage`
|
|
174
|
+
`qrl_requestAccounts`, `qrl_sendTransaction`, `qrl_signTransaction`, `qrl_signMessage`, `qrl_signTypedData`, `wallet_switchQrlChain`
|
|
175
|
+
|
|
176
|
+
`qrl_signMessage` uses SHAKE256 plus native ML-DSA-87 ctx and returns a rich `{ signature, publicKey, descriptor, signer, digest, schemeVersion }` object. During the QIP-55 port, `qrl_signTypedData` stays in the approval policy and fails locally before relay use. Its v1 encoding fixes addresses to a 32-byte slot, so a new 64-byte encoding needs a new scheme version and regenerated wallet parity vectors.
|
|
177
|
+
|
|
178
|
+
`request()` returns `unknown`. First validate the exact response shape with
|
|
179
|
+
`isQrlSignedMessageResult` or `isQrlSignedTypedDataResult`, then require
|
|
180
|
+
`hasSigningDescriptor`. PQP3 authenticates the paired transport session. It
|
|
181
|
+
does not prove that the returned ML-DSA signature is valid or that its public
|
|
182
|
+
key belongs to the claimed signer. Authentication and authorization flows must
|
|
183
|
+
call `verifyMessageForSigner` or the historical v1 `verifyTypedDataForSigner`
|
|
184
|
+
with the original challenge or payload and the expected QIP-55 Q + 128 hex
|
|
185
|
+
address.
|
|
186
|
+
|
|
187
|
+
The lower-level `verifyMessageSignature` / `verifyTypedDataSignature` helpers
|
|
188
|
+
verify only the supplied key and signature; they do not prove that the key
|
|
189
|
+
belongs to a claimed signer. The old `verifyMessage` / `verifyTypedData` names
|
|
190
|
+
remain as deprecated aliases. `descriptor` remains optional in the response
|
|
191
|
+
type for older-wallet compatibility. Strict shape guards accept its absence,
|
|
192
|
+
while `hasSigningDescriptor` and both bound verifiers fail closed.
|
|
193
|
+
|
|
194
|
+
Approval-bound calls are serialized. Unknown methods and direct node mutation
|
|
195
|
+
surfaces such as `qrl_sendRawTransaction` and `wallet_addQrlChain` are rejected
|
|
196
|
+
locally. Signing and transaction calls also fail locally until exactly one
|
|
197
|
+
account has been approved by `qrl_requestAccounts`. Their signer or `from`
|
|
198
|
+
field must exactly match that approved address.
|
|
150
199
|
|
|
151
200
|
**Auto-proxied (no approval needed):**
|
|
152
|
-
`
|
|
201
|
+
`qrl_chainId`, `qrl_blockNumber`, `qrl_getBalance`,
|
|
202
|
+
`qrl_getTransactionCount`, `qrl_getBlockByNumber`,
|
|
203
|
+
`qrl_getTransactionReceipt`, `qrl_call`, `qrl_estimateGas`, `qrl_gasPrice`,
|
|
204
|
+
`qrl_getCode`, `qrl_getLogs`, `net_version`, and `net_listening`.
|
|
205
|
+
|
|
206
|
+
`qrl_accounts` is a wallet-local authorization read. It returns `[]` until
|
|
207
|
+
`qrl_requestAccounts` succeeds and is never forwarded to hosted node RPC.
|
|
208
|
+
An account string is not proof of control. For authentication, request a fresh
|
|
209
|
+
challenge with `qrl_signMessage` and verify it with `verifyMessageForSigner`.
|
|
210
|
+
|
|
211
|
+
## Address display
|
|
212
|
+
|
|
213
|
+
Use `formatQrlAddressFingerprint(address)` for compact QRL account labels. It
|
|
214
|
+
preserves checksum case and shows the first, exact middle, and final 8 hex
|
|
215
|
+
characters. Keep the complete raw address in links, clipboard values, QR codes,
|
|
216
|
+
RPC requests, and signing payloads.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
const label = formatQrlAddressFingerprint(account);
|
|
220
|
+
// Q11111111...33333333...55555555
|
|
221
|
+
```
|
|
153
222
|
|
|
154
223
|
## Sessions
|
|
155
224
|
|
|
156
|
-
|
|
225
|
+
Established v5 sessions persist in `localStorage` for 7 days. When a user
|
|
226
|
+
returns to your dApp, the SDK can automatically reconnect without requiring a
|
|
227
|
+
new QR scan. Pairing capabilities are never persisted. An exclusive Web Lock
|
|
228
|
+
gives one browser tab ownership of the persisted key and AEAD counters. A
|
|
229
|
+
second tab cannot restore or use that stream. Browsers without Web Locks use a
|
|
230
|
+
memory-only session and require a fresh pairing after reload.
|
|
231
|
+
|
|
232
|
+
Malformed handshake frames and ambiguous relay acknowledgements retire the
|
|
233
|
+
affected pairing. A fresh QR starts a new key, channel, and request generation;
|
|
234
|
+
pending work from the prior wallet is rejected locally, and the old relay
|
|
235
|
+
channel must be tombstoned before the new bearer capability is issued.
|
|
157
236
|
|
|
158
237
|
A paired session also survives the wallet app being backgrounded or closed. On the same device at most one of the two apps is ever foregrounded, so the wallet's socket being absent is the normal steady state of a mobile flow, not an error:
|
|
159
238
|
|
|
@@ -189,6 +268,11 @@ const qrl = new QRLConnect({
|
|
|
189
268
|
});
|
|
190
269
|
```
|
|
191
270
|
|
|
271
|
+
Relay URLs must use HTTPS. Plain HTTP is accepted only for an explicit
|
|
272
|
+
`localhost`, `.localhost`, `127.0.0.1`, or `[::1]` development endpoint. Relay
|
|
273
|
+
URLs identify an origin only: credentials, paths, query strings, and fragments
|
|
274
|
+
are rejected before the URL can be logged, connected, or embedded in a QR.
|
|
275
|
+
|
|
192
276
|
## Development
|
|
193
277
|
|
|
194
278
|
```bash
|
|
@@ -234,14 +318,18 @@ The example connects to the production relay at `wss://qrlwallet.com/relay` by d
|
|
|
234
318
|
|
|
235
319
|
- Private keys and seeds never leave the wallet
|
|
236
320
|
- All relay traffic is end-to-end encrypted with **AES-256-GCM** bound to a
|
|
237
|
-
**transcript hash** derived from the full handshake
|
|
321
|
+
**transcript hash** derived from the full handshake
|
|
322
|
+
(`"pq-pair/v3" || cid || pk || ct || capability`)
|
|
238
323
|
- Session keys are established with **ML-KEM-768** (FIPS 203, NIST Level 3),
|
|
239
|
-
|
|
324
|
+
with the public key uploaded to the relay and pinned by a full 32-byte
|
|
325
|
+
capability-bound fingerprint in the QR
|
|
326
|
+
- The PQP3 capability is a bearer secret. Do not log, persist, cache, or send
|
|
327
|
+
a pairing URI to analytics. Retire the channel when a pairing UI is cancelled
|
|
240
328
|
- Ciphertext tampering is detected exclusively at the AES-GCM tag;
|
|
241
329
|
ML-KEM's implicit rejection is NOT used for authentication
|
|
242
330
|
- PIN or biometric authentication required for every transaction
|
|
243
|
-
- dApp URL
|
|
244
|
-
- Unknown RPC methods are rejected
|
|
331
|
+
- dApp name and URL are displayed as unverified, dApp-supplied metadata
|
|
332
|
+
- Unknown RPC methods are rejected locally before a relay round trip
|
|
245
333
|
|
|
246
334
|
## License
|
|
247
335
|
|