@qrlwallet/connect 5.0.3 → 5.1.1
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 +7 -6
- package/dist/index.d.mts +61 -20
- package/dist/index.d.ts +61 -20
- package/dist/index.js +158 -83
- package/dist/index.mjs +154 -83
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -59,8 +59,10 @@ __export(index_exports, {
|
|
|
59
59
|
RESTRICTED_METHODS: () => RESTRICTED_METHODS,
|
|
60
60
|
SCHEME_TAG_MSG: () => SCHEME_TAG_MSG,
|
|
61
61
|
SCHEME_TAG_TYPED: () => SCHEME_TAG_TYPED,
|
|
62
|
+
SCHEME_TAG_TYPED_V2: () => SCHEME_TAG_TYPED_V2,
|
|
62
63
|
SCHEME_VERSION_MSG: () => SCHEME_VERSION_MSG,
|
|
63
64
|
SCHEME_VERSION_TYPED: () => SCHEME_VERSION_TYPED,
|
|
65
|
+
SCHEME_VERSION_TYPED_V2: () => SCHEME_VERSION_TYPED_V2,
|
|
64
66
|
SocketClient: () => SocketClient,
|
|
65
67
|
TYPED_DATA_LIMITS: () => TYPED_DATA_LIMITS,
|
|
66
68
|
UNRESTRICTED_METHODS: () => UNRESTRICTED_METHODS,
|
|
@@ -94,6 +96,8 @@ __export(index_exports, {
|
|
|
94
96
|
parseConnectionURI: () => parseConnectionURI,
|
|
95
97
|
qip55AddressFromBytes: () => qip55AddressFromBytes,
|
|
96
98
|
typeHash: () => typeHash,
|
|
99
|
+
typedDataSchemeTag: () => typedDataSchemeTag,
|
|
100
|
+
typedDataSchemeVersion: () => typedDataSchemeVersion,
|
|
97
101
|
verifyMessage: () => verifyMessage,
|
|
98
102
|
verifyMessageForSigner: () => verifyMessageForSigner,
|
|
99
103
|
verifyMessageSignature: () => verifyMessageSignature,
|
|
@@ -566,7 +570,7 @@ function isQrlSignedTypedDataResult(value) {
|
|
|
566
570
|
try {
|
|
567
571
|
if (!isSigningResultRecord(value)) return false;
|
|
568
572
|
const keys = hasOwn(value, "descriptor") ? TYPED_RESULT_KEYS_WITH_DESCRIPTOR : TYPED_RESULT_KEYS;
|
|
569
|
-
return hasExactOwnKeys(value, keys) && value.schemeVersion === "QRL-SIGN-TYPED-v1" && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
573
|
+
return hasExactOwnKeys(value, keys) && (value.schemeVersion === "QRL-SIGN-TYPED-v1" || value.schemeVersion === "QRL-SIGN-TYPED-v2") && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
570
574
|
} catch {
|
|
571
575
|
return false;
|
|
572
576
|
}
|
|
@@ -3081,8 +3085,10 @@ var ConnectionManager = _ConnectionManager;
|
|
|
3081
3085
|
// src/signing/ctx.ts
|
|
3082
3086
|
var SCHEME_VERSION_MSG = "QRL-SIGN-MSG-v1";
|
|
3083
3087
|
var SCHEME_VERSION_TYPED = "QRL-SIGN-TYPED-v1";
|
|
3088
|
+
var SCHEME_VERSION_TYPED_V2 = "QRL-SIGN-TYPED-v2";
|
|
3084
3089
|
var SCHEME_TAG_MSG = new TextEncoder().encode(SCHEME_VERSION_MSG);
|
|
3085
3090
|
var SCHEME_TAG_TYPED = new TextEncoder().encode(SCHEME_VERSION_TYPED);
|
|
3091
|
+
var SCHEME_TAG_TYPED_V2 = new TextEncoder().encode(SCHEME_VERSION_TYPED_V2);
|
|
3086
3092
|
var DIGEST_LEN = 64;
|
|
3087
3093
|
|
|
3088
3094
|
// src/signing/bytes.ts
|
|
@@ -3122,7 +3128,14 @@ function concatBytesArr(parts) {
|
|
|
3122
3128
|
}
|
|
3123
3129
|
|
|
3124
3130
|
// src/signing/typedData.ts
|
|
3125
|
-
var
|
|
3131
|
+
var SLOT_BYTES = Object.freeze({
|
|
3132
|
+
[SCHEME_VERSION_TYPED]: 32,
|
|
3133
|
+
[SCHEME_VERSION_TYPED_V2]: 64
|
|
3134
|
+
});
|
|
3135
|
+
var SCHEME_TAGS = Object.freeze({
|
|
3136
|
+
[SCHEME_VERSION_TYPED]: SCHEME_TAG_TYPED,
|
|
3137
|
+
[SCHEME_VERSION_TYPED_V2]: SCHEME_TAG_TYPED_V2
|
|
3138
|
+
});
|
|
3126
3139
|
var TYPED_DATA_LIMITS = Object.freeze({
|
|
3127
3140
|
maxTypes: 32,
|
|
3128
3141
|
maxFieldsPerType: 32,
|
|
@@ -3174,21 +3187,30 @@ function parsePayload(payload) {
|
|
|
3174
3187
|
var ATOMIC_RE = /^(?:(address|bool|string|bytes)|(u?int)(\d+)|bytes(\d+)|(.+?)\[(\d*)\])$/;
|
|
3175
3188
|
var IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3176
3189
|
var UNSAFE_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3190
|
+
function newBudget(schemeVersion) {
|
|
3191
|
+
return {
|
|
3192
|
+
remainingValues: TYPED_DATA_LIMITS.maxEncodedValues,
|
|
3193
|
+
slot: SLOT_BYTES[schemeVersion]
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3177
3196
|
function consumeValue(budget) {
|
|
3178
3197
|
budget.remainingValues--;
|
|
3179
3198
|
if (budget.remainingValues < 0) throw new Error("typed data contains too many encoded values");
|
|
3180
3199
|
}
|
|
3200
|
+
function inputPreview(value) {
|
|
3201
|
+
return value.length > 32 ? `${value.slice(0, 32)}... (length ${value.length})` : value;
|
|
3202
|
+
}
|
|
3181
3203
|
function assertIdentifier(value, label) {
|
|
3182
3204
|
if (!IDENTIFIER_RE.test(value) || value.length > TYPED_DATA_LIMITS.maxIdentifierLength || UNSAFE_OBJECT_KEYS.has(value)) {
|
|
3183
|
-
throw new Error(`invalid ${label}: ${value}`);
|
|
3205
|
+
throw new Error(`invalid ${label}: ${inputPreview(value)}`);
|
|
3184
3206
|
}
|
|
3185
3207
|
}
|
|
3186
3208
|
function parseFieldType(type, types, depth = 0) {
|
|
3187
3209
|
if (depth > TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3188
|
-
throw new Error(`type nesting too deep: ${type}`);
|
|
3210
|
+
throw new Error(`type nesting too deep: ${inputPreview(type)}`);
|
|
3189
3211
|
}
|
|
3190
3212
|
if (type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3191
|
-
throw new Error(`field type too long: ${type}`);
|
|
3213
|
+
throw new Error(`field type too long: ${inputPreview(type)}`);
|
|
3192
3214
|
}
|
|
3193
3215
|
const m = ATOMIC_RE.exec(type);
|
|
3194
3216
|
if (m) {
|
|
@@ -3199,14 +3221,14 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3199
3221
|
if (intKind) {
|
|
3200
3222
|
const width = Number(intWidthStr);
|
|
3201
3223
|
if (!Number.isInteger(width) || width < 8 || width > 256 || width % 8 !== 0) {
|
|
3202
|
-
throw new Error(`invalid int width: ${type}`);
|
|
3224
|
+
throw new Error(`invalid int width: ${inputPreview(type)}`);
|
|
3203
3225
|
}
|
|
3204
3226
|
return { kind: intKind === "uint" ? "uintN" : "intN", width };
|
|
3205
3227
|
}
|
|
3206
3228
|
if (bytesWidthStr) {
|
|
3207
3229
|
const width = Number(bytesWidthStr);
|
|
3208
3230
|
if (!Number.isInteger(width) || width < 1 || width > 32) {
|
|
3209
|
-
throw new Error(`invalid bytesN width: ${type}`);
|
|
3231
|
+
throw new Error(`invalid bytesN width: ${inputPreview(type)}`);
|
|
3210
3232
|
}
|
|
3211
3233
|
return { kind: "bytesN", width };
|
|
3212
3234
|
}
|
|
@@ -3215,7 +3237,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3215
3237
|
if (sizeStr) {
|
|
3216
3238
|
const size = Number(sizeStr);
|
|
3217
3239
|
if (!Number.isSafeInteger(size) || size <= 0 || size > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3218
|
-
throw new Error(`invalid array size: ${type}`);
|
|
3240
|
+
throw new Error(`invalid array size: ${inputPreview(type)}`);
|
|
3219
3241
|
}
|
|
3220
3242
|
return { kind: "array", inner: innerType, size };
|
|
3221
3243
|
}
|
|
@@ -3225,7 +3247,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3225
3247
|
if (Object.prototype.hasOwnProperty.call(types, type)) {
|
|
3226
3248
|
return { kind: "ref", name: type };
|
|
3227
3249
|
}
|
|
3228
|
-
throw new Error(`unknown type: ${type}`);
|
|
3250
|
+
throw new Error(`unknown type: ${inputPreview(type)}`);
|
|
3229
3251
|
}
|
|
3230
3252
|
function isReservedAtomicTypeName(name) {
|
|
3231
3253
|
if (/^(?:address|bool|string|bytes)$/.test(name)) return true;
|
|
@@ -3242,24 +3264,35 @@ function isReservedAtomicTypeName(name) {
|
|
|
3242
3264
|
return false;
|
|
3243
3265
|
}
|
|
3244
3266
|
function baseTypeName(type) {
|
|
3245
|
-
|
|
3267
|
+
let end = type.length;
|
|
3268
|
+
while (end > 0 && type[end - 1] === "]") {
|
|
3269
|
+
let start = end - 2;
|
|
3270
|
+
while (start >= 0) {
|
|
3271
|
+
const code = type.charCodeAt(start);
|
|
3272
|
+
if (code < 48 || code > 57) break;
|
|
3273
|
+
start--;
|
|
3274
|
+
}
|
|
3275
|
+
if (start < 0 || type[start] !== "[") break;
|
|
3276
|
+
end = start;
|
|
3277
|
+
}
|
|
3278
|
+
return type.slice(0, end);
|
|
3246
3279
|
}
|
|
3247
3280
|
function collectDependencies(primary, types) {
|
|
3248
3281
|
if (!Object.prototype.hasOwnProperty.call(types, primary)) {
|
|
3249
|
-
throw new Error(`unknown primary type: ${primary}`);
|
|
3282
|
+
throw new Error(`unknown primary type: ${inputPreview(primary)}`);
|
|
3250
3283
|
}
|
|
3251
3284
|
const visited = /* @__PURE__ */ new Set();
|
|
3252
3285
|
const visit = (name, path) => {
|
|
3253
3286
|
if (path.length > TYPED_DATA_LIMITS.maxTypeGraphDepth) {
|
|
3254
|
-
throw new Error(`type graph nesting too deep: ${[...path, name].join(" -> ")}`);
|
|
3287
|
+
throw new Error(`type graph nesting too deep: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3255
3288
|
}
|
|
3256
3289
|
if (path.includes(name)) {
|
|
3257
|
-
throw new Error(`cyclic type reference: ${[...path, name].join(" -> ")}`);
|
|
3290
|
+
throw new Error(`cyclic type reference: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3258
3291
|
}
|
|
3259
3292
|
if (visited.has(name)) return;
|
|
3260
3293
|
visited.add(name);
|
|
3261
3294
|
const def = types[name];
|
|
3262
|
-
if (!def) throw new Error(`unknown type: ${name}`);
|
|
3295
|
+
if (!def) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3263
3296
|
for (const f of def) {
|
|
3264
3297
|
const base = baseTypeName(f.type);
|
|
3265
3298
|
if (Object.prototype.hasOwnProperty.call(types, base)) {
|
|
@@ -3280,11 +3313,11 @@ function validateTypeMap(types) {
|
|
|
3280
3313
|
for (const [name, def] of entries) {
|
|
3281
3314
|
assertIdentifier(name, "struct name");
|
|
3282
3315
|
if (name !== "QRLDomain" && isReservedAtomicTypeName(name)) {
|
|
3283
|
-
throw new Error(`struct name is reserved by an atomic type: ${name}`);
|
|
3316
|
+
throw new Error(`struct name is reserved by an atomic type: ${inputPreview(name)}`);
|
|
3284
3317
|
}
|
|
3285
3318
|
const defUnknown = def;
|
|
3286
3319
|
if (!Array.isArray(defUnknown) || def.length === 0 || def.length > TYPED_DATA_LIMITS.maxFieldsPerType) {
|
|
3287
|
-
throw new Error(`empty or invalid struct: ${name}`);
|
|
3320
|
+
throw new Error(`empty or invalid struct: ${inputPreview(name)}`);
|
|
3288
3321
|
}
|
|
3289
3322
|
totalFields += def.length;
|
|
3290
3323
|
if (totalFields > TYPED_DATA_LIMITS.maxTotalFields) {
|
|
@@ -3293,14 +3326,17 @@ function validateTypeMap(types) {
|
|
|
3293
3326
|
const seen = /* @__PURE__ */ new Set();
|
|
3294
3327
|
for (const f of def) {
|
|
3295
3328
|
if (!f || typeof f.name !== "string" || !f.name) {
|
|
3296
|
-
throw new Error(`bad field in ${name}`);
|
|
3329
|
+
throw new Error(`bad field in ${inputPreview(name)}`);
|
|
3297
3330
|
}
|
|
3298
|
-
assertIdentifier(f.name, `field name in ${name}`);
|
|
3331
|
+
assertIdentifier(f.name, `field name in ${inputPreview(name)}`);
|
|
3299
3332
|
if (typeof f.type !== "string" || !f.type) {
|
|
3300
|
-
throw new Error(`bad field type in ${name}.${f.name}`);
|
|
3333
|
+
throw new Error(`bad field type in ${inputPreview(name)}.${inputPreview(f.name)}`);
|
|
3334
|
+
}
|
|
3335
|
+
if (f.type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3336
|
+
throw new Error(`field type too long: ${inputPreview(f.type)}`);
|
|
3301
3337
|
}
|
|
3302
3338
|
if (seen.has(f.name)) {
|
|
3303
|
-
throw new Error(`duplicate field "${f.name}" in ${name}`);
|
|
3339
|
+
throw new Error(`duplicate field "${inputPreview(f.name)}" in ${inputPreview(name)}`);
|
|
3304
3340
|
}
|
|
3305
3341
|
seen.add(f.name);
|
|
3306
3342
|
}
|
|
@@ -3312,7 +3348,7 @@ function encodeType(primary, types) {
|
|
|
3312
3348
|
const others = [...deps].filter((n) => n !== primary).sort();
|
|
3313
3349
|
return [primary, ...others].map((name) => {
|
|
3314
3350
|
const fields = types[name];
|
|
3315
|
-
if (!fields) throw new Error(`unknown type: ${name}`);
|
|
3351
|
+
if (!fields) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3316
3352
|
const inner = fields.map((f) => `${f.type} ${f.name}`).join(",");
|
|
3317
3353
|
return `${name}(${inner})`;
|
|
3318
3354
|
}).join("");
|
|
@@ -3320,44 +3356,46 @@ function encodeType(primary, types) {
|
|
|
3320
3356
|
function typeHash(primary, types) {
|
|
3321
3357
|
return shake256Digest(new TextEncoder().encode(encodeType(primary, types)), DIGEST_LEN);
|
|
3322
3358
|
}
|
|
3323
|
-
function parseQAddress(addr) {
|
|
3359
|
+
function parseQAddress(addr, slot) {
|
|
3324
3360
|
if (!isCurrentQrlAddress(addr)) {
|
|
3325
|
-
throw new Error(`invalid Q-address: ${String(addr)}`);
|
|
3361
|
+
throw new Error(`invalid Q-address: ${inputPreview(String(addr))}`);
|
|
3326
3362
|
}
|
|
3327
3363
|
const bytes = hexToBytes("0x" + addr.slice(1).toLowerCase());
|
|
3328
|
-
if (bytes.length >
|
|
3364
|
+
if (bytes.length > slot) {
|
|
3329
3365
|
throw new Error("qrl_signTypedData v1 does not support QIP-55 address fields");
|
|
3330
3366
|
}
|
|
3331
3367
|
return bytes;
|
|
3332
3368
|
}
|
|
3333
|
-
function
|
|
3334
|
-
if (bytes.length >
|
|
3335
|
-
const out = new Uint8Array(
|
|
3336
|
-
out.set(bytes,
|
|
3369
|
+
function padLeft(bytes, slot) {
|
|
3370
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3371
|
+
const out = new Uint8Array(slot);
|
|
3372
|
+
out.set(bytes, slot - bytes.length);
|
|
3337
3373
|
return out;
|
|
3338
3374
|
}
|
|
3339
|
-
function
|
|
3340
|
-
if (bytes.length >
|
|
3341
|
-
const out = new Uint8Array(
|
|
3375
|
+
function padRight(bytes, slot) {
|
|
3376
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3377
|
+
const out = new Uint8Array(slot);
|
|
3342
3378
|
out.set(bytes, 0);
|
|
3343
3379
|
return out;
|
|
3344
3380
|
}
|
|
3345
|
-
function bigIntToSlot(value, width, signed) {
|
|
3381
|
+
function bigIntToSlot(value, width, signed, slot) {
|
|
3346
3382
|
if (signed) {
|
|
3347
3383
|
const limit = 1n << BigInt(width - 1);
|
|
3348
3384
|
if (value >= limit || value < -limit) {
|
|
3349
|
-
throw new Error(`int${width} out of range: ${value}`);
|
|
3385
|
+
throw new Error(`int${width} out of range: ${inputPreview(String(value))}`);
|
|
3350
3386
|
}
|
|
3351
3387
|
} else {
|
|
3352
|
-
if (value < 0n) throw new Error(`uint${width} negative: ${value}`);
|
|
3388
|
+
if (value < 0n) throw new Error(`uint${width} negative: ${inputPreview(String(value))}`);
|
|
3353
3389
|
const limit = 1n << BigInt(width);
|
|
3354
|
-
if (value >= limit)
|
|
3390
|
+
if (value >= limit)
|
|
3391
|
+
throw new Error(`uint${width} out of range: ${inputPreview(String(value))}`);
|
|
3355
3392
|
}
|
|
3356
|
-
const
|
|
3357
|
-
const
|
|
3358
|
-
const
|
|
3393
|
+
const slotBits = BigInt(slot * 8);
|
|
3394
|
+
const slotMask = (1n << slotBits) - 1n;
|
|
3395
|
+
const repr = value < 0n ? value + (1n << slotBits) & slotMask : value;
|
|
3396
|
+
const out = new Uint8Array(slot);
|
|
3359
3397
|
let v = repr;
|
|
3360
|
-
for (let i =
|
|
3398
|
+
for (let i = slot - 1; i >= 0; i--) {
|
|
3361
3399
|
out[i] = Number(v & 0xffn);
|
|
3362
3400
|
v >>= 8n;
|
|
3363
3401
|
}
|
|
@@ -3373,7 +3411,7 @@ function parseIntValue(v, typeLabel) {
|
|
|
3373
3411
|
return isNegative ? -abs : abs;
|
|
3374
3412
|
}
|
|
3375
3413
|
if (!/^-?(0|[1-9]\d*)$/.test(v)) {
|
|
3376
|
-
throw new Error(`invalid ${typeLabel} string: ${v}`);
|
|
3414
|
+
throw new Error(`invalid ${typeLabel} string: ${inputPreview(v)}`);
|
|
3377
3415
|
}
|
|
3378
3416
|
return BigInt(v);
|
|
3379
3417
|
}
|
|
@@ -3387,26 +3425,26 @@ function parseIntValue(v, typeLabel) {
|
|
|
3387
3425
|
}
|
|
3388
3426
|
function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
3389
3427
|
if (depth > TYPED_DATA_LIMITS.maxTypeGraphDepth + TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3390
|
-
throw new Error(`value nesting too deep: ${type}`);
|
|
3428
|
+
throw new Error(`value nesting too deep: ${inputPreview(type)}`);
|
|
3391
3429
|
}
|
|
3392
3430
|
consumeValue(budget);
|
|
3393
3431
|
const parsed = parseFieldType(type, types);
|
|
3394
3432
|
switch (parsed.kind) {
|
|
3395
3433
|
case "ref":
|
|
3396
3434
|
if (!isMessageObject(value)) {
|
|
3397
|
-
throw new Error(`struct field expects object: ${type}`);
|
|
3435
|
+
throw new Error(`struct field expects object: ${inputPreview(type)}`);
|
|
3398
3436
|
}
|
|
3399
3437
|
return hashStructWithBudget(parsed.name, value, types, budget, depth + 1);
|
|
3400
3438
|
case "address":
|
|
3401
3439
|
if (typeof value !== "string") {
|
|
3402
3440
|
throw new Error(`address field expects string: ${typeof value}`);
|
|
3403
3441
|
}
|
|
3404
|
-
return
|
|
3442
|
+
return padLeft(parseQAddress(value, budget.slot), budget.slot);
|
|
3405
3443
|
case "bool":
|
|
3406
3444
|
if (typeof value !== "boolean") {
|
|
3407
3445
|
throw new Error(`bool field expects boolean: ${typeof value}`);
|
|
3408
3446
|
}
|
|
3409
|
-
return
|
|
3447
|
+
return padLeft(new Uint8Array([value ? 1 : 0]), budget.slot);
|
|
3410
3448
|
case "string": {
|
|
3411
3449
|
if (typeof value !== "string") {
|
|
3412
3450
|
throw new Error(`string field expects string: ${typeof value}`);
|
|
@@ -3429,9 +3467,19 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3429
3467
|
}
|
|
3430
3468
|
return shake256Digest(hexToBytes(value), DIGEST_LEN);
|
|
3431
3469
|
case "uintN":
|
|
3432
|
-
return bigIntToSlot(
|
|
3470
|
+
return bigIntToSlot(
|
|
3471
|
+
parseIntValue(value, `uint${parsed.width}`),
|
|
3472
|
+
parsed.width,
|
|
3473
|
+
false,
|
|
3474
|
+
budget.slot
|
|
3475
|
+
);
|
|
3433
3476
|
case "intN":
|
|
3434
|
-
return bigIntToSlot(
|
|
3477
|
+
return bigIntToSlot(
|
|
3478
|
+
parseIntValue(value, `int${parsed.width}`),
|
|
3479
|
+
parsed.width,
|
|
3480
|
+
true,
|
|
3481
|
+
budget.slot
|
|
3482
|
+
);
|
|
3435
3483
|
case "bytesN": {
|
|
3436
3484
|
if (typeof value !== "string") {
|
|
3437
3485
|
throw new Error(`bytes${parsed.width} expects 0x-hex string: ${typeof value}`);
|
|
@@ -3443,17 +3491,19 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3443
3491
|
if (raw.length !== parsed.width) {
|
|
3444
3492
|
throw new Error(`bytes${parsed.width} requires ${parsed.width} bytes, got ${raw.length}`);
|
|
3445
3493
|
}
|
|
3446
|
-
return
|
|
3494
|
+
return padRight(raw, budget.slot);
|
|
3447
3495
|
}
|
|
3448
3496
|
case "array": {
|
|
3449
3497
|
if (!Array.isArray(value)) {
|
|
3450
3498
|
throw new Error(`array field expects array: ${typeof value}`);
|
|
3451
3499
|
}
|
|
3452
3500
|
if (value.length > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3453
|
-
throw new Error(`array ${type} exceeds length limit`);
|
|
3501
|
+
throw new Error(`array ${inputPreview(type)} exceeds length limit`);
|
|
3454
3502
|
}
|
|
3455
3503
|
if (parsed.size !== void 0 && value.length !== parsed.size) {
|
|
3456
|
-
throw new Error(
|
|
3504
|
+
throw new Error(
|
|
3505
|
+
`fixed array ${inputPreview(type)} requires length ${parsed.size}, got ${value.length}`
|
|
3506
|
+
);
|
|
3457
3507
|
}
|
|
3458
3508
|
const chunks = value.map(
|
|
3459
3509
|
(v) => encodeFieldWithBudget(parsed.inner, v, types, budget, depth + 1)
|
|
@@ -3462,20 +3512,15 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3462
3512
|
}
|
|
3463
3513
|
}
|
|
3464
3514
|
}
|
|
3465
|
-
function encodeField(type, value, types) {
|
|
3515
|
+
function encodeField(type, value, types, schemeVersion) {
|
|
3466
3516
|
validateTypeMap(types);
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
value,
|
|
3470
|
-
types,
|
|
3471
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3472
|
-
0
|
|
3473
|
-
);
|
|
3517
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3518
|
+
return encodeFieldWithBudget(type, value, types, newBudget(scheme), 0);
|
|
3474
3519
|
}
|
|
3475
3520
|
function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
3476
3521
|
consumeValue(budget);
|
|
3477
3522
|
const fields = types[primary];
|
|
3478
|
-
if (!fields) throw new Error(`unknown struct: ${primary}`);
|
|
3523
|
+
if (!fields) throw new Error(`unknown struct: ${inputPreview(primary)}`);
|
|
3479
3524
|
const expected = new Set(fields.map((f) => f.name));
|
|
3480
3525
|
let ownFieldCount = 0;
|
|
3481
3526
|
for (const k in data) {
|
|
@@ -3484,26 +3529,23 @@ function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
|
3484
3529
|
if (ownFieldCount > TYPED_DATA_LIMITS.maxEncodedValues) {
|
|
3485
3530
|
throw new Error("typed data object contains too many fields");
|
|
3486
3531
|
}
|
|
3487
|
-
if (!expected.has(k))
|
|
3532
|
+
if (!expected.has(k)) {
|
|
3533
|
+
throw new Error(`unknown field in ${inputPreview(primary)}: ${inputPreview(k)}`);
|
|
3534
|
+
}
|
|
3488
3535
|
}
|
|
3489
3536
|
const parts = [typeHash(primary, types)];
|
|
3490
3537
|
for (const f of fields) {
|
|
3491
3538
|
if (!Object.prototype.hasOwnProperty.call(data, f.name)) {
|
|
3492
|
-
throw new Error(`missing field ${primary}.${f.name}`);
|
|
3539
|
+
throw new Error(`missing field ${inputPreview(primary)}.${inputPreview(f.name)}`);
|
|
3493
3540
|
}
|
|
3494
3541
|
parts.push(encodeFieldWithBudget(f.type, data[f.name], types, budget, depth + 1));
|
|
3495
3542
|
}
|
|
3496
3543
|
return shake256Digest(concatBytesArr(parts), DIGEST_LEN);
|
|
3497
3544
|
}
|
|
3498
|
-
function hashStruct(primary, data, types) {
|
|
3545
|
+
function hashStruct(primary, data, types, schemeVersion) {
|
|
3499
3546
|
validateTypeMap(types);
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
data,
|
|
3503
|
-
types,
|
|
3504
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3505
|
-
0
|
|
3506
|
-
);
|
|
3547
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3548
|
+
return hashStructWithBudget(primary, data, types, newBudget(scheme), 0);
|
|
3507
3549
|
}
|
|
3508
3550
|
var RESERVED_DOMAIN_FIELDS = {
|
|
3509
3551
|
name: "string",
|
|
@@ -3520,11 +3562,13 @@ function validateDomainTypes(types) {
|
|
|
3520
3562
|
const expected = RESERVED_DOMAIN_FIELDS[f.name];
|
|
3521
3563
|
if (!expected) {
|
|
3522
3564
|
throw new Error(
|
|
3523
|
-
`QRLDomain field "${f.name}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3565
|
+
`QRLDomain field "${inputPreview(f.name)}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3524
3566
|
);
|
|
3525
3567
|
}
|
|
3526
3568
|
if (f.type !== expected) {
|
|
3527
|
-
throw new Error(
|
|
3569
|
+
throw new Error(
|
|
3570
|
+
`QRLDomain field "${inputPreview(f.name)}" must be type "${expected}", got "${inputPreview(f.type)}"`
|
|
3571
|
+
);
|
|
3528
3572
|
}
|
|
3529
3573
|
if (f.name === "name") hasName = true;
|
|
3530
3574
|
}
|
|
@@ -3536,10 +3580,10 @@ function validatePayloadReachability(primary, types) {
|
|
|
3536
3580
|
for (const t of collectDependencies(root, types)) reachable.add(t);
|
|
3537
3581
|
}
|
|
3538
3582
|
for (const k of Object.keys(types)) {
|
|
3539
|
-
if (!reachable.has(k)) throw new Error(`unused referenced type: ${k}`);
|
|
3583
|
+
if (!reachable.has(k)) throw new Error(`unused referenced type: ${inputPreview(k)}`);
|
|
3540
3584
|
}
|
|
3541
3585
|
}
|
|
3542
|
-
function
|
|
3586
|
+
function parseAndValidatePayload(payload) {
|
|
3543
3587
|
const parsed = parsePayload(payload);
|
|
3544
3588
|
validateTypeMap(parsed.types);
|
|
3545
3589
|
assertIdentifier(parsed.primaryType, "primary type");
|
|
@@ -3548,7 +3592,26 @@ function computeTypedDataDigest(payload) {
|
|
|
3548
3592
|
}
|
|
3549
3593
|
validateDomainTypes(parsed.types);
|
|
3550
3594
|
validatePayloadReachability(parsed.primaryType, parsed.types);
|
|
3551
|
-
|
|
3595
|
+
return parsed;
|
|
3596
|
+
}
|
|
3597
|
+
function schemeVersionForTypes(types) {
|
|
3598
|
+
for (const fields of Object.values(types)) {
|
|
3599
|
+
if (fields.some((f) => baseTypeName(f.type) === "address")) {
|
|
3600
|
+
return SCHEME_VERSION_TYPED_V2;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
return SCHEME_VERSION_TYPED;
|
|
3604
|
+
}
|
|
3605
|
+
function typedDataSchemeVersion(payload) {
|
|
3606
|
+
return schemeVersionForTypes(parseAndValidatePayload(payload).types);
|
|
3607
|
+
}
|
|
3608
|
+
function typedDataSchemeTag(schemeVersion) {
|
|
3609
|
+
return SCHEME_TAGS[schemeVersion];
|
|
3610
|
+
}
|
|
3611
|
+
function computeTypedDataDigest(payload) {
|
|
3612
|
+
const parsed = parseAndValidatePayload(payload);
|
|
3613
|
+
const schemeVersion = schemeVersionForTypes(parsed.types);
|
|
3614
|
+
const budget = newBudget(schemeVersion);
|
|
3552
3615
|
const domainHash = hashStructWithBudget("QRLDomain", parsed.domain, parsed.types, budget, 0);
|
|
3553
3616
|
const messageHash = hashStructWithBudget(
|
|
3554
3617
|
parsed.primaryType,
|
|
@@ -3557,7 +3620,10 @@ function computeTypedDataDigest(payload) {
|
|
|
3557
3620
|
budget,
|
|
3558
3621
|
0
|
|
3559
3622
|
);
|
|
3560
|
-
return shake256Digest(
|
|
3623
|
+
return shake256Digest(
|
|
3624
|
+
concatBytes(SCHEME_TAGS[schemeVersion], domainHash, messageHash),
|
|
3625
|
+
DIGEST_LEN
|
|
3626
|
+
);
|
|
3561
3627
|
}
|
|
3562
3628
|
|
|
3563
3629
|
// src/utils/platform.ts
|
|
@@ -3749,9 +3815,8 @@ function validateRestrictedRequest(method, params, authorizedAccounts, currentCh
|
|
|
3749
3815
|
if (params[0] !== authorizedAccount) {
|
|
3750
3816
|
throw new Error("qrl_signTypedData signer is not the authorized account");
|
|
3751
3817
|
}
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
);
|
|
3818
|
+
computeTypedDataDigest(params[1]);
|
|
3819
|
+
return void 0;
|
|
3755
3820
|
}
|
|
3756
3821
|
function requiresAuthorizedAccount(method) {
|
|
3757
3822
|
return method === "qrl_sendTransaction" || method === "qrl_signTransaction" || method === "qrl_signMessage" || method === "qrl_signTypedData";
|
|
@@ -4397,9 +4462,13 @@ function verifyMessageBytes(signature, publicKey, messageBytes) {
|
|
|
4397
4462
|
const digest = computeMessageDigest(messageBytes);
|
|
4398
4463
|
return mldsaVerify(signature, digest, publicKey, SCHEME_TAG_MSG);
|
|
4399
4464
|
}
|
|
4400
|
-
function verifyTypedDataBytes(signature, publicKey, payload) {
|
|
4465
|
+
function verifyTypedDataBytes(signature, publicKey, payload, claimedSchemeVersion) {
|
|
4466
|
+
const schemeVersion = typedDataSchemeVersion(payload);
|
|
4467
|
+
if (claimedSchemeVersion !== void 0 && claimedSchemeVersion !== schemeVersion) {
|
|
4468
|
+
return false;
|
|
4469
|
+
}
|
|
4401
4470
|
const digest = computeTypedDataDigest(payload);
|
|
4402
|
-
return mldsaVerify(signature, digest, publicKey,
|
|
4471
|
+
return mldsaVerify(signature, digest, publicKey, typedDataSchemeTag(schemeVersion));
|
|
4403
4472
|
}
|
|
4404
4473
|
function verifyMessageSignature({
|
|
4405
4474
|
signature,
|
|
@@ -4419,13 +4488,14 @@ function verifyMessageSignature({
|
|
|
4419
4488
|
function verifyTypedDataSignature({
|
|
4420
4489
|
signature,
|
|
4421
4490
|
publicKey,
|
|
4422
|
-
payload
|
|
4491
|
+
payload,
|
|
4492
|
+
schemeVersion
|
|
4423
4493
|
}) {
|
|
4424
4494
|
try {
|
|
4425
4495
|
const sig = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4426
4496
|
const pk = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4427
4497
|
if (!sig || !pk) return false;
|
|
4428
|
-
return verifyTypedDataBytes(sig, pk, payload);
|
|
4498
|
+
return verifyTypedDataBytes(sig, pk, payload, schemeVersion);
|
|
4429
4499
|
} catch {
|
|
4430
4500
|
return false;
|
|
4431
4501
|
}
|
|
@@ -4463,7 +4533,8 @@ function verifyTypedDataForSigner({
|
|
|
4463
4533
|
descriptor,
|
|
4464
4534
|
signature,
|
|
4465
4535
|
publicKey,
|
|
4466
|
-
payload
|
|
4536
|
+
payload,
|
|
4537
|
+
schemeVersion
|
|
4467
4538
|
}) {
|
|
4468
4539
|
try {
|
|
4469
4540
|
if (descriptor === void 0) return false;
|
|
@@ -4471,7 +4542,7 @@ function verifyTypedDataForSigner({
|
|
|
4471
4542
|
const publicKeyBytes = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4472
4543
|
const signatureBytes = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4473
4544
|
if (!descriptorBytes || !publicKeyBytes || !signatureBytes) return false;
|
|
4474
|
-
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload);
|
|
4545
|
+
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload, schemeVersion);
|
|
4475
4546
|
} catch {
|
|
4476
4547
|
return false;
|
|
4477
4548
|
}
|
|
@@ -4513,8 +4584,10 @@ function verifyTypedData(params) {
|
|
|
4513
4584
|
RESTRICTED_METHODS,
|
|
4514
4585
|
SCHEME_TAG_MSG,
|
|
4515
4586
|
SCHEME_TAG_TYPED,
|
|
4587
|
+
SCHEME_TAG_TYPED_V2,
|
|
4516
4588
|
SCHEME_VERSION_MSG,
|
|
4517
4589
|
SCHEME_VERSION_TYPED,
|
|
4590
|
+
SCHEME_VERSION_TYPED_V2,
|
|
4518
4591
|
SocketClient,
|
|
4519
4592
|
TYPED_DATA_LIMITS,
|
|
4520
4593
|
UNRESTRICTED_METHODS,
|
|
@@ -4548,6 +4621,8 @@ function verifyTypedData(params) {
|
|
|
4548
4621
|
parseConnectionURI,
|
|
4549
4622
|
qip55AddressFromBytes,
|
|
4550
4623
|
typeHash,
|
|
4624
|
+
typedDataSchemeTag,
|
|
4625
|
+
typedDataSchemeVersion,
|
|
4551
4626
|
verifyMessage,
|
|
4552
4627
|
verifyMessageForSigner,
|
|
4553
4628
|
verifyMessageSignature,
|