@callcium/sdk 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,8 @@
1
- import { hexToBytes, bytesToHex, readU16, readU32, bigintToHex } from "./bytes";
1
+ import { hexToBytes, bytesToHex, readU16, readU32, bigintToHex, toAddress } from "./bytes";
2
2
  import { loadWord, readPointer } from "./calldata-reader";
3
- import {
4
- PolicyFormat as PF,
5
- Scope,
6
- MAX_CONTEXT_PROPERTY_ID,
7
- Limits,
8
- TypeCode,
9
- classifyTypeCode,
10
- lookupContextProperty,
11
- } from "./constants";
3
+ import { PolicyFormat as PF, Scope, MAX_CONTEXT_PROPERTY_ID, Op, TypeCode, lookupContextProperty } from "./constants";
12
4
  import { CallciumError, PolicyViolationError } from "./errors";
13
- import { applyOperator, toBigInt, isLengthOp, isLengthValidType, canonicalize } from "./operators";
5
+ import { applyOperator, toBigInt, isLengthOp, isLengthValidType, canonicalize, classifyTypeCode } from "./operators";
14
6
  import { decodePolicy } from "./policy-coder";
15
7
 
16
8
  import type { ReadResult } from "./calldata-reader";
@@ -29,20 +21,17 @@ import type {
29
21
  // Helpers
30
22
  ///////////////////////////////////////////////////////////////////////////
31
23
 
32
- /** Violations that abort evaluation instead of failing only their group (spec §9.3). */
33
- const ABORT_VIOLATION_CODES: ReadonlySet<ViolationCode> = new Set<ViolationCode>([
24
+ /** Violations that abort evaluation instead of failing only their group. */
25
+ const abortViolationCodes: ReadonlySet<ViolationCode> = new Set<ViolationCode>([
34
26
  "CALLDATA_OUT_OF_BOUNDS",
35
27
  "ARRAY_INDEX_OUT_OF_BOUNDS",
36
28
  "NON_CANONICAL_VALUE",
37
29
  "QUANTIFIER_LIMIT_EXCEEDED",
38
30
  ]);
39
31
 
40
- /** Convert a hex address to a 256-bit bigint (zero-padded to 32 bytes). */
32
+ /** Validate a hex address and read it as a 256-bit bigint. */
41
33
  function addressToBigInt(hex: string): bigint {
42
- const bytes = hexToBytes(hex);
43
- const padded = new Uint8Array(32);
44
- padded.set(bytes, 32 - bytes.length);
45
- return toBigInt(padded, 0);
34
+ return BigInt(toAddress(hex));
46
35
  }
47
36
 
48
37
  ///////////////////////////////////////////////////////////////////////////
@@ -99,8 +88,8 @@ function check(policy: Hex, callData: Hex, context?: Context): EnforceResult {
99
88
  const violation = evaluateRule(rule, policyBytes, callDataBytes, baseOffset, groupIndex, ruleIndex, context);
100
89
  if (violation !== null) {
101
90
  allViolations.push(violation);
102
- if (ABORT_VIOLATION_CODES.has(violation.code)) {
103
- // Abort violations reject the policy outright; later groups are not consulted (spec §9.3).
91
+ if (abortViolationCodes.has(violation.code)) {
92
+ // Abort violations reject the policy outright; later groups are not consulted.
104
93
  return { ok: false, violations: allViolations };
105
94
  }
106
95
  groupFailed = true;
@@ -172,9 +161,21 @@ function evaluateRule(
172
161
  groupIndex,
173
162
  ruleIndex,
174
163
  rule.path.value,
164
+ context,
175
165
  );
176
166
  }
177
167
 
168
+ /**
169
+ * Resolve an EQ_CTX operand word to the referenced context property's value as a raw word.
170
+ * Returns the missing property's type code when the context does not supply it.
171
+ */
172
+ function resolveContextOperand(operandData: Uint8Array, context?: Context): bigint | { ctxTypeCode: number } {
173
+ const propertyInfo = lookupContextProperty(Number(toBigInt(operandData, 0)));
174
+ const value = context?.[propertyInfo.contextKey];
175
+ if (value === undefined) return { ctxTypeCode: propertyInfo.typeCode };
176
+ return typeof value === "string" ? addressToBigInt(value) : value;
177
+ }
178
+
178
179
  ///////////////////////////////////////////////////////////////////////////
179
180
  // Hint chain resolution
180
181
  ///////////////////////////////////////////////////////////////////////////
@@ -247,9 +248,10 @@ function chainResolve(
247
248
  ///////////////////////////////////////////////////////////////////////////
248
249
 
249
250
  type TargetResult =
250
- | { passed: boolean; value: bigint }
251
+ | { passed: boolean; value: bigint; ctxOperand?: bigint }
251
252
  | { error: NavigationViolationCode }
252
- | { error: "NON_CANONICAL_VALUE"; value: bigint };
253
+ | { error: "NON_CANONICAL_VALUE"; value: bigint }
254
+ | { error: "MISSING_CONTEXT"; ctxTypeCode: number };
253
255
 
254
256
  /**
255
257
  * Load the value at a resolved target offset and apply the operator.
@@ -264,13 +266,14 @@ function evalTarget(
264
266
  block: TargetBlock,
265
267
  opCode: number,
266
268
  operandData: Uint8Array,
269
+ context?: Context,
267
270
  ): TargetResult {
268
271
  const typeCode = block.typeCode;
269
272
 
270
273
  // A dynamic target's chain ends at its payload, so the word there is the declared length.
271
274
  if (isLengthValidType(typeCode)) {
272
275
  if (!isLengthOp(opCode)) {
273
- throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Value operator on a target without a scalar word.");
276
+ throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Value operator on a target without a scalar word");
274
277
  }
275
278
  const length = readPointer(callData, target);
276
279
  if (typeof length !== "number") return { error: length.code };
@@ -285,7 +288,7 @@ function evalTarget(
285
288
  }
286
289
 
287
290
  if (classifyTypeCode(typeCode).typeClass !== "elementary") {
288
- throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Operator target does not carry a scalar word.");
291
+ throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Operator target does not carry a scalar word");
289
292
  }
290
293
  const word = loadWord(callData, target);
291
294
  if (!(word instanceof Uint8Array)) return { error: word.code };
@@ -295,6 +298,13 @@ function evalTarget(
295
298
  return { error: "NON_CANONICAL_VALUE", value };
296
299
  }
297
300
 
301
+ if ((opCode & ~Op.NOT) === Op.EQ_CTX) {
302
+ const operand = resolveContextOperand(operandData, context);
303
+ if (typeof operand !== "bigint") return { error: "MISSING_CONTEXT", ctxTypeCode: operand.ctxTypeCode };
304
+ const passed = (value === operand) !== ((opCode & Op.NOT) !== 0);
305
+ return { passed, value, ctxOperand: operand };
306
+ }
307
+
298
308
  const passed = applyOperator(opCode, value, 32, operandData, typeCode);
299
309
  return { passed, value };
300
310
  }
@@ -331,6 +341,18 @@ function navigationViolation(frame: RuleFrame, code: NavigationViolationCode, el
331
341
  /** Convert a failed or erroring target result into its violation. Passing results map to null. */
332
342
  function targetViolation(frame: RuleFrame, result: TargetResult, elementIndex?: number): Violation | null {
333
343
  if ("error" in result) {
344
+ if (result.error === "MISSING_CONTEXT") {
345
+ return {
346
+ group: frame.group,
347
+ rule: frame.rule,
348
+ code: result.error,
349
+ scope: Scope.CALLDATA,
350
+ path: frame.path,
351
+ opCode: frame.opCode,
352
+ operandData: bytesToHex(frame.operandData),
353
+ typeCode: result.ctxTypeCode,
354
+ };
355
+ }
334
356
  if (result.error === "NON_CANONICAL_VALUE") {
335
357
  return {
336
358
  group: frame.group,
@@ -359,6 +381,7 @@ function targetViolation(frame: RuleFrame, result: TargetResult, elementIndex?:
359
381
  operandData: bytesToHex(frame.operandData),
360
382
  typeCode: frame.typeCode,
361
383
  resolvedValue: bigintToHex(result.value),
384
+ ...(result.ctxOperand !== undefined && { resolvedOperand: bigintToHex(result.ctxOperand) }),
362
385
  ...(elementIndex !== undefined && { elementIndex }),
363
386
  };
364
387
  }
@@ -373,6 +396,7 @@ function evaluateCalldataRule(
373
396
  groupIndex: number,
374
397
  ruleIndex: number,
375
398
  pathHex: Hex,
399
+ context?: Context,
376
400
  ): Violation | null {
377
401
  const header = hint[0]!;
378
402
  const hopCount = header & PF.HINT_HOP_COUNT_MASK;
@@ -392,6 +416,7 @@ function evaluateCalldataRule(
392
416
  groupIndex,
393
417
  ruleIndex,
394
418
  pathHex,
419
+ context,
395
420
  );
396
421
  }
397
422
 
@@ -408,7 +433,10 @@ function evaluateCalldataRule(
408
433
  const base = chainResolve(hint, callDataBytes, baseOffset, hopsOffset, hopCount);
409
434
  if (typeof base !== "number") return navigationViolation(frame, base.code);
410
435
 
411
- return targetViolation(frame, evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData));
436
+ return targetViolation(
437
+ frame,
438
+ evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, context),
439
+ );
412
440
  }
413
441
 
414
442
  ///////////////////////////////////////////////////////////////////////////
@@ -428,6 +456,7 @@ function evaluateQuantified(
428
456
  groupIndex: number,
429
457
  ruleIndex: number,
430
458
  pathHex: Hex,
459
+ context?: Context,
431
460
  ): Violation | null {
432
461
  const frameOffset = hopsOffset + hopCount * PF.HINT_HOP_SIZE;
433
462
  const suffixHeaderOffset = frameOffset + PF.HINT_FRAME_PREFIX_SIZE;
@@ -459,7 +488,7 @@ function evaluateQuantified(
459
488
  elems += 32;
460
489
  }
461
490
 
462
- if (count > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) {
491
+ if (count > PF.MAX_QUANTIFIED_ARRAY_LENGTH) {
463
492
  return {
464
493
  group: groupIndex,
465
494
  rule: ruleIndex,
@@ -486,13 +515,15 @@ function evaluateQuantified(
486
515
  const elemStride = (meta & PF.HINT_META_STRIDE_MASK) * 32;
487
516
  const elemIsDynamic = (meta & PF.HINT_META_ELEM_DYNAMIC) !== 0;
488
517
 
518
+ // A reference operand resolves identically for every element; any element's result carries it.
519
+ let ctxOperand: bigint | undefined;
489
520
  for (let elemIndex = 0; elemIndex < count; elemIndex++) {
490
521
  const slot = elems + elemIndex * elemStride;
491
522
  let elem: number = slot;
492
523
  if (elemIsDynamic) {
493
524
  const followed = follow(callDataBytes, elems, slot);
494
525
  // Navigation failures abort under every quantifier: calldata the enforcer cannot read is
495
- // not an element that merely fails the operator (spec §9.3).
526
+ // not an element that merely fails the operator.
496
527
  if (typeof followed !== "number") return navigationViolation(frame, followed.code, elemIndex);
497
528
  elem = followed;
498
529
  }
@@ -504,10 +535,11 @@ function evaluateQuantified(
504
535
  base = chainedElem;
505
536
  }
506
537
 
507
- const applied = evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData);
538
+ const applied = evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, context);
508
539
  if ("error" in applied) {
509
- // An abort-effect violation ends evaluation whatever the quantifier; a later element cannot
510
- // rescue calldata the enforcer cannot read (spec §9.3).
540
+ // Error results end the rule whatever the quantifier: a later element cannot rescue
541
+ // calldata the enforcer cannot read (an abort effect), and a missing context
542
+ // property is missing for every element alike (group-local).
511
543
  return targetViolation(frame, applied, elemIndex);
512
544
  }
513
545
 
@@ -517,6 +549,7 @@ function evaluateQuantified(
517
549
  } else if (isUniversal) {
518
550
  return targetViolation(frame, applied, elemIndex);
519
551
  }
552
+ ctxOperand = applied.ctxOperand;
520
553
  }
521
554
 
522
555
  if (isUniversal) return null;
@@ -530,6 +563,7 @@ function evaluateQuantified(
530
563
  opCode,
531
564
  operandData: bytesToHex(operandData),
532
565
  typeCode: block.typeCode,
566
+ ...(ctxOperand !== undefined && { resolvedOperand: bigintToHex(ctxOperand) }),
533
567
  };
534
568
  }
535
569
 
@@ -556,8 +590,8 @@ function evaluateContextRule(
556
590
  );
557
591
  }
558
592
 
559
- const propInfo = lookupContextProperty(propertyId);
560
- const contextValue = context?.[propInfo.contextKey];
593
+ const propertyInfo = lookupContextProperty(propertyId);
594
+ const contextValue = context?.[propertyInfo.contextKey];
561
595
 
562
596
  if (contextValue === undefined) {
563
597
  return {
@@ -566,7 +600,7 @@ function evaluateContextRule(
566
600
  code: "MISSING_CONTEXT",
567
601
  scope: Scope.CONTEXT,
568
602
  path: pathHex,
569
- typeCode: propInfo.typeCode,
603
+ typeCode: propertyInfo.typeCode,
570
604
  };
571
605
  }
572
606
 
@@ -577,7 +611,27 @@ function evaluateContextRule(
577
611
  value = contextValue;
578
612
  }
579
613
 
580
- const result = applyOperator(opCode, value, 32, operandData, propInfo.typeCode);
614
+ let result: boolean;
615
+ let ctxOperand: bigint | undefined;
616
+ if ((opCode & ~Op.NOT) === Op.EQ_CTX) {
617
+ const operand = resolveContextOperand(operandData, context);
618
+ if (typeof operand !== "bigint") {
619
+ return {
620
+ group: groupIndex,
621
+ rule: ruleIndex,
622
+ code: "MISSING_CONTEXT",
623
+ scope: Scope.CONTEXT,
624
+ path: pathHex,
625
+ opCode,
626
+ operandData: bytesToHex(operandData),
627
+ typeCode: operand.ctxTypeCode,
628
+ };
629
+ }
630
+ result = (value === operand) !== ((opCode & Op.NOT) !== 0);
631
+ ctxOperand = operand;
632
+ } else {
633
+ result = applyOperator(opCode, value, 32, operandData, propertyInfo.typeCode);
634
+ }
581
635
 
582
636
  if (!result) {
583
637
  return {
@@ -588,8 +642,9 @@ function evaluateContextRule(
588
642
  path: pathHex,
589
643
  opCode,
590
644
  operandData: bytesToHex(operandData),
591
- typeCode: propInfo.typeCode,
645
+ typeCode: propertyInfo.typeCode,
592
646
  resolvedValue: bigintToHex(value),
647
+ ...(ctxOperand !== undefined && { resolvedOperand: bigintToHex(ctxOperand) }),
593
648
  };
594
649
  }
595
650
 
@@ -1,31 +1,28 @@
1
1
  import { bigintToHex, bytesToHex, hexToBytes } from "./bytes";
2
- import {
3
- classifyTypeCode,
4
- isQuantifier,
5
- isValidOperatorData,
6
- Limits,
7
- lookupContextProperty,
8
- MAX_CONTEXT_PROPERTY_ID,
9
- Op,
10
- Quantifier,
11
- Scope,
12
- TypeCode,
13
- } from "./constants";
2
+ import { PolicyFormat, lookupContextProperty, MAX_CONTEXT_PROPERTY_ID, Op, Scope, TypeCode } from "./constants";
14
3
  import { Descriptor, type TypeInfo } from "./descriptor";
15
4
  import { decodeDescriptor } from "./descriptor-coder";
16
5
  import { CallciumError, type CallciumErrorCode } from "./errors";
17
- import { canonicalize, isLeftAligned, isSigned, isLengthOp, isLengthValidType } from "./operators";
18
- import { parsePathSteps } from "./policy-coder";
6
+ import {
7
+ canonicalize,
8
+ isLeftAligned,
9
+ isSigned,
10
+ isLengthOp,
11
+ isLengthValidType,
12
+ classifyTypeCode,
13
+ isValidOperatorData,
14
+ } from "./operators";
15
+ import { Quantifier, isQuantifier, parsePathSteps } from "./path";
19
16
  import * as ValidationIssue from "./validation-issue";
20
17
 
21
- import type { Constraint, Hex, Issue, PolicyData } from "./types";
18
+ import type { Constraint, Hex, Issue, IssueCode, PolicyData } from "./types";
22
19
 
23
20
  ///////////////////////////////////////////////////////////////////////////
24
21
  // Internal types
25
22
  ///////////////////////////////////////////////////////////////////////////
26
23
 
27
24
  /** Codes a path walk raises when the path does not navigate the descriptor (PV-1). */
28
- const NAVIGABILITY_CODES: ReadonlySet<CallciumErrorCode> = new Set<CallciumErrorCode>([
25
+ const navigabilityCodes: ReadonlySet<CallciumErrorCode> = new Set<CallciumErrorCode>([
29
26
  "EMPTY_PATH",
30
27
  "PARAM_INDEX_OUT_OF_BOUNDS",
31
28
  "TUPLE_FIELD_OUT_OF_BOUNDS",
@@ -139,7 +136,7 @@ function getDomainLimits(typeCode: number): { min: bigint; max: bigint } {
139
136
  // Operator classification
140
137
  ///////////////////////////////////////////////////////////////////////////
141
138
 
142
- /** True for EQ, GT, LT, GTE, LTE, BETWEEN, IN, BITMASK_*. */
139
+ /** True for EQ, GT, LT, GTE, LTE, BETWEEN, IN, EQ_CTX, BITMASK_*. */
143
140
  function isValueOp(opBase: number): boolean {
144
141
  return opBase <= Op.BITMASK_NONE;
145
142
  }
@@ -159,7 +156,7 @@ function isBitmaskOp(opBase: number): boolean {
159
156
  ///////////////////////////////////////////////////////////////////////////
160
157
 
161
158
  /** Return the incompatibility reason, or null if the operator is allowed. */
162
- function getIncompat(opBase: number, typeInfo: TypeInfo): { code: string; message: string } | null {
159
+ function getIncompat(opBase: number, typeInfo: TypeInfo): { code: IssueCode; message: string } | null {
163
160
  const { typeCode, isDynamic, staticSize } = typeInfo;
164
161
  if (isValueOp(opBase)) {
165
162
  if (isDynamic || staticSize !== 32) {
@@ -178,6 +175,14 @@ function getIncompat(opBase: number, typeInfo: TypeInfo): { code: string; messag
178
175
  if (opBase === Op.IN && typeCode === TypeCode.BOOL) {
179
176
  return { code: "IN_ON_BOOL", message: "IN operator used on boolean type" };
180
177
  }
178
+ // Context properties are addresses or uint256, so only those target classes can match.
179
+ if (
180
+ opBase === Op.EQ_CTX &&
181
+ typeCode !== TypeCode.ADDRESS &&
182
+ !(typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX)
183
+ ) {
184
+ return { code: "CONTEXT_TYPE_MISMATCH", message: "Context reference operator used on incompatible type" };
185
+ }
181
186
  return null;
182
187
  }
183
188
  if (isLengthOp(opBase)) {
@@ -890,7 +895,35 @@ function validateConstraint(
890
895
  continue;
891
896
  }
892
897
 
893
- if (isValueOp(base)) {
898
+ if (base === Op.EQ_CTX) {
899
+ // The operand names a context property, not a value: nothing to fold into a domain.
900
+ const ctxOperand = readValue(opHex);
901
+ if (ctxOperand > BigInt(MAX_CONTEXT_PROPERTY_ID)) {
902
+ issues.push(
903
+ ValidationIssue.unknownContextProperty(
904
+ groupIndex,
905
+ constraintIndex,
906
+ bigintToHex(ctxOperand),
907
+ bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID)),
908
+ ),
909
+ );
910
+ } else {
911
+ // Compatibility has already narrowed the target to an address or unsigned type,
912
+ // so the pairing reduces to whether both sides are addresses.
913
+ const propertyIsAddress = lookupContextProperty(Number(ctxOperand)).typeCode === TypeCode.ADDRESS;
914
+ const targetIsAddress = ctx.typeInfo.typeCode === TypeCode.ADDRESS;
915
+ if (propertyIsAddress !== targetIsAddress) {
916
+ issues.push(
917
+ ValidationIssue.contextTypeMismatch(
918
+ groupIndex,
919
+ constraintIndex,
920
+ bigintToHex(ctxOperand),
921
+ bigintToHex(BigInt(ctx.typeInfo.typeCode)),
922
+ ),
923
+ );
924
+ }
925
+ }
926
+ } else if (isValueOp(base)) {
894
927
  // A non-canonical operand can never equal a canonicalized runtime value (PC-1, spec
895
928
  // section 4.5), so the rule is unsatisfiable or vacuous; skip analyzing the garbage
896
929
  // word. Scoped to the left-aligned types: numeric, address, and bool operands are
@@ -1000,13 +1033,13 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1000
1033
  let typeInfo: TypeInfo;
1001
1034
  if (constraint.scope === Scope.CALLDATA) {
1002
1035
  // Compatibility warnings against the limits the spec fixes (PWF-17, PV-7).
1003
- if (steps.length > Limits.MAX_PATH_DEPTH) {
1036
+ if (steps.length > PolicyFormat.MAX_PATH_DEPTH) {
1004
1037
  issues.push(
1005
1038
  ValidationIssue.pathDepthExceeded(
1006
1039
  groupIndex,
1007
1040
  constraintIndex,
1008
1041
  bigintToHex(BigInt(steps.length)),
1009
- bigintToHex(BigInt(Limits.MAX_PATH_DEPTH)),
1042
+ bigintToHex(BigInt(PolicyFormat.MAX_PATH_DEPTH)),
1010
1043
  ),
1011
1044
  );
1012
1045
  }
@@ -1015,7 +1048,7 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1015
1048
  walk = Descriptor.walkPath(descBytes, steps);
1016
1049
  } catch (error) {
1017
1050
  // Navigability faults become issues; anything else propagates.
1018
- if (error instanceof CallciumError && NAVIGABILITY_CODES.has(error.code)) {
1051
+ if (error instanceof CallciumError && navigabilityCodes.has(error.code)) {
1019
1052
  issues.push(ValidationIssue.unnavigablePath(groupIndex, constraintIndex));
1020
1053
  continue;
1021
1054
  }
@@ -1026,13 +1059,13 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1026
1059
  issues.push(ValidationIssue.nestedQuantifier(groupIndex, constraintIndex));
1027
1060
  continue;
1028
1061
  }
1029
- if (walk.quantifiedStaticLength > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) {
1062
+ if (walk.quantifiedStaticLength > PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH) {
1030
1063
  issues.push(
1031
1064
  ValidationIssue.quantifierOverStaticLimit(
1032
1065
  groupIndex,
1033
1066
  constraintIndex,
1034
1067
  bigintToHex(BigInt(walk.quantifiedStaticLength)),
1035
- bigintToHex(BigInt(Limits.MAX_QUANTIFIED_ARRAY_LENGTH)),
1068
+ bigintToHex(BigInt(PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH)),
1036
1069
  ),
1037
1070
  );
1038
1071
  }
@@ -1067,7 +1100,7 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1067
1100
  if (
1068
1101
  constraint.scope === Scope.CALLDATA &&
1069
1102
  constraint.hint !== undefined &&
1070
- ctx.steps.length <= Limits.MAX_PATH_DEPTH
1103
+ ctx.steps.length <= PolicyFormat.MAX_PATH_DEPTH
1071
1104
  ) {
1072
1105
  const compiled = bytesToHex(Descriptor.compileHint(descBytes, ctx.steps));
1073
1106
  if (compiled !== constraint.hint.toLowerCase()) {
package/src/type-desc.ts CHANGED
@@ -48,7 +48,7 @@ export function function_(): Uint8Array {
48
48
  */
49
49
  export function uintN(bits: number): Uint8Array {
50
50
  if (bits < 8 || bits > 256 || bits % 8 !== 0) {
51
- throw new CallciumError("UNKNOWN_TYPE", `Invalid uintN bits: ${bits}. Must be 8–256 in steps of 8.`);
51
+ throw new CallciumError("UNKNOWN_TYPE", `Invalid uintN bits: ${bits}. Must be 8–256 in steps of 8`);
52
52
  }
53
53
  return new Uint8Array([TypeCode.UINT_MIN + (bits / 8 - 1)]);
54
54
  }
@@ -60,7 +60,7 @@ export function uintN(bits: number): Uint8Array {
60
60
  */
61
61
  export function intN(bits: number): Uint8Array {
62
62
  if (bits < 8 || bits > 256 || bits % 8 !== 0) {
63
- throw new CallciumError("UNKNOWN_TYPE", `Invalid intN bits: ${bits}. Must be 8–256 in steps of 8.`);
63
+ throw new CallciumError("UNKNOWN_TYPE", `Invalid intN bits: ${bits}. Must be 8–256 in steps of 8`);
64
64
  }
65
65
  return new Uint8Array([TypeCode.INT_MIN + (bits / 8 - 1)]);
66
66
  }
@@ -82,7 +82,7 @@ export function string_(): Uint8Array {
82
82
  */
83
83
  export function bytesN(n: number): Uint8Array {
84
84
  if (n < 1 || n > 32) {
85
- throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${n}. Must be 1–32.`);
85
+ throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${n}. Must be 1–32`);
86
86
  }
87
87
  return new Uint8Array([TypeCode.FIXED_BYTES_MIN + (n - 1)]);
88
88
  }
@@ -108,7 +108,7 @@ export function array(elemDesc: Uint8Array, length?: number): Uint8Array {
108
108
  if (nodeLength > DF.MAX_NODE_LENGTH) {
109
109
  throw new CallciumError(
110
110
  "NODE_LENGTH_TOO_LARGE",
111
- `Dynamic array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}.`,
111
+ `Dynamic array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}`,
112
112
  );
113
113
  }
114
114
  const meta24 = (0 << DF.META_STATIC_WORDS_SHIFT) | nodeLength;
@@ -123,14 +123,14 @@ export function array(elemDesc: Uint8Array, length?: number): Uint8Array {
123
123
  if (length <= 0 || length > DF.MAX_STATIC_ARRAY_LENGTH) {
124
124
  throw new CallciumError(
125
125
  "INVALID_ARRAY_LENGTH",
126
- `Static array length ${length} out of range. Must be 1–${DF.MAX_STATIC_ARRAY_LENGTH}.`,
126
+ `Static array length ${length} out of range. Must be 1–${DF.MAX_STATIC_ARRAY_LENGTH}`,
127
127
  );
128
128
  }
129
129
  const nodeLength = DF.ARRAY_HEADER_SIZE + elemDesc.length + DF.ARRAY_LENGTH_SIZE;
130
130
  if (nodeLength > DF.MAX_NODE_LENGTH) {
131
131
  throw new CallciumError(
132
132
  "NODE_LENGTH_TOO_LARGE",
133
- `Static array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}.`,
133
+ `Static array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}`,
134
134
  );
135
135
  }
136
136
  const elemStaticWords = extractStaticWords(elemDesc);
@@ -138,7 +138,7 @@ export function array(elemDesc: Uint8Array, length?: number): Uint8Array {
138
138
  if (staticWords > DF.MAX_STATIC_WORDS) {
139
139
  throw new CallciumError(
140
140
  "STATIC_WORDS_TOO_LARGE",
141
- `Static array static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}.`,
141
+ `Static array static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}`,
142
142
  );
143
143
  }
144
144
  const meta24 = (staticWords << DF.META_STATIC_WORDS_SHIFT) | nodeLength;
@@ -162,7 +162,7 @@ export function tuple(fieldDescs: Uint8Array[]): Uint8Array {
162
162
  if (fieldDescs.length === 0 || fieldDescs.length > DF.MAX_TUPLE_FIELDS) {
163
163
  throw new CallciumError(
164
164
  "INVALID_TUPLE_FIELD_COUNT",
165
- `Tuple field count ${fieldDescs.length} out of range. Must be 1–${DF.MAX_TUPLE_FIELDS}.`,
165
+ `Tuple field count ${fieldDescs.length} out of range. Must be 1–${DF.MAX_TUPLE_FIELDS}`,
166
166
  );
167
167
  }
168
168
  const totalFieldBytes = fieldDescs.reduce((sum, f) => sum + f.length, 0);
@@ -170,7 +170,7 @@ export function tuple(fieldDescs: Uint8Array[]): Uint8Array {
170
170
  if (nodeLength > DF.MAX_NODE_LENGTH) {
171
171
  throw new CallciumError(
172
172
  "NODE_LENGTH_TOO_LARGE",
173
- `Tuple node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}.`,
173
+ `Tuple node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}`,
174
174
  );
175
175
  }
176
176
 
@@ -187,7 +187,7 @@ export function tuple(fieldDescs: Uint8Array[]): Uint8Array {
187
187
  if (staticWords > DF.MAX_STATIC_WORDS) {
188
188
  throw new CallciumError(
189
189
  "STATIC_WORDS_TOO_LARGE",
190
- `Tuple static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}.`,
190
+ `Tuple static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}`,
191
191
  );
192
192
  }
193
193
  const meta24 = (staticWords << DF.META_STATIC_WORDS_SHIFT) | nodeLength;