@qrlwallet/connect 3.3.0 → 4.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 +115 -52
- package/dist/index.d.mts +248 -69
- package/dist/index.d.ts +248 -69
- package/dist/index.js +2897 -1159
- package/dist/index.mjs +2873 -1158
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -10,7 +10,10 @@ Connect your dApp to QRL Wallet. Users scan a QR code (desktop) or tap a button
|
|
|
10
10
|
4. Your dApp sends JSON-RPC requests, the wallet prompts for approval
|
|
11
11
|
5. Signed results come back to your dApp
|
|
12
12
|
|
|
13
|
-
All
|
|
13
|
+
All application traffic is end-to-end encrypted with ML-KEM-768 (FIPS 203)
|
|
14
|
+
key encapsulation and AES-256-GCM. PQP3 adds a fresh 32-byte pairing
|
|
15
|
+
capability to every QR/deep link. The relay serves the dApp public key, but it
|
|
16
|
+
never receives that capability or application plaintext.
|
|
14
17
|
|
|
15
18
|
## Install
|
|
16
19
|
|
|
@@ -18,6 +21,10 @@ All communication is end-to-end encrypted with ML-KEM-768 (FIPS 203) key encapsu
|
|
|
18
21
|
npm install @qrlwallet/connect
|
|
19
22
|
```
|
|
20
23
|
|
|
24
|
+
Package installation and source builds require Node.js 20.19.0 or newer because
|
|
25
|
+
the direct cryptography dependencies use that engine floor. Browser execution
|
|
26
|
+
requires Web Crypto.
|
|
27
|
+
|
|
21
28
|
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
29
|
|
|
23
30
|
## Quick start
|
|
@@ -68,11 +75,13 @@ const accounts = await qrl.request({ method: 'qrl_requestAccounts' });
|
|
|
68
75
|
|
|
69
76
|
const txHash = await qrl.request({
|
|
70
77
|
method: 'qrl_sendTransaction',
|
|
71
|
-
params: [
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
params: [
|
|
79
|
+
{
|
|
80
|
+
from: accounts[0],
|
|
81
|
+
to: 'Q20E7Bde67f00EA38ABb2aC57e1B0DD93f518446c',
|
|
82
|
+
value: '0x2386F26FC10000', // 0.01 QRL
|
|
83
|
+
},
|
|
84
|
+
],
|
|
76
85
|
});
|
|
77
86
|
```
|
|
78
87
|
|
|
@@ -85,75 +94,120 @@ const qrl = new QRLConnect({
|
|
|
85
94
|
name: 'QuantaPool',
|
|
86
95
|
url: 'https://quantapool.com',
|
|
87
96
|
icon: 'https://quantapool.com/icon.png', // optional
|
|
97
|
+
redirectUrl: 'https://quantapool.com/return', // optional
|
|
88
98
|
},
|
|
89
99
|
|
|
90
100
|
// Optional
|
|
91
|
-
relayUrl: 'https://qrlwallet.com',
|
|
92
|
-
chainId: '0x0',
|
|
93
|
-
autoReconnect: true,
|
|
94
|
-
walletRedirectOnRequest: true,
|
|
95
|
-
|
|
96
|
-
debug: false,
|
|
101
|
+
relayUrl: 'https://qrlwallet.com', // default relay
|
|
102
|
+
chainId: '0x0', // QRL chain ID
|
|
103
|
+
autoReconnect: true, // reconnect on page load (default: true)
|
|
104
|
+
walletRedirectOnRequest: true, // mobile: deep-link the wallet app awake
|
|
105
|
+
// for approval requests (default: true)
|
|
106
|
+
debug: false, // console logging
|
|
97
107
|
});
|
|
98
108
|
```
|
|
99
109
|
|
|
110
|
+
`dappMetadata.url` and optional `redirectUrl` are canonicalized
|
|
111
|
+
credential-free HTTP(S) URLs. HTTPS is required except on an explicit
|
|
112
|
+
`localhost`, `.localhost`, `127.0.0.1`, or `[::1]` development hostname. A
|
|
113
|
+
custom app scheme is not accepted as a return target.
|
|
114
|
+
|
|
100
115
|
## API
|
|
101
116
|
|
|
102
117
|
### `QRLConnect`
|
|
103
118
|
|
|
104
119
|
The main class. Creates a connection manager and EIP-1193 provider.
|
|
105
120
|
|
|
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()`
|
|
121
|
+
| Method | Description |
|
|
122
|
+
| ----------------------------- | ------------------------------------------------------------------- |
|
|
123
|
+
| `getConnectionURI()` | Retires any prior pairing and returns a fresh `qrlconnect://` URI |
|
|
124
|
+
| `request({ method, params })` | Send a JSON-RPC request to the wallet |
|
|
125
|
+
| `isMobile()` | Check if the user is on a mobile browser |
|
|
126
|
+
| `getAppStoreUrl()` | Get the app store link for QRL Wallet |
|
|
127
|
+
| `isConnected()` | Whether the wallet is connected |
|
|
128
|
+
| `isPaired()` | Whether a pairing exists, even while the wallet app is backgrounded |
|
|
129
|
+
| `isWalletPresent()` | Whether the wallet is currently live in the relay channel |
|
|
130
|
+
| `getAccounts()` | Get connected accounts |
|
|
131
|
+
| `getStatus()` | Current connection status |
|
|
132
|
+
| `hasStoredSession()` | Check if a reconnectable session exists in local storage |
|
|
133
|
+
| `newConnection()` | Reset current pairing and generate a new channel/URI |
|
|
134
|
+
| `disconnect()` | End the session |
|
|
135
|
+
| `getChannelId()` | Get the current relay channel ID (useful for debugging) |
|
|
121
136
|
|
|
122
137
|
### Events
|
|
123
138
|
|
|
124
|
-
| Event
|
|
125
|
-
|
|
126
|
-
| `connect`
|
|
127
|
-
| `disconnect`
|
|
128
|
-
| `accountsChanged` | `string[]`
|
|
129
|
-
| `chainChanged`
|
|
130
|
-
| `statusChanged`
|
|
131
|
-
| `connection_lost` | (none)
|
|
139
|
+
| Event | Payload | Description |
|
|
140
|
+
| ----------------- | ------------------- | ------------------------------------------------ |
|
|
141
|
+
| `connect` | `{ chainId }` | Wallet connected |
|
|
142
|
+
| `disconnect` | `{ code, message }` | Wallet disconnected |
|
|
143
|
+
| `accountsChanged` | `string[]` | Account list changed |
|
|
144
|
+
| `chainChanged` | `string` | Chain switched |
|
|
145
|
+
| `statusChanged` | `ConnectionStatus` | Intermediate lifecycle states (see values below) |
|
|
146
|
+
| `connection_lost` | (none) | Emitted after 5 failed reconnection attempts |
|
|
132
147
|
|
|
133
148
|
#### `ConnectionStatus` values
|
|
134
149
|
|
|
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
|
|
150
|
+
| Value | Meaning |
|
|
151
|
+
| -------------- | ------------------------------------------------------------------------------------------------ |
|
|
152
|
+
| `disconnected` | No active connection |
|
|
153
|
+
| `connecting` | Connecting to relay server |
|
|
154
|
+
| `waiting` | Waiting for the wallet: a first scan, or (when `isPaired()`) a backgrounded wallet app to return |
|
|
155
|
+
| `key_exchange` | Post-quantum key exchange in progress |
|
|
156
|
+
| `connected` | Wallet connected, ready for requests |
|
|
157
|
+
| `reconnecting` | Attempting to restore a previous session |
|
|
143
158
|
|
|
144
159
|
### Supported RPC methods
|
|
145
160
|
|
|
146
161
|
**Require user approval:**
|
|
147
|
-
`qrl_requestAccounts`, `qrl_sendTransaction`, `qrl_signTransaction`, `qrl_signMessage`, `qrl_signTypedData`, `
|
|
148
|
-
|
|
149
|
-
`qrl_signMessage` and `qrl_signTypedData` (v3.0.0) replace the Ethereum-flavored `personal_sign` / `qrl_sign` / `qrl_signTypedData_v3` / `qrl_signTypedData_v4`. Both use SHAKE256 + native ML-DSA-87 ctx and return a rich `{ signature, publicKey, signer, digest, schemeVersion }` object
|
|
162
|
+
`qrl_requestAccounts`, `qrl_sendTransaction`, `qrl_signTransaction`, `qrl_signMessage`, `qrl_signTypedData`, `wallet_switchQrlChain`
|
|
163
|
+
|
|
164
|
+
`qrl_signMessage` and `qrl_signTypedData` (v3.0.0) replace the Ethereum-flavored `personal_sign` / `qrl_sign` / `qrl_signTypedData_v3` / `qrl_signTypedData_v4`. Both use SHAKE256 + native ML-DSA-87 ctx and return a rich `{ signature, publicKey, descriptor, signer, digest, schemeVersion }` object.
|
|
165
|
+
|
|
166
|
+
`request()` returns `unknown`. First validate the exact response shape with
|
|
167
|
+
`isQrlSignedMessageResult` or `isQrlSignedTypedDataResult`, then require
|
|
168
|
+
`hasSigningDescriptor`. PQP3 authenticates the paired transport session. It
|
|
169
|
+
does not prove that the returned ML-DSA signature is valid or that its public
|
|
170
|
+
key belongs to the claimed signer. Authentication and authorization flows must
|
|
171
|
+
call `verifyMessageForSigner` or `verifyTypedDataForSigner` with the original
|
|
172
|
+
challenge or payload and the expected current Q + 40 hex address.
|
|
173
|
+
|
|
174
|
+
The lower-level `verifyMessageSignature` / `verifyTypedDataSignature` helpers
|
|
175
|
+
verify only the supplied key and signature; they do not prove that the key
|
|
176
|
+
belongs to a claimed signer. The old `verifyMessage` / `verifyTypedData` names
|
|
177
|
+
remain as deprecated aliases. `descriptor` remains optional in the response
|
|
178
|
+
type for older-wallet compatibility. Strict shape guards accept its absence,
|
|
179
|
+
while `hasSigningDescriptor` and both bound verifiers fail closed.
|
|
180
|
+
|
|
181
|
+
Approval-bound calls are serialized. Unknown methods and direct node mutation
|
|
182
|
+
surfaces such as `qrl_sendRawTransaction` and `wallet_addQrlChain` are rejected
|
|
183
|
+
locally. Signing and transaction calls also fail locally until exactly one
|
|
184
|
+
account has been approved by `qrl_requestAccounts`. Their signer or `from`
|
|
185
|
+
field must exactly match that approved address.
|
|
150
186
|
|
|
151
187
|
**Auto-proxied (no approval needed):**
|
|
152
|
-
`
|
|
188
|
+
`qrl_chainId`, `qrl_blockNumber`, `qrl_getBalance`,
|
|
189
|
+
`qrl_getTransactionCount`, `qrl_getBlockByNumber`,
|
|
190
|
+
`qrl_getTransactionReceipt`, `qrl_call`, `qrl_estimateGas`, `qrl_gasPrice`,
|
|
191
|
+
`qrl_getCode`, `qrl_getLogs`, `net_version`, and `net_listening`.
|
|
192
|
+
|
|
193
|
+
`qrl_accounts` is a wallet-local authorization read. It returns `[]` until
|
|
194
|
+
`qrl_requestAccounts` succeeds and is never forwarded to hosted node RPC.
|
|
195
|
+
An account string is not proof of control. For authentication, request a fresh
|
|
196
|
+
challenge with `qrl_signMessage` and verify it with `verifyMessageForSigner`.
|
|
153
197
|
|
|
154
198
|
## Sessions
|
|
155
199
|
|
|
156
|
-
|
|
200
|
+
Established v5 sessions persist in `localStorage` for 7 days. When a user
|
|
201
|
+
returns to your dApp, the SDK can automatically reconnect without requiring a
|
|
202
|
+
new QR scan. Pairing capabilities are never persisted. An exclusive Web Lock
|
|
203
|
+
gives one browser tab ownership of the persisted key and AEAD counters. A
|
|
204
|
+
second tab cannot restore or use that stream. Browsers without Web Locks use a
|
|
205
|
+
memory-only session and require a fresh pairing after reload.
|
|
206
|
+
|
|
207
|
+
Malformed handshake frames and ambiguous relay acknowledgements retire the
|
|
208
|
+
affected pairing. A fresh QR starts a new key, channel, and request generation;
|
|
209
|
+
pending work from the prior wallet is rejected locally, and the old relay
|
|
210
|
+
channel must be tombstoned before the new bearer capability is issued.
|
|
157
211
|
|
|
158
212
|
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
213
|
|
|
@@ -189,6 +243,11 @@ const qrl = new QRLConnect({
|
|
|
189
243
|
});
|
|
190
244
|
```
|
|
191
245
|
|
|
246
|
+
Relay URLs must use HTTPS. Plain HTTP is accepted only for an explicit
|
|
247
|
+
`localhost`, `.localhost`, `127.0.0.1`, or `[::1]` development endpoint. Relay
|
|
248
|
+
URLs identify an origin only: credentials, paths, query strings, and fragments
|
|
249
|
+
are rejected before the URL can be logged, connected, or embedded in a QR.
|
|
250
|
+
|
|
192
251
|
## Development
|
|
193
252
|
|
|
194
253
|
```bash
|
|
@@ -234,14 +293,18 @@ The example connects to the production relay at `wss://qrlwallet.com/relay` by d
|
|
|
234
293
|
|
|
235
294
|
- Private keys and seeds never leave the wallet
|
|
236
295
|
- All relay traffic is end-to-end encrypted with **AES-256-GCM** bound to a
|
|
237
|
-
**transcript hash** derived from the full handshake
|
|
296
|
+
**transcript hash** derived from the full handshake
|
|
297
|
+
(`"pq-pair/v3" || cid || pk || ct || capability`)
|
|
238
298
|
- Session keys are established with **ML-KEM-768** (FIPS 203, NIST Level 3),
|
|
239
|
-
|
|
299
|
+
with the public key uploaded to the relay and pinned by a full 32-byte
|
|
300
|
+
capability-bound fingerprint in the QR
|
|
301
|
+
- The PQP3 capability is a bearer secret. Do not log, persist, cache, or send
|
|
302
|
+
a pairing URI to analytics. Retire the channel when a pairing UI is cancelled
|
|
240
303
|
- Ciphertext tampering is detected exclusively at the AES-GCM tag;
|
|
241
304
|
ML-KEM's implicit rejection is NOT used for authentication
|
|
242
305
|
- PIN or biometric authentication required for every transaction
|
|
243
|
-
- dApp URL
|
|
244
|
-
- Unknown RPC methods are rejected
|
|
306
|
+
- dApp name and URL are displayed as unverified, dApp-supplied metadata
|
|
307
|
+
- Unknown RPC methods are rejected locally before a relay round trip
|
|
245
308
|
|
|
246
309
|
## License
|
|
247
310
|
|