@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.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { keccak_256 } from "@noble/hashes/sha3.js";
2
2
  //#region src/errors.ts
3
- var CallciumError = class CallciumError extends Error {
3
+ var CallciumError = class extends Error {
4
4
  code;
5
5
  offset;
6
6
  constructor(code, message, offset) {
@@ -8,25 +8,22 @@ var CallciumError = class CallciumError extends Error {
8
8
  super(`${prefix}${message}`);
9
9
  this.name = "CallciumError";
10
10
  this.code = code;
11
- Object.setPrototypeOf(this, CallciumError.prototype);
12
11
  }
13
12
  };
14
- var ValidationError = class ValidationError extends CallciumError {
13
+ var ValidationError = class extends CallciumError {
15
14
  issues;
16
15
  constructor(issues) {
17
16
  super("VALIDATION_ERROR", issues[0]?.message ?? "Policy validation failed");
18
17
  this.name = "ValidationError";
19
18
  this.issues = issues;
20
- Object.setPrototypeOf(this, ValidationError.prototype);
21
19
  }
22
20
  };
23
- var PolicyViolationError = class PolicyViolationError extends Error {
21
+ var PolicyViolationError = class extends Error {
24
22
  violations;
25
23
  constructor(violations) {
26
24
  super(formatDiagnostic(violations[0]));
27
25
  this.name = "PolicyViolationError";
28
26
  this.violations = violations;
29
- Object.setPrototypeOf(this, PolicyViolationError.prototype);
30
27
  }
31
28
  };
32
29
  function formatDiagnostic(violation) {
@@ -36,29 +33,29 @@ function formatDiagnostic(violation) {
36
33
  }
37
34
  //#endregion
38
35
  //#region src/bytes.ts
39
- const HEX_BODY_RE = /^[0-9a-fA-F]*$/;
36
+ const hexBodyPattern = /^[0-9a-fA-F]*$/;
40
37
  const HEX_DIGITS = "0123456789abcdef";
41
- const NIBBLE = new Int8Array(128).fill(-1);
38
+ const nibble = new Int8Array(128).fill(-1);
42
39
  for (let value = 0; value < 16; value++) {
43
- NIBBLE[HEX_DIGITS.charCodeAt(value)] = value;
44
- NIBBLE[HEX_DIGITS.toUpperCase().charCodeAt(value)] = value;
40
+ nibble[HEX_DIGITS.charCodeAt(value)] = value;
41
+ nibble[HEX_DIGITS.toUpperCase().charCodeAt(value)] = value;
45
42
  }
46
43
  function hexToBytes(hex) {
47
44
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
48
45
  if (clean.length % 2 !== 0) throw new CallciumError("INVALID_HEX", "Odd-length hex string");
49
46
  const bytes = new Uint8Array(clean.length / 2);
50
47
  for (let i = 0; i < bytes.length; i++) {
51
- const high = NIBBLE[clean.charCodeAt(i * 2)] ?? -1;
52
- const low = NIBBLE[clean.charCodeAt(i * 2 + 1)] ?? -1;
48
+ const high = nibble[clean.charCodeAt(i * 2)] ?? -1;
49
+ const low = nibble[clean.charCodeAt(i * 2 + 1)] ?? -1;
53
50
  if (high < 0 || low < 0) throw new CallciumError("INVALID_HEX", "Invalid hex characters");
54
51
  bytes[i] = high << 4 | low;
55
52
  }
56
53
  return bytes;
57
54
  }
58
- const HEX_LUT = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
55
+ const hexPairs = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
59
56
  function bytesToHex(bytes) {
60
57
  let body = "";
61
- for (let i = 0; i < bytes.length; i++) body += HEX_LUT[bytes[i]];
58
+ for (let i = 0; i < bytes.length; i++) body += hexPairs[bytes[i]];
62
59
  return `0x${body}`;
63
60
  }
64
61
  function toHex(data, start, end) {
@@ -94,7 +91,7 @@ function bigintToHex(value) {
94
91
  function toAddress(hex) {
95
92
  const body = hex.startsWith("0x") ? hex.slice(2) : hex;
96
93
  if (body.length !== 40) throw new CallciumError("INVALID_HEX", `Invalid address length: expected 40 hex chars, got ${body.length}`);
97
- if (!HEX_BODY_RE.test(body)) throw new CallciumError("INVALID_HEX", "Invalid hex characters in address");
94
+ if (!hexBodyPattern.test(body)) throw new CallciumError("INVALID_HEX", "Invalid hex characters in address");
98
95
  return `0x${body}`;
99
96
  }
100
97
  //#endregion
@@ -117,6 +114,7 @@ const DescriptorFormat = {
117
114
  MAX_PARAMS: 255,
118
115
  MAX_NESTING_DEPTH: 64
119
116
  };
117
+ const RULE_DATALENGTH_SIZE = 2;
120
118
  const PolicyFormat = {
121
119
  VERSION: 2,
122
120
  VERSION_MASK: 15,
@@ -138,7 +136,7 @@ const PolicyFormat = {
138
136
  RULE_PATH_OFFSET: 4,
139
137
  PATH_STEP_SIZE: 2,
140
138
  RULE_OPCODE_SIZE: 1,
141
- RULE_DATALENGTH_SIZE: 2,
139
+ RULE_DATALENGTH_SIZE,
142
140
  RULE_FIXED_OVERHEAD: 7,
143
141
  RULE_MIN_SIZE: 9,
144
142
  HINT_HEADER_SIZE: 1,
@@ -163,7 +161,10 @@ const PolicyFormat = {
163
161
  HINT_META_ELEM_DYNAMIC: 32768,
164
162
  HINT_META_DYNAMIC_ARRAY: 16384,
165
163
  HINT_META_RESERVED_MASK: 12288,
166
- HINT_META_STRIDE_MASK: 4095
164
+ HINT_META_STRIDE_MASK: 4095,
165
+ MAX_PATH_DEPTH: 32,
166
+ MAX_QUANTIFIED_ARRAY_LENGTH: 256,
167
+ MAX_SET_MEMBERS: Math.floor((256 ** RULE_DATALENGTH_SIZE - 1) / 32)
167
168
  };
168
169
  function buildCodeMap(table) {
169
170
  return Object.fromEntries(table.map((e) => [e.key, e.code]));
@@ -200,83 +201,76 @@ const TypeCode = {
200
201
  DYNAMIC_ARRAY: 129,
201
202
  TUPLE: 144
202
203
  };
203
- const CTX_PROP_TABLE = [
204
+ const CTX_PROPERTY_TABLE = [
204
205
  {
205
206
  key: "MSG_SENDER",
206
207
  code: 0,
207
208
  label: "msg.sender",
209
+ contextKey: "msgSender",
208
210
  typeCode: TypeCode.ADDRESS
209
211
  },
210
212
  {
211
213
  key: "MSG_VALUE",
212
214
  code: 1,
213
215
  label: "msg.value",
216
+ contextKey: "msgValue",
214
217
  typeCode: TypeCode.UINT_MAX
215
218
  },
216
219
  {
217
220
  key: "BLOCK_TIMESTAMP",
218
221
  code: 2,
219
222
  label: "block.timestamp",
223
+ contextKey: "blockTimestamp",
220
224
  typeCode: TypeCode.UINT_MAX
221
225
  },
222
226
  {
223
227
  key: "BLOCK_NUMBER",
224
228
  code: 3,
225
229
  label: "block.number",
230
+ contextKey: "blockNumber",
226
231
  typeCode: TypeCode.UINT_MAX
227
232
  },
228
233
  {
229
234
  key: "CHAIN_ID",
230
235
  code: 4,
231
236
  label: "block.chainid",
237
+ contextKey: "chainId",
232
238
  typeCode: TypeCode.UINT_MAX
233
239
  },
234
240
  {
235
241
  key: "TX_ORIGIN",
236
242
  code: 5,
237
243
  label: "tx.origin",
244
+ contextKey: "txOrigin",
238
245
  typeCode: TypeCode.ADDRESS
239
246
  },
240
247
  {
241
248
  key: "BASE_FEE",
242
249
  code: 6,
243
250
  label: "block.basefee",
251
+ contextKey: "baseFee",
244
252
  typeCode: TypeCode.UINT_MAX
245
253
  },
246
254
  {
247
255
  key: "GAS_PRICE",
248
256
  code: 7,
249
257
  label: "tx.gasprice",
258
+ contextKey: "gasPrice",
250
259
  typeCode: TypeCode.UINT_MAX
251
260
  }
252
261
  ];
253
- const ContextProperty = buildCodeMap(CTX_PROP_TABLE);
254
- const MAX_CONTEXT_PROPERTY_ID = Math.max(...CTX_PROP_TABLE.map((e) => e.code));
255
- const CTX_KEY_MAP = {
256
- MSG_SENDER: "msgSender",
257
- MSG_VALUE: "msgValue",
258
- BLOCK_TIMESTAMP: "blockTimestamp",
259
- BLOCK_NUMBER: "blockNumber",
260
- CHAIN_ID: "chainId",
261
- TX_ORIGIN: "txOrigin",
262
- BASE_FEE: "baseFee",
263
- GAS_PRICE: "gasPrice"
264
- };
265
- const ctxPropByCode = new Map(CTX_PROP_TABLE.map((e) => [e.code, {
262
+ const ContextProperty = buildCodeMap(CTX_PROPERTY_TABLE);
263
+ const MAX_CONTEXT_PROPERTY_ID = Math.max(...CTX_PROPERTY_TABLE.map((e) => e.code));
264
+ const ctxPropertyByCode = new Map(CTX_PROPERTY_TABLE.map((e) => [e.code, {
266
265
  label: e.label,
267
- contextKey: CTX_KEY_MAP[e.key],
266
+ contextKey: e.contextKey,
268
267
  typeCode: e.typeCode
269
268
  }]));
270
269
  function lookupContextProperty(code) {
271
- const info = ctxPropByCode.get(code);
270
+ const info = ctxPropertyByCode.get(code);
272
271
  if (!info) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ${code}`);
273
272
  return info;
274
273
  }
275
- const Limits = {
276
- MAX_PATH_DEPTH: 32,
277
- MAX_QUANTIFIED_ARRAY_LENGTH: 256,
278
- MAX_SET_MEMBERS: Math.floor((256 ** PolicyFormat.RULE_DATALENGTH_SIZE - 1) / 32)
279
- };
280
274
  const OP_TABLE = [
281
275
  {
282
276
  key: "EQ",
@@ -320,6 +314,12 @@ const OP_TABLE = [
320
314
  label: "in",
321
315
  operands: "variadic"
322
316
  },
317
+ {
318
+ key: "EQ_CTX",
319
+ code: 8,
320
+ label: "== ctx",
321
+ operands: "single"
322
+ },
323
323
  {
324
324
  key: "BITMASK_ALL",
325
325
  code: 16,
@@ -389,13 +389,11 @@ function lookupOp(code) {
389
389
  if (!info) throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code 0x${base.toString(16).padStart(2, "0")}`);
390
390
  return info;
391
391
  }
392
- function isValidOperatorData(opBase, dataLength) {
393
- const operands = opByCode.get(opBase)?.operands;
394
- if (operands === "single") return dataLength === 32;
395
- if (operands === "range") return dataLength === 64;
396
- if (operands === "variadic") return dataLength > 0 && dataLength % 32 === 0;
397
- return false;
392
+ function operandsOf(opBase) {
393
+ return opByCode.get(opBase)?.operands;
398
394
  }
395
+ //#endregion
396
+ //#region src/path.ts
399
397
  const QUANTIFIER_TABLE = [{
400
398
  key: "ALL",
401
399
  code: 65535,
@@ -415,71 +413,17 @@ function lookupQuantifier(code) {
415
413
  if (!info) throw new CallciumError("UNKNOWN_QUANTIFIER", `Unknown quantifier step 0x${code.toString(16)}`);
416
414
  return info;
417
415
  }
418
- function unknownTypeCode(code) {
419
- throw new CallciumError("UNKNOWN_TYPE_CODE", `Unknown type code 0x${code.toString(16).padStart(2, "0")}`);
420
- }
421
- const ELEMENTARY = {
422
- typeClass: "elementary",
423
- isDynamic: false
424
- };
425
- const ELEMENTARY_DYN = {
426
- typeClass: "elementary",
427
- isDynamic: true
428
- };
429
- const STATIC_ARR = {
430
- typeClass: "staticArray",
431
- isDynamic: false
432
- };
433
- const DYNAMIC_ARR = {
434
- typeClass: "dynamicArray",
435
- isDynamic: true
436
- };
437
- const TUPLE = {
438
- typeClass: "tuple",
439
- isDynamic: false
440
- };
441
- function classifyTypeCode(code) {
442
- if (code >= TypeCode.UINT_MIN && code <= TypeCode.UINT_MAX) return ELEMENTARY;
443
- if (code >= TypeCode.INT_MIN && code <= TypeCode.INT_MAX) return ELEMENTARY;
444
- if (code === TypeCode.ADDRESS || code === TypeCode.BOOL || code === TypeCode.FUNCTION) return ELEMENTARY;
445
- if (code >= 68 && code <= 79) unknownTypeCode(code);
446
- if (code >= TypeCode.FIXED_BYTES_MIN && code <= TypeCode.FIXED_BYTES_MAX) return ELEMENTARY;
447
- if (code === TypeCode.BYTES || code === TypeCode.STRING) return ELEMENTARY_DYN;
448
- if (code >= 114 && code <= 127) unknownTypeCode(code);
449
- if (code === TypeCode.STATIC_ARRAY) return STATIC_ARR;
450
- if (code === TypeCode.DYNAMIC_ARRAY) return DYNAMIC_ARR;
451
- if (code >= 130 && code <= 143) unknownTypeCode(code);
452
- if (code === TypeCode.TUPLE) return TUPLE;
453
- unknownTypeCode(code);
454
- }
455
- function isAddressableTarget(code) {
456
- let info;
457
- try {
458
- info = classifyTypeCode(code);
459
- } catch {
460
- return false;
461
- }
462
- return info.typeClass === "elementary" || info.typeClass === "dynamicArray";
463
- }
464
- function typeCodeLabel(code) {
465
- if (code >= TypeCode.UINT_MIN && code <= TypeCode.UINT_MAX) return `uint${(code - TypeCode.UINT_MIN + 1) * 8}`;
466
- if (code >= TypeCode.INT_MIN && code <= TypeCode.INT_MAX) return `int${(code - TypeCode.INT_MIN + 1) * 8}`;
467
- if (code === TypeCode.ADDRESS) return "address";
468
- if (code === TypeCode.BOOL) return "bool";
469
- if (code === TypeCode.FUNCTION) return "function";
470
- if (code >= TypeCode.FIXED_BYTES_MIN && code <= TypeCode.FIXED_BYTES_MAX) return `bytes${code - TypeCode.FIXED_BYTES_MIN + 1}`;
471
- if (code === TypeCode.BYTES) return "bytes";
472
- if (code === TypeCode.STRING) return "string";
473
- if (code === TypeCode.STATIC_ARRAY) return "T[k]";
474
- if (code === TypeCode.DYNAMIC_ARRAY) return "T[]";
475
- if (code === TypeCode.TUPLE) return "tuple";
476
- return `0x${code.toString(16).padStart(2, "0")}`;
416
+ function encodePath(steps) {
417
+ const buffer = new Uint8Array(steps.length * 2);
418
+ for (let i = 0; i < steps.length; i++) writeBE16(buffer, i * 2, steps[i]);
419
+ return bytesToHex(buffer);
477
420
  }
478
- function lookupTypeCode(code) {
479
- return {
480
- label: typeCodeLabel(code),
481
- ...classifyTypeCode(code)
482
- };
421
+ function parsePathSteps(path) {
422
+ const bytes = hexToBytes(path);
423
+ if (bytes.length % PolicyFormat.PATH_STEP_SIZE !== 0) throw new CallciumError("MALFORMED_PATH", `Path byte length ${bytes.length} is not a whole number of steps`);
424
+ const steps = [];
425
+ for (let offset = 0; offset < bytes.length; offset += PolicyFormat.PATH_STEP_SIZE) steps.push(readU16(bytes, offset));
426
+ return steps;
483
427
  }
484
428
  //#endregion
485
429
  //#region src/descriptor.ts
@@ -497,7 +441,7 @@ function paramCount(desc) {
497
441
  }
498
442
  function paramOffset(desc, index) {
499
443
  const count = desc[1];
500
- if (index >= count) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${index} out of range (paramCount=${count}).`);
444
+ if (index >= count) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${index} out of range (paramCount=${count})`);
501
445
  let cursor = DescriptorFormat.HEADER_SIZE;
502
446
  for (let i = 0; i < index; i++) cursor += nodeLength(desc, cursor);
503
447
  return cursor;
@@ -517,10 +461,10 @@ function typeAt(desc, steps) {
517
461
  return walkPath(desc, steps).typeInfo;
518
462
  }
519
463
  function walkPath(desc, steps) {
520
- if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step.");
464
+ if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step");
521
465
  const paramIndex = steps[0];
522
466
  const count = desc[1];
523
- if (paramIndex >= count) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${paramIndex} out of range (paramCount=${count}).`);
467
+ if (paramIndex >= count) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${paramIndex} out of range (paramCount=${count})`);
524
468
  let cursor = paramOffset(desc, paramIndex);
525
469
  let quantifiedStaticLength = 0;
526
470
  for (let stepIndex = 1; stepIndex < steps.length; stepIndex++) {
@@ -528,16 +472,16 @@ function walkPath(desc, steps) {
528
472
  const step = steps[stepIndex];
529
473
  if (typeCode === TypeCode.TUPLE) {
530
474
  const fields = tupleFieldCount(desc, cursor);
531
- if (step >= fields) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fields} fields).`);
475
+ if (step >= fields) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fields} fields)`);
532
476
  cursor = tupleFieldOffset(desc, cursor, step);
533
477
  } else if (typeCode === TypeCode.STATIC_ARRAY || typeCode === TypeCode.DYNAMIC_ARRAY) {
534
478
  if (typeCode === TypeCode.STATIC_ARRAY) if (isQuantifier(step)) quantifiedStaticLength = staticArrayLength(desc, cursor);
535
479
  else {
536
480
  const length = staticArrayLength(desc, cursor);
537
- if (step >= length) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (length=${length}).`);
481
+ if (step >= length) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (length=${length})`);
538
482
  }
539
483
  cursor = arrayElementOffset(cursor);
540
- } else throw new CallciumError("NOT_COMPOSITE", `Cannot descend into elementary type at offset ${cursor}.`);
484
+ } else throw new CallciumError("NOT_COMPOSITE", `Cannot descend into elementary type at offset ${cursor}`);
541
485
  }
542
486
  return {
543
487
  typeInfo: inspect$1(desc, cursor),
@@ -555,7 +499,7 @@ function arrayElementOffset(offset) {
555
499
  return offset + DescriptorFormat.ARRAY_HEADER_SIZE;
556
500
  }
557
501
  function uncompilablePath(stepIndex) {
558
- return new CallciumError("UNCOMPILABLE_PATH", `Path step ${stepIndex} does not resolve to a hop-chain target.`);
502
+ return new CallciumError("UNCOMPILABLE_PATH", `Path step ${stepIndex} does not resolve to a hop-chain target`);
559
503
  }
560
504
  function headSpan(desc, offset, count) {
561
505
  let span = 0;
@@ -662,7 +606,7 @@ function encodeHint(walk, targetMeta, typeCode, depth) {
662
606
  return out;
663
607
  }
664
608
  function compileHint(desc, steps) {
665
- if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step.");
609
+ if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step");
666
610
  const argIndex = steps[0];
667
611
  if (argIndex >= paramCount(desc)) throw uncompilablePath(0);
668
612
  const arg = headSpan(desc, DescriptorFormat.HEADER_SIZE, argIndex);
@@ -698,6 +642,199 @@ const Descriptor = {
698
642
  compileHint
699
643
  };
700
644
  //#endregion
645
+ //#region src/operators.ts
646
+ function toBigInt(bytes, offset = 0) {
647
+ const view = new DataView(bytes.buffer, bytes.byteOffset + offset, 32);
648
+ return view.getBigUint64(0, false) << 192n | view.getBigUint64(8, false) << 128n | view.getBigUint64(16, false) << 64n | view.getBigUint64(24, false);
649
+ }
650
+ function isSigned(typeCode) {
651
+ return typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX;
652
+ }
653
+ function isLeftAligned(typeCode) {
654
+ return typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX || typeCode === TypeCode.FUNCTION;
655
+ }
656
+ function canonicalize(value, typeCode) {
657
+ if (typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX) {
658
+ const bits = BigInt(typeCode * 8);
659
+ return bits === 256n ? value : value & (1n << bits) - 1n;
660
+ }
661
+ if (typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX) {
662
+ const bits = (typeCode - TypeCode.INT_MIN + 1) * 8;
663
+ return bits === 256 ? value : BigInt.asUintN(256, BigInt.asIntN(bits, value));
664
+ }
665
+ if (typeCode === TypeCode.ADDRESS) return value & (1n << 160n) - 1n;
666
+ if (typeCode === TypeCode.BOOL) return value & 1n;
667
+ if (typeCode === TypeCode.FUNCTION) return value >> 64n << 64n;
668
+ if (typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX) {
669
+ const n = typeCode - TypeCode.FIXED_BYTES_MIN + 1;
670
+ if (n === 32) return value;
671
+ const padBits = BigInt((32 - n) * 8);
672
+ return value >> padBits << padBits;
673
+ }
674
+ return value;
675
+ }
676
+ function isLengthOp(opCode) {
677
+ const base = opCode & ~Op.NOT;
678
+ return base >= Op.LENGTH_EQ && base <= Op.LENGTH_BETWEEN;
679
+ }
680
+ function lengthToValueOp(base) {
681
+ if (base === Op.LENGTH_EQ) return Op.EQ;
682
+ if (base === Op.LENGTH_GT) return Op.GT;
683
+ if (base === Op.LENGTH_LT) return Op.LT;
684
+ if (base === Op.LENGTH_GTE) return Op.GTE;
685
+ if (base === Op.LENGTH_LTE) return Op.LTE;
686
+ return Op.BETWEEN;
687
+ }
688
+ function isLengthValidType(typeCode) {
689
+ return typeCode === TypeCode.BYTES || typeCode === TypeCode.STRING || typeCode === TypeCode.DYNAMIC_ARRAY;
690
+ }
691
+ function compareTyped(a, b, typeCode) {
692
+ if (isSigned(typeCode)) {
693
+ const sa = BigInt.asIntN(256, a);
694
+ const sb = BigInt.asIntN(256, b);
695
+ return sa < sb ? -1 : sa > sb ? 1 : 0;
696
+ }
697
+ return a < b ? -1 : a > b ? 1 : 0;
698
+ }
699
+ function isIn(value, data) {
700
+ const count = data.length / 32;
701
+ let low = 0;
702
+ let high = count - 1;
703
+ while (low <= high) {
704
+ const mid = low + high >>> 1;
705
+ const elem = toBigInt(data, mid * 32);
706
+ if (value === elem) return true;
707
+ if (value < elem) high = mid - 1;
708
+ else low = mid + 1;
709
+ }
710
+ return false;
711
+ }
712
+ function applyOperator(opCode, value, valueLength, operandData, typeCode) {
713
+ let base = opCode & ~Op.NOT;
714
+ if (isLengthOp(base)) {
715
+ value = BigInt(valueLength);
716
+ typeCode = TypeCode.UINT_MAX;
717
+ base = lengthToValueOp(base);
718
+ }
719
+ let result;
720
+ switch (base) {
721
+ case Op.EQ:
722
+ result = value === toBigInt(operandData, 0);
723
+ break;
724
+ case Op.GT:
725
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) > 0;
726
+ break;
727
+ case Op.LT:
728
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) < 0;
729
+ break;
730
+ case Op.GTE:
731
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) >= 0;
732
+ break;
733
+ case Op.LTE:
734
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) <= 0;
735
+ break;
736
+ case Op.BETWEEN: {
737
+ const min = toBigInt(operandData, 0);
738
+ const max = toBigInt(operandData, 32);
739
+ result = compareTyped(value, min, typeCode) >= 0 && compareTyped(value, max, typeCode) <= 0;
740
+ break;
741
+ }
742
+ case Op.IN:
743
+ result = isIn(value, operandData);
744
+ break;
745
+ case Op.BITMASK_ALL: {
746
+ const mask = toBigInt(operandData, 0);
747
+ result = (value & mask) === mask;
748
+ break;
749
+ }
750
+ case Op.BITMASK_ANY: {
751
+ const mask = toBigInt(operandData, 0);
752
+ result = (value & mask) !== 0n;
753
+ break;
754
+ }
755
+ case Op.BITMASK_NONE: {
756
+ const mask = toBigInt(operandData, 0);
757
+ result = (value & mask) === 0n;
758
+ break;
759
+ }
760
+ default: throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code 0x${base.toString(16).padStart(2, "0")}`);
761
+ }
762
+ return (opCode & Op.NOT) !== 0 ? !result : result;
763
+ }
764
+ function isValidOperatorData(opBase, dataLength) {
765
+ const operands = operandsOf(opBase);
766
+ if (operands === "single") return dataLength === 32;
767
+ if (operands === "range") return dataLength === 64;
768
+ if (operands === "variadic") return dataLength > 0 && dataLength % 32 === 0;
769
+ return false;
770
+ }
771
+ function unknownTypeCode(code) {
772
+ throw new CallciumError("UNKNOWN_TYPE_CODE", `Unknown type code 0x${code.toString(16).padStart(2, "0")}`);
773
+ }
774
+ const ELEMENTARY = {
775
+ typeClass: "elementary",
776
+ isDynamic: false
777
+ };
778
+ const ELEMENTARY_DYNAMIC = {
779
+ typeClass: "elementary",
780
+ isDynamic: true
781
+ };
782
+ const STATIC_ARR = {
783
+ typeClass: "staticArray",
784
+ isDynamic: false
785
+ };
786
+ const DYNAMIC_ARR = {
787
+ typeClass: "dynamicArray",
788
+ isDynamic: true
789
+ };
790
+ const TUPLE = {
791
+ typeClass: "tuple",
792
+ isDynamic: false
793
+ };
794
+ function classifyTypeCode(code) {
795
+ if (code >= TypeCode.UINT_MIN && code <= TypeCode.UINT_MAX) return ELEMENTARY;
796
+ if (code >= TypeCode.INT_MIN && code <= TypeCode.INT_MAX) return ELEMENTARY;
797
+ if (code === TypeCode.ADDRESS || code === TypeCode.BOOL || code === TypeCode.FUNCTION) return ELEMENTARY;
798
+ if (code >= 68 && code <= 79) unknownTypeCode(code);
799
+ if (code >= TypeCode.FIXED_BYTES_MIN && code <= TypeCode.FIXED_BYTES_MAX) return ELEMENTARY;
800
+ if (code === TypeCode.BYTES || code === TypeCode.STRING) return ELEMENTARY_DYNAMIC;
801
+ if (code >= 114 && code <= 127) unknownTypeCode(code);
802
+ if (code === TypeCode.STATIC_ARRAY) return STATIC_ARR;
803
+ if (code === TypeCode.DYNAMIC_ARRAY) return DYNAMIC_ARR;
804
+ if (code >= 130 && code <= 143) unknownTypeCode(code);
805
+ if (code === TypeCode.TUPLE) return TUPLE;
806
+ unknownTypeCode(code);
807
+ }
808
+ function isAddressableTarget(code) {
809
+ let info;
810
+ try {
811
+ info = classifyTypeCode(code);
812
+ } catch {
813
+ return false;
814
+ }
815
+ return info.typeClass === "elementary" || info.typeClass === "dynamicArray";
816
+ }
817
+ function typeCodeLabel(code) {
818
+ if (code >= TypeCode.UINT_MIN && code <= TypeCode.UINT_MAX) return `uint${(code - TypeCode.UINT_MIN + 1) * 8}`;
819
+ if (code >= TypeCode.INT_MIN && code <= TypeCode.INT_MAX) return `int${(code - TypeCode.INT_MIN + 1) * 8}`;
820
+ if (code === TypeCode.ADDRESS) return "address";
821
+ if (code === TypeCode.BOOL) return "bool";
822
+ if (code === TypeCode.FUNCTION) return "function";
823
+ if (code >= TypeCode.FIXED_BYTES_MIN && code <= TypeCode.FIXED_BYTES_MAX) return `bytes${code - TypeCode.FIXED_BYTES_MIN + 1}`;
824
+ if (code === TypeCode.BYTES) return "bytes";
825
+ if (code === TypeCode.STRING) return "string";
826
+ if (code === TypeCode.STATIC_ARRAY) return "T[k]";
827
+ if (code === TypeCode.DYNAMIC_ARRAY) return "T[]";
828
+ if (code === TypeCode.TUPLE) return "tuple";
829
+ return `0x${code.toString(16).padStart(2, "0")}`;
830
+ }
831
+ function lookupTypeCode(code) {
832
+ return {
833
+ label: typeCodeLabel(code),
834
+ ...classifyTypeCode(code)
835
+ };
836
+ }
837
+ //#endregion
701
838
  //#region src/type-desc.ts
702
839
  function extractStaticWords(desc) {
703
840
  const typeCode = desc[0];
@@ -714,11 +851,11 @@ function function_() {
714
851
  return new Uint8Array([TypeCode.FUNCTION]);
715
852
  }
716
853
  function uintN(bits) {
717
- if (bits < 8 || bits > 256 || bits % 8 !== 0) throw new CallciumError("UNKNOWN_TYPE", `Invalid uintN bits: ${bits}. Must be 8–256 in steps of 8.`);
854
+ if (bits < 8 || bits > 256 || bits % 8 !== 0) throw new CallciumError("UNKNOWN_TYPE", `Invalid uintN bits: ${bits}. Must be 8–256 in steps of 8`);
718
855
  return new Uint8Array([TypeCode.UINT_MIN + (bits / 8 - 1)]);
719
856
  }
720
857
  function intN(bits) {
721
- if (bits < 8 || bits > 256 || bits % 8 !== 0) throw new CallciumError("UNKNOWN_TYPE", `Invalid intN bits: ${bits}. Must be 8–256 in steps of 8.`);
858
+ if (bits < 8 || bits > 256 || bits % 8 !== 0) throw new CallciumError("UNKNOWN_TYPE", `Invalid intN bits: ${bits}. Must be 8–256 in steps of 8`);
722
859
  return new Uint8Array([TypeCode.INT_MIN + (bits / 8 - 1)]);
723
860
  }
724
861
  function bytes() {
@@ -728,13 +865,13 @@ function string_() {
728
865
  return new Uint8Array([TypeCode.STRING]);
729
866
  }
730
867
  function bytesN(n) {
731
- if (n < 1 || n > 32) throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${n}. Must be 1–32.`);
868
+ if (n < 1 || n > 32) throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${n}. Must be 1–32`);
732
869
  return new Uint8Array([TypeCode.FIXED_BYTES_MIN + (n - 1)]);
733
870
  }
734
871
  function array(elemDesc, length) {
735
872
  if (length === void 0) {
736
873
  const nodeLength = DescriptorFormat.ARRAY_HEADER_SIZE + elemDesc.length;
737
- if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Dynamic array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}.`);
874
+ if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Dynamic array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}`);
738
875
  const meta24 = 0 << DescriptorFormat.META_STATIC_WORDS_SHIFT | nodeLength;
739
876
  const buf = new Uint8Array(1 + DescriptorFormat.COMPOSITE_META_SIZE + elemDesc.length);
740
877
  buf[0] = TypeCode.DYNAMIC_ARRAY;
@@ -742,12 +879,12 @@ function array(elemDesc, length) {
742
879
  buf.set(elemDesc, 4);
743
880
  return buf;
744
881
  }
745
- if (length <= 0 || length > DescriptorFormat.MAX_STATIC_ARRAY_LENGTH) throw new CallciumError("INVALID_ARRAY_LENGTH", `Static array length ${length} out of range. Must be 1–${DescriptorFormat.MAX_STATIC_ARRAY_LENGTH}.`);
882
+ if (length <= 0 || length > DescriptorFormat.MAX_STATIC_ARRAY_LENGTH) throw new CallciumError("INVALID_ARRAY_LENGTH", `Static array length ${length} out of range. Must be 1–${DescriptorFormat.MAX_STATIC_ARRAY_LENGTH}`);
746
883
  const nodeLength = DescriptorFormat.ARRAY_HEADER_SIZE + elemDesc.length + DescriptorFormat.ARRAY_LENGTH_SIZE;
747
- if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Static array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}.`);
884
+ if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Static array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}`);
748
885
  const elemStaticWords = extractStaticWords(elemDesc);
749
886
  const staticWords = elemStaticWords === 0 ? 0 : length * elemStaticWords;
750
- if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Static array static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}.`);
887
+ if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Static array static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}`);
751
888
  const meta24 = staticWords << DescriptorFormat.META_STATIC_WORDS_SHIFT | nodeLength;
752
889
  const buf = new Uint8Array(1 + DescriptorFormat.COMPOSITE_META_SIZE + elemDesc.length + DescriptorFormat.ARRAY_LENGTH_SIZE);
753
890
  buf[0] = TypeCode.STATIC_ARRAY;
@@ -757,10 +894,10 @@ function array(elemDesc, length) {
757
894
  return buf;
758
895
  }
759
896
  function tuple(fieldDescs) {
760
- if (fieldDescs.length === 0 || fieldDescs.length > DescriptorFormat.MAX_TUPLE_FIELDS) throw new CallciumError("INVALID_TUPLE_FIELD_COUNT", `Tuple field count ${fieldDescs.length} out of range. Must be 1–${DescriptorFormat.MAX_TUPLE_FIELDS}.`);
897
+ if (fieldDescs.length === 0 || fieldDescs.length > DescriptorFormat.MAX_TUPLE_FIELDS) throw new CallciumError("INVALID_TUPLE_FIELD_COUNT", `Tuple field count ${fieldDescs.length} out of range. Must be 1–${DescriptorFormat.MAX_TUPLE_FIELDS}`);
761
898
  const totalFieldBytes = fieldDescs.reduce((sum, f) => sum + f.length, 0);
762
899
  const nodeLength = DescriptorFormat.TUPLE_HEADER_SIZE + totalFieldBytes;
763
- if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Tuple node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}.`);
900
+ if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Tuple node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}`);
764
901
  let anyDynamic = false;
765
902
  let staticWordsSum = 0;
766
903
  for (const field of fieldDescs) {
@@ -769,7 +906,7 @@ function tuple(fieldDescs) {
769
906
  staticWordsSum += staticWords;
770
907
  }
771
908
  const staticWords = anyDynamic ? 0 : staticWordsSum;
772
- if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Tuple static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}.`);
909
+ if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Tuple static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}`);
773
910
  const meta24 = staticWords << DescriptorFormat.META_STATIC_WORDS_SHIFT | nodeLength;
774
911
  const buf = new Uint8Array(1 + DescriptorFormat.COMPOSITE_META_SIZE + DescriptorFormat.TUPLE_FIELDCOUNT_SIZE + totalFieldBytes);
775
912
  buf[0] = TypeCode.TUPLE;
@@ -811,19 +948,19 @@ function parseUint(input, start, end) {
811
948
  function parseTuple(input, start, end) {
812
949
  if (input[start] !== "(") throw new CallciumError("MALFORMED_TYPE_STRING", `Expected '(' at position ${start}`);
813
950
  let depth = 0;
814
- let closePos = -1;
951
+ let closeIndex = -1;
815
952
  for (let i = start; i < end; i++) if (input[i] === "(") depth++;
816
953
  else if (input[i] === ")") {
817
954
  depth--;
818
955
  if (depth === 0) {
819
- closePos = i;
956
+ closeIndex = i;
820
957
  break;
821
958
  }
822
959
  }
823
- if (closePos === -1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched '(' at position ${start}`);
824
- if (closePos !== end - 1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unexpected characters after ')' at position ${closePos + 1}`);
960
+ if (closeIndex === -1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched '(' at position ${start}`);
961
+ if (closeIndex !== end - 1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unexpected characters after ')' at position ${closeIndex + 1}`);
825
962
  const innerStart = start + 1;
826
- const innerEnd = closePos;
963
+ const innerEnd = closeIndex;
827
964
  const commas = commaPositions(input, innerStart, innerEnd);
828
965
  const segments = [];
829
966
  let segStart = innerStart;
@@ -853,14 +990,14 @@ function parseType(input, start, end) {
853
990
  const suffixes = [];
854
991
  let baseEnd = end;
855
992
  while (baseEnd > start && input[baseEnd - 1] === "]") {
856
- const closePos = baseEnd - 1;
857
- let openPos = closePos - 1;
858
- while (openPos > start && input[openPos] !== "[") openPos--;
859
- if (input[openPos] !== "[") throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched ']' at position ${closePos}`);
860
- const innerStart = openPos + 1;
861
- const innerEnd = closePos;
993
+ const closeIndex = baseEnd - 1;
994
+ let openIndex = closeIndex - 1;
995
+ while (openIndex > start && input[openIndex] !== "[") openIndex--;
996
+ if (input[openIndex] !== "[") throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched ']' at position ${closeIndex}`);
997
+ const innerStart = openIndex + 1;
998
+ const innerEnd = closeIndex;
862
999
  suffixes.unshift(innerStart === innerEnd ? void 0 : parseUint(input, innerStart, innerEnd));
863
- baseEnd = openPos;
1000
+ baseEnd = openIndex;
864
1001
  }
865
1002
  if (input[start] !== "(" && input.slice(start, baseEnd).includes("[")) throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched '[' in '${input.slice(start, baseEnd)}'`);
866
1003
  let desc = parseBaseType(input, start, baseEnd);
@@ -878,7 +1015,7 @@ function fromTypes(typesCsv) {
878
1015
  }
879
1016
  segments.push([segStart, typesCsv.length]);
880
1017
  const paramDescs = segments.map(([start, end]) => parseType(typesCsv, start, end));
881
- if (paramDescs.length > DescriptorFormat.MAX_PARAMS) throw new CallciumError("TOO_MANY_PARAMS", `Parameter count ${paramDescs.length} exceeds maximum ${DescriptorFormat.MAX_PARAMS}.`);
1018
+ if (paramDescs.length > DescriptorFormat.MAX_PARAMS) throw new CallciumError("TOO_MANY_PARAMS", `Parameter count ${paramDescs.length} exceeds maximum ${DescriptorFormat.MAX_PARAMS}`);
882
1019
  const totalBytes = paramDescs.reduce((sum, d) => sum + d.length, 0);
883
1020
  const result = new Uint8Array(DescriptorFormat.HEADER_SIZE + totalBytes);
884
1021
  result[0] = DescriptorFormat.VERSION;
@@ -1016,134 +1153,7 @@ const DescriptorCoder = {
1016
1153
  toTypes
1017
1154
  };
1018
1155
  //#endregion
1019
- //#region src/operators.ts
1020
- function toBigInt(bytes, offset = 0) {
1021
- const view = new DataView(bytes.buffer, bytes.byteOffset + offset, 32);
1022
- return view.getBigUint64(0, false) << 192n | view.getBigUint64(8, false) << 128n | view.getBigUint64(16, false) << 64n | view.getBigUint64(24, false);
1023
- }
1024
- function isSigned(typeCode) {
1025
- return typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX;
1026
- }
1027
- function isLeftAligned(typeCode) {
1028
- return typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX || typeCode === TypeCode.FUNCTION;
1029
- }
1030
- function canonicalize(value, typeCode) {
1031
- if (typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX) {
1032
- const bits = BigInt(typeCode * 8);
1033
- return bits === 256n ? value : value & (1n << bits) - 1n;
1034
- }
1035
- if (typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX) {
1036
- const bits = (typeCode - TypeCode.INT_MIN + 1) * 8;
1037
- return bits === 256 ? value : BigInt.asUintN(256, BigInt.asIntN(bits, value));
1038
- }
1039
- if (typeCode === TypeCode.ADDRESS) return value & (1n << 160n) - 1n;
1040
- if (typeCode === TypeCode.BOOL) return value & 1n;
1041
- if (typeCode === TypeCode.FUNCTION) return value >> 64n << 64n;
1042
- if (typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX) {
1043
- const n = typeCode - TypeCode.FIXED_BYTES_MIN + 1;
1044
- if (n === 32) return value;
1045
- const padBits = BigInt((32 - n) * 8);
1046
- return value >> padBits << padBits;
1047
- }
1048
- return value;
1049
- }
1050
- function isLengthOp(opCode) {
1051
- const base = opCode & ~Op.NOT;
1052
- return base >= Op.LENGTH_EQ && base <= Op.LENGTH_BETWEEN;
1053
- }
1054
- function lengthToValueOp(base) {
1055
- if (base === Op.LENGTH_EQ) return Op.EQ;
1056
- if (base === Op.LENGTH_GT) return Op.GT;
1057
- if (base === Op.LENGTH_LT) return Op.LT;
1058
- if (base === Op.LENGTH_GTE) return Op.GTE;
1059
- if (base === Op.LENGTH_LTE) return Op.LTE;
1060
- return Op.BETWEEN;
1061
- }
1062
- function isLengthValidType(typeCode) {
1063
- return typeCode === TypeCode.BYTES || typeCode === TypeCode.STRING || typeCode === TypeCode.DYNAMIC_ARRAY;
1064
- }
1065
- function compareTyped(a, b, typeCode) {
1066
- if (isSigned(typeCode)) {
1067
- const sa = BigInt.asIntN(256, a);
1068
- const sb = BigInt.asIntN(256, b);
1069
- return sa < sb ? -1 : sa > sb ? 1 : 0;
1070
- }
1071
- return a < b ? -1 : a > b ? 1 : 0;
1072
- }
1073
- function isIn(value, data) {
1074
- const count = data.length / 32;
1075
- let lo = 0;
1076
- let hi = count - 1;
1077
- while (lo <= hi) {
1078
- const mid = lo + hi >>> 1;
1079
- const elem = toBigInt(data, mid * 32);
1080
- if (value === elem) return true;
1081
- if (value < elem) hi = mid - 1;
1082
- else lo = mid + 1;
1083
- }
1084
- return false;
1085
- }
1086
- function applyOperator(opCode, value, valueLength, operandData, typeCode) {
1087
- let base = opCode & ~Op.NOT;
1088
- if (isLengthOp(base)) {
1089
- value = BigInt(valueLength);
1090
- typeCode = TypeCode.UINT_MAX;
1091
- base = lengthToValueOp(base);
1092
- }
1093
- let result;
1094
- switch (base) {
1095
- case Op.EQ:
1096
- result = value === toBigInt(operandData, 0);
1097
- break;
1098
- case Op.GT:
1099
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) > 0;
1100
- break;
1101
- case Op.LT:
1102
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) < 0;
1103
- break;
1104
- case Op.GTE:
1105
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) >= 0;
1106
- break;
1107
- case Op.LTE:
1108
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) <= 0;
1109
- break;
1110
- case Op.BETWEEN: {
1111
- const min = toBigInt(operandData, 0);
1112
- const max = toBigInt(operandData, 32);
1113
- result = compareTyped(value, min, typeCode) >= 0 && compareTyped(value, max, typeCode) <= 0;
1114
- break;
1115
- }
1116
- case Op.IN:
1117
- result = isIn(value, operandData);
1118
- break;
1119
- case Op.BITMASK_ALL: {
1120
- const mask = toBigInt(operandData, 0);
1121
- result = (value & mask) === mask;
1122
- break;
1123
- }
1124
- case Op.BITMASK_ANY: {
1125
- const mask = toBigInt(operandData, 0);
1126
- result = (value & mask) !== 0n;
1127
- break;
1128
- }
1129
- case Op.BITMASK_NONE: {
1130
- const mask = toBigInt(operandData, 0);
1131
- result = (value & mask) === 0n;
1132
- break;
1133
- }
1134
- default: throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code 0x${base.toString(16).padStart(2, "0")}.`);
1135
- }
1136
- return (opCode & Op.NOT) !== 0 ? !result : result;
1137
- }
1138
- //#endregion
1139
1156
  //#region src/policy-coder.ts
1140
- function parsePathSteps(path) {
1141
- const bytes = hexToBytes(path);
1142
- if (bytes.length % PolicyFormat.PATH_STEP_SIZE !== 0) throw new CallciumError("MALFORMED_PATH", `Path byte length ${bytes.length} is not a whole number of steps`);
1143
- const steps = [];
1144
- for (let offset = 0; offset < bytes.length; offset += PolicyFormat.PATH_STEP_SIZE) steps.push(readU16(bytes, offset));
1145
- return steps;
1146
- }
1147
1157
  function field(value, start, end) {
1148
1158
  return {
1149
1159
  value,
@@ -1289,8 +1299,11 @@ function decodePolicy(blob) {
1289
1299
  const wordCount = dataLengthValue / 32;
1290
1300
  for (let word = 1; word < wordCount; word++) if (compareBytes(data.subarray(dataStart + (word - 1) * 32, dataStart + word * 32), data.subarray(dataStart + word * 32, dataStart + (word + 1) * 32)) >= 0) throw new CallciumError("UNSORTED_IN_SET", "IN operands must be strictly ascending", ruleOffset);
1291
1301
  }
1302
+ if (opBase === Op.EQ_CTX) {
1303
+ if (toBigInt(data, dataStart) > BigInt(MAX_CONTEXT_PROPERTY_ID)) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", "EQ_CTX operand references an undefined context property", ruleOffset);
1304
+ }
1292
1305
  if (depthValue === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step", ruleOffset);
1293
- if (depthValue > Limits.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depthValue} exceeds maximum ${Limits.MAX_PATH_DEPTH}`, ruleOffset);
1306
+ if (depthValue > PolicyFormat.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depthValue} exceeds maximum ${PolicyFormat.MAX_PATH_DEPTH}`, ruleOffset);
1294
1307
  if (scopeValue === Scope.CONTEXT) {
1295
1308
  if (depthValue !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step", ruleOffset);
1296
1309
  const contextPropertyId = readU16(data, pathStart);
@@ -1357,12 +1370,12 @@ function decodePolicy(blob) {
1357
1370
  }
1358
1371
  function flattenConstraint(constraint, desc) {
1359
1372
  const operators = constraint.operators.map(hexToBytes);
1360
- for (const operator of operators) if (operator.length < 1) throw new CallciumError("INVALID_OPERATOR_BYTES", "Operator must have at least one byte (opcode).");
1373
+ for (const operator of operators) if (operator.length < 1) throw new CallciumError("INVALID_OPERATOR_BYTES", "Operator must have at least one byte (opcode)");
1361
1374
  const path = hexToBytes(constraint.path);
1362
- if (path.length === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step.");
1363
- if ((path.length & 1) !== 0) throw new CallciumError("MALFORMED_PATH", "Path byte length must be even.");
1375
+ if (path.length === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step");
1376
+ if ((path.length & 1) !== 0) throw new CallciumError("MALFORMED_PATH", "Path byte length must be even");
1364
1377
  const depth = path.length / 2;
1365
- if (depth > Limits.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depth} exceeds maximum ${Limits.MAX_PATH_DEPTH}.`);
1378
+ if (depth > PolicyFormat.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depth} exceeds maximum ${PolicyFormat.MAX_PATH_DEPTH}`);
1366
1379
  const hint = constraint.scope === Scope.CALLDATA ? Descriptor.compileHint(desc, parsePathSteps(constraint.path)) : new Uint8Array(0);
1367
1380
  return operators.map((operator) => ({
1368
1381
  scope: constraint.scope,
@@ -1389,7 +1402,7 @@ function sortRules(rules) {
1389
1402
  }
1390
1403
  function encodeRule(rule) {
1391
1404
  const depth = rule.path.length / 2;
1392
- if (rule.scope === Scope.CONTEXT && depth !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step.");
1405
+ if (rule.scope === Scope.CONTEXT && depth !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step");
1393
1406
  const data = rule.operator.subarray(1);
1394
1407
  const ruleSize = PolicyFormat.RULE_FIXED_OVERHEAD + rule.path.length + rule.hint.length + data.length;
1395
1408
  if (ruleSize > 65535) throw new CallciumError("RULE_SIZE_OVERFLOW", `Rule size ${ruleSize} exceeds maximum 65535`);
@@ -1557,6 +1570,18 @@ function hintMismatch(groupIndex, constraintIndex) {
1557
1570
  message: "Hint does not match the path compiled against the descriptor"
1558
1571
  };
1559
1572
  }
1573
+ function contextTypeMismatch(groupIndex, constraintIndex, contextId, typeCode) {
1574
+ return {
1575
+ severity: "error",
1576
+ category: "typeMismatch",
1577
+ groupIndex,
1578
+ constraintIndex,
1579
+ code: "CONTEXT_TYPE_MISMATCH",
1580
+ value1: contextId,
1581
+ value2: typeCode,
1582
+ message: "Context property type does not match the target type"
1583
+ };
1584
+ }
1560
1585
  function nonCanonicalOperand(groupIndex, constraintIndex, operand, canonical) {
1561
1586
  return {
1562
1587
  severity: "error",
@@ -1722,7 +1747,7 @@ function unsortedInSet(groupIndex, constraintIndex) {
1722
1747
  code: "UNSORTED_IN_SET",
1723
1748
  value1: ZERO,
1724
1749
  value2: ZERO,
1725
- message: "isIn/notIn set is not strictly sorted and deduplicated."
1750
+ message: "isIn/notIn set is not strictly sorted and deduplicated"
1726
1751
  };
1727
1752
  }
1728
1753
  function emptyGroup(groupIndex) {
@@ -1734,7 +1759,7 @@ function emptyGroup(groupIndex) {
1734
1759
  code: "EMPTY_GROUP",
1735
1760
  value1: ZERO,
1736
1761
  value2: ZERO,
1737
- message: "Group contains zero constraints."
1762
+ message: "Group contains zero constraints"
1738
1763
  };
1739
1764
  }
1740
1765
  function dominatedBound(isLength, groupIndex, constraintIndex, value) {
@@ -1919,7 +1944,7 @@ function negationUnderAny(groupIndex, constraintIndex, opCode) {
1919
1944
  }
1920
1945
  //#endregion
1921
1946
  //#region src/policy-validator.ts
1922
- const NAVIGABILITY_CODES = new Set([
1947
+ const navigabilityCodes = new Set([
1923
1948
  "EMPTY_PATH",
1924
1949
  "PARAM_INDEX_OUT_OF_BOUNDS",
1925
1950
  "TUPLE_FIELD_OUT_OF_BOUNDS",
@@ -2006,6 +2031,10 @@ function getIncompat(opBase, typeInfo) {
2006
2031
  code: "IN_ON_BOOL",
2007
2032
  message: "IN operator used on boolean type"
2008
2033
  };
2034
+ if (opBase === Op.EQ_CTX && typeCode !== TypeCode.ADDRESS && !(typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX)) return {
2035
+ code: "CONTEXT_TYPE_MISMATCH",
2036
+ message: "Context reference operator used on incompatible type"
2037
+ };
2009
2038
  return null;
2010
2039
  }
2011
2040
  if (isLengthOp(opBase)) {
@@ -2334,7 +2363,11 @@ function validateConstraint(ctx, constraint, groupIndex, constraintIndex, issues
2334
2363
  issues.push(fromOpRule(incompat.code, incompat.message, groupIndex, constraintIndex, bigintToHex(BigInt(opCode))));
2335
2364
  continue;
2336
2365
  }
2337
- if (isValueOp(base)) {
2366
+ if (base === Op.EQ_CTX) {
2367
+ const ctxOperand = readValue(opHex);
2368
+ if (ctxOperand > BigInt(MAX_CONTEXT_PROPERTY_ID)) issues.push(unknownContextProperty(groupIndex, constraintIndex, bigintToHex(ctxOperand), bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID))));
2369
+ else if (lookupContextProperty(Number(ctxOperand)).typeCode === TypeCode.ADDRESS !== (ctx.typeInfo.typeCode === TypeCode.ADDRESS)) issues.push(contextTypeMismatch(groupIndex, constraintIndex, bigintToHex(ctxOperand), bigintToHex(BigInt(ctx.typeInfo.typeCode))));
2370
+ } else if (isValueOp(base)) {
2338
2371
  const typeCode = ctx.typeInfo.typeCode;
2339
2372
  if (isLeftAligned(typeCode)) {
2340
2373
  const nonCanonical = findNonCanonicalWord(opHex, typeCode);
@@ -2389,12 +2422,12 @@ function validateGroup(data, descBytes, groupIndex, issues) {
2389
2422
  const steps = parsePathSteps(constraint.path);
2390
2423
  let typeInfo;
2391
2424
  if (constraint.scope === Scope.CALLDATA) {
2392
- if (steps.length > Limits.MAX_PATH_DEPTH) issues.push(pathDepthExceeded(groupIndex, constraintIndex, bigintToHex(BigInt(steps.length)), bigintToHex(BigInt(Limits.MAX_PATH_DEPTH))));
2425
+ if (steps.length > PolicyFormat.MAX_PATH_DEPTH) issues.push(pathDepthExceeded(groupIndex, constraintIndex, bigintToHex(BigInt(steps.length)), bigintToHex(BigInt(PolicyFormat.MAX_PATH_DEPTH))));
2393
2426
  let walk;
2394
2427
  try {
2395
2428
  walk = Descriptor.walkPath(descBytes, steps);
2396
2429
  } catch (error) {
2397
- if (error instanceof CallciumError && NAVIGABILITY_CODES.has(error.code)) {
2430
+ if (error instanceof CallciumError && navigabilityCodes.has(error.code)) {
2398
2431
  issues.push(unnavigablePath(groupIndex, constraintIndex));
2399
2432
  continue;
2400
2433
  }
@@ -2404,7 +2437,7 @@ function validateGroup(data, descBytes, groupIndex, issues) {
2404
2437
  issues.push(nestedQuantifier(groupIndex, constraintIndex));
2405
2438
  continue;
2406
2439
  }
2407
- if (walk.quantifiedStaticLength > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) issues.push(quantifierOverStaticLimit(groupIndex, constraintIndex, bigintToHex(BigInt(walk.quantifiedStaticLength)), bigintToHex(BigInt(Limits.MAX_QUANTIFIED_ARRAY_LENGTH))));
2440
+ if (walk.quantifiedStaticLength > PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH) issues.push(quantifierOverStaticLimit(groupIndex, constraintIndex, bigintToHex(BigInt(walk.quantifiedStaticLength)), bigintToHex(BigInt(PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH))));
2408
2441
  typeInfo = walk.typeInfo;
2409
2442
  } else {
2410
2443
  const ctxId = steps[0];
@@ -2424,7 +2457,7 @@ function validateGroup(data, descBytes, groupIndex, issues) {
2424
2457
  ctx = initContext(constraint.scope, normalizedPath, steps, typeInfo);
2425
2458
  contexts.push(ctx);
2426
2459
  }
2427
- if (constraint.scope === Scope.CALLDATA && constraint.hint !== void 0 && ctx.steps.length <= Limits.MAX_PATH_DEPTH) {
2460
+ if (constraint.scope === Scope.CALLDATA && constraint.hint !== void 0 && ctx.steps.length <= PolicyFormat.MAX_PATH_DEPTH) {
2428
2461
  if (bytesToHex(Descriptor.compileHint(descBytes, ctx.steps)) !== constraint.hint.toLowerCase()) issues.push(hintMismatch(groupIndex, constraintIndex));
2429
2462
  }
2430
2463
  validateConstraint(ctx, constraint, groupIndex, constraintIndex, issues);
@@ -2481,14 +2514,14 @@ const SignatureParser = { parse };
2481
2514
  //#endregion
2482
2515
  //#region src/policy-builder.ts
2483
2516
  function validateContextPath(steps) {
2484
- if (steps.length !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope path must be exactly one step.");
2517
+ if (steps.length !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope path must be exactly one step");
2485
2518
  const step = steps[0];
2486
- if (step > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID 0x${step.toString(16).padStart(4, "0")}.`);
2519
+ if (step > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID 0x${step.toString(16).padStart(4, "0")}`);
2487
2520
  }
2488
2521
  function validateCalldataPath(steps, desc) {
2489
2522
  const argIndex = steps[0];
2490
2523
  const paramCount = Descriptor.paramCount(desc);
2491
- if (argIndex >= paramCount) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${argIndex} out of range (descriptor has ${paramCount} params).`);
2524
+ if (argIndex >= paramCount) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${argIndex} out of range (descriptor has ${paramCount} params)`);
2492
2525
  let offset = Descriptor.paramOffset(desc, argIndex);
2493
2526
  let hasQuantifier = false;
2494
2527
  for (let i = 1; i < steps.length; i++) {
@@ -2496,21 +2529,21 @@ function validateCalldataPath(steps, desc) {
2496
2529
  const info = Descriptor.inspect(desc, offset);
2497
2530
  const isArray = info.typeCode === TypeCode.STATIC_ARRAY || info.typeCode === TypeCode.DYNAMIC_ARRAY;
2498
2531
  if (isQuantifier(step)) {
2499
- if (!isArray) throw new CallciumError("QUANTIFIER_ON_NON_ARRAY", "Quantifier step is only valid on an array node.");
2500
- if (hasQuantifier) throw new CallciumError("NESTED_QUANTIFIER", "Nested quantifiers are not allowed.");
2532
+ if (!isArray) throw new CallciumError("QUANTIFIER_ON_NON_ARRAY", "Quantifier step is only valid on an array node");
2533
+ if (hasQuantifier) throw new CallciumError("NESTED_QUANTIFIER", "Nested quantifiers are not allowed");
2501
2534
  hasQuantifier = true;
2502
2535
  }
2503
2536
  if (info.typeCode === TypeCode.TUPLE) {
2504
2537
  const fieldCount = Descriptor.tupleFieldCount(desc, offset);
2505
- if (step >= fieldCount) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fieldCount} fields).`);
2538
+ if (step >= fieldCount) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fieldCount} fields)`);
2506
2539
  offset = Descriptor.tupleFieldOffset(desc, offset, step);
2507
2540
  } else if (isArray) {
2508
2541
  if (!isQuantifier(step) && info.typeCode === TypeCode.STATIC_ARRAY) {
2509
2542
  const arrayLength = Descriptor.staticArrayLength(desc, offset);
2510
- if (step >= arrayLength) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (static array has ${arrayLength} elements).`);
2543
+ if (step >= arrayLength) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (static array has ${arrayLength} elements)`);
2511
2544
  }
2512
2545
  offset = Descriptor.arrayElementOffset(offset);
2513
- } else throw new CallciumError("NOT_COMPOSITE", "Cannot descend into an elementary type.");
2546
+ } else throw new CallciumError("NOT_COMPOSITE", "Cannot descend into an elementary type");
2514
2547
  }
2515
2548
  }
2516
2549
  var PolicyBuilder = class PolicyBuilder {
@@ -2544,21 +2577,21 @@ var PolicyBuilder = class PolicyBuilder {
2544
2577
  path: constraint.path,
2545
2578
  operators: [...constraint.operators]
2546
2579
  };
2547
- if (c.operators.length === 0) throw new CallciumError("NO_CONSTRAINT_OPERATORS", "Constraint must have at least one operator.");
2580
+ if (c.operators.length === 0) throw new CallciumError("NO_CONSTRAINT_OPERATORS", "Constraint must have at least one operator");
2548
2581
  const steps = parsePathSteps(c.path);
2549
- if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step.");
2582
+ if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step");
2550
2583
  if (c.scope === Scope.CONTEXT) validateContextPath(steps);
2551
2584
  else if (c.scope === Scope.CALLDATA) validateCalldataPath(steps, this.draft.descriptor);
2552
- else throw new CallciumError("INVALID_SCOPE", `Unknown scope value ${c.scope}.`);
2585
+ else throw new CallciumError("INVALID_SCOPE", `Unknown scope value ${c.scope}`);
2553
2586
  const key = `${c.scope}:${c.path.toLowerCase()}`;
2554
2587
  const currentHashes = this.draft.pathHashes[this.draft.pathHashes.length - 1];
2555
- if (currentHashes.has(key)) throw new CallciumError("DUPLICATE_PATH_IN_GROUP", `Duplicate path ${c.path} in the same group.`);
2588
+ if (currentHashes.has(key)) throw new CallciumError("DUPLICATE_PATH_IN_GROUP", `Duplicate path ${c.path} in the same group`);
2556
2589
  currentHashes.add(key);
2557
2590
  this.draft.groups[this.draft.groups.length - 1].push(c);
2558
2591
  return this;
2559
2592
  }
2560
2593
  or() {
2561
- if (this.draft.groups[this.draft.groups.length - 1].length === 0) throw new CallciumError("EMPTY_GROUP", "Cannot start a new group when the current group is empty.");
2594
+ if (this.draft.groups[this.draft.groups.length - 1].length === 0) throw new CallciumError("EMPTY_GROUP", "Cannot start a new group when the current group is empty");
2562
2595
  this.draft.groups.push([]);
2563
2596
  this.draft.pathHashes.push(/* @__PURE__ */ new Set());
2564
2597
  return this;
@@ -2580,7 +2613,7 @@ var PolicyBuilder = class PolicyBuilder {
2580
2613
  return PolicyValidator.validate(policyData);
2581
2614
  }
2582
2615
  checkGroups() {
2583
- for (let i = 0; i < this.draft.groups.length; i++) if (this.draft.groups[i].length === 0) throw new CallciumError("EMPTY_GROUP", `Group ${i} is empty.`);
2616
+ for (let i = 0; i < this.draft.groups.length; i++) if (this.draft.groups[i].length === 0) throw new CallciumError("EMPTY_GROUP", `Group ${i} is empty`);
2584
2617
  }
2585
2618
  toPolicyData() {
2586
2619
  return {
@@ -2603,8 +2636,7 @@ function encodeWord(value) {
2603
2636
  return word;
2604
2637
  }
2605
2638
  if (typeof value === "string") {
2606
- const body = addressBody(value);
2607
- for (let i = 0; i < 20; i++) word[12 + i] = parseInt(body.substring(i * 2, i * 2 + 2), 16);
2639
+ word.set(hexToBytes(addressBody(value)), 12);
2608
2640
  return word;
2609
2641
  }
2610
2642
  let bigValue = typeof value === "number" ? BigInt(value) : value;
@@ -2628,7 +2660,10 @@ function rangeOp(opCode, min, max) {
2628
2660
  buffer.set(encodeWord(max), 33);
2629
2661
  return bytesToHex(buffer);
2630
2662
  }
2631
- const MAX_SET_MEMBERS = Limits.MAX_SET_MEMBERS;
2663
+ function checkContextPropertyId(contextPropertyId) {
2664
+ if (!Number.isInteger(contextPropertyId) || contextPropertyId < 0 || contextPropertyId > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID ${contextPropertyId}`);
2665
+ return contextPropertyId;
2666
+ }
2632
2667
  function setOp(opCode, values) {
2633
2668
  const bigs = values.map((v) => {
2634
2669
  if (typeof v === "bigint") return v;
@@ -2636,21 +2671,14 @@ function setOp(opCode, values) {
2636
2671
  if (typeof v === "boolean") return v ? 1n : 0n;
2637
2672
  return BigInt("0x" + addressBody(v));
2638
2673
  });
2639
- bigs.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
2640
- const deduped = [];
2641
- for (const value of bigs) if (deduped.length === 0 || deduped[deduped.length - 1] !== value) deduped.push(value);
2674
+ const deduped = [...new Set(bigs)].toSorted((a, b) => a < b ? -1 : a > b ? 1 : 0);
2642
2675
  if (deduped.length === 0) throw new CallciumError("EMPTY_SET", "Set must contain at least one value");
2643
- if (deduped.length > MAX_SET_MEMBERS) throw new CallciumError("SET_TOO_LARGE", `Set must contain at most ${MAX_SET_MEMBERS} values`);
2676
+ if (deduped.length > PolicyFormat.MAX_SET_MEMBERS) throw new CallciumError("SET_TOO_LARGE", `Set must contain at most ${PolicyFormat.MAX_SET_MEMBERS} values`);
2644
2677
  const buffer = new Uint8Array(1 + deduped.length * 32);
2645
2678
  buffer[0] = opCode;
2646
2679
  for (let i = 0; i < deduped.length; i++) buffer.set(encodeWord(deduped[i]), 1 + i * 32);
2647
2680
  return bytesToHex(buffer);
2648
2681
  }
2649
- function encodePath(steps) {
2650
- const buffer = new Uint8Array(steps.length * 2);
2651
- for (let i = 0; i < steps.length; i++) writeBE16(buffer, i * 2, steps[i]);
2652
- return bytesToHex(buffer);
2653
- }
2654
2682
  var ConstraintBuilder = class {
2655
2683
  scope;
2656
2684
  path;
@@ -2670,6 +2698,12 @@ var ConstraintBuilder = class {
2670
2698
  neq(value) {
2671
2699
  return this.push(singleOp(Op.EQ | Op.NOT, value));
2672
2700
  }
2701
+ eqCtx(contextPropertyId) {
2702
+ return this.push(singleOp(Op.EQ_CTX, checkContextPropertyId(contextPropertyId)));
2703
+ }
2704
+ neqCtx(contextPropertyId) {
2705
+ return this.push(singleOp(Op.EQ_CTX | Op.NOT, checkContextPropertyId(contextPropertyId)));
2706
+ }
2673
2707
  gt(bound) {
2674
2708
  return this.push(singleOp(Op.GT, bound));
2675
2709
  }
@@ -2762,17 +2796,14 @@ function readPointer(callData, head) {
2762
2796
  }
2763
2797
  //#endregion
2764
2798
  //#region src/policy-enforcer.ts
2765
- const ABORT_VIOLATION_CODES = new Set([
2799
+ const abortViolationCodes = new Set([
2766
2800
  "CALLDATA_OUT_OF_BOUNDS",
2767
2801
  "ARRAY_INDEX_OUT_OF_BOUNDS",
2768
2802
  "NON_CANONICAL_VALUE",
2769
2803
  "QUANTIFIER_LIMIT_EXCEEDED"
2770
2804
  ]);
2771
2805
  function addressToBigInt(hex) {
2772
- const bytes = hexToBytes(hex);
2773
- const padded = new Uint8Array(32);
2774
- padded.set(bytes, 32 - bytes.length);
2775
- return toBigInt(padded, 0);
2806
+ return BigInt(toAddress(hex));
2776
2807
  }
2777
2808
  function check(policy, callData, context) {
2778
2809
  const { policy: decoded, data: policyBytes } = decodePolicy(policy);
@@ -2803,7 +2834,7 @@ function check(policy, callData, context) {
2803
2834
  const violation = evaluateRule(rule, policyBytes, callDataBytes, baseOffset, groupIndex, ruleIndex, context);
2804
2835
  if (violation !== null) {
2805
2836
  allViolations.push(violation);
2806
- if (ABORT_VIOLATION_CODES.has(violation.code)) return {
2837
+ if (abortViolationCodes.has(violation.code)) return {
2807
2838
  ok: false,
2808
2839
  violations: allViolations
2809
2840
  };
@@ -2833,7 +2864,13 @@ function evaluateRule(rule, policyBytes, callDataBytes, baseOffset, groupIndex,
2833
2864
  const operandData = fieldBytes(policyBytes, rule.data);
2834
2865
  if (rule.scope.value === Scope.CONTEXT) return evaluateContextRule(fieldBytes(policyBytes, rule.path), opCode, operandData, groupIndex, ruleIndex, rule.path.value, context);
2835
2866
  if (rule.hint === void 0) throw new CallciumError("MALFORMED_HINT", "Calldata rule carries no hint block");
2836
- return evaluateCalldataRule(fieldBytes(policyBytes, rule.hint), callDataBytes, baseOffset, opCode, operandData, groupIndex, ruleIndex, rule.path.value);
2867
+ return evaluateCalldataRule(fieldBytes(policyBytes, rule.hint), callDataBytes, baseOffset, opCode, operandData, groupIndex, ruleIndex, rule.path.value, context);
2868
+ }
2869
+ function resolveContextOperand(operandData, context) {
2870
+ const propertyInfo = lookupContextProperty(Number(toBigInt(operandData, 0)));
2871
+ const value = context?.[propertyInfo.contextKey];
2872
+ if (value === void 0) return { ctxTypeCode: propertyInfo.typeCode };
2873
+ return typeof value === "string" ? addressToBigInt(value) : value;
2837
2874
  }
2838
2875
  function readTargetBlock(hint, targetOffset) {
2839
2876
  return {
@@ -2874,10 +2911,10 @@ function chainResolve(hint, callData, base, hopsOffset, hopCount) {
2874
2911
  }
2875
2912
  return base;
2876
2913
  }
2877
- function evalTarget(callData, target, block, opCode, operandData) {
2914
+ function evalTarget(callData, target, block, opCode, operandData, context) {
2878
2915
  const typeCode = block.typeCode;
2879
2916
  if (isLengthValidType(typeCode)) {
2880
- if (!isLengthOp(opCode)) throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Value operator on a target without a scalar word.");
2917
+ if (!isLengthOp(opCode)) throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Value operator on a target without a scalar word");
2881
2918
  const length = readPointer(callData, target);
2882
2919
  if (typeof length !== "number") return { error: length.code };
2883
2920
  const stride = typeCode === TypeCode.DYNAMIC_ARRAY ? (block.targetMeta & PolicyFormat.HINT_META_STRIDE_MASK) * 32 : 1;
@@ -2888,7 +2925,7 @@ function evalTarget(callData, target, block, opCode, operandData) {
2888
2925
  value: BigInt(length)
2889
2926
  };
2890
2927
  }
2891
- if (classifyTypeCode(typeCode).typeClass !== "elementary") throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Operator target does not carry a scalar word.");
2928
+ if (classifyTypeCode(typeCode).typeClass !== "elementary") throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Operator target does not carry a scalar word");
2892
2929
  const word = loadWord(callData, target);
2893
2930
  if (!(word instanceof Uint8Array)) return { error: word.code };
2894
2931
  const value = toBigInt(word, 0);
@@ -2896,6 +2933,18 @@ function evalTarget(callData, target, block, opCode, operandData) {
2896
2933
  error: "NON_CANONICAL_VALUE",
2897
2934
  value
2898
2935
  };
2936
+ if ((opCode & ~Op.NOT) === Op.EQ_CTX) {
2937
+ const operand = resolveContextOperand(operandData, context);
2938
+ if (typeof operand !== "bigint") return {
2939
+ error: "MISSING_CONTEXT",
2940
+ ctxTypeCode: operand.ctxTypeCode
2941
+ };
2942
+ return {
2943
+ passed: value === operand !== ((opCode & Op.NOT) !== 0),
2944
+ value,
2945
+ ctxOperand: operand
2946
+ };
2947
+ }
2899
2948
  return {
2900
2949
  passed: applyOperator(opCode, value, 32, operandData, typeCode),
2901
2950
  value
@@ -2916,6 +2965,16 @@ function navigationViolation(frame, code, elementIndex) {
2916
2965
  }
2917
2966
  function targetViolation(frame, result, elementIndex) {
2918
2967
  if ("error" in result) {
2968
+ if (result.error === "MISSING_CONTEXT") return {
2969
+ group: frame.group,
2970
+ rule: frame.rule,
2971
+ code: result.error,
2972
+ scope: Scope.CALLDATA,
2973
+ path: frame.path,
2974
+ opCode: frame.opCode,
2975
+ operandData: bytesToHex(frame.operandData),
2976
+ typeCode: result.ctxTypeCode
2977
+ };
2919
2978
  if (result.error === "NON_CANONICAL_VALUE") return {
2920
2979
  group: frame.group,
2921
2980
  rule: frame.rule,
@@ -2941,15 +3000,16 @@ function targetViolation(frame, result, elementIndex) {
2941
3000
  operandData: bytesToHex(frame.operandData),
2942
3001
  typeCode: frame.typeCode,
2943
3002
  resolvedValue: bigintToHex(result.value),
3003
+ ...result.ctxOperand !== void 0 && { resolvedOperand: bigintToHex(result.ctxOperand) },
2944
3004
  ...elementIndex !== void 0 && { elementIndex }
2945
3005
  };
2946
3006
  }
2947
- function evaluateCalldataRule(hint, callDataBytes, baseOffset, opCode, operandData, groupIndex, ruleIndex, pathHex) {
3007
+ function evaluateCalldataRule(hint, callDataBytes, baseOffset, opCode, operandData, groupIndex, ruleIndex, pathHex, context) {
2948
3008
  const header = hint[0];
2949
3009
  const hopCount = header & PolicyFormat.HINT_HOP_COUNT_MASK;
2950
3010
  const kind = header >> PolicyFormat.HINT_KIND_SHIFT;
2951
3011
  const hopsOffset = PolicyFormat.HINT_HEADER_SIZE;
2952
- if (kind !== PolicyFormat.HINT_KIND_NONE) return evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, hopCount, opCode, operandData, groupIndex, ruleIndex, pathHex);
3012
+ if (kind !== PolicyFormat.HINT_KIND_NONE) return evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, hopCount, opCode, operandData, groupIndex, ruleIndex, pathHex, context);
2953
3013
  const block = readTargetBlock(hint, hopsOffset + hopCount * PolicyFormat.HINT_HOP_SIZE);
2954
3014
  const frame = {
2955
3015
  group: groupIndex,
@@ -2961,9 +3021,9 @@ function evaluateCalldataRule(hint, callDataBytes, baseOffset, opCode, operandDa
2961
3021
  };
2962
3022
  const base = chainResolve(hint, callDataBytes, baseOffset, hopsOffset, hopCount);
2963
3023
  if (typeof base !== "number") return navigationViolation(frame, base.code);
2964
- return targetViolation(frame, evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData));
3024
+ return targetViolation(frame, evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, context));
2965
3025
  }
2966
- function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, hopCount, opCode, operandData, groupIndex, ruleIndex, pathHex) {
3026
+ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, hopCount, opCode, operandData, groupIndex, ruleIndex, pathHex, context) {
2967
3027
  const frameOffset = hopsOffset + hopCount * PolicyFormat.HINT_HOP_SIZE;
2968
3028
  const suffixHeaderOffset = frameOffset + PolicyFormat.HINT_FRAME_PREFIX_SIZE;
2969
3029
  const suffixHopCount = hint[suffixHeaderOffset] & PolicyFormat.HINT_HOP_COUNT_MASK;
@@ -2987,7 +3047,7 @@ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, h
2987
3047
  count = length;
2988
3048
  elems += 32;
2989
3049
  }
2990
- if (count > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) return {
3050
+ if (count > PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH) return {
2991
3051
  group: groupIndex,
2992
3052
  rule: ruleIndex,
2993
3053
  code: "QUANTIFIER_LIMIT_EXCEEDED",
@@ -3009,6 +3069,7 @@ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, h
3009
3069
  const meta = readU16(hint, frameOffset + PolicyFormat.HINT_FRAME_META_OFFSET);
3010
3070
  const elemStride = (meta & PolicyFormat.HINT_META_STRIDE_MASK) * 32;
3011
3071
  const elemIsDynamic = (meta & PolicyFormat.HINT_META_ELEM_DYNAMIC) !== 0;
3072
+ let ctxOperand;
3012
3073
  for (let elemIndex = 0; elemIndex < count; elemIndex++) {
3013
3074
  const slot = elems + elemIndex * elemStride;
3014
3075
  let elem = slot;
@@ -3023,11 +3084,12 @@ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, h
3023
3084
  if (typeof chainedElem !== "number") return navigationViolation(frame, chainedElem.code, elemIndex);
3024
3085
  base = chainedElem;
3025
3086
  }
3026
- const applied = evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData);
3087
+ const applied = evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, context);
3027
3088
  if ("error" in applied) return targetViolation(frame, applied, elemIndex);
3028
3089
  if (applied.passed) {
3029
3090
  if (!isUniversal) return null;
3030
3091
  } else if (isUniversal) return targetViolation(frame, applied, elemIndex);
3092
+ ctxOperand = applied.ctxOperand;
3031
3093
  }
3032
3094
  if (isUniversal) return null;
3033
3095
  return {
@@ -3038,26 +3100,44 @@ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, h
3038
3100
  path: pathHex,
3039
3101
  opCode,
3040
3102
  operandData: bytesToHex(operandData),
3041
- typeCode: block.typeCode
3103
+ typeCode: block.typeCode,
3104
+ ...ctxOperand !== void 0 && { resolvedOperand: bigintToHex(ctxOperand) }
3042
3105
  };
3043
3106
  }
3044
3107
  function evaluateContextRule(pathBytes, opCode, operandData, groupIndex, ruleIndex, pathHex, context) {
3045
3108
  const propertyId = readU16(pathBytes, 0);
3046
3109
  if (propertyId > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID 0x${propertyId.toString(16).padStart(4, "0")}`);
3047
- const propInfo = lookupContextProperty(propertyId);
3048
- const contextValue = context?.[propInfo.contextKey];
3110
+ const propertyInfo = lookupContextProperty(propertyId);
3111
+ const contextValue = context?.[propertyInfo.contextKey];
3049
3112
  if (contextValue === void 0) return {
3050
3113
  group: groupIndex,
3051
3114
  rule: ruleIndex,
3052
3115
  code: "MISSING_CONTEXT",
3053
3116
  scope: Scope.CONTEXT,
3054
3117
  path: pathHex,
3055
- typeCode: propInfo.typeCode
3118
+ typeCode: propertyInfo.typeCode
3056
3119
  };
3057
3120
  let value;
3058
3121
  if (typeof contextValue === "string") value = addressToBigInt(contextValue);
3059
3122
  else value = contextValue;
3060
- if (!applyOperator(opCode, value, 32, operandData, propInfo.typeCode)) return {
3123
+ let result;
3124
+ let ctxOperand;
3125
+ if ((opCode & ~Op.NOT) === Op.EQ_CTX) {
3126
+ const operand = resolveContextOperand(operandData, context);
3127
+ if (typeof operand !== "bigint") return {
3128
+ group: groupIndex,
3129
+ rule: ruleIndex,
3130
+ code: "MISSING_CONTEXT",
3131
+ scope: Scope.CONTEXT,
3132
+ path: pathHex,
3133
+ opCode,
3134
+ operandData: bytesToHex(operandData),
3135
+ typeCode: operand.ctxTypeCode
3136
+ };
3137
+ result = value === operand !== ((opCode & Op.NOT) !== 0);
3138
+ ctxOperand = operand;
3139
+ } else result = applyOperator(opCode, value, 32, operandData, propertyInfo.typeCode);
3140
+ if (!result) return {
3061
3141
  group: groupIndex,
3062
3142
  rule: ruleIndex,
3063
3143
  code: "VALUE_MISMATCH",
@@ -3065,8 +3145,9 @@ function evaluateContextRule(pathBytes, opCode, operandData, groupIndex, ruleInd
3065
3145
  path: pathHex,
3066
3146
  opCode,
3067
3147
  operandData: bytesToHex(operandData),
3068
- typeCode: propInfo.typeCode,
3069
- resolvedValue: bigintToHex(value)
3148
+ typeCode: propertyInfo.typeCode,
3149
+ resolvedValue: bigintToHex(value),
3150
+ ...ctxOperand !== void 0 && { resolvedOperand: bigintToHex(ctxOperand) }
3070
3151
  };
3071
3152
  return null;
3072
3153
  }
@@ -3075,6 +3156,6 @@ const PolicyEnforcer = {
3075
3156
  enforce
3076
3157
  };
3077
3158
  //#endregion
3078
- export { CallciumError, ContextProperty, Descriptor, DescriptorCoder, Limits, Op, PolicyBuilder, PolicyCoder, PolicyEnforcer, PolicyFormat, PolicyValidator, PolicyViolationError, Quantifier, Scope, TypeCode, ValidationError, arg, baseFee, blockNumber, blockTimestamp, bytesToHex, chainId, gasPrice, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
3159
+ export { CallciumError, ContextProperty, Descriptor, DescriptorCoder, MAX_CONTEXT_PROPERTY_ID, Op, PolicyBuilder, PolicyCoder, PolicyEnforcer, PolicyFormat, PolicyValidator, PolicyViolationError, Quantifier, Scope, TypeCode, ValidationError, arg, baseFee, blockNumber, blockTimestamp, bytesToHex, chainId, gasPrice, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
3079
3160
 
3080
3161
  //# sourceMappingURL=index.js.map