@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.mjs
CHANGED
|
@@ -461,7 +461,7 @@ function isQrlSignedTypedDataResult(value) {
|
|
|
461
461
|
try {
|
|
462
462
|
if (!isSigningResultRecord(value)) return false;
|
|
463
463
|
const keys = hasOwn(value, "descriptor") ? TYPED_RESULT_KEYS_WITH_DESCRIPTOR : TYPED_RESULT_KEYS;
|
|
464
|
-
return hasExactOwnKeys(value, keys) && value.schemeVersion === "QRL-SIGN-TYPED-v1" && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
464
|
+
return hasExactOwnKeys(value, keys) && (value.schemeVersion === "QRL-SIGN-TYPED-v1" || value.schemeVersion === "QRL-SIGN-TYPED-v2") && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
465
465
|
} catch {
|
|
466
466
|
return false;
|
|
467
467
|
}
|
|
@@ -2976,8 +2976,10 @@ var ConnectionManager = _ConnectionManager;
|
|
|
2976
2976
|
// src/signing/ctx.ts
|
|
2977
2977
|
var SCHEME_VERSION_MSG = "QRL-SIGN-MSG-v1";
|
|
2978
2978
|
var SCHEME_VERSION_TYPED = "QRL-SIGN-TYPED-v1";
|
|
2979
|
+
var SCHEME_VERSION_TYPED_V2 = "QRL-SIGN-TYPED-v2";
|
|
2979
2980
|
var SCHEME_TAG_MSG = new TextEncoder().encode(SCHEME_VERSION_MSG);
|
|
2980
2981
|
var SCHEME_TAG_TYPED = new TextEncoder().encode(SCHEME_VERSION_TYPED);
|
|
2982
|
+
var SCHEME_TAG_TYPED_V2 = new TextEncoder().encode(SCHEME_VERSION_TYPED_V2);
|
|
2981
2983
|
var DIGEST_LEN = 64;
|
|
2982
2984
|
|
|
2983
2985
|
// src/signing/bytes.ts
|
|
@@ -3017,7 +3019,14 @@ function concatBytesArr(parts) {
|
|
|
3017
3019
|
}
|
|
3018
3020
|
|
|
3019
3021
|
// src/signing/typedData.ts
|
|
3020
|
-
var
|
|
3022
|
+
var SLOT_BYTES = Object.freeze({
|
|
3023
|
+
[SCHEME_VERSION_TYPED]: 32,
|
|
3024
|
+
[SCHEME_VERSION_TYPED_V2]: 64
|
|
3025
|
+
});
|
|
3026
|
+
var SCHEME_TAGS = Object.freeze({
|
|
3027
|
+
[SCHEME_VERSION_TYPED]: SCHEME_TAG_TYPED,
|
|
3028
|
+
[SCHEME_VERSION_TYPED_V2]: SCHEME_TAG_TYPED_V2
|
|
3029
|
+
});
|
|
3021
3030
|
var TYPED_DATA_LIMITS = Object.freeze({
|
|
3022
3031
|
maxTypes: 32,
|
|
3023
3032
|
maxFieldsPerType: 32,
|
|
@@ -3069,21 +3078,30 @@ function parsePayload(payload) {
|
|
|
3069
3078
|
var ATOMIC_RE = /^(?:(address|bool|string|bytes)|(u?int)(\d+)|bytes(\d+)|(.+?)\[(\d*)\])$/;
|
|
3070
3079
|
var IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3071
3080
|
var UNSAFE_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3081
|
+
function newBudget(schemeVersion) {
|
|
3082
|
+
return {
|
|
3083
|
+
remainingValues: TYPED_DATA_LIMITS.maxEncodedValues,
|
|
3084
|
+
slot: SLOT_BYTES[schemeVersion]
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3072
3087
|
function consumeValue(budget) {
|
|
3073
3088
|
budget.remainingValues--;
|
|
3074
3089
|
if (budget.remainingValues < 0) throw new Error("typed data contains too many encoded values");
|
|
3075
3090
|
}
|
|
3091
|
+
function inputPreview(value) {
|
|
3092
|
+
return value.length > 32 ? `${value.slice(0, 32)}... (length ${value.length})` : value;
|
|
3093
|
+
}
|
|
3076
3094
|
function assertIdentifier(value, label) {
|
|
3077
3095
|
if (!IDENTIFIER_RE.test(value) || value.length > TYPED_DATA_LIMITS.maxIdentifierLength || UNSAFE_OBJECT_KEYS.has(value)) {
|
|
3078
|
-
throw new Error(`invalid ${label}: ${value}`);
|
|
3096
|
+
throw new Error(`invalid ${label}: ${inputPreview(value)}`);
|
|
3079
3097
|
}
|
|
3080
3098
|
}
|
|
3081
3099
|
function parseFieldType(type, types, depth = 0) {
|
|
3082
3100
|
if (depth > TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3083
|
-
throw new Error(`type nesting too deep: ${type}`);
|
|
3101
|
+
throw new Error(`type nesting too deep: ${inputPreview(type)}`);
|
|
3084
3102
|
}
|
|
3085
3103
|
if (type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3086
|
-
throw new Error(`field type too long: ${type}`);
|
|
3104
|
+
throw new Error(`field type too long: ${inputPreview(type)}`);
|
|
3087
3105
|
}
|
|
3088
3106
|
const m = ATOMIC_RE.exec(type);
|
|
3089
3107
|
if (m) {
|
|
@@ -3094,14 +3112,14 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3094
3112
|
if (intKind) {
|
|
3095
3113
|
const width = Number(intWidthStr);
|
|
3096
3114
|
if (!Number.isInteger(width) || width < 8 || width > 256 || width % 8 !== 0) {
|
|
3097
|
-
throw new Error(`invalid int width: ${type}`);
|
|
3115
|
+
throw new Error(`invalid int width: ${inputPreview(type)}`);
|
|
3098
3116
|
}
|
|
3099
3117
|
return { kind: intKind === "uint" ? "uintN" : "intN", width };
|
|
3100
3118
|
}
|
|
3101
3119
|
if (bytesWidthStr) {
|
|
3102
3120
|
const width = Number(bytesWidthStr);
|
|
3103
3121
|
if (!Number.isInteger(width) || width < 1 || width > 32) {
|
|
3104
|
-
throw new Error(`invalid bytesN width: ${type}`);
|
|
3122
|
+
throw new Error(`invalid bytesN width: ${inputPreview(type)}`);
|
|
3105
3123
|
}
|
|
3106
3124
|
return { kind: "bytesN", width };
|
|
3107
3125
|
}
|
|
@@ -3110,7 +3128,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3110
3128
|
if (sizeStr) {
|
|
3111
3129
|
const size = Number(sizeStr);
|
|
3112
3130
|
if (!Number.isSafeInteger(size) || size <= 0 || size > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3113
|
-
throw new Error(`invalid array size: ${type}`);
|
|
3131
|
+
throw new Error(`invalid array size: ${inputPreview(type)}`);
|
|
3114
3132
|
}
|
|
3115
3133
|
return { kind: "array", inner: innerType, size };
|
|
3116
3134
|
}
|
|
@@ -3120,7 +3138,7 @@ function parseFieldType(type, types, depth = 0) {
|
|
|
3120
3138
|
if (Object.prototype.hasOwnProperty.call(types, type)) {
|
|
3121
3139
|
return { kind: "ref", name: type };
|
|
3122
3140
|
}
|
|
3123
|
-
throw new Error(`unknown type: ${type}`);
|
|
3141
|
+
throw new Error(`unknown type: ${inputPreview(type)}`);
|
|
3124
3142
|
}
|
|
3125
3143
|
function isReservedAtomicTypeName(name) {
|
|
3126
3144
|
if (/^(?:address|bool|string|bytes)$/.test(name)) return true;
|
|
@@ -3137,24 +3155,35 @@ function isReservedAtomicTypeName(name) {
|
|
|
3137
3155
|
return false;
|
|
3138
3156
|
}
|
|
3139
3157
|
function baseTypeName(type) {
|
|
3140
|
-
|
|
3158
|
+
let end = type.length;
|
|
3159
|
+
while (end > 0 && type[end - 1] === "]") {
|
|
3160
|
+
let start = end - 2;
|
|
3161
|
+
while (start >= 0) {
|
|
3162
|
+
const code = type.charCodeAt(start);
|
|
3163
|
+
if (code < 48 || code > 57) break;
|
|
3164
|
+
start--;
|
|
3165
|
+
}
|
|
3166
|
+
if (start < 0 || type[start] !== "[") break;
|
|
3167
|
+
end = start;
|
|
3168
|
+
}
|
|
3169
|
+
return type.slice(0, end);
|
|
3141
3170
|
}
|
|
3142
3171
|
function collectDependencies(primary, types) {
|
|
3143
3172
|
if (!Object.prototype.hasOwnProperty.call(types, primary)) {
|
|
3144
|
-
throw new Error(`unknown primary type: ${primary}`);
|
|
3173
|
+
throw new Error(`unknown primary type: ${inputPreview(primary)}`);
|
|
3145
3174
|
}
|
|
3146
3175
|
const visited = /* @__PURE__ */ new Set();
|
|
3147
3176
|
const visit = (name, path) => {
|
|
3148
3177
|
if (path.length > TYPED_DATA_LIMITS.maxTypeGraphDepth) {
|
|
3149
|
-
throw new Error(`type graph nesting too deep: ${[...path, name].join(" -> ")}`);
|
|
3178
|
+
throw new Error(`type graph nesting too deep: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3150
3179
|
}
|
|
3151
3180
|
if (path.includes(name)) {
|
|
3152
|
-
throw new Error(`cyclic type reference: ${[...path, name].join(" -> ")}`);
|
|
3181
|
+
throw new Error(`cyclic type reference: ${inputPreview([...path, name].join(" -> "))}`);
|
|
3153
3182
|
}
|
|
3154
3183
|
if (visited.has(name)) return;
|
|
3155
3184
|
visited.add(name);
|
|
3156
3185
|
const def = types[name];
|
|
3157
|
-
if (!def) throw new Error(`unknown type: ${name}`);
|
|
3186
|
+
if (!def) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3158
3187
|
for (const f of def) {
|
|
3159
3188
|
const base = baseTypeName(f.type);
|
|
3160
3189
|
if (Object.prototype.hasOwnProperty.call(types, base)) {
|
|
@@ -3175,11 +3204,11 @@ function validateTypeMap(types) {
|
|
|
3175
3204
|
for (const [name, def] of entries) {
|
|
3176
3205
|
assertIdentifier(name, "struct name");
|
|
3177
3206
|
if (name !== "QRLDomain" && isReservedAtomicTypeName(name)) {
|
|
3178
|
-
throw new Error(`struct name is reserved by an atomic type: ${name}`);
|
|
3207
|
+
throw new Error(`struct name is reserved by an atomic type: ${inputPreview(name)}`);
|
|
3179
3208
|
}
|
|
3180
3209
|
const defUnknown = def;
|
|
3181
3210
|
if (!Array.isArray(defUnknown) || def.length === 0 || def.length > TYPED_DATA_LIMITS.maxFieldsPerType) {
|
|
3182
|
-
throw new Error(`empty or invalid struct: ${name}`);
|
|
3211
|
+
throw new Error(`empty or invalid struct: ${inputPreview(name)}`);
|
|
3183
3212
|
}
|
|
3184
3213
|
totalFields += def.length;
|
|
3185
3214
|
if (totalFields > TYPED_DATA_LIMITS.maxTotalFields) {
|
|
@@ -3188,14 +3217,17 @@ function validateTypeMap(types) {
|
|
|
3188
3217
|
const seen = /* @__PURE__ */ new Set();
|
|
3189
3218
|
for (const f of def) {
|
|
3190
3219
|
if (!f || typeof f.name !== "string" || !f.name) {
|
|
3191
|
-
throw new Error(`bad field in ${name}`);
|
|
3220
|
+
throw new Error(`bad field in ${inputPreview(name)}`);
|
|
3192
3221
|
}
|
|
3193
|
-
assertIdentifier(f.name, `field name in ${name}`);
|
|
3222
|
+
assertIdentifier(f.name, `field name in ${inputPreview(name)}`);
|
|
3194
3223
|
if (typeof f.type !== "string" || !f.type) {
|
|
3195
|
-
throw new Error(`bad field type in ${name}.${f.name}`);
|
|
3224
|
+
throw new Error(`bad field type in ${inputPreview(name)}.${inputPreview(f.name)}`);
|
|
3225
|
+
}
|
|
3226
|
+
if (f.type.length > TYPED_DATA_LIMITS.maxFieldTypeLength) {
|
|
3227
|
+
throw new Error(`field type too long: ${inputPreview(f.type)}`);
|
|
3196
3228
|
}
|
|
3197
3229
|
if (seen.has(f.name)) {
|
|
3198
|
-
throw new Error(`duplicate field "${f.name}" in ${name}`);
|
|
3230
|
+
throw new Error(`duplicate field "${inputPreview(f.name)}" in ${inputPreview(name)}`);
|
|
3199
3231
|
}
|
|
3200
3232
|
seen.add(f.name);
|
|
3201
3233
|
}
|
|
@@ -3207,7 +3239,7 @@ function encodeType(primary, types) {
|
|
|
3207
3239
|
const others = [...deps].filter((n) => n !== primary).sort();
|
|
3208
3240
|
return [primary, ...others].map((name) => {
|
|
3209
3241
|
const fields = types[name];
|
|
3210
|
-
if (!fields) throw new Error(`unknown type: ${name}`);
|
|
3242
|
+
if (!fields) throw new Error(`unknown type: ${inputPreview(name)}`);
|
|
3211
3243
|
const inner = fields.map((f) => `${f.type} ${f.name}`).join(",");
|
|
3212
3244
|
return `${name}(${inner})`;
|
|
3213
3245
|
}).join("");
|
|
@@ -3215,44 +3247,46 @@ function encodeType(primary, types) {
|
|
|
3215
3247
|
function typeHash(primary, types) {
|
|
3216
3248
|
return shake256Digest(new TextEncoder().encode(encodeType(primary, types)), DIGEST_LEN);
|
|
3217
3249
|
}
|
|
3218
|
-
function parseQAddress(addr) {
|
|
3250
|
+
function parseQAddress(addr, slot) {
|
|
3219
3251
|
if (!isCurrentQrlAddress(addr)) {
|
|
3220
|
-
throw new Error(`invalid Q-address: ${String(addr)}`);
|
|
3252
|
+
throw new Error(`invalid Q-address: ${inputPreview(String(addr))}`);
|
|
3221
3253
|
}
|
|
3222
3254
|
const bytes = hexToBytes("0x" + addr.slice(1).toLowerCase());
|
|
3223
|
-
if (bytes.length >
|
|
3255
|
+
if (bytes.length > slot) {
|
|
3224
3256
|
throw new Error("qrl_signTypedData v1 does not support QIP-55 address fields");
|
|
3225
3257
|
}
|
|
3226
3258
|
return bytes;
|
|
3227
3259
|
}
|
|
3228
|
-
function
|
|
3229
|
-
if (bytes.length >
|
|
3230
|
-
const out = new Uint8Array(
|
|
3231
|
-
out.set(bytes,
|
|
3260
|
+
function padLeft(bytes, slot) {
|
|
3261
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3262
|
+
const out = new Uint8Array(slot);
|
|
3263
|
+
out.set(bytes, slot - bytes.length);
|
|
3232
3264
|
return out;
|
|
3233
3265
|
}
|
|
3234
|
-
function
|
|
3235
|
-
if (bytes.length >
|
|
3236
|
-
const out = new Uint8Array(
|
|
3266
|
+
function padRight(bytes, slot) {
|
|
3267
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3268
|
+
const out = new Uint8Array(slot);
|
|
3237
3269
|
out.set(bytes, 0);
|
|
3238
3270
|
return out;
|
|
3239
3271
|
}
|
|
3240
|
-
function bigIntToSlot(value, width, signed) {
|
|
3272
|
+
function bigIntToSlot(value, width, signed, slot) {
|
|
3241
3273
|
if (signed) {
|
|
3242
3274
|
const limit = 1n << BigInt(width - 1);
|
|
3243
3275
|
if (value >= limit || value < -limit) {
|
|
3244
|
-
throw new Error(`int${width} out of range: ${value}`);
|
|
3276
|
+
throw new Error(`int${width} out of range: ${inputPreview(String(value))}`);
|
|
3245
3277
|
}
|
|
3246
3278
|
} else {
|
|
3247
|
-
if (value < 0n) throw new Error(`uint${width} negative: ${value}`);
|
|
3279
|
+
if (value < 0n) throw new Error(`uint${width} negative: ${inputPreview(String(value))}`);
|
|
3248
3280
|
const limit = 1n << BigInt(width);
|
|
3249
|
-
if (value >= limit)
|
|
3281
|
+
if (value >= limit)
|
|
3282
|
+
throw new Error(`uint${width} out of range: ${inputPreview(String(value))}`);
|
|
3250
3283
|
}
|
|
3251
|
-
const
|
|
3252
|
-
const
|
|
3253
|
-
const
|
|
3284
|
+
const slotBits = BigInt(slot * 8);
|
|
3285
|
+
const slotMask = (1n << slotBits) - 1n;
|
|
3286
|
+
const repr = value < 0n ? value + (1n << slotBits) & slotMask : value;
|
|
3287
|
+
const out = new Uint8Array(slot);
|
|
3254
3288
|
let v = repr;
|
|
3255
|
-
for (let i =
|
|
3289
|
+
for (let i = slot - 1; i >= 0; i--) {
|
|
3256
3290
|
out[i] = Number(v & 0xffn);
|
|
3257
3291
|
v >>= 8n;
|
|
3258
3292
|
}
|
|
@@ -3268,7 +3302,7 @@ function parseIntValue(v, typeLabel) {
|
|
|
3268
3302
|
return isNegative ? -abs : abs;
|
|
3269
3303
|
}
|
|
3270
3304
|
if (!/^-?(0|[1-9]\d*)$/.test(v)) {
|
|
3271
|
-
throw new Error(`invalid ${typeLabel} string: ${v}`);
|
|
3305
|
+
throw new Error(`invalid ${typeLabel} string: ${inputPreview(v)}`);
|
|
3272
3306
|
}
|
|
3273
3307
|
return BigInt(v);
|
|
3274
3308
|
}
|
|
@@ -3282,26 +3316,26 @@ function parseIntValue(v, typeLabel) {
|
|
|
3282
3316
|
}
|
|
3283
3317
|
function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
3284
3318
|
if (depth > TYPED_DATA_LIMITS.maxTypeGraphDepth + TYPED_DATA_LIMITS.maxArrayNesting) {
|
|
3285
|
-
throw new Error(`value nesting too deep: ${type}`);
|
|
3319
|
+
throw new Error(`value nesting too deep: ${inputPreview(type)}`);
|
|
3286
3320
|
}
|
|
3287
3321
|
consumeValue(budget);
|
|
3288
3322
|
const parsed = parseFieldType(type, types);
|
|
3289
3323
|
switch (parsed.kind) {
|
|
3290
3324
|
case "ref":
|
|
3291
3325
|
if (!isMessageObject(value)) {
|
|
3292
|
-
throw new Error(`struct field expects object: ${type}`);
|
|
3326
|
+
throw new Error(`struct field expects object: ${inputPreview(type)}`);
|
|
3293
3327
|
}
|
|
3294
3328
|
return hashStructWithBudget(parsed.name, value, types, budget, depth + 1);
|
|
3295
3329
|
case "address":
|
|
3296
3330
|
if (typeof value !== "string") {
|
|
3297
3331
|
throw new Error(`address field expects string: ${typeof value}`);
|
|
3298
3332
|
}
|
|
3299
|
-
return
|
|
3333
|
+
return padLeft(parseQAddress(value, budget.slot), budget.slot);
|
|
3300
3334
|
case "bool":
|
|
3301
3335
|
if (typeof value !== "boolean") {
|
|
3302
3336
|
throw new Error(`bool field expects boolean: ${typeof value}`);
|
|
3303
3337
|
}
|
|
3304
|
-
return
|
|
3338
|
+
return padLeft(new Uint8Array([value ? 1 : 0]), budget.slot);
|
|
3305
3339
|
case "string": {
|
|
3306
3340
|
if (typeof value !== "string") {
|
|
3307
3341
|
throw new Error(`string field expects string: ${typeof value}`);
|
|
@@ -3324,9 +3358,19 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3324
3358
|
}
|
|
3325
3359
|
return shake256Digest(hexToBytes(value), DIGEST_LEN);
|
|
3326
3360
|
case "uintN":
|
|
3327
|
-
return bigIntToSlot(
|
|
3361
|
+
return bigIntToSlot(
|
|
3362
|
+
parseIntValue(value, `uint${parsed.width}`),
|
|
3363
|
+
parsed.width,
|
|
3364
|
+
false,
|
|
3365
|
+
budget.slot
|
|
3366
|
+
);
|
|
3328
3367
|
case "intN":
|
|
3329
|
-
return bigIntToSlot(
|
|
3368
|
+
return bigIntToSlot(
|
|
3369
|
+
parseIntValue(value, `int${parsed.width}`),
|
|
3370
|
+
parsed.width,
|
|
3371
|
+
true,
|
|
3372
|
+
budget.slot
|
|
3373
|
+
);
|
|
3330
3374
|
case "bytesN": {
|
|
3331
3375
|
if (typeof value !== "string") {
|
|
3332
3376
|
throw new Error(`bytes${parsed.width} expects 0x-hex string: ${typeof value}`);
|
|
@@ -3338,17 +3382,19 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3338
3382
|
if (raw.length !== parsed.width) {
|
|
3339
3383
|
throw new Error(`bytes${parsed.width} requires ${parsed.width} bytes, got ${raw.length}`);
|
|
3340
3384
|
}
|
|
3341
|
-
return
|
|
3385
|
+
return padRight(raw, budget.slot);
|
|
3342
3386
|
}
|
|
3343
3387
|
case "array": {
|
|
3344
3388
|
if (!Array.isArray(value)) {
|
|
3345
3389
|
throw new Error(`array field expects array: ${typeof value}`);
|
|
3346
3390
|
}
|
|
3347
3391
|
if (value.length > TYPED_DATA_LIMITS.maxArrayLength) {
|
|
3348
|
-
throw new Error(`array ${type} exceeds length limit`);
|
|
3392
|
+
throw new Error(`array ${inputPreview(type)} exceeds length limit`);
|
|
3349
3393
|
}
|
|
3350
3394
|
if (parsed.size !== void 0 && value.length !== parsed.size) {
|
|
3351
|
-
throw new Error(
|
|
3395
|
+
throw new Error(
|
|
3396
|
+
`fixed array ${inputPreview(type)} requires length ${parsed.size}, got ${value.length}`
|
|
3397
|
+
);
|
|
3352
3398
|
}
|
|
3353
3399
|
const chunks = value.map(
|
|
3354
3400
|
(v) => encodeFieldWithBudget(parsed.inner, v, types, budget, depth + 1)
|
|
@@ -3357,20 +3403,15 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3357
3403
|
}
|
|
3358
3404
|
}
|
|
3359
3405
|
}
|
|
3360
|
-
function encodeField(type, value, types) {
|
|
3406
|
+
function encodeField(type, value, types, schemeVersion) {
|
|
3361
3407
|
validateTypeMap(types);
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
value,
|
|
3365
|
-
types,
|
|
3366
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3367
|
-
0
|
|
3368
|
-
);
|
|
3408
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3409
|
+
return encodeFieldWithBudget(type, value, types, newBudget(scheme), 0);
|
|
3369
3410
|
}
|
|
3370
3411
|
function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
3371
3412
|
consumeValue(budget);
|
|
3372
3413
|
const fields = types[primary];
|
|
3373
|
-
if (!fields) throw new Error(`unknown struct: ${primary}`);
|
|
3414
|
+
if (!fields) throw new Error(`unknown struct: ${inputPreview(primary)}`);
|
|
3374
3415
|
const expected = new Set(fields.map((f) => f.name));
|
|
3375
3416
|
let ownFieldCount = 0;
|
|
3376
3417
|
for (const k in data) {
|
|
@@ -3379,26 +3420,23 @@ function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
|
3379
3420
|
if (ownFieldCount > TYPED_DATA_LIMITS.maxEncodedValues) {
|
|
3380
3421
|
throw new Error("typed data object contains too many fields");
|
|
3381
3422
|
}
|
|
3382
|
-
if (!expected.has(k))
|
|
3423
|
+
if (!expected.has(k)) {
|
|
3424
|
+
throw new Error(`unknown field in ${inputPreview(primary)}: ${inputPreview(k)}`);
|
|
3425
|
+
}
|
|
3383
3426
|
}
|
|
3384
3427
|
const parts = [typeHash(primary, types)];
|
|
3385
3428
|
for (const f of fields) {
|
|
3386
3429
|
if (!Object.prototype.hasOwnProperty.call(data, f.name)) {
|
|
3387
|
-
throw new Error(`missing field ${primary}.${f.name}`);
|
|
3430
|
+
throw new Error(`missing field ${inputPreview(primary)}.${inputPreview(f.name)}`);
|
|
3388
3431
|
}
|
|
3389
3432
|
parts.push(encodeFieldWithBudget(f.type, data[f.name], types, budget, depth + 1));
|
|
3390
3433
|
}
|
|
3391
3434
|
return shake256Digest(concatBytesArr(parts), DIGEST_LEN);
|
|
3392
3435
|
}
|
|
3393
|
-
function hashStruct(primary, data, types) {
|
|
3436
|
+
function hashStruct(primary, data, types, schemeVersion) {
|
|
3394
3437
|
validateTypeMap(types);
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
data,
|
|
3398
|
-
types,
|
|
3399
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3400
|
-
0
|
|
3401
|
-
);
|
|
3438
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3439
|
+
return hashStructWithBudget(primary, data, types, newBudget(scheme), 0);
|
|
3402
3440
|
}
|
|
3403
3441
|
var RESERVED_DOMAIN_FIELDS = {
|
|
3404
3442
|
name: "string",
|
|
@@ -3415,11 +3453,13 @@ function validateDomainTypes(types) {
|
|
|
3415
3453
|
const expected = RESERVED_DOMAIN_FIELDS[f.name];
|
|
3416
3454
|
if (!expected) {
|
|
3417
3455
|
throw new Error(
|
|
3418
|
-
`QRLDomain field "${f.name}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3456
|
+
`QRLDomain field "${inputPreview(f.name)}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
3419
3457
|
);
|
|
3420
3458
|
}
|
|
3421
3459
|
if (f.type !== expected) {
|
|
3422
|
-
throw new Error(
|
|
3460
|
+
throw new Error(
|
|
3461
|
+
`QRLDomain field "${inputPreview(f.name)}" must be type "${expected}", got "${inputPreview(f.type)}"`
|
|
3462
|
+
);
|
|
3423
3463
|
}
|
|
3424
3464
|
if (f.name === "name") hasName = true;
|
|
3425
3465
|
}
|
|
@@ -3431,10 +3471,10 @@ function validatePayloadReachability(primary, types) {
|
|
|
3431
3471
|
for (const t of collectDependencies(root, types)) reachable.add(t);
|
|
3432
3472
|
}
|
|
3433
3473
|
for (const k of Object.keys(types)) {
|
|
3434
|
-
if (!reachable.has(k)) throw new Error(`unused referenced type: ${k}`);
|
|
3474
|
+
if (!reachable.has(k)) throw new Error(`unused referenced type: ${inputPreview(k)}`);
|
|
3435
3475
|
}
|
|
3436
3476
|
}
|
|
3437
|
-
function
|
|
3477
|
+
function parseAndValidatePayload(payload) {
|
|
3438
3478
|
const parsed = parsePayload(payload);
|
|
3439
3479
|
validateTypeMap(parsed.types);
|
|
3440
3480
|
assertIdentifier(parsed.primaryType, "primary type");
|
|
@@ -3443,7 +3483,26 @@ function computeTypedDataDigest(payload) {
|
|
|
3443
3483
|
}
|
|
3444
3484
|
validateDomainTypes(parsed.types);
|
|
3445
3485
|
validatePayloadReachability(parsed.primaryType, parsed.types);
|
|
3446
|
-
|
|
3486
|
+
return parsed;
|
|
3487
|
+
}
|
|
3488
|
+
function schemeVersionForTypes(types) {
|
|
3489
|
+
for (const fields of Object.values(types)) {
|
|
3490
|
+
if (fields.some((f) => baseTypeName(f.type) === "address")) {
|
|
3491
|
+
return SCHEME_VERSION_TYPED_V2;
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
return SCHEME_VERSION_TYPED;
|
|
3495
|
+
}
|
|
3496
|
+
function typedDataSchemeVersion(payload) {
|
|
3497
|
+
return schemeVersionForTypes(parseAndValidatePayload(payload).types);
|
|
3498
|
+
}
|
|
3499
|
+
function typedDataSchemeTag(schemeVersion) {
|
|
3500
|
+
return SCHEME_TAGS[schemeVersion];
|
|
3501
|
+
}
|
|
3502
|
+
function computeTypedDataDigest(payload) {
|
|
3503
|
+
const parsed = parseAndValidatePayload(payload);
|
|
3504
|
+
const schemeVersion = schemeVersionForTypes(parsed.types);
|
|
3505
|
+
const budget = newBudget(schemeVersion);
|
|
3447
3506
|
const domainHash = hashStructWithBudget("QRLDomain", parsed.domain, parsed.types, budget, 0);
|
|
3448
3507
|
const messageHash = hashStructWithBudget(
|
|
3449
3508
|
parsed.primaryType,
|
|
@@ -3452,7 +3511,10 @@ function computeTypedDataDigest(payload) {
|
|
|
3452
3511
|
budget,
|
|
3453
3512
|
0
|
|
3454
3513
|
);
|
|
3455
|
-
return shake256Digest(
|
|
3514
|
+
return shake256Digest(
|
|
3515
|
+
concatBytes(SCHEME_TAGS[schemeVersion], domainHash, messageHash),
|
|
3516
|
+
DIGEST_LEN
|
|
3517
|
+
);
|
|
3456
3518
|
}
|
|
3457
3519
|
|
|
3458
3520
|
// src/utils/platform.ts
|
|
@@ -3644,9 +3706,8 @@ function validateRestrictedRequest(method, params, authorizedAccounts, currentCh
|
|
|
3644
3706
|
if (params[0] !== authorizedAccount) {
|
|
3645
3707
|
throw new Error("qrl_signTypedData signer is not the authorized account");
|
|
3646
3708
|
}
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
);
|
|
3709
|
+
computeTypedDataDigest(params[1]);
|
|
3710
|
+
return void 0;
|
|
3650
3711
|
}
|
|
3651
3712
|
function requiresAuthorizedAccount(method) {
|
|
3652
3713
|
return method === "qrl_sendTransaction" || method === "qrl_signTransaction" || method === "qrl_signMessage" || method === "qrl_signTypedData";
|
|
@@ -4292,9 +4353,13 @@ function verifyMessageBytes(signature, publicKey, messageBytes) {
|
|
|
4292
4353
|
const digest = computeMessageDigest(messageBytes);
|
|
4293
4354
|
return mldsaVerify(signature, digest, publicKey, SCHEME_TAG_MSG);
|
|
4294
4355
|
}
|
|
4295
|
-
function verifyTypedDataBytes(signature, publicKey, payload) {
|
|
4356
|
+
function verifyTypedDataBytes(signature, publicKey, payload, claimedSchemeVersion) {
|
|
4357
|
+
const schemeVersion = typedDataSchemeVersion(payload);
|
|
4358
|
+
if (claimedSchemeVersion !== void 0 && claimedSchemeVersion !== schemeVersion) {
|
|
4359
|
+
return false;
|
|
4360
|
+
}
|
|
4296
4361
|
const digest = computeTypedDataDigest(payload);
|
|
4297
|
-
return mldsaVerify(signature, digest, publicKey,
|
|
4362
|
+
return mldsaVerify(signature, digest, publicKey, typedDataSchemeTag(schemeVersion));
|
|
4298
4363
|
}
|
|
4299
4364
|
function verifyMessageSignature({
|
|
4300
4365
|
signature,
|
|
@@ -4314,13 +4379,14 @@ function verifyMessageSignature({
|
|
|
4314
4379
|
function verifyTypedDataSignature({
|
|
4315
4380
|
signature,
|
|
4316
4381
|
publicKey,
|
|
4317
|
-
payload
|
|
4382
|
+
payload,
|
|
4383
|
+
schemeVersion
|
|
4318
4384
|
}) {
|
|
4319
4385
|
try {
|
|
4320
4386
|
const sig = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4321
4387
|
const pk = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4322
4388
|
if (!sig || !pk) return false;
|
|
4323
|
-
return verifyTypedDataBytes(sig, pk, payload);
|
|
4389
|
+
return verifyTypedDataBytes(sig, pk, payload, schemeVersion);
|
|
4324
4390
|
} catch {
|
|
4325
4391
|
return false;
|
|
4326
4392
|
}
|
|
@@ -4358,7 +4424,8 @@ function verifyTypedDataForSigner({
|
|
|
4358
4424
|
descriptor,
|
|
4359
4425
|
signature,
|
|
4360
4426
|
publicKey,
|
|
4361
|
-
payload
|
|
4427
|
+
payload,
|
|
4428
|
+
schemeVersion
|
|
4362
4429
|
}) {
|
|
4363
4430
|
try {
|
|
4364
4431
|
if (descriptor === void 0) return false;
|
|
@@ -4366,7 +4433,7 @@ function verifyTypedDataForSigner({
|
|
|
4366
4433
|
const publicKeyBytes = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4367
4434
|
const signatureBytes = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4368
4435
|
if (!descriptorBytes || !publicKeyBytes || !signatureBytes) return false;
|
|
4369
|
-
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload);
|
|
4436
|
+
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload, schemeVersion);
|
|
4370
4437
|
} catch {
|
|
4371
4438
|
return false;
|
|
4372
4439
|
}
|
|
@@ -4407,8 +4474,10 @@ export {
|
|
|
4407
4474
|
RESTRICTED_METHODS,
|
|
4408
4475
|
SCHEME_TAG_MSG,
|
|
4409
4476
|
SCHEME_TAG_TYPED,
|
|
4477
|
+
SCHEME_TAG_TYPED_V2,
|
|
4410
4478
|
SCHEME_VERSION_MSG,
|
|
4411
4479
|
SCHEME_VERSION_TYPED,
|
|
4480
|
+
SCHEME_VERSION_TYPED_V2,
|
|
4412
4481
|
SocketClient,
|
|
4413
4482
|
TYPED_DATA_LIMITS,
|
|
4414
4483
|
UNRESTRICTED_METHODS,
|
|
@@ -4442,6 +4511,8 @@ export {
|
|
|
4442
4511
|
parseConnectionURI,
|
|
4443
4512
|
qip55AddressFromBytes,
|
|
4444
4513
|
typeHash,
|
|
4514
|
+
typedDataSchemeTag,
|
|
4515
|
+
typedDataSchemeVersion,
|
|
4445
4516
|
verifyMessage,
|
|
4446
4517
|
verifyMessageForSigner,
|
|
4447
4518
|
verifyMessageSignature,
|