@qrlwallet/connect 5.1.0 → 5.1.2
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 +5 -0
- package/dist/index.js +82 -46
- package/dist/index.mjs +82 -46
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -286,6 +286,9 @@ npm run dev
|
|
|
286
286
|
# Type-check
|
|
287
287
|
npm run typecheck
|
|
288
288
|
|
|
289
|
+
# Type-check tests
|
|
290
|
+
npm run typecheck:test
|
|
291
|
+
|
|
289
292
|
# Unit tests (vitest)
|
|
290
293
|
npm test
|
|
291
294
|
|
|
@@ -296,6 +299,8 @@ npm run lint
|
|
|
296
299
|
node test-e2e.mjs
|
|
297
300
|
```
|
|
298
301
|
|
|
302
|
+
CI runs `node scripts/audit-gate.mjs` after the SDK install and `node scripts/audit-gate.mjs --dir ui` after the UI install; both read `audit-allowlist.json` at the repository root. The gate blocks high and critical production advisories unless a matching, dated exception in `audit-allowlist.json` explains why the advisory cannot reach users.
|
|
303
|
+
|
|
299
304
|
## Running the example dApp
|
|
300
305
|
|
|
301
306
|
The `example/` directory contains a Vite test dApp with QR code generation, transaction sending, message signing, and read-only RPC calls.
|
package/dist/index.js
CHANGED
|
@@ -121,7 +121,8 @@ var import_ml_kem = require("@noble/post-quantum/ml-kem.js");
|
|
|
121
121
|
var import_sha3 = require("@noble/hashes/sha3.js");
|
|
122
122
|
var mldsa = __toESM(require("@theqrl/mldsa87"));
|
|
123
123
|
function webCrypto() {
|
|
124
|
-
const
|
|
124
|
+
const environment = globalThis;
|
|
125
|
+
const c = environment.crypto;
|
|
125
126
|
if (!c) {
|
|
126
127
|
throw new Error("crypto: WebCrypto is not available in this environment");
|
|
127
128
|
}
|
|
@@ -641,7 +642,8 @@ var KeyExchange = class extends import_eventemitter3.default {
|
|
|
641
642
|
this.lastAck = null;
|
|
642
643
|
this.isOriginator = isOriginator;
|
|
643
644
|
if (restored) {
|
|
644
|
-
|
|
645
|
+
const protocolVersion = restored.protocolVersion;
|
|
646
|
+
if (protocolVersion !== PROTOCOL_VERSION) {
|
|
645
647
|
throw new Error("KeyExchange: cannot restore a pre-v3 session");
|
|
646
648
|
}
|
|
647
649
|
this.session = snapshotSession(restored);
|
|
@@ -975,7 +977,8 @@ var KeyExchange = class extends import_eventemitter3.default {
|
|
|
975
977
|
}
|
|
976
978
|
/** Rehydrate a Session from its persisted form. */
|
|
977
979
|
static async sessionFromPersisted(p) {
|
|
978
|
-
|
|
980
|
+
const protocolVersion = p.protocolVersion;
|
|
981
|
+
if (protocolVersion !== PROTOCOL_VERSION) {
|
|
979
982
|
throw new Error("KeyExchange: cannot hydrate a pre-v3 session");
|
|
980
983
|
}
|
|
981
984
|
const kAeadRaw = fromBase64Exact(p.kAeadRaw, 32, "persisted AEAD key");
|
|
@@ -1553,7 +1556,12 @@ function startsWith(buf, prefix) {
|
|
|
1553
1556
|
}
|
|
1554
1557
|
return true;
|
|
1555
1558
|
}
|
|
1556
|
-
|
|
1559
|
+
function parseConnectionURI(uri) {
|
|
1560
|
+
return new Promise((resolve) => {
|
|
1561
|
+
resolve(parseURI(uri));
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
function parseURI(uri) {
|
|
1557
1565
|
if (typeof uri !== "string" || uri.length === 0) {
|
|
1558
1566
|
throw new Error("qrUri: empty URI");
|
|
1559
1567
|
}
|
|
@@ -1598,7 +1606,7 @@ async function parseConnectionURI(uri) {
|
|
|
1598
1606
|
blob = base45Decode(q);
|
|
1599
1607
|
} catch (err) {
|
|
1600
1608
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1601
|
-
throw new Error(`qrUri: base45 decode failed: ${msg}
|
|
1609
|
+
throw new Error(`qrUri: base45 decode failed: ${msg}`, { cause: err });
|
|
1602
1610
|
}
|
|
1603
1611
|
try {
|
|
1604
1612
|
if (blob.length !== BLOB_LEN) {
|
|
@@ -1671,7 +1679,8 @@ function getBrowserStorage() {
|
|
|
1671
1679
|
function getBrowserLockManager() {
|
|
1672
1680
|
try {
|
|
1673
1681
|
if (typeof navigator === "undefined") return null;
|
|
1674
|
-
|
|
1682
|
+
const browserNavigator = navigator;
|
|
1683
|
+
return browserNavigator.locks ?? null;
|
|
1675
1684
|
} catch {
|
|
1676
1685
|
return null;
|
|
1677
1686
|
}
|
|
@@ -2012,7 +2021,8 @@ var _ConnectionManager = class _ConnectionManager extends import_eventemitter33.
|
|
|
2012
2021
|
});
|
|
2013
2022
|
socketClient.on("participants_changed", (data) => {
|
|
2014
2023
|
if (!isCurrentSocket()) return;
|
|
2015
|
-
|
|
2024
|
+
const clientType = data.clientType;
|
|
2025
|
+
if (clientType !== "wallet") return;
|
|
2016
2026
|
if (data.event === "join" && data.clientType === "wallet") {
|
|
2017
2027
|
this.walletPresent = true;
|
|
2018
2028
|
log("ConnectionManager", "Wallet joined channel");
|
|
@@ -2179,7 +2189,8 @@ var _ConnectionManager = class _ConnectionManager extends import_eventemitter33.
|
|
|
2179
2189
|
log("ConnectionManager", `Generated v3 connection URI for channel ${context.channelId}`);
|
|
2180
2190
|
return uri;
|
|
2181
2191
|
} catch (err) {
|
|
2182
|
-
|
|
2192
|
+
const keyExchange = this.keyExchange;
|
|
2193
|
+
if (context ? this.isSessionWorkCurrent(context) : keyExchange !== null) {
|
|
2183
2194
|
this.invalidateSessionWork();
|
|
2184
2195
|
this.keyExchange = null;
|
|
2185
2196
|
this.socketClient.leaveChannel();
|
|
@@ -3001,7 +3012,7 @@ var _ConnectionManager = class _ConnectionManager extends import_eventemitter33.
|
|
|
3001
3012
|
const persistedKex = await context.keyExchange.exportPersisted();
|
|
3002
3013
|
if (!persistedKex) throw new Error("Cannot export established AEAD session");
|
|
3003
3014
|
this.assertSessionWorkCurrent(context);
|
|
3004
|
-
if (!this.sessionOwnership
|
|
3015
|
+
if (!this.sessionOwnership.isOwned()) {
|
|
3005
3016
|
throw new Error("Browser-tab ownership changed while persisting AEAD counters");
|
|
3006
3017
|
}
|
|
3007
3018
|
const session = {
|
|
@@ -3020,7 +3031,7 @@ var _ConnectionManager = class _ConnectionManager extends import_eventemitter33.
|
|
|
3020
3031
|
this.persistenceBroken = true;
|
|
3021
3032
|
this.removeStoredSession();
|
|
3022
3033
|
error("ConnectionManager", "Failed to persist AEAD counters:", err);
|
|
3023
|
-
throw new Error("Failed to persist AEAD counters");
|
|
3034
|
+
throw new Error("Failed to persist AEAD counters", { cause: err });
|
|
3024
3035
|
}
|
|
3025
3036
|
}
|
|
3026
3037
|
readStoredSession() {
|
|
@@ -3197,17 +3208,20 @@ function consumeValue(budget) {
|
|
|
3197
3208
|
budget.remainingValues--;
|
|
3198
3209
|
if (budget.remainingValues < 0) throw new Error("typed data contains too many encoded values");
|
|
3199
3210
|
}
|
|
3211
|
+
function inputPreview(value) {
|
|
3212
|
+
return value.length > 32 ? `${value.slice(0, 32)}... (length ${value.length})` : value;
|
|
3213
|
+
}
|
|
3200
3214
|
function assertIdentifier(value, label) {
|
|
3201
3215
|
if (!IDENTIFIER_RE.test(value) || value.length > TYPED_DATA_LIMITS.maxIdentifierLength || UNSAFE_OBJECT_KEYS.has(value)) {
|
|
3202
|
-
throw new Error(`invalid ${label}: ${value}`);
|
|
3216
|
+
throw new Error(`invalid ${label}: ${inputPreview(value)}`);
|
|
3203
3217
|
}
|
|
3204
3218
|
}
|
|
3205
3219
|
function parseFieldType(type, types, depth = 0) {
|
|
3206
3220
|
if (depth > TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3207
|
-
throw new Error(`type nesting too deep: ${type}`);
|
|
3221
|
+
throw new Error(`type nesting too deep: ${inputPreview(type)}`);
|
|
3208
3222
|
}
|
|
3209
3223
|
if (type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3210
|
-
throw new Error(`field type too long: ${type}`);
|
|
3224
|
+
throw new Error(`field type too long: ${inputPreview(type)}`);
|
|
3211
3225
|
}
|
|
3212
3226
|
const m = ATOMIC_RE.exec(type);
|
|
3213
3227
|
if (m) {
|
|
@@ -3218,14 +3232,14 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3218
3232
|
if (intKind) {
|
|
3219
3233
|
const width = Number(intWidthStr);
|
|
3220
3234
|
if (!Number.isInteger(width) || width < 8 || width > 256 || width % 8 !== 0) {
|
|
3221
|
-
throw new Error(`invalid int width: ${type}`);
|
|
3235
|
+
throw new Error(`invalid int width: ${inputPreview(type)}`);
|
|
3222
3236
|
}
|
|
3223
3237
|
return { kind: intKind === "uint" ? "uintN" : "intN", width };
|
|
3224
3238
|
}
|
|
3225
3239
|
if (bytesWidthStr) {
|
|
3226
3240
|
const width = Number(bytesWidthStr);
|
|
3227
3241
|
if (!Number.isInteger(width) || width < 1 || width > 32) {
|
|
3228
|
-
throw new Error(`invalid bytesN width: ${type}`);
|
|
3242
|
+
throw new Error(`invalid bytesN width: ${inputPreview(type)}`);
|
|
3229
3243
|
}
|
|
3230
3244
|
return { kind: "bytesN", width };
|
|
3231
3245
|
}
|
|
@@ -3234,7 +3248,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3234
3248
|
if (sizeStr) {
|
|
3235
3249
|
const size = Number(sizeStr);
|
|
3236
3250
|
if (!Number.isSafeInteger(size) || size <= 0 || size > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3237
|
-
throw new Error(`invalid array size: ${type}`);
|
|
3251
|
+
throw new Error(`invalid array size: ${inputPreview(type)}`);
|
|
3238
3252
|
}
|
|
3239
3253
|
return { kind: "array", inner: innerType, size };
|
|
3240
3254
|
}
|
|
@@ -3244,7 +3258,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3244
3258
|
if (Object.prototype.hasOwnProperty.call(types, type)) {
|
|
3245
3259
|
return { kind: "ref", name: type };
|
|
3246
3260
|
}
|
|
3247
|
-
throw new Error(`unknown type: ${type}`);
|
|
3261
|
+
throw new Error(`unknown type: ${inputPreview(type)}`);
|
|
3248
3262
|
}
|
|
3249
3263
|
function isReservedAtomicTypeName(name) {
|
|
3250
3264
|
if (/^(?:address|bool|string|bytes)$/.test(name)) return true;
|
|
@@ -3261,24 +3275,35 @@ function isReservedAtomicTypeName(name) {
|
|
|
3261
3275
|
return false;
|
|
3262
3276
|
}
|
|
3263
3277
|
function baseTypeName(type) {
|
|
3264
|
-
|
|
3278
|
+
let end = type.length;
|
|
3279
|
+
while (end > 0 && type[end - 1] === "]") {
|
|
3280
|
+
let start = end - 2;
|
|
3281
|
+
while (start >= 0) {
|
|
3282
|
+
const code = type.charCodeAt(start);
|
|
3283
|
+
if (code < 48 || code > 57) break;
|
|
3284
|
+
start--;
|
|
3285
|
+
}
|
|
3286
|
+
if (start < 0 || type[start] !== "[") break;
|
|
3287
|
+
end = start;
|
|
3288
|
+
}
|
|
3289
|
+
return type.slice(0, end);
|
|
3265
3290
|
}
|
|
3266
3291
|
function collectDependencies(primary, types) {
|
|
3267
3292
|
if (!Object.prototype.hasOwnProperty.call(types, primary)) {
|
|
3268
|
-
throw new Error(`unknown primary type: ${primary}`);
|
|
3293
|
+
throw new Error(`unknown primary type: ${inputPreview(primary)}`);
|
|
3269
3294
|
}
|
|
3270
3295
|
const visited = /* @__PURE__ */ new Set();
|
|
3271
3296
|
const visit = (name, path) => {
|
|
3272
3297
|
if (path.length > TYPED_DATA_LIMITS.maxTypeGraphDepth) {
|
|
3273
|
-
throw new Error(`type graph nesting too deep: ${[...path, name].join(" -> ")}`);
|
|
3298
|
+
throw new Error(`type graph nesting too deep: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3274
3299
|
}
|
|
3275
3300
|
if (path.includes(name)) {
|
|
3276
|
-
throw new Error(`cyclic type reference: ${[...path, name].join(" -> ")}`);
|
|
3301
|
+
throw new Error(`cyclic type reference: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3277
3302
|
}
|
|
3278
3303
|
if (visited.has(name)) return;
|
|
3279
3304
|
visited.add(name);
|
|
3280
3305
|
const def = types[name];
|
|
3281
|
-
if (!def) throw new Error(`unknown type: ${name}`);
|
|
3306
|
+
if (!def) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3282
3307
|
for (const f of def) {
|
|
3283
3308
|
const base = baseTypeName(f.type);
|
|
3284
3309
|
if (Object.prototype.hasOwnProperty.call(types, base)) {
|
|
@@ -3299,11 +3324,11 @@ function validateTypeMap(types) {
|
|
|
3299
3324
|
for (const [name, def] of entries) {
|
|
3300
3325
|
assertIdentifier(name, "struct name");
|
|
3301
3326
|
if (name !== "QRLDomain" && isReservedAtomicTypeName(name)) {
|
|
3302
|
-
throw new Error(`struct name is reserved by an atomic type: ${name}`);
|
|
3327
|
+
throw new Error(`struct name is reserved by an atomic type: ${inputPreview(name)}`);
|
|
3303
3328
|
}
|
|
3304
3329
|
const defUnknown = def;
|
|
3305
3330
|
if (!Array.isArray(defUnknown) || def.length === 0 || def.length > TYPED_DATA_LIMITS.maxFieldsPerType) {
|
|
3306
|
-
throw new Error(`empty or invalid struct: ${name}`);
|
|
3331
|
+
throw new Error(`empty or invalid struct: ${inputPreview(name)}`);
|
|
3307
3332
|
}
|
|
3308
3333
|
totalFields += def.length;
|
|
3309
3334
|
if (totalFields > TYPED_DATA_LIMITS.maxTotalFields) {
|
|
@@ -3312,14 +3337,17 @@ function validateTypeMap(types) {
|
|
|
3312
3337
|
const seen = /* @__PURE__ */ new Set();
|
|
3313
3338
|
for (const f of def) {
|
|
3314
3339
|
if (!f || typeof f.name !== "string" || !f.name) {
|
|
3315
|
-
throw new Error(`bad field in ${name}`);
|
|
3340
|
+
throw new Error(`bad field in ${inputPreview(name)}`);
|
|
3316
3341
|
}
|
|
3317
|
-
assertIdentifier(f.name, `field name in ${name}`);
|
|
3342
|
+
assertIdentifier(f.name, `field name in ${inputPreview(name)}`);
|
|
3318
3343
|
if (typeof f.type !== "string" || !f.type) {
|
|
3319
|
-
throw new Error(`bad field type in ${name}.${f.name}`);
|
|
3344
|
+
throw new Error(`bad field type in ${inputPreview(name)}.${inputPreview(f.name)}`);
|
|
3345
|
+
}
|
|
3346
|
+
if (f.type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3347
|
+
throw new Error(`field type too long: ${inputPreview(f.type)}`);
|
|
3320
3348
|
}
|
|
3321
3349
|
if (seen.has(f.name)) {
|
|
3322
|
-
throw new Error(`duplicate field "${f.name}" in ${name}`);
|
|
3350
|
+
throw new Error(`duplicate field "${inputPreview(f.name)}" in ${inputPreview(name)}`);
|
|
3323
3351
|
}
|
|
3324
3352
|
seen.add(f.name);
|
|
3325
3353
|
}
|
|
@@ -3331,7 +3359,7 @@ function encodeType(primary, types) {
|
|
|
3331
3359
|
const others = [...deps].filter((n) => n !== primary).sort();
|
|
3332
3360
|
return [primary, ...others].map((name) => {
|
|
3333
3361
|
const fields = types[name];
|
|
3334
|
-
if (!fields) throw new Error(`unknown type: ${name}`);
|
|
3362
|
+
if (!fields) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3335
3363
|
const inner = fields.map((f) => `${f.type} ${f.name}`).join(",");
|
|
3336
3364
|
return `${name}(${inner})`;
|
|
3337
3365
|
}).join("");
|
|
@@ -3341,7 +3369,7 @@ function typeHash(primary, types) {
|
|
|
3341
3369
|
}
|
|
3342
3370
|
function parseQAddress(addr, slot) {
|
|
3343
3371
|
if (!isCurrentQrlAddress(addr)) {
|
|
3344
|
-
throw new Error(`invalid Q-address: ${String(addr)}`);
|
|
3372
|
+
throw new Error(`invalid Q-address: ${inputPreview(String(addr))}`);
|
|
3345
3373
|
}
|
|
3346
3374
|
const bytes = hexToBytes("0x" + addr.slice(1).toLowerCase());
|
|
3347
3375
|
if (bytes.length > slot) {
|
|
@@ -3365,12 +3393,13 @@ function bigIntToSlot(value, width, signed, slot) {
|
|
|
3365
3393
|
if (signed) {
|
|
3366
3394
|
const limit = 1n << BigInt(width - 1);
|
|
3367
3395
|
if (value >= limit || value < -limit) {
|
|
3368
|
-
throw new Error(`int${width} out of range: ${value}`);
|
|
3396
|
+
throw new Error(`int${width} out of range: ${inputPreview(String(value))}`);
|
|
3369
3397
|
}
|
|
3370
3398
|
} else {
|
|
3371
|
-
if (value < 0n) throw new Error(`uint${width} negative: ${value}`);
|
|
3399
|
+
if (value < 0n) throw new Error(`uint${width} negative: ${inputPreview(String(value))}`);
|
|
3372
3400
|
const limit = 1n << BigInt(width);
|
|
3373
|
-
if (value >= limit)
|
|
3401
|
+
if (value >= limit)
|
|
3402
|
+
throw new Error(`uint${width} out of range: ${inputPreview(String(value))}`);
|
|
3374
3403
|
}
|
|
3375
3404
|
const slotBits = BigInt(slot * 8);
|
|
3376
3405
|
const slotMask = (1n << slotBits) - 1n;
|
|
@@ -3393,7 +3422,7 @@ function parseIntValue(v, typeLabel) {
|
|
|
3393
3422
|
return isNegative ? -abs : abs;
|
|
3394
3423
|
}
|
|
3395
3424
|
if (!/^-?(0|[1-9]\d*)$/.test(v)) {
|
|
3396
|
-
throw new Error(`invalid ${typeLabel} string: ${v}`);
|
|
3425
|
+
throw new Error(`invalid ${typeLabel} string: ${inputPreview(v)}`);
|
|
3397
3426
|
}
|
|
3398
3427
|
return BigInt(v);
|
|
3399
3428
|
}
|
|
@@ -3407,14 +3436,14 @@ function parseIntValue(v, typeLabel) {
|
|
|
3407
3436
|
}
|
|
3408
3437
|
function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
3409
3438
|
if (depth > TYPED_DATA_LIMITS.maxTypeGraphDepth + TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3410
|
-
throw new Error(`value nesting too deep: ${type}`);
|
|
3439
|
+
throw new Error(`value nesting too deep: ${inputPreview(type)}`);
|
|
3411
3440
|
}
|
|
3412
3441
|
consumeValue(budget);
|
|
3413
3442
|
const parsed = parseFieldType(type, types);
|
|
3414
3443
|
switch (parsed.kind) {
|
|
3415
3444
|
case "ref":
|
|
3416
3445
|
if (!isMessageObject(value)) {
|
|
3417
|
-
throw new Error(`struct field expects object: ${type}`);
|
|
3446
|
+
throw new Error(`struct field expects object: ${inputPreview(type)}`);
|
|
3418
3447
|
}
|
|
3419
3448
|
return hashStructWithBudget(parsed.name, value, types, budget, depth + 1);
|
|
3420
3449
|
case "address":
|
|
@@ -3480,10 +3509,12 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3480
3509
|
throw new Error(`array field expects array: ${typeof value}`);
|
|
3481
3510
|
}
|
|
3482
3511
|
if (value.length > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3483
|
-
throw new Error(`array ${type} exceeds length limit`);
|
|
3512
|
+
throw new Error(`array ${inputPreview(type)} exceeds length limit`);
|
|
3484
3513
|
}
|
|
3485
3514
|
if (parsed.size !== void 0 && value.length !== parsed.size) {
|
|
3486
|
-
throw new Error(
|
|
3515
|
+
throw new Error(
|
|
3516
|
+
`fixed array ${inputPreview(type)} requires length ${parsed.size}, got ${value.length}`
|
|
3517
|
+
);
|
|
3487
3518
|
}
|
|
3488
3519
|
const chunks = value.map(
|
|
3489
3520
|
(v) => encodeFieldWithBudget(parsed.inner, v, types, budget, depth + 1)
|
|
@@ -3500,7 +3531,7 @@ function encodeField(type, value, types, schemeVersion) {
|
|
|
3500
3531
|
function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
3501
3532
|
consumeValue(budget);
|
|
3502
3533
|
const fields = types[primary];
|
|
3503
|
-
if (!fields) throw new Error(`unknown struct: ${primary}`);
|
|
3534
|
+
if (!fields) throw new Error(`unknown struct: ${inputPreview(primary)}`);
|
|
3504
3535
|
const expected = new Set(fields.map((f) => f.name));
|
|
3505
3536
|
let ownFieldCount = 0;
|
|
3506
3537
|
for (const k in data) {
|
|
@@ -3509,12 +3540,14 @@ function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
|
3509
3540
|
if (ownFieldCount > TYPED_DATA_LIMITS.maxEncodedValues) {
|
|
3510
3541
|
throw new Error("typed data object contains too many fields");
|
|
3511
3542
|
}
|
|
3512
|
-
if (!expected.has(k))
|
|
3543
|
+
if (!expected.has(k)) {
|
|
3544
|
+
throw new Error(`unknown field in ${inputPreview(primary)}: ${inputPreview(k)}`);
|
|
3545
|
+
}
|
|
3513
3546
|
}
|
|
3514
3547
|
const parts = [typeHash(primary, types)];
|
|
3515
3548
|
for (const f of fields) {
|
|
3516
3549
|
if (!Object.prototype.hasOwnProperty.call(data, f.name)) {
|
|
3517
|
-
throw new Error(`missing field ${primary}.${f.name}`);
|
|
3550
|
+
throw new Error(`missing field ${inputPreview(primary)}.${inputPreview(f.name)}`);
|
|
3518
3551
|
}
|
|
3519
3552
|
parts.push(encodeFieldWithBudget(f.type, data[f.name], types, budget, depth + 1));
|
|
3520
3553
|
}
|
|
@@ -3540,11 +3573,13 @@ function validateDomainTypes(types) {
|
|
|
3540
3573
|
const expected = RESERVED_DOMAIN_FIELDS[f.name];
|
|
3541
3574
|
if (!expected) {
|
|
3542
3575
|
throw new Error(
|
|
3543
|
-
`QRLDomain field "${f.name}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3576
|
+
`QRLDomain field "${inputPreview(f.name)}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3544
3577
|
);
|
|
3545
3578
|
}
|
|
3546
3579
|
if (f.type !== expected) {
|
|
3547
|
-
throw new Error(
|
|
3580
|
+
throw new Error(
|
|
3581
|
+
`QRLDomain field "${inputPreview(f.name)}" must be type "${expected}", got "${inputPreview(f.type)}"`
|
|
3582
|
+
);
|
|
3548
3583
|
}
|
|
3549
3584
|
if (f.name === "name") hasName = true;
|
|
3550
3585
|
}
|
|
@@ -3556,7 +3591,7 @@ function validatePayloadReachability(primary, types) {
|
|
|
3556
3591
|
for (const t of collectDependencies(root, types)) reachable.add(t);
|
|
3557
3592
|
}
|
|
3558
3593
|
for (const k of Object.keys(types)) {
|
|
3559
|
-
if (!reachable.has(k)) throw new Error(`unused referenced type: ${k}`);
|
|
3594
|
+
if (!reachable.has(k)) throw new Error(`unused referenced type: ${inputPreview(k)}`);
|
|
3560
3595
|
}
|
|
3561
3596
|
}
|
|
3562
3597
|
function parseAndValidatePayload(payload) {
|
|
@@ -3678,7 +3713,8 @@ function canonicalChainId2(value) {
|
|
|
3678
3713
|
return `0x${BigInt(value).toString(16)}`;
|
|
3679
3714
|
}
|
|
3680
3715
|
function requestedSwitchChainId(params) {
|
|
3681
|
-
|
|
3716
|
+
const parameterCount = params?.length;
|
|
3717
|
+
if (parameterCount !== 1) {
|
|
3682
3718
|
throw new Error("wallet_switchQrlChain requires one chain configuration object");
|
|
3683
3719
|
}
|
|
3684
3720
|
const request = params?.[0];
|
|
@@ -3744,7 +3780,7 @@ function snapshotRequestParams(params) {
|
|
|
3744
3780
|
return snapshot;
|
|
3745
3781
|
} catch (error2) {
|
|
3746
3782
|
const detail = error2 instanceof Error ? error2.message : String(error2);
|
|
3747
|
-
throw new Error(`Request params must be JSON-serializable: ${detail}
|
|
3783
|
+
throw new Error(`Request params must be JSON-serializable: ${detail}`, { cause: error2 });
|
|
3748
3784
|
}
|
|
3749
3785
|
}
|
|
3750
3786
|
function validateRestrictedRequest(method, params, authorizedAccounts, currentChainId) {
|
package/dist/index.mjs
CHANGED
|
@@ -12,7 +12,8 @@ import { ml_kem768 } from "@noble/post-quantum/ml-kem.js";
|
|
|
12
12
|
import { shake256 } from "@noble/hashes/sha3.js";
|
|
13
13
|
import * as mldsa from "@theqrl/mldsa87";
|
|
14
14
|
function webCrypto() {
|
|
15
|
-
const
|
|
15
|
+
const environment = globalThis;
|
|
16
|
+
const c = environment.crypto;
|
|
16
17
|
if (!c) {
|
|
17
18
|
throw new Error("crypto: WebCrypto is not available in this environment");
|
|
18
19
|
}
|
|
@@ -532,7 +533,8 @@ var KeyExchange = class extends EventEmitter {
|
|
|
532
533
|
this.lastAck = null;
|
|
533
534
|
this.isOriginator = isOriginator;
|
|
534
535
|
if (restored) {
|
|
535
|
-
|
|
536
|
+
const protocolVersion = restored.protocolVersion;
|
|
537
|
+
if (protocolVersion !== PROTOCOL_VERSION) {
|
|
536
538
|
throw new Error("KeyExchange: cannot restore a pre-v3 session");
|
|
537
539
|
}
|
|
538
540
|
this.session = snapshotSession(restored);
|
|
@@ -866,7 +868,8 @@ var KeyExchange = class extends EventEmitter {
|
|
|
866
868
|
}
|
|
867
869
|
/** Rehydrate a Session from its persisted form. */
|
|
868
870
|
static async sessionFromPersisted(p) {
|
|
869
|
-
|
|
871
|
+
const protocolVersion = p.protocolVersion;
|
|
872
|
+
if (protocolVersion !== PROTOCOL_VERSION) {
|
|
870
873
|
throw new Error("KeyExchange: cannot hydrate a pre-v3 session");
|
|
871
874
|
}
|
|
872
875
|
const kAeadRaw = fromBase64Exact(p.kAeadRaw, 32, "persisted AEAD key");
|
|
@@ -1444,7 +1447,12 @@ function startsWith(buf, prefix) {
|
|
|
1444
1447
|
}
|
|
1445
1448
|
return true;
|
|
1446
1449
|
}
|
|
1447
|
-
|
|
1450
|
+
function parseConnectionURI(uri) {
|
|
1451
|
+
return new Promise((resolve) => {
|
|
1452
|
+
resolve(parseURI(uri));
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
function parseURI(uri) {
|
|
1448
1456
|
if (typeof uri !== "string" || uri.length === 0) {
|
|
1449
1457
|
throw new Error("qrUri: empty URI");
|
|
1450
1458
|
}
|
|
@@ -1489,7 +1497,7 @@ async function parseConnectionURI(uri) {
|
|
|
1489
1497
|
blob = base45Decode(q);
|
|
1490
1498
|
} catch (err) {
|
|
1491
1499
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1492
|
-
throw new Error(`qrUri: base45 decode failed: ${msg}
|
|
1500
|
+
throw new Error(`qrUri: base45 decode failed: ${msg}`, { cause: err });
|
|
1493
1501
|
}
|
|
1494
1502
|
try {
|
|
1495
1503
|
if (blob.length !== BLOB_LEN) {
|
|
@@ -1562,7 +1570,8 @@ function getBrowserStorage() {
|
|
|
1562
1570
|
function getBrowserLockManager() {
|
|
1563
1571
|
try {
|
|
1564
1572
|
if (typeof navigator === "undefined") return null;
|
|
1565
|
-
|
|
1573
|
+
const browserNavigator = navigator;
|
|
1574
|
+
return browserNavigator.locks ?? null;
|
|
1566
1575
|
} catch {
|
|
1567
1576
|
return null;
|
|
1568
1577
|
}
|
|
@@ -1903,7 +1912,8 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1903
1912
|
});
|
|
1904
1913
|
socketClient.on("participants_changed", (data) => {
|
|
1905
1914
|
if (!isCurrentSocket()) return;
|
|
1906
|
-
|
|
1915
|
+
const clientType = data.clientType;
|
|
1916
|
+
if (clientType !== "wallet") return;
|
|
1907
1917
|
if (data.event === "join" && data.clientType === "wallet") {
|
|
1908
1918
|
this.walletPresent = true;
|
|
1909
1919
|
log("ConnectionManager", "Wallet joined channel");
|
|
@@ -2070,7 +2080,8 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
2070
2080
|
log("ConnectionManager", `Generated v3 connection URI for channel ${context.channelId}`);
|
|
2071
2081
|
return uri;
|
|
2072
2082
|
} catch (err) {
|
|
2073
|
-
|
|
2083
|
+
const keyExchange = this.keyExchange;
|
|
2084
|
+
if (context ? this.isSessionWorkCurrent(context) : keyExchange !== null) {
|
|
2074
2085
|
this.invalidateSessionWork();
|
|
2075
2086
|
this.keyExchange = null;
|
|
2076
2087
|
this.socketClient.leaveChannel();
|
|
@@ -2892,7 +2903,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
2892
2903
|
const persistedKex = await context.keyExchange.exportPersisted();
|
|
2893
2904
|
if (!persistedKex) throw new Error("Cannot export established AEAD session");
|
|
2894
2905
|
this.assertSessionWorkCurrent(context);
|
|
2895
|
-
if (!this.sessionOwnership
|
|
2906
|
+
if (!this.sessionOwnership.isOwned()) {
|
|
2896
2907
|
throw new Error("Browser-tab ownership changed while persisting AEAD counters");
|
|
2897
2908
|
}
|
|
2898
2909
|
const session = {
|
|
@@ -2911,7 +2922,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
2911
2922
|
this.persistenceBroken = true;
|
|
2912
2923
|
this.removeStoredSession();
|
|
2913
2924
|
error("ConnectionManager", "Failed to persist AEAD counters:", err);
|
|
2914
|
-
throw new Error("Failed to persist AEAD counters");
|
|
2925
|
+
throw new Error("Failed to persist AEAD counters", { cause: err });
|
|
2915
2926
|
}
|
|
2916
2927
|
}
|
|
2917
2928
|
readStoredSession() {
|
|
@@ -3088,17 +3099,20 @@ function consumeValue(budget) {
|
|
|
3088
3099
|
budget.remainingValues--;
|
|
3089
3100
|
if (budget.remainingValues < 0) throw new Error("typed data contains too many encoded values");
|
|
3090
3101
|
}
|
|
3102
|
+
function inputPreview(value) {
|
|
3103
|
+
return value.length > 32 ? `${value.slice(0, 32)}... (length ${value.length})` : value;
|
|
3104
|
+
}
|
|
3091
3105
|
function assertIdentifier(value, label) {
|
|
3092
3106
|
if (!IDENTIFIER_RE.test(value) || value.length > TYPED_DATA_LIMITS.maxIdentifierLength || UNSAFE_OBJECT_KEYS.has(value)) {
|
|
3093
|
-
throw new Error(`invalid ${label}: ${value}`);
|
|
3107
|
+
throw new Error(`invalid ${label}: ${inputPreview(value)}`);
|
|
3094
3108
|
}
|
|
3095
3109
|
}
|
|
3096
3110
|
function parseFieldType(type, types, depth = 0) {
|
|
3097
3111
|
if (depth > TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3098
|
-
throw new Error(`type nesting too deep: ${type}`);
|
|
3112
|
+
throw new Error(`type nesting too deep: ${inputPreview(type)}`);
|
|
3099
3113
|
}
|
|
3100
3114
|
if (type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3101
|
-
throw new Error(`field type too long: ${type}`);
|
|
3115
|
+
throw new Error(`field type too long: ${inputPreview(type)}`);
|
|
3102
3116
|
}
|
|
3103
3117
|
const m = ATOMIC_RE.exec(type);
|
|
3104
3118
|
if (m) {
|
|
@@ -3109,14 +3123,14 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3109
3123
|
if (intKind) {
|
|
3110
3124
|
const width = Number(intWidthStr);
|
|
3111
3125
|
if (!Number.isInteger(width) || width < 8 || width > 256 || width % 8 !== 0) {
|
|
3112
|
-
throw new Error(`invalid int width: ${type}`);
|
|
3126
|
+
throw new Error(`invalid int width: ${inputPreview(type)}`);
|
|
3113
3127
|
}
|
|
3114
3128
|
return { kind: intKind === "uint" ? "uintN" : "intN", width };
|
|
3115
3129
|
}
|
|
3116
3130
|
if (bytesWidthStr) {
|
|
3117
3131
|
const width = Number(bytesWidthStr);
|
|
3118
3132
|
if (!Number.isInteger(width) || width < 1 || width > 32) {
|
|
3119
|
-
throw new Error(`invalid bytesN width: ${type}`);
|
|
3133
|
+
throw new Error(`invalid bytesN width: ${inputPreview(type)}`);
|
|
3120
3134
|
}
|
|
3121
3135
|
return { kind: "bytesN", width };
|
|
3122
3136
|
}
|
|
@@ -3125,7 +3139,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3125
3139
|
if (sizeStr) {
|
|
3126
3140
|
const size = Number(sizeStr);
|
|
3127
3141
|
if (!Number.isSafeInteger(size) || size <= 0 || size > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3128
|
-
throw new Error(`invalid array size: ${type}`);
|
|
3142
|
+
throw new Error(`invalid array size: ${inputPreview(type)}`);
|
|
3129
3143
|
}
|
|
3130
3144
|
return { kind: "array", inner: innerType, size };
|
|
3131
3145
|
}
|
|
@@ -3135,7 +3149,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3135
3149
|
if (Object.prototype.hasOwnProperty.call(types, type)) {
|
|
3136
3150
|
return { kind: "ref", name: type };
|
|
3137
3151
|
}
|
|
3138
|
-
throw new Error(`unknown type: ${type}`);
|
|
3152
|
+
throw new Error(`unknown type: ${inputPreview(type)}`);
|
|
3139
3153
|
}
|
|
3140
3154
|
function isReservedAtomicTypeName(name) {
|
|
3141
3155
|
if (/^(?:address|bool|string|bytes)$/.test(name)) return true;
|
|
@@ -3152,24 +3166,35 @@ function isReservedAtomicTypeName(name) {
|
|
|
3152
3166
|
return false;
|
|
3153
3167
|
}
|
|
3154
3168
|
function baseTypeName(type) {
|
|
3155
|
-
|
|
3169
|
+
let end = type.length;
|
|
3170
|
+
while (end > 0 && type[end - 1] === "]") {
|
|
3171
|
+
let start = end - 2;
|
|
3172
|
+
while (start >= 0) {
|
|
3173
|
+
const code = type.charCodeAt(start);
|
|
3174
|
+
if (code < 48 || code > 57) break;
|
|
3175
|
+
start--;
|
|
3176
|
+
}
|
|
3177
|
+
if (start < 0 || type[start] !== "[") break;
|
|
3178
|
+
end = start;
|
|
3179
|
+
}
|
|
3180
|
+
return type.slice(0, end);
|
|
3156
3181
|
}
|
|
3157
3182
|
function collectDependencies(primary, types) {
|
|
3158
3183
|
if (!Object.prototype.hasOwnProperty.call(types, primary)) {
|
|
3159
|
-
throw new Error(`unknown primary type: ${primary}`);
|
|
3184
|
+
throw new Error(`unknown primary type: ${inputPreview(primary)}`);
|
|
3160
3185
|
}
|
|
3161
3186
|
const visited = /* @__PURE__ */ new Set();
|
|
3162
3187
|
const visit = (name, path) => {
|
|
3163
3188
|
if (path.length > TYPED_DATA_LIMITS.maxTypeGraphDepth) {
|
|
3164
|
-
throw new Error(`type graph nesting too deep: ${[...path, name].join(" -> ")}`);
|
|
3189
|
+
throw new Error(`type graph nesting too deep: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3165
3190
|
}
|
|
3166
3191
|
if (path.includes(name)) {
|
|
3167
|
-
throw new Error(`cyclic type reference: ${[...path, name].join(" -> ")}`);
|
|
3192
|
+
throw new Error(`cyclic type reference: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3168
3193
|
}
|
|
3169
3194
|
if (visited.has(name)) return;
|
|
3170
3195
|
visited.add(name);
|
|
3171
3196
|
const def = types[name];
|
|
3172
|
-
if (!def) throw new Error(`unknown type: ${name}`);
|
|
3197
|
+
if (!def) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3173
3198
|
for (const f of def) {
|
|
3174
3199
|
const base = baseTypeName(f.type);
|
|
3175
3200
|
if (Object.prototype.hasOwnProperty.call(types, base)) {
|
|
@@ -3190,11 +3215,11 @@ function validateTypeMap(types) {
|
|
|
3190
3215
|
for (const [name, def] of entries) {
|
|
3191
3216
|
assertIdentifier(name, "struct name");
|
|
3192
3217
|
if (name !== "QRLDomain" && isReservedAtomicTypeName(name)) {
|
|
3193
|
-
throw new Error(`struct name is reserved by an atomic type: ${name}`);
|
|
3218
|
+
throw new Error(`struct name is reserved by an atomic type: ${inputPreview(name)}`);
|
|
3194
3219
|
}
|
|
3195
3220
|
const defUnknown = def;
|
|
3196
3221
|
if (!Array.isArray(defUnknown) || def.length === 0 || def.length > TYPED_DATA_LIMITS.maxFieldsPerType) {
|
|
3197
|
-
throw new Error(`empty or invalid struct: ${name}`);
|
|
3222
|
+
throw new Error(`empty or invalid struct: ${inputPreview(name)}`);
|
|
3198
3223
|
}
|
|
3199
3224
|
totalFields += def.length;
|
|
3200
3225
|
if (totalFields > TYPED_DATA_LIMITS.maxTotalFields) {
|
|
@@ -3203,14 +3228,17 @@ function validateTypeMap(types) {
|
|
|
3203
3228
|
const seen = /* @__PURE__ */ new Set();
|
|
3204
3229
|
for (const f of def) {
|
|
3205
3230
|
if (!f || typeof f.name !== "string" || !f.name) {
|
|
3206
|
-
throw new Error(`bad field in ${name}`);
|
|
3231
|
+
throw new Error(`bad field in ${inputPreview(name)}`);
|
|
3207
3232
|
}
|
|
3208
|
-
assertIdentifier(f.name, `field name in ${name}`);
|
|
3233
|
+
assertIdentifier(f.name, `field name in ${inputPreview(name)}`);
|
|
3209
3234
|
if (typeof f.type !== "string" || !f.type) {
|
|
3210
|
-
throw new Error(`bad field type in ${name}.${f.name}`);
|
|
3235
|
+
throw new Error(`bad field type in ${inputPreview(name)}.${inputPreview(f.name)}`);
|
|
3236
|
+
}
|
|
3237
|
+
if (f.type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3238
|
+
throw new Error(`field type too long: ${inputPreview(f.type)}`);
|
|
3211
3239
|
}
|
|
3212
3240
|
if (seen.has(f.name)) {
|
|
3213
|
-
throw new Error(`duplicate field "${f.name}" in ${name}`);
|
|
3241
|
+
throw new Error(`duplicate field "${inputPreview(f.name)}" in ${inputPreview(name)}`);
|
|
3214
3242
|
}
|
|
3215
3243
|
seen.add(f.name);
|
|
3216
3244
|
}
|
|
@@ -3222,7 +3250,7 @@ function encodeType(primary, types) {
|
|
|
3222
3250
|
const others = [...deps].filter((n) => n !== primary).sort();
|
|
3223
3251
|
return [primary, ...others].map((name) => {
|
|
3224
3252
|
const fields = types[name];
|
|
3225
|
-
if (!fields) throw new Error(`unknown type: ${name}`);
|
|
3253
|
+
if (!fields) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3226
3254
|
const inner = fields.map((f) => `${f.type} ${f.name}`).join(",");
|
|
3227
3255
|
return `${name}(${inner})`;
|
|
3228
3256
|
}).join("");
|
|
@@ -3232,7 +3260,7 @@ function typeHash(primary, types) {
|
|
|
3232
3260
|
}
|
|
3233
3261
|
function parseQAddress(addr, slot) {
|
|
3234
3262
|
if (!isCurrentQrlAddress(addr)) {
|
|
3235
|
-
throw new Error(`invalid Q-address: ${String(addr)}`);
|
|
3263
|
+
throw new Error(`invalid Q-address: ${inputPreview(String(addr))}`);
|
|
3236
3264
|
}
|
|
3237
3265
|
const bytes = hexToBytes("0x" + addr.slice(1).toLowerCase());
|
|
3238
3266
|
if (bytes.length > slot) {
|
|
@@ -3256,12 +3284,13 @@ function bigIntToSlot(value, width, signed, slot) {
|
|
|
3256
3284
|
if (signed) {
|
|
3257
3285
|
const limit = 1n << BigInt(width - 1);
|
|
3258
3286
|
if (value >= limit || value < -limit) {
|
|
3259
|
-
throw new Error(`int${width} out of range: ${value}`);
|
|
3287
|
+
throw new Error(`int${width} out of range: ${inputPreview(String(value))}`);
|
|
3260
3288
|
}
|
|
3261
3289
|
} else {
|
|
3262
|
-
if (value < 0n) throw new Error(`uint${width} negative: ${value}`);
|
|
3290
|
+
if (value < 0n) throw new Error(`uint${width} negative: ${inputPreview(String(value))}`);
|
|
3263
3291
|
const limit = 1n << BigInt(width);
|
|
3264
|
-
if (value >= limit)
|
|
3292
|
+
if (value >= limit)
|
|
3293
|
+
throw new Error(`uint${width} out of range: ${inputPreview(String(value))}`);
|
|
3265
3294
|
}
|
|
3266
3295
|
const slotBits = BigInt(slot * 8);
|
|
3267
3296
|
const slotMask = (1n << slotBits) - 1n;
|
|
@@ -3284,7 +3313,7 @@ function parseIntValue(v, typeLabel) {
|
|
|
3284
3313
|
return isNegative ? -abs : abs;
|
|
3285
3314
|
}
|
|
3286
3315
|
if (!/^-?(0|[1-9]\d*)$/.test(v)) {
|
|
3287
|
-
throw new Error(`invalid ${typeLabel} string: ${v}`);
|
|
3316
|
+
throw new Error(`invalid ${typeLabel} string: ${inputPreview(v)}`);
|
|
3288
3317
|
}
|
|
3289
3318
|
return BigInt(v);
|
|
3290
3319
|
}
|
|
@@ -3298,14 +3327,14 @@ function parseIntValue(v, typeLabel) {
|
|
|
3298
3327
|
}
|
|
3299
3328
|
function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
3300
3329
|
if (depth > TYPED_DATA_LIMITS.maxTypeGraphDepth + TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3301
|
-
throw new Error(`value nesting too deep: ${type}`);
|
|
3330
|
+
throw new Error(`value nesting too deep: ${inputPreview(type)}`);
|
|
3302
3331
|
}
|
|
3303
3332
|
consumeValue(budget);
|
|
3304
3333
|
const parsed = parseFieldType(type, types);
|
|
3305
3334
|
switch (parsed.kind) {
|
|
3306
3335
|
case "ref":
|
|
3307
3336
|
if (!isMessageObject(value)) {
|
|
3308
|
-
throw new Error(`struct field expects object: ${type}`);
|
|
3337
|
+
throw new Error(`struct field expects object: ${inputPreview(type)}`);
|
|
3309
3338
|
}
|
|
3310
3339
|
return hashStructWithBudget(parsed.name, value, types, budget, depth + 1);
|
|
3311
3340
|
case "address":
|
|
@@ -3371,10 +3400,12 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3371
3400
|
throw new Error(`array field expects array: ${typeof value}`);
|
|
3372
3401
|
}
|
|
3373
3402
|
if (value.length > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3374
|
-
throw new Error(`array ${type} exceeds length limit`);
|
|
3403
|
+
throw new Error(`array ${inputPreview(type)} exceeds length limit`);
|
|
3375
3404
|
}
|
|
3376
3405
|
if (parsed.size !== void 0 && value.length !== parsed.size) {
|
|
3377
|
-
throw new Error(
|
|
3406
|
+
throw new Error(
|
|
3407
|
+
`fixed array ${inputPreview(type)} requires length ${parsed.size}, got ${value.length}`
|
|
3408
|
+
);
|
|
3378
3409
|
}
|
|
3379
3410
|
const chunks = value.map(
|
|
3380
3411
|
(v) => encodeFieldWithBudget(parsed.inner, v, types, budget, depth + 1)
|
|
@@ -3391,7 +3422,7 @@ function encodeField(type, value, types, schemeVersion) {
|
|
|
3391
3422
|
function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
3392
3423
|
consumeValue(budget);
|
|
3393
3424
|
const fields = types[primary];
|
|
3394
|
-
if (!fields) throw new Error(`unknown struct: ${primary}`);
|
|
3425
|
+
if (!fields) throw new Error(`unknown struct: ${inputPreview(primary)}`);
|
|
3395
3426
|
const expected = new Set(fields.map((f) => f.name));
|
|
3396
3427
|
let ownFieldCount = 0;
|
|
3397
3428
|
for (const k in data) {
|
|
@@ -3400,12 +3431,14 @@ function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
|
3400
3431
|
if (ownFieldCount > TYPED_DATA_LIMITS.maxEncodedValues) {
|
|
3401
3432
|
throw new Error("typed data object contains too many fields");
|
|
3402
3433
|
}
|
|
3403
|
-
if (!expected.has(k))
|
|
3434
|
+
if (!expected.has(k)) {
|
|
3435
|
+
throw new Error(`unknown field in ${inputPreview(primary)}: ${inputPreview(k)}`);
|
|
3436
|
+
}
|
|
3404
3437
|
}
|
|
3405
3438
|
const parts = [typeHash(primary, types)];
|
|
3406
3439
|
for (const f of fields) {
|
|
3407
3440
|
if (!Object.prototype.hasOwnProperty.call(data, f.name)) {
|
|
3408
|
-
throw new Error(`missing field ${primary}.${f.name}`);
|
|
3441
|
+
throw new Error(`missing field ${inputPreview(primary)}.${inputPreview(f.name)}`);
|
|
3409
3442
|
}
|
|
3410
3443
|
parts.push(encodeFieldWithBudget(f.type, data[f.name], types, budget, depth + 1));
|
|
3411
3444
|
}
|
|
@@ -3431,11 +3464,13 @@ function validateDomainTypes(types) {
|
|
|
3431
3464
|
const expected = RESERVED_DOMAIN_FIELDS[f.name];
|
|
3432
3465
|
if (!expected) {
|
|
3433
3466
|
throw new Error(
|
|
3434
|
-
`QRLDomain field "${f.name}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3467
|
+
`QRLDomain field "${inputPreview(f.name)}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3435
3468
|
);
|
|
3436
3469
|
}
|
|
3437
3470
|
if (f.type !== expected) {
|
|
3438
|
-
throw new Error(
|
|
3471
|
+
throw new Error(
|
|
3472
|
+
`QRLDomain field "${inputPreview(f.name)}" must be type "${expected}", got "${inputPreview(f.type)}"`
|
|
3473
|
+
);
|
|
3439
3474
|
}
|
|
3440
3475
|
if (f.name === "name") hasName = true;
|
|
3441
3476
|
}
|
|
@@ -3447,7 +3482,7 @@ function validatePayloadReachability(primary, types) {
|
|
|
3447
3482
|
for (const t of collectDependencies(root, types)) reachable.add(t);
|
|
3448
3483
|
}
|
|
3449
3484
|
for (const k of Object.keys(types)) {
|
|
3450
|
-
if (!reachable.has(k)) throw new Error(`unused referenced type: ${k}`);
|
|
3485
|
+
if (!reachable.has(k)) throw new Error(`unused referenced type: ${inputPreview(k)}`);
|
|
3451
3486
|
}
|
|
3452
3487
|
}
|
|
3453
3488
|
function parseAndValidatePayload(payload) {
|
|
@@ -3569,7 +3604,8 @@ function canonicalChainId2(value) {
|
|
|
3569
3604
|
return `0x${BigInt(value).toString(16)}`;
|
|
3570
3605
|
}
|
|
3571
3606
|
function requestedSwitchChainId(params) {
|
|
3572
|
-
|
|
3607
|
+
const parameterCount = params?.length;
|
|
3608
|
+
if (parameterCount !== 1) {
|
|
3573
3609
|
throw new Error("wallet_switchQrlChain requires one chain configuration object");
|
|
3574
3610
|
}
|
|
3575
3611
|
const request = params?.[0];
|
|
@@ -3635,7 +3671,7 @@ function snapshotRequestParams(params) {
|
|
|
3635
3671
|
return snapshot;
|
|
3636
3672
|
} catch (error2) {
|
|
3637
3673
|
const detail = error2 instanceof Error ? error2.message : String(error2);
|
|
3638
|
-
throw new Error(`Request params must be JSON-serializable: ${detail}
|
|
3674
|
+
throw new Error(`Request params must be JSON-serializable: ${detail}`, { cause: error2 });
|
|
3639
3675
|
}
|
|
3640
3676
|
}
|
|
3641
3677
|
function validateRestrictedRequest(method, params, authorizedAccounts, currentChainId) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qrlwallet/connect",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.2",
|
|
4
4
|
"packageManager": "npm@10.9.8",
|
|
5
5
|
"description": "Connect your dApp to QRL Wallet via QR code or deep link",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,16 +13,19 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
15
15
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
16
|
-
"typecheck": "tsc --noEmit",
|
|
16
|
+
"typecheck": "tsc --noEmit && npm run typecheck:test",
|
|
17
|
+
"typecheck:test": "tsc --noEmit -p tsconfig.eslint.json",
|
|
17
18
|
"test": "vitest run",
|
|
18
19
|
"test:watch": "vitest",
|
|
19
|
-
"lint": "eslint src
|
|
20
|
-
"lint:fix": "eslint src
|
|
20
|
+
"lint": "eslint --config eslint.config.mjs src test scripts \"*.mjs\" vitest.config.ts --report-unused-disable-directives --max-warnings 0",
|
|
21
|
+
"lint:fix": "eslint --config eslint.config.mjs src test scripts \"*.mjs\" vitest.config.ts --report-unused-disable-directives --max-warnings 0 --fix",
|
|
21
22
|
"format": "prettier --write src/",
|
|
22
23
|
"format:check": "prettier --check src/",
|
|
23
24
|
"size": "size-limit",
|
|
24
25
|
"size:check": "size-limit --highlight-less",
|
|
25
|
-
"prepublishOnly": "npm run lint && npm run typecheck && npm test && npm run build"
|
|
26
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm test && npm run build",
|
|
27
|
+
"lint:ratchet": "node scripts/lint-ratchet.mjs --check",
|
|
28
|
+
"lint:ratchet:update": "node scripts/lint-ratchet.mjs"
|
|
26
29
|
},
|
|
27
30
|
"keywords": [
|
|
28
31
|
"qrl",
|
|
@@ -49,13 +52,15 @@
|
|
|
49
52
|
"socket.io-client": "^4.8.1"
|
|
50
53
|
},
|
|
51
54
|
"devDependencies": {
|
|
55
|
+
"@eslint/js": "^10.0.1",
|
|
52
56
|
"@size-limit/file": "^11.0.0",
|
|
53
57
|
"@theqrl/wallet.js": "^6.3.0",
|
|
54
58
|
"@types/node": "^25.3.3",
|
|
55
59
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
56
60
|
"@typescript-eslint/parser": "^8.0.0",
|
|
57
|
-
"eslint": "^
|
|
61
|
+
"eslint": "^10.12.0",
|
|
58
62
|
"eslint-config-prettier": "^9.1.0",
|
|
63
|
+
"globals": "^17.13.0",
|
|
59
64
|
"prettier": "^3.4.0",
|
|
60
65
|
"size-limit": "^11.0.0",
|
|
61
66
|
"tsup": "^8.4.0",
|