@callcium/sdk 0.9.0 → 0.11.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,7 @@
1
1
  import { keccak_256 } from "@noble/hashes/sha3.js";
2
+ import { utf8ToBytes } from "@noble/hashes/utils.js";
2
3
  //#region src/errors.ts
3
- var CallciumError = class CallciumError extends Error {
4
+ var CallciumError = class extends Error {
4
5
  code;
5
6
  offset;
6
7
  constructor(code, message, offset) {
@@ -8,25 +9,23 @@ var CallciumError = class CallciumError extends Error {
8
9
  super(`${prefix}${message}`);
9
10
  this.name = "CallciumError";
10
11
  this.code = code;
11
- Object.setPrototypeOf(this, CallciumError.prototype);
12
+ if (offset !== void 0) this.offset = offset;
12
13
  }
13
14
  };
14
- var ValidationError = class ValidationError extends CallciumError {
15
+ var ValidationError = class extends CallciumError {
15
16
  issues;
16
17
  constructor(issues) {
17
18
  super("VALIDATION_ERROR", issues[0]?.message ?? "Policy validation failed");
18
19
  this.name = "ValidationError";
19
20
  this.issues = issues;
20
- Object.setPrototypeOf(this, ValidationError.prototype);
21
21
  }
22
22
  };
23
- var PolicyViolationError = class PolicyViolationError extends Error {
23
+ var PolicyViolationError = class extends Error {
24
24
  violations;
25
25
  constructor(violations) {
26
26
  super(formatDiagnostic(violations[0]));
27
27
  this.name = "PolicyViolationError";
28
28
  this.violations = violations;
29
- Object.setPrototypeOf(this, PolicyViolationError.prototype);
30
29
  }
31
30
  };
32
31
  function formatDiagnostic(violation) {
@@ -36,29 +35,29 @@ function formatDiagnostic(violation) {
36
35
  }
37
36
  //#endregion
38
37
  //#region src/bytes.ts
39
- const HEX_BODY_RE = /^[0-9a-fA-F]*$/;
38
+ const hexBodyPattern = /^[0-9a-fA-F]*$/;
40
39
  const HEX_DIGITS = "0123456789abcdef";
41
- const NIBBLE = new Int8Array(128).fill(-1);
40
+ const nibble = new Int8Array(128).fill(-1);
42
41
  for (let value = 0; value < 16; value++) {
43
- NIBBLE[HEX_DIGITS.charCodeAt(value)] = value;
44
- NIBBLE[HEX_DIGITS.toUpperCase().charCodeAt(value)] = value;
42
+ nibble[HEX_DIGITS.charCodeAt(value)] = value;
43
+ nibble[HEX_DIGITS.toUpperCase().charCodeAt(value)] = value;
45
44
  }
46
45
  function hexToBytes(hex) {
47
46
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
48
47
  if (clean.length % 2 !== 0) throw new CallciumError("INVALID_HEX", "Odd-length hex string");
49
48
  const bytes = new Uint8Array(clean.length / 2);
50
49
  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;
50
+ const high = nibble[clean.charCodeAt(i * 2)] ?? -1;
51
+ const low = nibble[clean.charCodeAt(i * 2 + 1)] ?? -1;
53
52
  if (high < 0 || low < 0) throw new CallciumError("INVALID_HEX", "Invalid hex characters");
54
53
  bytes[i] = high << 4 | low;
55
54
  }
56
55
  return bytes;
57
56
  }
58
- const HEX_LUT = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
57
+ const hexPairs = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
59
58
  function bytesToHex(bytes) {
60
59
  let body = "";
61
- for (let i = 0; i < bytes.length; i++) body += HEX_LUT[bytes[i]];
60
+ for (let i = 0; i < bytes.length; i++) body += hexPairs[bytes[i]];
62
61
  return `0x${body}`;
63
62
  }
64
63
  function toHex(data, start, end) {
@@ -94,7 +93,7 @@ function bigintToHex(value) {
94
93
  function toAddress(hex) {
95
94
  const body = hex.startsWith("0x") ? hex.slice(2) : hex;
96
95
  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");
96
+ if (!hexBodyPattern.test(body)) throw new CallciumError("INVALID_HEX", "Invalid hex characters in address");
98
97
  return `0x${body}`;
99
98
  }
100
99
  //#endregion
@@ -117,6 +116,12 @@ const DescriptorFormat = {
117
116
  MAX_PARAMS: 255,
118
117
  MAX_NESTING_DEPTH: 64
119
118
  };
119
+ const RULE_DATALENGTH_SIZE = 2;
120
+ const PATH_STEP_SIZE = 2;
121
+ const RULE_FIXED_OVERHEAD = 7;
122
+ function fieldMax(byteWidth) {
123
+ return 256 ** byteWidth - 1;
124
+ }
120
125
  const PolicyFormat = {
121
126
  VERSION: 2,
122
127
  VERSION_MASK: 15,
@@ -136,11 +141,12 @@ const PolicyFormat = {
136
141
  RULE_SCOPE_OFFSET: 2,
137
142
  RULE_DEPTH_OFFSET: 3,
138
143
  RULE_PATH_OFFSET: 4,
139
- PATH_STEP_SIZE: 2,
144
+ PATH_STEP_SIZE,
145
+ PATH_STEP_MAX: fieldMax(PATH_STEP_SIZE),
140
146
  RULE_OPCODE_SIZE: 1,
141
- RULE_DATALENGTH_SIZE: 2,
142
- RULE_FIXED_OVERHEAD: 7,
143
- RULE_MIN_SIZE: 9,
147
+ RULE_DATALENGTH_SIZE,
148
+ RULE_FIXED_OVERHEAD,
149
+ RULE_MIN_SIZE: RULE_FIXED_OVERHEAD + PATH_STEP_SIZE,
144
150
  HINT_HEADER_SIZE: 1,
145
151
  HINT_HOP_SIZE: 8,
146
152
  HINT_HOP_INDEX_OFFSET: 4,
@@ -163,10 +169,17 @@ const PolicyFormat = {
163
169
  HINT_META_ELEM_DYNAMIC: 32768,
164
170
  HINT_META_DYNAMIC_ARRAY: 16384,
165
171
  HINT_META_RESERVED_MASK: 12288,
166
- HINT_META_STRIDE_MASK: 4095
172
+ HINT_META_STRIDE_MASK: 4095,
173
+ MAX_PATH_DEPTH: 32,
174
+ MAX_QUANTIFIED_ARRAY_LENGTH: 256,
175
+ MAX_SET_MEMBERS: Math.floor(fieldMax(RULE_DATALENGTH_SIZE) / 32)
167
176
  };
177
+ function formatCode(code, byteWidth) {
178
+ if (!Number.isInteger(code) || code < 0 || code > fieldMax(byteWidth)) return String(code);
179
+ return `0x${code.toString(16).padStart(byteWidth * 2, "0")}`;
180
+ }
168
181
  function buildCodeMap(table) {
169
- return Object.fromEntries(table.map((e) => [e.key, e.code]));
182
+ return Object.fromEntries(table.map((entry) => [entry.key, entry.code]));
170
183
  }
171
184
  const SCOPE_TABLE = [{
172
185
  key: "CONTEXT",
@@ -178,7 +191,7 @@ const SCOPE_TABLE = [{
178
191
  label: "calldata"
179
192
  }];
180
193
  const Scope = buildCodeMap(SCOPE_TABLE);
181
- const scopeByCode = new Map(SCOPE_TABLE.map((e) => [e.code, { label: e.label }]));
194
+ const scopeByCode = new Map(SCOPE_TABLE.map((entry) => [entry.code, { label: entry.label }]));
182
195
  function lookupScope(code) {
183
196
  const info = scopeByCode.get(code);
184
197
  if (!info) throw new CallciumError("INVALID_SCOPE", `Unknown scope value ${code}`);
@@ -200,83 +213,79 @@ const TypeCode = {
200
213
  DYNAMIC_ARRAY: 129,
201
214
  TUPLE: 144
202
215
  };
203
- const CTX_PROP_TABLE = [
216
+ const CTX_PROPERTY_TABLE = [
204
217
  {
205
218
  key: "MSG_SENDER",
206
219
  code: 0,
207
220
  label: "msg.sender",
221
+ contextKey: "msgSender",
208
222
  typeCode: TypeCode.ADDRESS
209
223
  },
210
224
  {
211
225
  key: "MSG_VALUE",
212
226
  code: 1,
213
227
  label: "msg.value",
228
+ contextKey: "msgValue",
214
229
  typeCode: TypeCode.UINT_MAX
215
230
  },
216
231
  {
217
232
  key: "BLOCK_TIMESTAMP",
218
233
  code: 2,
219
234
  label: "block.timestamp",
235
+ contextKey: "blockTimestamp",
220
236
  typeCode: TypeCode.UINT_MAX
221
237
  },
222
238
  {
223
239
  key: "BLOCK_NUMBER",
224
240
  code: 3,
225
241
  label: "block.number",
242
+ contextKey: "blockNumber",
226
243
  typeCode: TypeCode.UINT_MAX
227
244
  },
228
245
  {
229
246
  key: "CHAIN_ID",
230
247
  code: 4,
231
248
  label: "block.chainid",
249
+ contextKey: "chainId",
232
250
  typeCode: TypeCode.UINT_MAX
233
251
  },
234
252
  {
235
253
  key: "TX_ORIGIN",
236
254
  code: 5,
237
255
  label: "tx.origin",
256
+ contextKey: "txOrigin",
238
257
  typeCode: TypeCode.ADDRESS
239
258
  },
240
259
  {
241
260
  key: "BASE_FEE",
242
261
  code: 6,
243
262
  label: "block.basefee",
263
+ contextKey: "baseFee",
244
264
  typeCode: TypeCode.UINT_MAX
245
265
  },
246
266
  {
247
267
  key: "GAS_PRICE",
248
268
  code: 7,
249
269
  label: "tx.gasprice",
270
+ contextKey: "gasPrice",
250
271
  typeCode: TypeCode.UINT_MAX
251
272
  }
252
273
  ];
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, {
266
- label: e.label,
267
- contextKey: CTX_KEY_MAP[e.key],
268
- typeCode: e.typeCode
274
+ const ContextProperty = buildCodeMap(CTX_PROPERTY_TABLE);
275
+ const MAX_CONTEXT_PROPERTY_ID = Math.max(...CTX_PROPERTY_TABLE.map((entry) => entry.code));
276
+ const ctxPropertyByCode = new Map(CTX_PROPERTY_TABLE.map((entry) => [entry.code, {
277
+ label: entry.label,
278
+ contextKey: entry.contextKey,
279
+ typeCode: entry.typeCode
269
280
  }]));
281
+ function findContextProperty(code) {
282
+ return ctxPropertyByCode.get(code);
283
+ }
270
284
  function lookupContextProperty(code) {
271
- const info = ctxPropByCode.get(code);
272
- if (!info) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ${code}`);
285
+ const info = ctxPropertyByCode.get(code);
286
+ if (!info) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID ${formatCode(code, PATH_STEP_SIZE)}`);
273
287
  return info;
274
288
  }
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
289
  const OP_TABLE = [
281
290
  {
282
291
  key: "EQ",
@@ -385,23 +394,21 @@ const Op = {
385
394
  ...buildCodeMap(OP_TABLE),
386
395
  NOT: 128
387
396
  };
388
- const opByCode = new Map(OP_TABLE.map((e) => [e.code, {
389
- label: e.label,
390
- operands: e.operands
397
+ const opByCode = new Map(OP_TABLE.map((entry) => [entry.code, {
398
+ label: entry.label,
399
+ operands: entry.operands
391
400
  }]));
392
401
  function lookupOp(code) {
393
402
  const base = code & ~Op.NOT;
394
403
  const info = opByCode.get(base);
395
- if (!info) throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code 0x${base.toString(16).padStart(2, "0")}`);
404
+ if (!info) throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code ${formatCode(base, PolicyFormat.RULE_OPCODE_SIZE)}`);
396
405
  return info;
397
406
  }
398
- function isValidOperatorData(opBase, dataLength) {
399
- const operands = opByCode.get(opBase)?.operands;
400
- if (operands === "single") return dataLength === 32;
401
- if (operands === "range") return dataLength === 64;
402
- if (operands === "variadic") return dataLength > 0 && dataLength % 32 === 0;
403
- return false;
407
+ function operandsOf(opBase) {
408
+ return opByCode.get(opBase)?.operands;
404
409
  }
410
+ //#endregion
411
+ //#region src/path.ts
405
412
  const QUANTIFIER_TABLE = [{
406
413
  key: "ALL",
407
414
  code: 65535,
@@ -412,23 +419,347 @@ const QUANTIFIER_TABLE = [{
412
419
  label: "any"
413
420
  }];
414
421
  const Quantifier = buildCodeMap(QUANTIFIER_TABLE);
415
- const quantifierByCode = new Map(QUANTIFIER_TABLE.map((e) => [e.code, { label: e.label }]));
422
+ const quantifierByCode = new Map(QUANTIFIER_TABLE.map((entry) => [entry.code, { label: entry.label }]));
416
423
  function isQuantifier(step) {
417
424
  return step >= Quantifier.ANY;
418
425
  }
419
426
  function lookupQuantifier(code) {
420
427
  const info = quantifierByCode.get(code);
421
- if (!info) throw new CallciumError("UNKNOWN_QUANTIFIER", `Unknown quantifier step 0x${code.toString(16)}`);
428
+ if (!info) throw new CallciumError("UNKNOWN_QUANTIFIER", `Unknown quantifier step ${formatCode(code, PolicyFormat.PATH_STEP_SIZE)}`);
422
429
  return info;
423
430
  }
431
+ function encodePath(steps) {
432
+ const buffer = new Uint8Array(steps.length * PolicyFormat.PATH_STEP_SIZE);
433
+ for (let i = 0; i < steps.length; i++) {
434
+ const step = steps[i];
435
+ if (!Number.isInteger(step) || step < 0 || step > PolicyFormat.PATH_STEP_MAX) throw new CallciumError("PATH_STEP_OVERFLOW", `Path step ${step} is outside the path step field`);
436
+ writeBE16(buffer, i * PolicyFormat.PATH_STEP_SIZE, step);
437
+ }
438
+ return bytesToHex(buffer);
439
+ }
440
+ function parsePathSteps(path) {
441
+ const bytes = hexToBytes(path);
442
+ 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`);
443
+ const steps = [];
444
+ for (let offset = 0; offset < bytes.length; offset += PolicyFormat.PATH_STEP_SIZE) steps.push(readU16(bytes, offset));
445
+ return steps;
446
+ }
447
+ //#endregion
448
+ //#region src/constraint.ts
449
+ const OPERAND_MIN = -(2n ** 255n);
450
+ const OPERAND_MAX = 2n ** 256n - 1n;
451
+ function checkOperandRange(value) {
452
+ if (value < OPERAND_MIN || value > OPERAND_MAX) throw new CallciumError("OPERAND_OVERFLOW", `Operand ${value} is outside the range a 32-byte word represents`);
453
+ return value;
454
+ }
455
+ function addressBody(value) {
456
+ return toAddress(value).slice(2);
457
+ }
458
+ function encodeWord(value) {
459
+ const word = new Uint8Array(32);
460
+ if (typeof value === "boolean") {
461
+ word[31] = value ? 1 : 0;
462
+ return word;
463
+ }
464
+ if (typeof value === "string") {
465
+ word.set(hexToBytes(addressBody(value)), 12);
466
+ return word;
467
+ }
468
+ let bigValue = checkOperandRange(value);
469
+ for (let i = 31; i >= 0; i--) {
470
+ word[i] = Number(bigValue & 255n);
471
+ bigValue >>= 8n;
472
+ }
473
+ return word;
474
+ }
475
+ function singleOp(opCode, value) {
476
+ const buffer = new Uint8Array(33);
477
+ buffer[0] = opCode;
478
+ buffer.set(encodeWord(value), 1);
479
+ return bytesToHex(buffer);
480
+ }
481
+ function rangeOp(opCode, min, max) {
482
+ const minValue = checkOperandRange(min);
483
+ const maxValue = checkOperandRange(max);
484
+ if (minValue > maxValue) throw new CallciumError("INVALID_RANGE", `Range min (${minValue}) must not exceed max (${maxValue})`);
485
+ const buffer = new Uint8Array(65);
486
+ buffer[0] = opCode;
487
+ buffer.set(encodeWord(minValue), 1);
488
+ buffer.set(encodeWord(maxValue), 33);
489
+ return bytesToHex(buffer);
490
+ }
491
+ function contextOperand(contextPropertyId) {
492
+ lookupContextProperty(contextPropertyId);
493
+ return BigInt(contextPropertyId);
494
+ }
495
+ function setOp(opCode, values) {
496
+ const words = values.map((value) => {
497
+ if (typeof value === "bigint") return BigInt.asUintN(256, checkOperandRange(value));
498
+ if (typeof value === "boolean") return value ? 1n : 0n;
499
+ return BigInt("0x" + addressBody(value));
500
+ });
501
+ const deduped = [...new Set(words)].toSorted((a, b) => a < b ? -1 : a > b ? 1 : 0);
502
+ if (deduped.length === 0) throw new CallciumError("EMPTY_SET", "Set must contain at least one value");
503
+ if (deduped.length > PolicyFormat.MAX_SET_MEMBERS) throw new CallciumError("SET_TOO_LARGE", `Set must contain at most ${PolicyFormat.MAX_SET_MEMBERS} values`);
504
+ const buffer = new Uint8Array(1 + deduped.length * 32);
505
+ buffer[0] = opCode;
506
+ for (let i = 0; i < deduped.length; i++) buffer.set(encodeWord(deduped[i]), 1 + i * 32);
507
+ return bytesToHex(buffer);
508
+ }
509
+ function readOperandExtremes(constraint) {
510
+ return "operandExtremes" in constraint ? constraint.operandExtremes : void 0;
511
+ }
512
+ var ConstraintBuilder = class {
513
+ scope;
514
+ path;
515
+ operators;
516
+ operandExtremes;
517
+ constructor(scope, path) {
518
+ this.scope = scope;
519
+ this.path = path;
520
+ this.operators = [];
521
+ this.operandExtremes = {
522
+ leastNegative: 0n,
523
+ greatest: 0n
524
+ };
525
+ }
526
+ push(opHex, values = []) {
527
+ for (const value of values) {
528
+ if (typeof value !== "bigint") continue;
529
+ if (value < this.operandExtremes.leastNegative) this.operandExtremes.leastNegative = value;
530
+ if (value > this.operandExtremes.greatest) this.operandExtremes.greatest = value;
531
+ }
532
+ this.operators.push(opHex);
533
+ return this;
534
+ }
535
+ eq(value) {
536
+ return this.push(singleOp(Op.EQ, value), [value]);
537
+ }
538
+ neq(value) {
539
+ return this.push(singleOp(Op.EQ | Op.NOT, value), [value]);
540
+ }
541
+ eqCtx(contextPropertyId) {
542
+ return this.push(singleOp(Op.EQ_CTX, contextOperand(contextPropertyId)));
543
+ }
544
+ neqCtx(contextPropertyId) {
545
+ return this.push(singleOp(Op.EQ_CTX | Op.NOT, contextOperand(contextPropertyId)));
546
+ }
547
+ gt(bound) {
548
+ return this.push(singleOp(Op.GT, bound), [bound]);
549
+ }
550
+ lt(bound) {
551
+ return this.push(singleOp(Op.LT, bound), [bound]);
552
+ }
553
+ gte(bound) {
554
+ return this.push(singleOp(Op.GTE, bound), [bound]);
555
+ }
556
+ lte(bound) {
557
+ return this.push(singleOp(Op.LTE, bound), [bound]);
558
+ }
559
+ between(min, max) {
560
+ return this.push(rangeOp(Op.BETWEEN, min, max), [min, max]);
561
+ }
562
+ isIn(values) {
563
+ return this.push(setOp(Op.IN, values), values);
564
+ }
565
+ notIn(values) {
566
+ return this.push(setOp(Op.IN | Op.NOT, values), values);
567
+ }
568
+ bitmaskAll(mask) {
569
+ return this.push(singleOp(Op.BITMASK_ALL, mask));
570
+ }
571
+ bitmaskAny(mask) {
572
+ return this.push(singleOp(Op.BITMASK_ANY, mask));
573
+ }
574
+ bitmaskNone(mask) {
575
+ return this.push(singleOp(Op.BITMASK_NONE, mask));
576
+ }
577
+ lengthEq(length) {
578
+ return this.push(singleOp(Op.LENGTH_EQ, length));
579
+ }
580
+ lengthGt(length) {
581
+ return this.push(singleOp(Op.LENGTH_GT, length));
582
+ }
583
+ lengthLt(length) {
584
+ return this.push(singleOp(Op.LENGTH_LT, length));
585
+ }
586
+ lengthGte(length) {
587
+ return this.push(singleOp(Op.LENGTH_GTE, length));
588
+ }
589
+ lengthLte(length) {
590
+ return this.push(singleOp(Op.LENGTH_LTE, length));
591
+ }
592
+ lengthBetween(min, max) {
593
+ return this.push(rangeOp(Op.LENGTH_BETWEEN, min, max));
594
+ }
595
+ };
596
+ function arg(...steps) {
597
+ return new ConstraintBuilder(Scope.CALLDATA, encodePath(steps));
598
+ }
599
+ function msgSender() {
600
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.MSG_SENDER]));
601
+ }
602
+ function msgValue() {
603
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.MSG_VALUE]));
604
+ }
605
+ function blockTimestamp() {
606
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.BLOCK_TIMESTAMP]));
607
+ }
608
+ function blockNumber() {
609
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.BLOCK_NUMBER]));
610
+ }
611
+ function chainId() {
612
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.CHAIN_ID]));
613
+ }
614
+ function txOrigin() {
615
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.TX_ORIGIN]));
616
+ }
617
+ function baseFee() {
618
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.BASE_FEE]));
619
+ }
620
+ function gasPrice() {
621
+ return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.GAS_PRICE]));
622
+ }
623
+ //#endregion
624
+ //#region src/operators.ts
625
+ function toBigInt(bytes, offset = 0) {
626
+ const view = new DataView(bytes.buffer, bytes.byteOffset + offset, 32);
627
+ return view.getBigUint64(0, false) << 192n | view.getBigUint64(8, false) << 128n | view.getBigUint64(16, false) << 64n | view.getBigUint64(24, false);
628
+ }
629
+ function isSigned(typeCode) {
630
+ return typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX;
631
+ }
632
+ function isLeftAligned(typeCode) {
633
+ return typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX || typeCode === TypeCode.FUNCTION;
634
+ }
635
+ function canonicalize(value, typeCode) {
636
+ if (typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX) {
637
+ const bits = BigInt(typeCode * 8);
638
+ return bits === 256n ? value : value & (1n << bits) - 1n;
639
+ }
640
+ if (typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX) {
641
+ const bits = (typeCode - TypeCode.INT_MIN + 1) * 8;
642
+ return bits === 256 ? value : BigInt.asUintN(256, BigInt.asIntN(bits, value));
643
+ }
644
+ if (typeCode === TypeCode.ADDRESS) return value & (1n << 160n) - 1n;
645
+ if (typeCode === TypeCode.BOOL) return value & 1n;
646
+ if (typeCode === TypeCode.FUNCTION) return value >> 64n << 64n;
647
+ if (typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX) {
648
+ const n = typeCode - TypeCode.FIXED_BYTES_MIN + 1;
649
+ if (n === 32) return value;
650
+ const padBits = BigInt((32 - n) * 8);
651
+ return value >> padBits << padBits;
652
+ }
653
+ return value;
654
+ }
655
+ function isLengthOp(opCode) {
656
+ const base = opCode & ~Op.NOT;
657
+ return base >= Op.LENGTH_EQ && base <= Op.LENGTH_BETWEEN;
658
+ }
659
+ function lengthToValueOp(base) {
660
+ if (base === Op.LENGTH_EQ) return Op.EQ;
661
+ if (base === Op.LENGTH_GT) return Op.GT;
662
+ if (base === Op.LENGTH_LT) return Op.LT;
663
+ if (base === Op.LENGTH_GTE) return Op.GTE;
664
+ if (base === Op.LENGTH_LTE) return Op.LTE;
665
+ return Op.BETWEEN;
666
+ }
667
+ function isLengthValidType(typeCode) {
668
+ return typeCode === TypeCode.BYTES || typeCode === TypeCode.STRING || typeCode === TypeCode.DYNAMIC_ARRAY;
669
+ }
670
+ function compareTyped(a, b, typeCode) {
671
+ if (isSigned(typeCode)) {
672
+ const sa = BigInt.asIntN(256, a);
673
+ const sb = BigInt.asIntN(256, b);
674
+ return sa < sb ? -1 : sa > sb ? 1 : 0;
675
+ }
676
+ return a < b ? -1 : a > b ? 1 : 0;
677
+ }
678
+ function isIn(value, data) {
679
+ const count = data.length / 32;
680
+ let low = 0;
681
+ let high = count - 1;
682
+ while (low <= high) {
683
+ const mid = low + high >>> 1;
684
+ const elem = toBigInt(data, mid * 32);
685
+ if (value === elem) return true;
686
+ if (value < elem) high = mid - 1;
687
+ else low = mid + 1;
688
+ }
689
+ return false;
690
+ }
691
+ function applyOperator(opCode, value, valueLength, operandData, typeCode) {
692
+ let base = opCode & ~Op.NOT;
693
+ if (isLengthOp(base)) {
694
+ value = valueLength;
695
+ typeCode = TypeCode.UINT_MAX;
696
+ base = lengthToValueOp(base);
697
+ }
698
+ let result;
699
+ switch (base) {
700
+ case Op.EQ:
701
+ result = value === toBigInt(operandData, 0);
702
+ break;
703
+ case Op.GT:
704
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) > 0;
705
+ break;
706
+ case Op.LT:
707
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) < 0;
708
+ break;
709
+ case Op.GTE:
710
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) >= 0;
711
+ break;
712
+ case Op.LTE:
713
+ result = compareTyped(value, toBigInt(operandData, 0), typeCode) <= 0;
714
+ break;
715
+ case Op.BETWEEN: {
716
+ const min = toBigInt(operandData, 0);
717
+ const max = toBigInt(operandData, 32);
718
+ result = compareTyped(value, min, typeCode) >= 0 && compareTyped(value, max, typeCode) <= 0;
719
+ break;
720
+ }
721
+ case Op.IN:
722
+ result = isIn(value, operandData);
723
+ break;
724
+ case Op.BITMASK_ALL: {
725
+ const mask = toBigInt(operandData, 0);
726
+ result = (value & mask) === mask;
727
+ break;
728
+ }
729
+ case Op.BITMASK_ANY: {
730
+ const mask = toBigInt(operandData, 0);
731
+ result = (value & mask) !== 0n;
732
+ break;
733
+ }
734
+ case Op.BITMASK_NONE: {
735
+ const mask = toBigInt(operandData, 0);
736
+ result = (value & mask) === 0n;
737
+ break;
738
+ }
739
+ default: throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code ${formatCode(base, PolicyFormat.RULE_OPCODE_SIZE)}`);
740
+ }
741
+ return (opCode & Op.NOT) !== 0 ? !result : result;
742
+ }
743
+ function isValidOperatorData(opBase, dataLength) {
744
+ const operands = operandsOf(opBase);
745
+ if (operands === "single") return dataLength === 32;
746
+ if (operands === "range") return dataLength === 64;
747
+ if (operands === "variadic") return dataLength > 0 && dataLength % 32 === 0;
748
+ return false;
749
+ }
750
+ function toOperatorBytes(operator) {
751
+ const bytes = hexToBytes(operator);
752
+ if (bytes.length < 1) throw new CallciumError("INVALID_OPERATOR_BYTES", "Operator must have at least one byte (opcode)");
753
+ return bytes;
754
+ }
424
755
  function unknownTypeCode(code) {
425
- throw new CallciumError("UNKNOWN_TYPE_CODE", `Unknown type code 0x${code.toString(16).padStart(2, "0")}`);
756
+ throw new CallciumError("UNKNOWN_TYPE_CODE", `Unknown type code ${formatCode(code, DescriptorFormat.TYPECODE_SIZE)}`);
426
757
  }
427
758
  const ELEMENTARY = {
428
759
  typeClass: "elementary",
429
760
  isDynamic: false
430
761
  };
431
- const ELEMENTARY_DYN = {
762
+ const ELEMENTARY_DYNAMIC = {
432
763
  typeClass: "elementary",
433
764
  isDynamic: true
434
765
  };
@@ -450,7 +781,7 @@ function classifyTypeCode(code) {
450
781
  if (code === TypeCode.ADDRESS || code === TypeCode.BOOL || code === TypeCode.FUNCTION) return ELEMENTARY;
451
782
  if (code >= 68 && code <= 79) unknownTypeCode(code);
452
783
  if (code >= TypeCode.FIXED_BYTES_MIN && code <= TypeCode.FIXED_BYTES_MAX) return ELEMENTARY;
453
- if (code === TypeCode.BYTES || code === TypeCode.STRING) return ELEMENTARY_DYN;
784
+ if (code === TypeCode.BYTES || code === TypeCode.STRING) return ELEMENTARY_DYNAMIC;
454
785
  if (code >= 114 && code <= 127) unknownTypeCode(code);
455
786
  if (code === TypeCode.STATIC_ARRAY) return STATIC_ARR;
456
787
  if (code === TypeCode.DYNAMIC_ARRAY) return DYNAMIC_ARR;
@@ -458,6 +789,13 @@ function classifyTypeCode(code) {
458
789
  if (code === TypeCode.TUPLE) return TUPLE;
459
790
  unknownTypeCode(code);
460
791
  }
792
+ function isElementary(code) {
793
+ try {
794
+ return classifyTypeCode(code).typeClass === "elementary";
795
+ } catch {
796
+ return false;
797
+ }
798
+ }
461
799
  function isAddressableTarget(code) {
462
800
  let info;
463
801
  try {
@@ -489,9 +827,29 @@ function lookupTypeCode(code) {
489
827
  }
490
828
  //#endregion
491
829
  //#region src/descriptor.ts
830
+ function readNode(desc, offset) {
831
+ if (offset >= desc.length) throw new CallciumError("UNEXPECTED_END", "Unexpected end of descriptor", offset);
832
+ const typeCode = desc[offset];
833
+ const info = classifyTypeCode(typeCode);
834
+ if (info.typeClass === "elementary") return {
835
+ typeCode,
836
+ staticWords: info.isDynamic ? 0 : 1,
837
+ nodeLength: DescriptorFormat.TYPECODE_SIZE
838
+ };
839
+ if (offset + DescriptorFormat.TYPECODE_SIZE + DescriptorFormat.COMPOSITE_META_SIZE > desc.length) throw new CallciumError("UNEXPECTED_END", "Incomplete composite metadata", offset);
840
+ const meta = readU24(desc, offset + DescriptorFormat.TYPECODE_SIZE);
841
+ const length = meta & DescriptorFormat.META_NODE_LENGTH_MASK;
842
+ const minHeader = info.typeClass === "tuple" ? DescriptorFormat.TUPLE_HEADER_SIZE : DescriptorFormat.ARRAY_HEADER_SIZE;
843
+ if (length < minHeader) throw new CallciumError("NODE_LENGTH_TOO_SMALL", `Composite node length ${length} is smaller than minimum header ${minHeader}`, offset);
844
+ if (offset + length > desc.length) throw new CallciumError("NODE_OVERFLOW", "Composite node extends beyond descriptor", offset);
845
+ return {
846
+ typeCode,
847
+ staticWords: meta >> DescriptorFormat.META_STATIC_WORDS_SHIFT,
848
+ nodeLength: length
849
+ };
850
+ }
492
851
  function nodeLength(desc, offset) {
493
- if (desc[offset] < TypeCode.STATIC_ARRAY) return DescriptorFormat.TYPECODE_SIZE;
494
- return readU24(desc, offset + 1) & DescriptorFormat.META_NODE_LENGTH_MASK;
852
+ return readNode(desc, offset).nodeLength;
495
853
  }
496
854
  function tupleFieldOffset(desc, tupleOffset, fieldIndex) {
497
855
  let cursor = tupleOffset + DescriptorFormat.TUPLE_HEADER_SIZE;
@@ -499,20 +857,18 @@ function tupleFieldOffset(desc, tupleOffset, fieldIndex) {
499
857
  return cursor;
500
858
  }
501
859
  function paramCount(desc) {
860
+ if (desc.length < DescriptorFormat.HEADER_SIZE) throw new CallciumError("MALFORMED_HEADER", "Descriptor too short for header");
502
861
  return desc[1];
503
862
  }
504
863
  function paramOffset(desc, index) {
505
- const count = desc[1];
506
- if (index >= count) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${index} out of range (paramCount=${count}).`);
864
+ const count = paramCount(desc);
865
+ if (index >= count) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${index} out of range (paramCount=${count})`);
507
866
  let cursor = DescriptorFormat.HEADER_SIZE;
508
867
  for (let i = 0; i < index; i++) cursor += nodeLength(desc, cursor);
509
868
  return cursor;
510
869
  }
511
870
  function inspect$1(desc, offset) {
512
- const typeCode = desc[offset];
513
- let staticWords;
514
- if (typeCode < TypeCode.STATIC_ARRAY) staticWords = typeCode === TypeCode.BYTES || typeCode === TypeCode.STRING ? 0 : 1;
515
- else staticWords = readU24(desc, offset + 1) >> DescriptorFormat.META_STATIC_WORDS_SHIFT;
871
+ const { typeCode, staticWords } = readNode(desc, offset);
516
872
  return {
517
873
  typeCode,
518
874
  isDynamic: staticWords === 0,
@@ -523,27 +879,24 @@ function typeAt(desc, steps) {
523
879
  return walkPath(desc, steps).typeInfo;
524
880
  }
525
881
  function walkPath(desc, steps) {
526
- if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step.");
527
- const paramIndex = steps[0];
528
- const count = desc[1];
529
- if (paramIndex >= count) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${paramIndex} out of range (paramCount=${count}).`);
530
- let cursor = paramOffset(desc, paramIndex);
882
+ if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step");
883
+ let cursor = paramOffset(desc, steps[0]);
531
884
  let quantifiedStaticLength = 0;
532
885
  for (let stepIndex = 1; stepIndex < steps.length; stepIndex++) {
533
886
  const typeCode = desc[cursor];
534
887
  const step = steps[stepIndex];
535
888
  if (typeCode === TypeCode.TUPLE) {
536
889
  const fields = tupleFieldCount(desc, cursor);
537
- if (step >= fields) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fields} fields).`);
890
+ if (step >= fields) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fields} fields)`);
538
891
  cursor = tupleFieldOffset(desc, cursor, step);
539
892
  } else if (typeCode === TypeCode.STATIC_ARRAY || typeCode === TypeCode.DYNAMIC_ARRAY) {
540
893
  if (typeCode === TypeCode.STATIC_ARRAY) if (isQuantifier(step)) quantifiedStaticLength = staticArrayLength(desc, cursor);
541
894
  else {
542
895
  const length = staticArrayLength(desc, cursor);
543
- if (step >= length) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (length=${length}).`);
896
+ if (step >= length) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (length=${length})`);
544
897
  }
545
898
  cursor = arrayElementOffset(cursor);
546
- } else throw new CallciumError("NOT_COMPOSITE", `Cannot descend into elementary type at offset ${cursor}.`);
899
+ } else throw new CallciumError("NOT_COMPOSITE", `Cannot descend into elementary type at offset ${cursor}`);
547
900
  }
548
901
  return {
549
902
  typeInfo: inspect$1(desc, cursor),
@@ -561,7 +914,7 @@ function arrayElementOffset(offset) {
561
914
  return offset + DescriptorFormat.ARRAY_HEADER_SIZE;
562
915
  }
563
916
  function uncompilablePath(stepIndex) {
564
- return new CallciumError("UNCOMPILABLE_PATH", `Path step ${stepIndex} does not resolve to a hop-chain target.`);
917
+ return new CallciumError("UNCOMPILABLE_PATH", `Path step ${stepIndex} does not resolve to a hop-chain target`);
565
918
  }
566
919
  function headSpan(desc, offset, count) {
567
920
  let span = 0;
@@ -668,7 +1021,7 @@ function encodeHint(walk, targetMeta, typeCode, depth) {
668
1021
  return out;
669
1022
  }
670
1023
  function compileHint(desc, steps) {
671
- if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step.");
1024
+ if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step");
672
1025
  const argIndex = steps[0];
673
1026
  if (argIndex >= paramCount(desc)) throw uncompilablePath(0);
674
1027
  const arg = headSpan(desc, DescriptorFormat.HEADER_SIZE, argIndex);
@@ -720,11 +1073,11 @@ function function_() {
720
1073
  return new Uint8Array([TypeCode.FUNCTION]);
721
1074
  }
722
1075
  function uintN(bits) {
723
- 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.`);
1076
+ 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`);
724
1077
  return new Uint8Array([TypeCode.UINT_MIN + (bits / 8 - 1)]);
725
1078
  }
726
1079
  function intN(bits) {
727
- 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.`);
1080
+ 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`);
728
1081
  return new Uint8Array([TypeCode.INT_MIN + (bits / 8 - 1)]);
729
1082
  }
730
1083
  function bytes() {
@@ -733,14 +1086,14 @@ function bytes() {
733
1086
  function string_() {
734
1087
  return new Uint8Array([TypeCode.STRING]);
735
1088
  }
736
- function bytesN(n) {
737
- if (n < 1 || n > 32) throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${n}. Must be 1–32.`);
738
- return new Uint8Array([TypeCode.FIXED_BYTES_MIN + (n - 1)]);
1089
+ function bytesN(length) {
1090
+ if (length < 1 || length > 32) throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${length}. Must be 1–32`);
1091
+ return new Uint8Array([TypeCode.FIXED_BYTES_MIN + (length - 1)]);
739
1092
  }
740
1093
  function array(elemDesc, length) {
741
1094
  if (length === void 0) {
742
1095
  const nodeLength = DescriptorFormat.ARRAY_HEADER_SIZE + elemDesc.length;
743
- if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Dynamic array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}.`);
1096
+ if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Dynamic array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}`);
744
1097
  const meta24 = 0 << DescriptorFormat.META_STATIC_WORDS_SHIFT | nodeLength;
745
1098
  const buf = new Uint8Array(1 + DescriptorFormat.COMPOSITE_META_SIZE + elemDesc.length);
746
1099
  buf[0] = TypeCode.DYNAMIC_ARRAY;
@@ -748,12 +1101,12 @@ function array(elemDesc, length) {
748
1101
  buf.set(elemDesc, 4);
749
1102
  return buf;
750
1103
  }
751
- 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}.`);
1104
+ 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}`);
752
1105
  const nodeLength = DescriptorFormat.ARRAY_HEADER_SIZE + elemDesc.length + DescriptorFormat.ARRAY_LENGTH_SIZE;
753
- if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Static array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}.`);
1106
+ if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Static array node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}`);
754
1107
  const elemStaticWords = extractStaticWords(elemDesc);
755
1108
  const staticWords = elemStaticWords === 0 ? 0 : length * elemStaticWords;
756
- if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Static array static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}.`);
1109
+ if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Static array static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}`);
757
1110
  const meta24 = staticWords << DescriptorFormat.META_STATIC_WORDS_SHIFT | nodeLength;
758
1111
  const buf = new Uint8Array(1 + DescriptorFormat.COMPOSITE_META_SIZE + elemDesc.length + DescriptorFormat.ARRAY_LENGTH_SIZE);
759
1112
  buf[0] = TypeCode.STATIC_ARRAY;
@@ -763,10 +1116,10 @@ function array(elemDesc, length) {
763
1116
  return buf;
764
1117
  }
765
1118
  function tuple(fieldDescs) {
766
- 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}.`);
1119
+ 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}`);
767
1120
  const totalFieldBytes = fieldDescs.reduce((sum, f) => sum + f.length, 0);
768
1121
  const nodeLength = DescriptorFormat.TUPLE_HEADER_SIZE + totalFieldBytes;
769
- if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Tuple node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}.`);
1122
+ if (nodeLength > DescriptorFormat.MAX_NODE_LENGTH) throw new CallciumError("NODE_LENGTH_TOO_LARGE", `Tuple node length ${nodeLength} exceeds maximum ${DescriptorFormat.MAX_NODE_LENGTH}`);
770
1123
  let anyDynamic = false;
771
1124
  let staticWordsSum = 0;
772
1125
  for (const field of fieldDescs) {
@@ -775,7 +1128,7 @@ function tuple(fieldDescs) {
775
1128
  staticWordsSum += staticWords;
776
1129
  }
777
1130
  const staticWords = anyDynamic ? 0 : staticWordsSum;
778
- if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Tuple static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}.`);
1131
+ if (staticWords > DescriptorFormat.MAX_STATIC_WORDS) throw new CallciumError("STATIC_WORDS_TOO_LARGE", `Tuple static words ${staticWords} exceeds maximum ${DescriptorFormat.MAX_STATIC_WORDS}`);
779
1132
  const meta24 = staticWords << DescriptorFormat.META_STATIC_WORDS_SHIFT | nodeLength;
780
1133
  const buf = new Uint8Array(1 + DescriptorFormat.COMPOSITE_META_SIZE + DescriptorFormat.TUPLE_FIELDCOUNT_SIZE + totalFieldBytes);
781
1134
  buf[0] = TypeCode.TUPLE;
@@ -796,9 +1149,12 @@ function commaPositions(input, start, end) {
796
1149
  for (let i = start; i < end; i++) {
797
1150
  const char = input[i];
798
1151
  if (char === "(" || char === "[") depth++;
799
- else if (char === ")" || char === "]") depth--;
800
- else if (char === "," && depth === 0) positions.push(i);
1152
+ else if (char === ")" || char === "]") {
1153
+ if (depth === 0) throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched '${char}' at position ${i}`);
1154
+ depth--;
1155
+ } else if (char === "," && depth === 0) positions.push(i);
801
1156
  }
1157
+ if (depth !== 0) throw new CallciumError("MALFORMED_TYPE_STRING", `Unclosed '${input.slice(start, end)}'`);
802
1158
  return positions;
803
1159
  }
804
1160
  const MAX_TYPE_STRING_NUMBER = 256 ** DescriptorFormat.ARRAY_LENGTH_SIZE - 1;
@@ -817,27 +1173,27 @@ function parseUint(input, start, end) {
817
1173
  function parseTuple(input, start, end) {
818
1174
  if (input[start] !== "(") throw new CallciumError("MALFORMED_TYPE_STRING", `Expected '(' at position ${start}`);
819
1175
  let depth = 0;
820
- let closePos = -1;
1176
+ let closeIndex = -1;
821
1177
  for (let i = start; i < end; i++) if (input[i] === "(") depth++;
822
1178
  else if (input[i] === ")") {
823
1179
  depth--;
824
1180
  if (depth === 0) {
825
- closePos = i;
1181
+ closeIndex = i;
826
1182
  break;
827
1183
  }
828
1184
  }
829
- if (closePos === -1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched '(' at position ${start}`);
830
- if (closePos !== end - 1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unexpected characters after ')' at position ${closePos + 1}`);
1185
+ if (closeIndex === -1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched '(' at position ${start}`);
1186
+ if (closeIndex !== end - 1) throw new CallciumError("MALFORMED_TYPE_STRING", `Unexpected characters after ')' at position ${closeIndex + 1}`);
831
1187
  const innerStart = start + 1;
832
- const innerEnd = closePos;
1188
+ const innerEnd = closeIndex;
833
1189
  const commas = commaPositions(input, innerStart, innerEnd);
834
1190
  const segments = [];
835
- let segStart = innerStart;
1191
+ let segmentStart = innerStart;
836
1192
  for (const comma of commas) {
837
- segments.push([segStart, comma]);
838
- segStart = comma + 1;
1193
+ segments.push([segmentStart, comma]);
1194
+ segmentStart = comma + 1;
839
1195
  }
840
- segments.push([segStart, innerEnd]);
1196
+ segments.push([segmentStart, innerEnd]);
841
1197
  if (segments.length === 1 && segments[0][0] === segments[0][1]) return tuple([]);
842
1198
  return tuple(segments.map(([fieldStart, fieldEnd]) => parseType(input, fieldStart, fieldEnd)));
843
1199
  }
@@ -849,28 +1205,25 @@ function parseBaseType(input, start, end) {
849
1205
  if (segment === "bytes") return bytes();
850
1206
  if (segment === "string") return string_();
851
1207
  if (segment.startsWith("(")) return parseTuple(input, start, end);
852
- if (segment.startsWith("uint")) return uintN(parseUint(input, start + 4, end));
853
- if (segment.startsWith("int")) return intN(parseUint(input, start + 3, end));
854
- if (segment.startsWith("bytes")) return bytesN(parseUint(input, start + 5, end));
1208
+ if (segment.length > 4 && segment.startsWith("uint")) return uintN(parseUint(input, start + 4, end));
1209
+ if (segment.length > 3 && segment.startsWith("int")) return intN(parseUint(input, start + 3, end));
1210
+ if (segment.length > 5 && segment.startsWith("bytes")) return bytesN(parseUint(input, start + 5, end));
855
1211
  throw new CallciumError("UNKNOWN_TYPE", `Unrecognised type '${segment}'`);
856
1212
  }
857
1213
  function parseType(input, start, end) {
858
1214
  if (start >= end) throw new CallciumError("MALFORMED_TYPE_STRING", "Empty type segment");
859
- const suffixes = [];
1215
+ const spans = [];
860
1216
  let baseEnd = end;
861
1217
  while (baseEnd > start && input[baseEnd - 1] === "]") {
862
- const closePos = baseEnd - 1;
863
- let openPos = closePos - 1;
864
- while (openPos > start && input[openPos] !== "[") openPos--;
865
- if (input[openPos] !== "[") throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched ']' at position ${closePos}`);
866
- const innerStart = openPos + 1;
867
- const innerEnd = closePos;
868
- suffixes.unshift(innerStart === innerEnd ? void 0 : parseUint(input, innerStart, innerEnd));
869
- baseEnd = openPos;
870
- }
871
- if (input[start] !== "(" && input.slice(start, baseEnd).includes("[")) throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched '[' in '${input.slice(start, baseEnd)}'`);
1218
+ const closeIndex = baseEnd - 1;
1219
+ let openIndex = closeIndex - 1;
1220
+ while (openIndex > start && input[openIndex] !== "[") openIndex--;
1221
+ if (input[openIndex] !== "[") throw new CallciumError("MALFORMED_TYPE_STRING", `Unmatched ']' at position ${closeIndex}`);
1222
+ spans.unshift([openIndex + 1, closeIndex]);
1223
+ baseEnd = openIndex;
1224
+ }
872
1225
  let desc = parseBaseType(input, start, baseEnd);
873
- for (const length of suffixes) desc = array(desc, length);
1226
+ for (const [innerStart, innerEnd] of spans) desc = array(desc, innerStart === innerEnd ? void 0 : parseUint(input, innerStart, innerEnd));
874
1227
  return desc;
875
1228
  }
876
1229
  function fromTypes(typesCsv) {
@@ -884,7 +1237,7 @@ function fromTypes(typesCsv) {
884
1237
  }
885
1238
  segments.push([segStart, typesCsv.length]);
886
1239
  const paramDescs = segments.map(([start, end]) => parseType(typesCsv, start, end));
887
- if (paramDescs.length > DescriptorFormat.MAX_PARAMS) throw new CallciumError("TOO_MANY_PARAMS", `Parameter count ${paramDescs.length} exceeds maximum ${DescriptorFormat.MAX_PARAMS}.`);
1240
+ if (paramDescs.length > DescriptorFormat.MAX_PARAMS) throw new CallciumError("TOO_MANY_PARAMS", `Parameter count ${paramDescs.length} exceeds maximum ${DescriptorFormat.MAX_PARAMS}`);
888
1241
  const totalBytes = paramDescs.reduce((sum, d) => sum + d.length, 0);
889
1242
  const result = new Uint8Array(DescriptorFormat.HEADER_SIZE + totalBytes);
890
1243
  result[0] = DescriptorFormat.VERSION;
@@ -894,10 +1247,11 @@ function fromTypes(typesCsv) {
894
1247
  result.set(desc, offset);
895
1248
  offset += desc.length;
896
1249
  }
1250
+ decodeDescriptor(result);
897
1251
  return result;
898
1252
  }
899
1253
  function nodeToTypeString(desc, offset) {
900
- const typeCode = desc[offset];
1254
+ const { typeCode } = Descriptor.inspect(desc, offset);
901
1255
  if (typeCode === TypeCode.TUPLE) {
902
1256
  const fieldCount = Descriptor.tupleFieldCount(desc, offset);
903
1257
  const fieldTypes = [];
@@ -926,30 +1280,20 @@ function toTypes(desc) {
926
1280
  return types.join(",");
927
1281
  }
928
1282
  function parseNode(data, offset, depth) {
929
- if (offset >= data.length) throw new CallciumError("UNEXPECTED_END", "Unexpected end of descriptor", offset);
930
- const code = data[offset];
1283
+ const { typeCode: code, staticWords, nodeLength } = readNode(data, offset);
931
1284
  const info = classifyTypeCode(code);
932
- const metaOffset = offset + DescriptorFormat.TYPECODE_SIZE;
1285
+ const isDynamic = staticWords === 0;
1286
+ const staticSize = isDynamic ? 0 : staticWords * 32;
1287
+ const nodeEnd = offset + nodeLength;
933
1288
  if (info.typeClass === "elementary") return {
934
1289
  typeCode: code,
935
- isDynamic: info.isDynamic,
936
- staticSize: info.isDynamic ? 0 : 32,
937
- next: metaOffset
1290
+ isDynamic,
1291
+ staticSize,
1292
+ next: nodeEnd
938
1293
  };
939
1294
  if (depth > DescriptorFormat.MAX_NESTING_DEPTH) throw new CallciumError("NESTING_TOO_DEEP", `Composite nesting exceeds maximum depth ${DescriptorFormat.MAX_NESTING_DEPTH}`, offset);
940
- const metaEnd = metaOffset + DescriptorFormat.COMPOSITE_META_SIZE;
941
- if (metaEnd > data.length) throw new CallciumError("UNEXPECTED_END", "Incomplete composite metadata", offset);
942
- const meta = readU24(data, metaOffset);
943
- const staticWords = meta >> DescriptorFormat.META_STATIC_WORDS_SHIFT;
944
- const nodeLength = meta & DescriptorFormat.META_NODE_LENGTH_MASK;
945
- const minHeader = info.typeClass === "tuple" ? DescriptorFormat.TUPLE_HEADER_SIZE : DescriptorFormat.ARRAY_HEADER_SIZE;
946
- if (nodeLength < minHeader) throw new CallciumError("NODE_LENGTH_TOO_SMALL", `Composite node length ${nodeLength} is smaller than minimum header ${minHeader}`, offset);
947
- if (offset + nodeLength > data.length) throw new CallciumError("NODE_OVERFLOW", "Composite node extends beyond descriptor", offset);
948
- const isDynamic = staticWords === 0;
949
- const staticSize = isDynamic ? 0 : staticWords * 32;
950
- const nodeEnd = offset + nodeLength;
951
1295
  if (info.typeClass === "tuple") {
952
- const fieldCountOffset = metaEnd;
1296
+ const fieldCountOffset = offset + DescriptorFormat.TYPECODE_SIZE + DescriptorFormat.COMPOSITE_META_SIZE;
953
1297
  if (fieldCountOffset + 2 > data.length) throw new CallciumError("UNEXPECTED_END", "Incomplete tuple header", offset);
954
1298
  const fieldCount = readU16(data, fieldCountOffset);
955
1299
  if (fieldCount === 0) throw new CallciumError("INVALID_TUPLE_FIELD_COUNT", "Tuple must have at least one field", offset);
@@ -973,183 +1317,56 @@ function parseNode(data, offset, depth) {
973
1317
  typeCode: code,
974
1318
  isDynamic,
975
1319
  staticSize,
976
- next: nodeEnd
977
- };
978
- }
979
- parseNode(data, Descriptor.arrayElementOffset(offset), depth + 1);
980
- return {
981
- typeCode: code,
982
- isDynamic: true,
983
- staticSize: 0,
984
- next: nodeEnd
985
- };
986
- }
987
- function indexToPath(index) {
988
- return `0x${index.toString(16).padStart(4, "0")}`;
989
- }
990
- function decodeDescriptor(data) {
991
- if (data.length < 1) throw new CallciumError("MALFORMED_HEADER", "Descriptor is empty");
992
- const version = data[0];
993
- if (version !== DescriptorFormat.VERSION) throw new CallciumError("UNSUPPORTED_VERSION", `Version ${version} is not supported (expected ${DescriptorFormat.VERSION})`);
994
- if (data.length < DescriptorFormat.HEADER_SIZE) throw new CallciumError("MALFORMED_HEADER", "Descriptor too short for header");
995
- const params = [];
996
- let cursor = DescriptorFormat.HEADER_SIZE;
997
- while (cursor < data.length) {
998
- if (params.length >= DescriptorFormat.MAX_PARAMS) throw new CallciumError("TOO_MANY_PARAMS", `Descriptor exceeds the maximum of ${DescriptorFormat.MAX_PARAMS} top-level params`);
999
- const { typeCode, isDynamic, staticSize, next } = parseNode(data, cursor, 1);
1000
- params.push({
1001
- index: params.length,
1002
- typeCode,
1003
- isDynamic,
1004
- staticSize,
1005
- path: indexToPath(params.length),
1006
- span: {
1007
- start: cursor,
1008
- end: next
1009
- }
1010
- });
1011
- cursor = next;
1012
- }
1013
- const declaredCount = data[1];
1014
- if (params.length !== declaredCount) throw new CallciumError("PARAM_COUNT_MISMATCH", `Header declares ${declaredCount} params but ${params.length} were parsed`);
1015
- return { descriptor: {
1016
- version,
1017
- params
1018
- } };
1019
- }
1020
- const DescriptorCoder = {
1021
- fromTypes,
1022
- toTypes
1023
- };
1024
- //#endregion
1025
- //#region src/operators.ts
1026
- function toBigInt(bytes, offset = 0) {
1027
- const view = new DataView(bytes.buffer, bytes.byteOffset + offset, 32);
1028
- return view.getBigUint64(0, false) << 192n | view.getBigUint64(8, false) << 128n | view.getBigUint64(16, false) << 64n | view.getBigUint64(24, false);
1029
- }
1030
- function isSigned(typeCode) {
1031
- return typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX;
1032
- }
1033
- function isLeftAligned(typeCode) {
1034
- return typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX || typeCode === TypeCode.FUNCTION;
1035
- }
1036
- function canonicalize(value, typeCode) {
1037
- if (typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX) {
1038
- const bits = BigInt(typeCode * 8);
1039
- return bits === 256n ? value : value & (1n << bits) - 1n;
1040
- }
1041
- if (typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX) {
1042
- const bits = (typeCode - TypeCode.INT_MIN + 1) * 8;
1043
- return bits === 256 ? value : BigInt.asUintN(256, BigInt.asIntN(bits, value));
1044
- }
1045
- if (typeCode === TypeCode.ADDRESS) return value & (1n << 160n) - 1n;
1046
- if (typeCode === TypeCode.BOOL) return value & 1n;
1047
- if (typeCode === TypeCode.FUNCTION) return value >> 64n << 64n;
1048
- if (typeCode >= TypeCode.FIXED_BYTES_MIN && typeCode <= TypeCode.FIXED_BYTES_MAX) {
1049
- const n = typeCode - TypeCode.FIXED_BYTES_MIN + 1;
1050
- if (n === 32) return value;
1051
- const padBits = BigInt((32 - n) * 8);
1052
- return value >> padBits << padBits;
1053
- }
1054
- return value;
1055
- }
1056
- function isLengthOp(opCode) {
1057
- const base = opCode & ~Op.NOT;
1058
- return base >= Op.LENGTH_EQ && base <= Op.LENGTH_BETWEEN;
1059
- }
1060
- function lengthToValueOp(base) {
1061
- if (base === Op.LENGTH_EQ) return Op.EQ;
1062
- if (base === Op.LENGTH_GT) return Op.GT;
1063
- if (base === Op.LENGTH_LT) return Op.LT;
1064
- if (base === Op.LENGTH_GTE) return Op.GTE;
1065
- if (base === Op.LENGTH_LTE) return Op.LTE;
1066
- return Op.BETWEEN;
1067
- }
1068
- function isLengthValidType(typeCode) {
1069
- return typeCode === TypeCode.BYTES || typeCode === TypeCode.STRING || typeCode === TypeCode.DYNAMIC_ARRAY;
1070
- }
1071
- function compareTyped(a, b, typeCode) {
1072
- if (isSigned(typeCode)) {
1073
- const sa = BigInt.asIntN(256, a);
1074
- const sb = BigInt.asIntN(256, b);
1075
- return sa < sb ? -1 : sa > sb ? 1 : 0;
1076
- }
1077
- return a < b ? -1 : a > b ? 1 : 0;
1078
- }
1079
- function isIn(value, data) {
1080
- const count = data.length / 32;
1081
- let lo = 0;
1082
- let hi = count - 1;
1083
- while (lo <= hi) {
1084
- const mid = lo + hi >>> 1;
1085
- const elem = toBigInt(data, mid * 32);
1086
- if (value === elem) return true;
1087
- if (value < elem) hi = mid - 1;
1088
- else lo = mid + 1;
1089
- }
1090
- return false;
1091
- }
1092
- function applyOperator(opCode, value, valueLength, operandData, typeCode) {
1093
- let base = opCode & ~Op.NOT;
1094
- if (isLengthOp(base)) {
1095
- value = BigInt(valueLength);
1096
- typeCode = TypeCode.UINT_MAX;
1097
- base = lengthToValueOp(base);
1098
- }
1099
- let result;
1100
- switch (base) {
1101
- case Op.EQ:
1102
- result = value === toBigInt(operandData, 0);
1103
- break;
1104
- case Op.GT:
1105
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) > 0;
1106
- break;
1107
- case Op.LT:
1108
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) < 0;
1109
- break;
1110
- case Op.GTE:
1111
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) >= 0;
1112
- break;
1113
- case Op.LTE:
1114
- result = compareTyped(value, toBigInt(operandData, 0), typeCode) <= 0;
1115
- break;
1116
- case Op.BETWEEN: {
1117
- const min = toBigInt(operandData, 0);
1118
- const max = toBigInt(operandData, 32);
1119
- result = compareTyped(value, min, typeCode) >= 0 && compareTyped(value, max, typeCode) <= 0;
1120
- break;
1121
- }
1122
- case Op.IN:
1123
- result = isIn(value, operandData);
1124
- break;
1125
- case Op.BITMASK_ALL: {
1126
- const mask = toBigInt(operandData, 0);
1127
- result = (value & mask) === mask;
1128
- break;
1129
- }
1130
- case Op.BITMASK_ANY: {
1131
- const mask = toBigInt(operandData, 0);
1132
- result = (value & mask) !== 0n;
1133
- break;
1134
- }
1135
- case Op.BITMASK_NONE: {
1136
- const mask = toBigInt(operandData, 0);
1137
- result = (value & mask) === 0n;
1138
- break;
1139
- }
1140
- default: throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code 0x${base.toString(16).padStart(2, "0")}.`);
1320
+ next: nodeEnd
1321
+ };
1141
1322
  }
1142
- return (opCode & Op.NOT) !== 0 ? !result : result;
1323
+ parseNode(data, Descriptor.arrayElementOffset(offset), depth + 1);
1324
+ return {
1325
+ typeCode: code,
1326
+ isDynamic: true,
1327
+ staticSize: 0,
1328
+ next: nodeEnd
1329
+ };
1330
+ }
1331
+ function indexToPath(index) {
1332
+ return `0x${index.toString(16).padStart(4, "0")}`;
1333
+ }
1334
+ function decodeDescriptor(data) {
1335
+ if (data.length < 1) throw new CallciumError("MALFORMED_HEADER", "Descriptor is empty");
1336
+ const version = data[0];
1337
+ if (version !== DescriptorFormat.VERSION) throw new CallciumError("UNSUPPORTED_VERSION", `Version ${version} is not supported (expected ${DescriptorFormat.VERSION})`);
1338
+ if (data.length < DescriptorFormat.HEADER_SIZE) throw new CallciumError("MALFORMED_HEADER", "Descriptor too short for header");
1339
+ const params = [];
1340
+ let cursor = DescriptorFormat.HEADER_SIZE;
1341
+ while (cursor < data.length) {
1342
+ const { typeCode, isDynamic, staticSize, next } = parseNode(data, cursor, 1);
1343
+ if (params.length === DescriptorFormat.MAX_PARAMS) throw new CallciumError("TOO_MANY_PARAMS", `Descriptor exceeds the maximum of ${DescriptorFormat.MAX_PARAMS} top-level params`);
1344
+ params.push({
1345
+ index: params.length,
1346
+ typeCode,
1347
+ isDynamic,
1348
+ staticSize,
1349
+ path: indexToPath(params.length),
1350
+ span: {
1351
+ start: cursor,
1352
+ end: next
1353
+ }
1354
+ });
1355
+ cursor = next;
1356
+ }
1357
+ const declaredCount = data[1];
1358
+ if (params.length !== declaredCount) throw new CallciumError("PARAM_COUNT_MISMATCH", `Header declares ${declaredCount} params but ${params.length} were parsed`);
1359
+ return { descriptor: {
1360
+ version,
1361
+ params
1362
+ } };
1143
1363
  }
1364
+ const DescriptorCoder = {
1365
+ fromTypes,
1366
+ toTypes
1367
+ };
1144
1368
  //#endregion
1145
1369
  //#region src/policy-coder.ts
1146
- function parsePathSteps(path) {
1147
- const bytes = hexToBytes(path);
1148
- 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`);
1149
- const steps = [];
1150
- for (let offset = 0; offset < bytes.length; offset += PolicyFormat.PATH_STEP_SIZE) steps.push(readU16(bytes, offset));
1151
- return steps;
1152
- }
1153
1370
  function field(value, start, end) {
1154
1371
  return {
1155
1372
  value,
@@ -1159,6 +1376,21 @@ function field(value, start, end) {
1159
1376
  }
1160
1377
  };
1161
1378
  }
1379
+ function hexField(data, start, end) {
1380
+ let hex;
1381
+ return {
1382
+ get value() {
1383
+ return hex ??= toHex(data, start, end);
1384
+ },
1385
+ set value(replacement) {
1386
+ hex = replacement;
1387
+ },
1388
+ span: {
1389
+ start,
1390
+ end
1391
+ }
1392
+ };
1393
+ }
1162
1394
  function resolveHintSize(data, hintStart, ruleEnd, ruleOffset) {
1163
1395
  const overrun = () => {
1164
1396
  throw new CallciumError("RULE_FIELD_OUT_OF_BOUNDS", "Rule is too small to hold a hint block", ruleOffset);
@@ -1215,7 +1447,7 @@ function hintTargetTypeCode(data, hintStart, hintSize) {
1215
1447
  }
1216
1448
  function decodePolicy(blob) {
1217
1449
  const data = hexToBytes(blob);
1218
- if (data.length < PolicyFormat.DESC_OFFSET + 1) throw new CallciumError("MALFORMED_HEADER", "Policy blob is too short");
1450
+ if (data.length < PolicyFormat.DESC_OFFSET) throw new CallciumError("MALFORMED_HEADER", "Policy blob is too short");
1219
1451
  const headerByte = data[0];
1220
1452
  const version = headerByte & PolicyFormat.VERSION_MASK;
1221
1453
  if (version !== PolicyFormat.VERSION) throw new CallciumError("UNSUPPORTED_VERSION", `Version ${version} is not supported (expected ${PolicyFormat.VERSION})`);
@@ -1240,7 +1472,7 @@ function decodePolicy(blob) {
1240
1472
  end: descStart + param.span.end
1241
1473
  }
1242
1474
  }));
1243
- const descriptorRaw = toHex(data, descStart, descEnd);
1475
+ const descriptorRaw = hexField(data, descStart, descEnd);
1244
1476
  const groupCountStart = groupCountOffset;
1245
1477
  const groupCountEnd = groupCountStart + PolicyFormat.GROUP_COUNT_SIZE;
1246
1478
  const groupCountValue = data[groupCountOffset];
@@ -1253,18 +1485,18 @@ function decodePolicy(blob) {
1253
1485
  const groupSizeValue = readU32(data, groupSizeStart);
1254
1486
  const groupBodyStart = offset + PolicyFormat.GROUP_HEADER_SIZE;
1255
1487
  const groupEnd = groupBodyStart + groupSizeValue;
1488
+ if (groupEnd > data.length) throw new CallciumError("GROUP_OVERFLOW", "Group extends beyond policy blob", offset);
1256
1489
  const ruleCountStart = offset;
1257
1490
  const ruleCountValue = readU16(data, ruleCountStart);
1258
1491
  if (ruleCountValue === 0) throw new CallciumError("EMPTY_GROUP", "Group must contain at least one rule", offset);
1259
1492
  if (groupSizeValue < ruleCountValue * PolicyFormat.RULE_MIN_SIZE) throw new CallciumError("GROUP_TOO_SMALL", "Declared group size is too small for its rule count", offset);
1260
- if (groupEnd > data.length) throw new CallciumError("GROUP_OVERFLOW", "Group extends beyond policy blob", offset);
1261
1493
  const rules = [];
1262
1494
  let ruleOffset = groupBodyStart;
1263
1495
  for (let ruleIndex = 0; ruleIndex < ruleCountValue; ruleIndex++) {
1264
1496
  if (ruleOffset + PolicyFormat.RULE_SIZE_SIZE > data.length) throw new CallciumError("UNEXPECTED_END", "Unexpected end while reading rule size", ruleOffset);
1265
1497
  const ruleSizeValue = readU16(data, ruleOffset);
1266
1498
  if (ruleSizeValue < PolicyFormat.RULE_MIN_SIZE) throw new CallciumError("RULE_TOO_SMALL", `Rule size ${ruleSizeValue} is below minimum ${PolicyFormat.RULE_MIN_SIZE}`, ruleOffset);
1267
- if (ruleOffset + ruleSizeValue > groupEnd) throw new CallciumError("RULE_OVERFLOW", "Rule extends beyond group boundary", ruleOffset);
1499
+ if (ruleOffset + ruleSizeValue > data.length) throw new CallciumError("RULE_OVERFLOW", "Rule extends beyond policy blob", ruleOffset);
1268
1500
  const scopeOffset = ruleOffset + PolicyFormat.RULE_SCOPE_OFFSET;
1269
1501
  const scopeValue = data[scopeOffset];
1270
1502
  if (scopeValue !== Scope.CONTEXT && scopeValue !== Scope.CALLDATA) throw new CallciumError("INVALID_SCOPE", `Unknown scope value ${scopeValue}`, ruleOffset);
@@ -1293,17 +1525,22 @@ function decodePolicy(blob) {
1293
1525
  const dataStart = dataLengthOffset + PolicyFormat.RULE_DATALENGTH_SIZE;
1294
1526
  if (opBase === Op.IN) {
1295
1527
  const wordCount = dataLengthValue / 32;
1296
- 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);
1528
+ let prev = toBigInt(data, dataStart);
1529
+ for (let word = 1; word < wordCount; word++) {
1530
+ const cur = toBigInt(data, dataStart + word * 32);
1531
+ if (cur <= prev) throw new CallciumError("UNSORTED_IN_SET", "IN operands must be strictly ascending", ruleOffset);
1532
+ prev = cur;
1533
+ }
1297
1534
  }
1298
1535
  if (opBase === Op.EQ_CTX) {
1299
1536
  if (toBigInt(data, dataStart) > BigInt(MAX_CONTEXT_PROPERTY_ID)) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", "EQ_CTX operand references an undefined context property", ruleOffset);
1300
1537
  }
1301
1538
  if (depthValue === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step", ruleOffset);
1302
- if (depthValue > Limits.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depthValue} exceeds maximum ${Limits.MAX_PATH_DEPTH}`, ruleOffset);
1539
+ if (depthValue > PolicyFormat.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depthValue} exceeds maximum ${PolicyFormat.MAX_PATH_DEPTH}`, ruleOffset);
1303
1540
  if (scopeValue === Scope.CONTEXT) {
1304
1541
  if (depthValue !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step", ruleOffset);
1305
1542
  const contextPropertyId = readU16(data, pathStart);
1306
- if (contextPropertyId > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Context rule references undefined context property 0x${contextPropertyId.toString(16).padStart(4, "0")}`, ruleOffset);
1543
+ if (contextPropertyId > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Context rule references undefined context property ${formatCode(contextPropertyId, PolicyFormat.PATH_STEP_SIZE)}`, ruleOffset);
1307
1544
  }
1308
1545
  const pathHex = toHex(data, pathStart, pathStart + pathLength);
1309
1546
  rules.push({
@@ -1311,15 +1548,16 @@ function decodePolicy(blob) {
1311
1548
  scope: field(scopeValue, scopeOffset, scopeOffset + 1),
1312
1549
  pathDepth: field(depthValue, depthOffset, depthOffset + 1),
1313
1550
  path: field(pathHex, pathStart, pathStart + pathLength),
1314
- ...hintSize > 0 && { hint: field(toHex(data, hintStart, hintStart + hintSize), hintStart, hintStart + hintSize) },
1551
+ ...hintSize > 0 && { hint: hexField(data, hintStart, hintStart + hintSize) },
1315
1552
  opCode: field(opCodeValue, opCodeOffset, opCodeOffset + PolicyFormat.RULE_OPCODE_SIZE),
1316
1553
  dataLength: field(dataLengthValue, dataLengthOffset, dataLengthOffset + PolicyFormat.RULE_DATALENGTH_SIZE),
1317
- data: field(toHex(data, dataStart, dataStart + dataLengthValue), dataStart, dataStart + dataLengthValue),
1554
+ data: hexField(data, dataStart, dataStart + dataLengthValue),
1318
1555
  span: {
1319
1556
  start: ruleOffset,
1320
1557
  end: ruleOffset + ruleSizeValue
1321
1558
  }
1322
1559
  });
1560
+ if (ruleOffset + ruleSizeValue > groupEnd) throw new CallciumError("RULE_OVERFLOW", "Rule extends beyond group boundary", ruleOffset);
1323
1561
  ruleOffset += ruleSizeValue;
1324
1562
  }
1325
1563
  if (ruleOffset !== groupEnd) throw new CallciumError("GROUP_SIZE_MISMATCH", "Rules do not exactly fill the declared group size", offset);
@@ -1341,7 +1579,12 @@ function decodePolicy(blob) {
1341
1579
  selector: field(selectorHex, selectorStart, selectorEnd),
1342
1580
  descLength: field(descLengthValue, descLengthStart, descLengthStart + PolicyFormat.DESC_LENGTH_SIZE),
1343
1581
  descriptor: {
1344
- raw: descriptorRaw,
1582
+ get raw() {
1583
+ return descriptorRaw.value;
1584
+ },
1585
+ set raw(replacement) {
1586
+ descriptorRaw.value = replacement;
1587
+ },
1345
1588
  header: field({
1346
1589
  version: desc.version,
1347
1590
  paramCount: desc.params.length
@@ -1364,21 +1607,28 @@ function decodePolicy(blob) {
1364
1607
  data
1365
1608
  };
1366
1609
  }
1367
- function flattenConstraint(constraint, desc) {
1368
- const operators = constraint.operators.map(hexToBytes);
1369
- for (const operator of operators) if (operator.length < 1) throw new CallciumError("INVALID_OPERATOR_BYTES", "Operator must have at least one byte (opcode).");
1610
+ function readConstraintTarget(constraint, desc) {
1370
1611
  const path = hexToBytes(constraint.path);
1371
- if (path.length === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step.");
1372
- if ((path.length & 1) !== 0) throw new CallciumError("MALFORMED_PATH", "Path byte length must be even.");
1612
+ if (path.length === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step");
1613
+ if ((path.length & 1) !== 0) throw new CallciumError("MALFORMED_PATH", "Path byte length must be even");
1373
1614
  const depth = path.length / 2;
1374
- if (depth > Limits.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depth} exceeds maximum ${Limits.MAX_PATH_DEPTH}.`);
1375
- const hint = constraint.scope === Scope.CALLDATA ? Descriptor.compileHint(desc, parsePathSteps(constraint.path)) : new Uint8Array(0);
1376
- return operators.map((operator) => ({
1377
- scope: constraint.scope,
1615
+ if (depth > PolicyFormat.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depth} exceeds maximum ${PolicyFormat.MAX_PATH_DEPTH}`);
1616
+ return {
1378
1617
  path,
1379
- operator,
1380
- hint
1381
- }));
1618
+ hint: constraint.scope === Scope.CALLDATA ? Descriptor.compileHint(desc, parsePathSteps(constraint.path)) : new Uint8Array(0)
1619
+ };
1620
+ }
1621
+ function flattenConstraint(constraint, desc) {
1622
+ let target;
1623
+ return constraint.operators.map((operatorHex) => {
1624
+ const operator = toOperatorBytes(operatorHex);
1625
+ target ??= readConstraintTarget(constraint, desc);
1626
+ return {
1627
+ scope: constraint.scope,
1628
+ operator,
1629
+ ...target
1630
+ };
1631
+ });
1382
1632
  }
1383
1633
  function compareBytes(a, b) {
1384
1634
  const length = Math.min(a.length, b.length);
@@ -1396,12 +1646,16 @@ function sortRules(rules) {
1396
1646
  return compareBytes(a.operator, b.operator);
1397
1647
  });
1398
1648
  }
1649
+ function requireFieldFits(value, byteWidth, code, subject) {
1650
+ const max = fieldMax(byteWidth);
1651
+ if (value > max) throw new CallciumError(code, `${subject} ${value} exceeds maximum ${max}`);
1652
+ }
1399
1653
  function encodeRule(rule) {
1400
1654
  const depth = rule.path.length / 2;
1401
- if (rule.scope === Scope.CONTEXT && depth !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step.");
1655
+ if (rule.scope === Scope.CONTEXT && depth !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step");
1402
1656
  const data = rule.operator.subarray(1);
1403
1657
  const ruleSize = PolicyFormat.RULE_FIXED_OVERHEAD + rule.path.length + rule.hint.length + data.length;
1404
- if (ruleSize > 65535) throw new CallciumError("RULE_SIZE_OVERFLOW", `Rule size ${ruleSize} exceeds maximum 65535`);
1658
+ requireFieldFits(ruleSize, PolicyFormat.RULE_SIZE_SIZE, "RULE_SIZE_OVERFLOW", "Rule size");
1405
1659
  const buf = new Uint8Array(ruleSize);
1406
1660
  writeBE16(buf, 0, ruleSize);
1407
1661
  buf[2] = rule.scope;
@@ -1431,16 +1685,16 @@ function buildOperatorHex(rule) {
1431
1685
  function encode(data) {
1432
1686
  const descBytes = hexToBytes(data.descriptor);
1433
1687
  if (data.groups.length === 0) throw new CallciumError("EMPTY_POLICY", "Policy must contain at least one group");
1434
- if (data.groups.length > 255) throw new CallciumError("GROUP_COUNT_OVERFLOW", `Group count ${data.groups.length} exceeds maximum 255`);
1688
+ requireFieldFits(data.groups.length, PolicyFormat.GROUP_COUNT_SIZE, "GROUP_COUNT_OVERFLOW", "Group count");
1435
1689
  const sortedGroups = data.groups.map((group) => {
1436
1690
  const rules = group.flatMap((constraint) => flattenConstraint(constraint, descBytes));
1437
1691
  sortRules(rules);
1438
1692
  return rules;
1439
1693
  });
1440
- if (descBytes.length > 65535) throw new CallciumError("DESC_LENGTH_OVERFLOW", `Descriptor length ${descBytes.length} exceeds maximum 65535`);
1694
+ requireFieldFits(descBytes.length, PolicyFormat.DESC_LENGTH_SIZE, "DESC_LENGTH_OVERFLOW", "Descriptor length");
1441
1695
  const encodedGroups = sortedGroups.map((rules, groupIndex) => {
1442
1696
  if (rules.length === 0) throw new CallciumError("EMPTY_GROUP", `Group ${groupIndex} is empty`);
1443
- if (rules.length > 65535) throw new CallciumError("RULE_COUNT_OVERFLOW", `Group ${groupIndex} rule count ${rules.length} exceeds maximum 65535`);
1697
+ requireFieldFits(rules.length, PolicyFormat.GROUP_RULECOUNT_SIZE, "RULE_COUNT_OVERFLOW", `Group ${groupIndex} rule count`);
1444
1698
  return {
1445
1699
  wireBytes: encodeGroupRules(rules),
1446
1700
  ruleCount: rules.length
@@ -1450,7 +1704,8 @@ function encode(data) {
1450
1704
  const hashes = new Map(encodedGroups.map((group) => [group, keccak_256(group.wireBytes)]));
1451
1705
  encodedGroups.sort((a, b) => compareBytes(hashes.get(a), hashes.get(b)));
1452
1706
  }
1453
- const selectorBytes = data.isSelectorless ? new Uint8Array(4) : hexToBytes(data.selector);
1707
+ const selectorBytes = data.isSelectorless ? new Uint8Array(PolicyFormat.SELECTOR_SIZE) : hexToBytes(data.selector);
1708
+ if (selectorBytes.length !== PolicyFormat.SELECTOR_SIZE) throw new CallciumError("MALFORMED_SELECTOR", `Selector must be ${PolicyFormat.SELECTOR_SIZE} bytes, got ${selectorBytes.length}`);
1454
1709
  const headerByte = PolicyFormat.VERSION | (data.isSelectorless ? PolicyFormat.FLAG_NO_SELECTOR : 0);
1455
1710
  let totalSize = PolicyFormat.HEADER_SIZE + PolicyFormat.SELECTOR_SIZE + PolicyFormat.DESC_LENGTH_SIZE + descBytes.length + PolicyFormat.GROUP_COUNT_SIZE;
1456
1711
  for (const g of encodedGroups) totalSize += PolicyFormat.GROUP_HEADER_SIZE + g.wireBytes.length;
@@ -1478,7 +1733,6 @@ function decode(blob) {
1478
1733
  const { policy } = decodePolicy(blob);
1479
1734
  const groups = policy.groups.map((group) => {
1480
1735
  const constraintMap = /* @__PURE__ */ new Map();
1481
- const constraintOrder = [];
1482
1736
  for (const rule of group.rules) {
1483
1737
  const hintHex = rule.hint?.value;
1484
1738
  const key = `${rule.scope.value}:${rule.path.value}:${hintHex ?? ""}`;
@@ -1494,10 +1748,9 @@ function decode(blob) {
1494
1748
  span: rule.span
1495
1749
  };
1496
1750
  constraintMap.set(key, constraint);
1497
- constraintOrder.push(key);
1498
1751
  }
1499
1752
  }
1500
- return constraintOrder.map((k) => constraintMap.get(k));
1753
+ return [...constraintMap.values()];
1501
1754
  });
1502
1755
  return {
1503
1756
  isSelectorless: policy.isSelectorless,
@@ -1743,7 +1996,7 @@ function unsortedInSet(groupIndex, constraintIndex) {
1743
1996
  code: "UNSORTED_IN_SET",
1744
1997
  value1: ZERO,
1745
1998
  value2: ZERO,
1746
- message: "isIn/notIn set is not strictly sorted and deduplicated."
1999
+ message: "isIn/notIn set is not strictly sorted and deduplicated"
1747
2000
  };
1748
2001
  }
1749
2002
  function emptyGroup(groupIndex) {
@@ -1755,7 +2008,7 @@ function emptyGroup(groupIndex) {
1755
2008
  code: "EMPTY_GROUP",
1756
2009
  value1: ZERO,
1757
2010
  value2: ZERO,
1758
- message: "Group contains zero constraints."
2011
+ message: "Group contains zero constraints"
1759
2012
  };
1760
2013
  }
1761
2014
  function dominatedBound(isLength, groupIndex, constraintIndex, value) {
@@ -1940,8 +2193,7 @@ function negationUnderAny(groupIndex, constraintIndex, opCode) {
1940
2193
  }
1941
2194
  //#endregion
1942
2195
  //#region src/policy-validator.ts
1943
- const NAVIGABILITY_CODES = new Set([
1944
- "EMPTY_PATH",
2196
+ const navigabilityCodes = new Set([
1945
2197
  "PARAM_INDEX_OUT_OF_BOUNDS",
1946
2198
  "TUPLE_FIELD_OUT_OF_BOUNDS",
1947
2199
  "STATIC_ARRAY_INDEX_OUT_OF_BOUNDS",
@@ -1952,8 +2204,11 @@ const UINT256_MAX = (1n << 256n) - 1n;
1952
2204
  const INT256_SIGN_BIT = 1n << 255n;
1953
2205
  function signedCompare(a, b, signed) {
1954
2206
  if (!signed) return a - b;
1955
- const toS = (v) => v >= INT256_SIGN_BIT ? v - (1n << 256n) : v;
1956
- return toS(a) - toS(b);
2207
+ const toSigned = (word) => word >= INT256_SIGN_BIT ? word - (1n << 256n) : word;
2208
+ return toSigned(a) - toSigned(b);
2209
+ }
2210
+ function isOutsideDomain(domain, value) {
2211
+ return signedCompare(value, domain.min, domain.isSigned) < 0n || signedCompare(value, domain.max, domain.isSigned) > 0n;
1957
2212
  }
1958
2213
  function isNumericType(typeCode) {
1959
2214
  return typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX || typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX;
@@ -2011,7 +2266,7 @@ function getIncompat(opBase, typeInfo) {
2011
2266
  code: "VALUE_OP_ON_DYNAMIC",
2012
2267
  message: "Value operator used on dynamic type"
2013
2268
  };
2014
- if (classifyTypeCode(typeCode).typeClass !== "elementary") return {
2269
+ if (!isElementary(typeCode)) return {
2015
2270
  code: "VALUE_OP_ON_COMPOSITE",
2016
2271
  message: "Value operator used on composite type"
2017
2272
  };
@@ -2122,7 +2377,7 @@ function initContext(scope, path, steps, typeInfo) {
2122
2377
  numeric: emptyBoundDomain(isSigned(typeInfo.typeCode), min, max),
2123
2378
  bitmask: {
2124
2379
  mustBeOne: 0n,
2125
- mustBeZero: 0n
2380
+ mustBeZero: isBitmaskCompatible(typeInfo.typeCode) ? UINT256_MAX ^ max : 0n
2126
2381
  },
2127
2382
  length: emptyBoundDomain(false, 0n, LENGTH_MAX),
2128
2383
  set: {
@@ -2135,6 +2390,7 @@ function initContext(scope, path, steps, typeInfo) {
2135
2390
  function updateBound(domain, base, isNegated, value, isLength, groupIndex, constraintIndex, issues) {
2136
2391
  if (isNegated) {
2137
2392
  if (base === Op.EQ) {
2393
+ if (isOutsideDomain(domain, value)) issues.push(outOfPhysicalBounds(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2138
2394
  if (domain.hasEq && domain.eq === value) issues.push(eqNeqContradiction(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2139
2395
  let alreadyHole = false;
2140
2396
  for (let j = 0; j < domain.holes.length; j++) if (domain.holes[j] === value) {
@@ -2147,7 +2403,7 @@ function updateBound(domain, base, isNegated, value, isLength, groupIndex, const
2147
2403
  }
2148
2404
  if (base === Op.GTE && value === domain.min) issues.push(vacuousGte(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2149
2405
  else if (base === Op.LTE && value === domain.max) issues.push(vacuousLte(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2150
- if (signedCompare(value, domain.min, domain.isSigned) < 0n || signedCompare(value, domain.max, domain.isSigned) > 0n) issues.push(outOfPhysicalBounds(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2406
+ if (isOutsideDomain(domain, value)) issues.push(outOfPhysicalBounds(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2151
2407
  else if (base === Op.GT && value === domain.max) issues.push(impossibleGt(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2152
2408
  else if (base === Op.LT && value === domain.min) issues.push(impossibleLt(isLength, groupIndex, constraintIndex, bigintToHex(value)));
2153
2409
  let changedEq = false;
@@ -2262,6 +2518,7 @@ function checkSetEmpty(ctx, groupIndex, constraintIndex, issues) {
2262
2518
  else if (possibleCount < inCount) issues.push(setPartiallyExcluded(groupIndex, constraintIndex, bigintToHex(BigInt(inCount - possibleCount))));
2263
2519
  }
2264
2520
  function updateSet(ctx, isNegated, values, groupIndex, constraintIndex, issues) {
2521
+ for (const value of values) if (isOutsideDomain(ctx.numeric, value)) issues.push(outOfPhysicalBounds(false, groupIndex, constraintIndex, bigintToHex(value)));
2265
2522
  if (isNegated) {
2266
2523
  for (const value of values) {
2267
2524
  if (ctx.numeric.hasEq && ctx.numeric.eq === value) issues.push(setExcludesEquality(groupIndex, constraintIndex, bigintToHex(value)));
@@ -2277,7 +2534,6 @@ function updateSet(ctx, isNegated, values, groupIndex, constraintIndex, issues)
2277
2534
  }
2278
2535
  checkSetEmpty(ctx, groupIndex, constraintIndex, issues);
2279
2536
  } else {
2280
- for (const value of values) if (signedCompare(value, ctx.numeric.min, ctx.numeric.isSigned) < 0n || signedCompare(value, ctx.numeric.max, ctx.numeric.isSigned) > 0n) issues.push(outOfPhysicalBounds(false, groupIndex, constraintIndex, bigintToHex(value)));
2281
2537
  if (ctx.numeric.hasEq) {
2282
2538
  let found = false;
2283
2539
  for (const candidate of values) if (candidate === ctx.numeric.eq) {
@@ -2319,6 +2575,9 @@ function checkFusibleRange(ctx, operators, groupIndex, constraintIndex, issues)
2319
2575
  const lengthPair = findLonePair(operators, Op.LENGTH_GTE, Op.LENGTH_LTE);
2320
2576
  if (lengthPair && getIncompat(Op.LENGTH_GTE, ctx.typeInfo) === null && lengthPair.low <= lengthPair.high) issues.push(fusibleRange(true, groupIndex, constraintIndex, bigintToHex(lengthPair.low), bigintToHex(lengthPair.high)));
2321
2577
  }
2578
+ function hasValidPayload(opBase, dataLength) {
2579
+ return dataLength <= fieldMax(PolicyFormat.RULE_DATALENGTH_SIZE) && isValidOperatorData(opBase, dataLength);
2580
+ }
2322
2581
  function findLonePair(operators, lowerOp, upperOp) {
2323
2582
  let lowerCount = 0;
2324
2583
  let upperCount = 0;
@@ -2327,7 +2586,7 @@ function findLonePair(operators, lowerOp, upperOp) {
2327
2586
  for (const opHex of operators) {
2328
2587
  const opCode = parseInt(opHex.slice(2, 4), 16);
2329
2588
  const dataLength = (opHex.length - 4) / 2;
2330
- if (dataLength > 65535 || !isValidOperatorData(opCode & ~Op.NOT, dataLength)) continue;
2589
+ if (!hasValidPayload(opCode & ~Op.NOT, dataLength)) continue;
2331
2590
  if (opCode === lowerOp) {
2332
2591
  lowerCount++;
2333
2592
  low = readValue(opHex);
@@ -2345,10 +2604,10 @@ function validateConstraint(ctx, constraint, groupIndex, constraintIndex, issues
2345
2604
  const operators = constraint.operators;
2346
2605
  const underAny = constraint.scope === Scope.CALLDATA && ctx.steps.includes(Quantifier.ANY);
2347
2606
  for (const opHex of operators) {
2348
- const opCode = parseInt(opHex.slice(2, 4), 16);
2607
+ const opBytes = toOperatorBytes(opHex);
2608
+ const opCode = opBytes[0];
2349
2609
  const base = opCode & ~Op.NOT;
2350
- const dataLength = (opHex.length - 4) / 2;
2351
- if (dataLength > 65535 || !isValidOperatorData(base, dataLength)) {
2610
+ if (!hasValidPayload(base, opBytes.length - 1)) {
2352
2611
  issues.push(fromOpRule("UNKNOWN_OPERATOR", "Unknown operator code", groupIndex, constraintIndex, bigintToHex(BigInt(opCode))));
2353
2612
  continue;
2354
2613
  }
@@ -2361,8 +2620,9 @@ function validateConstraint(ctx, constraint, groupIndex, constraintIndex, issues
2361
2620
  }
2362
2621
  if (base === Op.EQ_CTX) {
2363
2622
  const ctxOperand = readValue(opHex);
2364
- if (ctxOperand > BigInt(MAX_CONTEXT_PROPERTY_ID)) issues.push(unknownContextProperty(groupIndex, constraintIndex, bigintToHex(ctxOperand), bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID))));
2365
- 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))));
2623
+ const property = findContextProperty(Number(ctxOperand));
2624
+ if (!property) issues.push(unknownContextProperty(groupIndex, constraintIndex, bigintToHex(ctxOperand), bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID))));
2625
+ else if (property.typeCode === TypeCode.ADDRESS !== (ctx.typeInfo.typeCode === TypeCode.ADDRESS)) issues.push(contextTypeMismatch(groupIndex, constraintIndex, bigintToHex(ctxOperand), bigintToHex(BigInt(ctx.typeInfo.typeCode))));
2366
2626
  } else if (isValueOp(base)) {
2367
2627
  const typeCode = ctx.typeInfo.typeCode;
2368
2628
  if (isLeftAligned(typeCode)) {
@@ -2416,14 +2676,15 @@ function validateGroup(data, descBytes, groupIndex, issues) {
2416
2676
  }
2417
2677
  if (!ctx) {
2418
2678
  const steps = parsePathSteps(constraint.path);
2679
+ if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step");
2419
2680
  let typeInfo;
2420
2681
  if (constraint.scope === Scope.CALLDATA) {
2421
- if (steps.length > Limits.MAX_PATH_DEPTH) issues.push(pathDepthExceeded(groupIndex, constraintIndex, bigintToHex(BigInt(steps.length)), bigintToHex(BigInt(Limits.MAX_PATH_DEPTH))));
2682
+ if (steps.length > PolicyFormat.MAX_PATH_DEPTH) issues.push(pathDepthExceeded(groupIndex, constraintIndex, bigintToHex(BigInt(steps.length)), bigintToHex(BigInt(PolicyFormat.MAX_PATH_DEPTH))));
2422
2683
  let walk;
2423
2684
  try {
2424
2685
  walk = Descriptor.walkPath(descBytes, steps);
2425
2686
  } catch (error) {
2426
- if (error instanceof CallciumError && NAVIGABILITY_CODES.has(error.code)) {
2687
+ if (error instanceof CallciumError && navigabilityCodes.has(error.code)) {
2427
2688
  issues.push(unnavigablePath(groupIndex, constraintIndex));
2428
2689
  continue;
2429
2690
  }
@@ -2433,19 +2694,14 @@ function validateGroup(data, descBytes, groupIndex, issues) {
2433
2694
  issues.push(nestedQuantifier(groupIndex, constraintIndex));
2434
2695
  continue;
2435
2696
  }
2436
- if (walk.quantifiedStaticLength > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) issues.push(quantifierOverStaticLimit(groupIndex, constraintIndex, bigintToHex(BigInt(walk.quantifiedStaticLength)), bigintToHex(BigInt(Limits.MAX_QUANTIFIED_ARRAY_LENGTH))));
2697
+ if (walk.quantifiedStaticLength > PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH) issues.push(quantifierOverStaticLimit(groupIndex, constraintIndex, bigintToHex(BigInt(walk.quantifiedStaticLength)), bigintToHex(BigInt(PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH))));
2437
2698
  typeInfo = walk.typeInfo;
2438
2699
  } else {
2439
2700
  const ctxId = steps[0];
2440
- if (ctxId > MAX_CONTEXT_PROPERTY_ID) {
2441
- issues.push(unknownContextProperty(groupIndex, constraintIndex, bigintToHex(BigInt(ctxId)), bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID))));
2442
- typeInfo = {
2443
- typeCode: TypeCode.UINT_MAX,
2444
- isDynamic: false,
2445
- staticSize: 32
2446
- };
2447
- } else typeInfo = {
2448
- typeCode: lookupContextProperty(ctxId).typeCode,
2701
+ const property = findContextProperty(ctxId);
2702
+ if (!property) issues.push(unknownContextProperty(groupIndex, constraintIndex, bigintToHex(BigInt(ctxId)), bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID))));
2703
+ typeInfo = {
2704
+ typeCode: property?.typeCode ?? TypeCode.UINT_MAX,
2449
2705
  isDynamic: false,
2450
2706
  staticSize: 32
2451
2707
  };
@@ -2453,7 +2709,7 @@ function validateGroup(data, descBytes, groupIndex, issues) {
2453
2709
  ctx = initContext(constraint.scope, normalizedPath, steps, typeInfo);
2454
2710
  contexts.push(ctx);
2455
2711
  }
2456
- if (constraint.scope === Scope.CALLDATA && constraint.hint !== void 0 && ctx.steps.length <= Limits.MAX_PATH_DEPTH) {
2712
+ if (constraint.scope === Scope.CALLDATA && constraint.hint !== void 0 && constraint.hint !== "0x" && ctx.steps.length <= PolicyFormat.MAX_PATH_DEPTH) {
2457
2713
  if (bytesToHex(Descriptor.compileHint(descBytes, ctx.steps)) !== constraint.hint.toLowerCase()) issues.push(hintMismatch(groupIndex, constraintIndex));
2458
2714
  }
2459
2715
  validateConstraint(ctx, constraint, groupIndex, constraintIndex, issues);
@@ -2476,33 +2732,31 @@ function validate(data) {
2476
2732
  const PolicyValidator = { validate };
2477
2733
  //#endregion
2478
2734
  //#region src/signature.ts
2479
- function isAlpha(c) {
2480
- return c >= 65 && c <= 90 || c >= 97 && c <= 122;
2735
+ function isAlpha(char) {
2736
+ return char >= 65 && char <= 90 || char >= 97 && char <= 122;
2481
2737
  }
2482
- function isAlphanum(c) {
2483
- return isAlpha(c) || c >= 48 && c <= 57;
2738
+ function isAlphanum(char) {
2739
+ return isAlpha(char) || char >= 48 && char <= 57;
2484
2740
  }
2485
2741
  function parse(signature) {
2486
2742
  if (signature.length < 3) throw new CallciumError("MALFORMED_SIGNATURE", `Invalid signature: too short, got "${signature}"`);
2487
2743
  let openParen = -1;
2488
2744
  for (let i = 0; i < signature.length; i++) {
2489
- const c = signature.charCodeAt(i);
2490
- if (c === 32 || c === 9 || c === 10 || c === 13) throw new CallciumError("SIGNATURE_CONTAINS_WHITESPACE", "Signature must not contain whitespace");
2491
- if (c > 126) throw new CallciumError("MALFORMED_SIGNATURE", "Signature must contain only ASCII characters");
2492
- if (openParen === -1 && c === 40) openParen = i;
2745
+ const char = signature.charCodeAt(i);
2746
+ if (char === 32 || char === 9 || char === 10 || char === 13) throw new CallciumError("SIGNATURE_CONTAINS_WHITESPACE", "Signature must not contain whitespace");
2747
+ if (openParen === -1 && char === 40) openParen = i;
2493
2748
  }
2494
- if (openParen < 1) throw new CallciumError("MALFORMED_SIGNATURE", `Invalid signature: must have a function name followed by parentheses, got "${signature}"`);
2749
+ if (openParen === -1) throw new CallciumError("MALFORMED_SIGNATURE", `Invalid signature: must have a function name followed by parentheses, got "${signature}"`);
2495
2750
  if (!signature.endsWith(")")) throw new CallciumError("MALFORMED_SIGNATURE", `Invalid signature: must end with ")", got "${signature}"`);
2751
+ if (openParen === 0) throw new CallciumError("INVALID_FUNCTION_NAME", "Function name must not be empty");
2496
2752
  const firstChar = signature.charCodeAt(0);
2497
2753
  if (!isAlpha(firstChar) && firstChar !== 95) throw new CallciumError("INVALID_FUNCTION_NAME", "Function name must start with a letter or underscore");
2498
2754
  for (let i = 1; i < openParen; i++) {
2499
- const c = signature.charCodeAt(i);
2500
- if (!isAlphanum(c) && c !== 95) throw new CallciumError("INVALID_FUNCTION_NAME", "Function name must contain only alphanumeric characters or underscores");
2755
+ const char = signature.charCodeAt(i);
2756
+ if (!isAlphanum(char) && char !== 95) throw new CallciumError("INVALID_FUNCTION_NAME", "Function name must contain only alphanumeric characters or underscores");
2501
2757
  }
2502
- const encoded = new Uint8Array(signature.length);
2503
- for (let i = 0; i < signature.length; i++) encoded[i] = signature.charCodeAt(i);
2504
2758
  return {
2505
- selector: bytesToHex(keccak_256(encoded).subarray(0, 4)),
2759
+ selector: bytesToHex(keccak_256(utf8ToBytes(signature)).subarray(0, 4)),
2506
2760
  types: signature.slice(openParen + 1, signature.length - 1)
2507
2761
  };
2508
2762
  }
@@ -2510,14 +2764,13 @@ const SignatureParser = { parse };
2510
2764
  //#endregion
2511
2765
  //#region src/policy-builder.ts
2512
2766
  function validateContextPath(steps) {
2513
- if (steps.length !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope path must be exactly one step.");
2514
- const step = steps[0];
2515
- if (step > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID 0x${step.toString(16).padStart(4, "0")}.`);
2767
+ if (steps.length !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope path must be exactly one step");
2768
+ return lookupContextProperty(steps[0]).typeCode;
2516
2769
  }
2517
2770
  function validateCalldataPath(steps, desc) {
2518
2771
  const argIndex = steps[0];
2519
2772
  const paramCount = Descriptor.paramCount(desc);
2520
- if (argIndex >= paramCount) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${argIndex} out of range (descriptor has ${paramCount} params).`);
2773
+ if (argIndex >= paramCount) throw new CallciumError("PARAM_INDEX_OUT_OF_BOUNDS", `Param index ${argIndex} out of range (descriptor has ${paramCount} params)`);
2521
2774
  let offset = Descriptor.paramOffset(desc, argIndex);
2522
2775
  let hasQuantifier = false;
2523
2776
  for (let i = 1; i < steps.length; i++) {
@@ -2525,22 +2778,49 @@ function validateCalldataPath(steps, desc) {
2525
2778
  const info = Descriptor.inspect(desc, offset);
2526
2779
  const isArray = info.typeCode === TypeCode.STATIC_ARRAY || info.typeCode === TypeCode.DYNAMIC_ARRAY;
2527
2780
  if (isQuantifier(step)) {
2528
- if (!isArray) throw new CallciumError("QUANTIFIER_ON_NON_ARRAY", "Quantifier step is only valid on an array node.");
2529
- if (hasQuantifier) throw new CallciumError("NESTED_QUANTIFIER", "Nested quantifiers are not allowed.");
2781
+ if (!isArray) throw new CallciumError("QUANTIFIER_ON_NON_ARRAY", "Quantifier step is only valid on an array node");
2782
+ if (hasQuantifier) throw new CallciumError("NESTED_QUANTIFIER", "Nested quantifiers are not allowed");
2530
2783
  hasQuantifier = true;
2531
2784
  }
2532
2785
  if (info.typeCode === TypeCode.TUPLE) {
2533
2786
  const fieldCount = Descriptor.tupleFieldCount(desc, offset);
2534
- if (step >= fieldCount) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fieldCount} fields).`);
2787
+ if (step >= fieldCount) throw new CallciumError("TUPLE_FIELD_OUT_OF_BOUNDS", `Tuple field index ${step} out of range (tuple has ${fieldCount} fields)`);
2535
2788
  offset = Descriptor.tupleFieldOffset(desc, offset, step);
2536
2789
  } else if (isArray) {
2537
2790
  if (!isQuantifier(step) && info.typeCode === TypeCode.STATIC_ARRAY) {
2538
2791
  const arrayLength = Descriptor.staticArrayLength(desc, offset);
2539
- if (step >= arrayLength) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (static array has ${arrayLength} elements).`);
2792
+ if (step >= arrayLength) throw new CallciumError("STATIC_ARRAY_INDEX_OUT_OF_BOUNDS", `Array index ${step} out of range (static array has ${arrayLength} elements)`);
2540
2793
  }
2541
2794
  offset = Descriptor.arrayElementOffset(offset);
2542
- } else throw new CallciumError("NOT_COMPOSITE", "Cannot descend into an elementary type.");
2795
+ } else throw new CallciumError("NOT_COMPOSITE", "Cannot descend into an elementary type");
2796
+ }
2797
+ return Descriptor.inspect(desc, offset).typeCode;
2798
+ }
2799
+ const WORD_VALUES = 1n << 256n;
2800
+ function targetBounds(typeCode) {
2801
+ if (typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX) return {
2802
+ min: 0n,
2803
+ max: (1n << BigInt(typeCode - TypeCode.UINT_MIN + 1) * 8n) - 1n
2804
+ };
2805
+ if (typeCode >= TypeCode.INT_MIN && typeCode <= TypeCode.INT_MAX) {
2806
+ const half = 1n << BigInt(typeCode - TypeCode.INT_MIN + 1) * 8n - 1n;
2807
+ return {
2808
+ min: -half,
2809
+ max: half - 1n
2810
+ };
2543
2811
  }
2812
+ return null;
2813
+ }
2814
+ function reject(written, folded) {
2815
+ throw new CallciumError("OUT_OF_PHYSICAL_BOUNDS", `Operand ${written} is outside the physical range of the type and encodes as ${folded}`);
2816
+ }
2817
+ function checkOperandDomain(constraint, typeCode) {
2818
+ const bounds = targetBounds(typeCode);
2819
+ const extremes = readOperandExtremes(constraint);
2820
+ if (bounds === null || extremes === void 0) return;
2821
+ if (extremes.leastNegative < 0n && typeCode === TypeCode.UINT_MAX) reject(extremes.leastNegative, extremes.leastNegative + WORD_VALUES);
2822
+ if (!isSigned(typeCode)) return;
2823
+ if (extremes.greatest >= WORD_VALUES + bounds.min) reject(extremes.greatest, extremes.greatest - WORD_VALUES);
2544
2824
  }
2545
2825
  var PolicyBuilder = class PolicyBuilder {
2546
2826
  draft;
@@ -2555,7 +2835,7 @@ var PolicyBuilder = class PolicyBuilder {
2555
2835
  selector: parsed.selector,
2556
2836
  descriptor,
2557
2837
  groups: [[]],
2558
- pathHashes: [/* @__PURE__ */ new Set()]
2838
+ groupPathKeys: /* @__PURE__ */ new Set()
2559
2839
  });
2560
2840
  }
2561
2841
  static createRaw(typesCsv) {
@@ -2564,32 +2844,34 @@ var PolicyBuilder = class PolicyBuilder {
2564
2844
  selector: "0x00000000",
2565
2845
  descriptor: DescriptorCoder.fromTypes(typesCsv),
2566
2846
  groups: [[]],
2567
- pathHashes: [/* @__PURE__ */ new Set()]
2847
+ groupPathKeys: /* @__PURE__ */ new Set()
2568
2848
  });
2569
2849
  }
2570
2850
  add(constraint) {
2571
- const c = {
2851
+ const added = {
2572
2852
  scope: constraint.scope,
2573
2853
  path: constraint.path,
2574
- operators: [...constraint.operators]
2854
+ operators: [...constraint.operators],
2855
+ ..."hint" in constraint && constraint.hint !== void 0 && { hint: constraint.hint }
2575
2856
  };
2576
- if (c.operators.length === 0) throw new CallciumError("NO_CONSTRAINT_OPERATORS", "Constraint must have at least one operator.");
2577
- const steps = parsePathSteps(c.path);
2578
- if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step.");
2579
- if (c.scope === Scope.CONTEXT) validateContextPath(steps);
2580
- else if (c.scope === Scope.CALLDATA) validateCalldataPath(steps, this.draft.descriptor);
2581
- else throw new CallciumError("INVALID_SCOPE", `Unknown scope value ${c.scope}.`);
2582
- const key = `${c.scope}:${c.path.toLowerCase()}`;
2583
- const currentHashes = this.draft.pathHashes[this.draft.pathHashes.length - 1];
2584
- if (currentHashes.has(key)) throw new CallciumError("DUPLICATE_PATH_IN_GROUP", `Duplicate path ${c.path} in the same group.`);
2585
- currentHashes.add(key);
2586
- this.draft.groups[this.draft.groups.length - 1].push(c);
2857
+ if (added.operators.length === 0) throw new CallciumError("NO_CONSTRAINT_OPERATORS", "Constraint must have at least one operator");
2858
+ const steps = parsePathSteps(added.path);
2859
+ if (steps.length === 0) throw new CallciumError("EMPTY_PATH", "Path must have at least one step");
2860
+ let targetTypeCode;
2861
+ if (added.scope === Scope.CONTEXT) targetTypeCode = validateContextPath(steps);
2862
+ else if (added.scope === Scope.CALLDATA) targetTypeCode = validateCalldataPath(steps, this.draft.descriptor);
2863
+ else throw new CallciumError("INVALID_SCOPE", `Unknown scope value ${added.scope}`);
2864
+ checkOperandDomain(constraint, targetTypeCode);
2865
+ const key = `${added.scope}:${added.path.toLowerCase()}`;
2866
+ if (this.draft.groupPathKeys.has(key)) throw new CallciumError("DUPLICATE_PATH_IN_GROUP", `Duplicate path ${added.path} in the same group`);
2867
+ this.draft.groupPathKeys.add(key);
2868
+ this.draft.groups[this.draft.groups.length - 1].push(added);
2587
2869
  return this;
2588
2870
  }
2589
2871
  or() {
2590
- 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.");
2872
+ 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");
2591
2873
  this.draft.groups.push([]);
2592
- this.draft.pathHashes.push(/* @__PURE__ */ new Set());
2874
+ this.draft.groupPathKeys.clear();
2593
2875
  return this;
2594
2876
  }
2595
2877
  build() {
@@ -2609,7 +2891,7 @@ var PolicyBuilder = class PolicyBuilder {
2609
2891
  return PolicyValidator.validate(policyData);
2610
2892
  }
2611
2893
  checkGroups() {
2612
- 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.`);
2894
+ 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`);
2613
2895
  }
2614
2896
  toPolicyData() {
2615
2897
  return {
@@ -2621,176 +2903,16 @@ var PolicyBuilder = class PolicyBuilder {
2621
2903
  }
2622
2904
  };
2623
2905
  //#endregion
2624
- //#region src/constraint.ts
2625
- function addressBody(value) {
2626
- return toAddress(value).slice(2);
2627
- }
2628
- function encodeWord(value) {
2629
- const word = new Uint8Array(32);
2630
- if (typeof value === "boolean") {
2631
- word[31] = value ? 1 : 0;
2632
- return word;
2633
- }
2634
- if (typeof value === "string") {
2635
- const body = addressBody(value);
2636
- for (let i = 0; i < 20; i++) word[12 + i] = parseInt(body.substring(i * 2, i * 2 + 2), 16);
2637
- return word;
2638
- }
2639
- let bigValue = typeof value === "number" ? BigInt(value) : value;
2640
- for (let i = 31; i >= 0; i--) {
2641
- word[i] = Number(bigValue & 255n);
2642
- bigValue >>= 8n;
2643
- }
2644
- return word;
2645
- }
2646
- function singleOp(opCode, value) {
2647
- const buffer = new Uint8Array(33);
2648
- buffer[0] = opCode;
2649
- buffer.set(encodeWord(value), 1);
2650
- return bytesToHex(buffer);
2651
- }
2652
- function rangeOp(opCode, min, max) {
2653
- if (min > max) throw new CallciumError("INVALID_RANGE", `Range min (${min}) must not exceed max (${max})`);
2654
- const buffer = new Uint8Array(65);
2655
- buffer[0] = opCode;
2656
- buffer.set(encodeWord(min), 1);
2657
- buffer.set(encodeWord(max), 33);
2658
- return bytesToHex(buffer);
2659
- }
2660
- function checkContextPropertyId(contextPropertyId) {
2661
- if (!Number.isInteger(contextPropertyId) || contextPropertyId < 0 || contextPropertyId > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID ${contextPropertyId}`);
2662
- return contextPropertyId;
2663
- }
2664
- const MAX_SET_MEMBERS = Limits.MAX_SET_MEMBERS;
2665
- function setOp(opCode, values) {
2666
- const bigs = values.map((v) => {
2667
- if (typeof v === "bigint") return v;
2668
- if (typeof v === "number") return BigInt(v);
2669
- if (typeof v === "boolean") return v ? 1n : 0n;
2670
- return BigInt("0x" + addressBody(v));
2671
- });
2672
- bigs.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
2673
- const deduped = [];
2674
- for (const value of bigs) if (deduped.length === 0 || deduped[deduped.length - 1] !== value) deduped.push(value);
2675
- if (deduped.length === 0) throw new CallciumError("EMPTY_SET", "Set must contain at least one value");
2676
- if (deduped.length > MAX_SET_MEMBERS) throw new CallciumError("SET_TOO_LARGE", `Set must contain at most ${MAX_SET_MEMBERS} values`);
2677
- const buffer = new Uint8Array(1 + deduped.length * 32);
2678
- buffer[0] = opCode;
2679
- for (let i = 0; i < deduped.length; i++) buffer.set(encodeWord(deduped[i]), 1 + i * 32);
2680
- return bytesToHex(buffer);
2681
- }
2682
- function encodePath(steps) {
2683
- const buffer = new Uint8Array(steps.length * 2);
2684
- for (let i = 0; i < steps.length; i++) writeBE16(buffer, i * 2, steps[i]);
2685
- return bytesToHex(buffer);
2686
- }
2687
- var ConstraintBuilder = class {
2688
- scope;
2689
- path;
2690
- operators;
2691
- constructor(scope, path) {
2692
- this.scope = scope;
2693
- this.path = path;
2694
- this.operators = [];
2695
- }
2696
- push(opHex) {
2697
- this.operators.push(opHex);
2698
- return this;
2699
- }
2700
- eq(value) {
2701
- return this.push(singleOp(Op.EQ, value));
2702
- }
2703
- neq(value) {
2704
- return this.push(singleOp(Op.EQ | Op.NOT, value));
2705
- }
2706
- eqCtx(contextPropertyId) {
2707
- return this.push(singleOp(Op.EQ_CTX, checkContextPropertyId(contextPropertyId)));
2708
- }
2709
- neqCtx(contextPropertyId) {
2710
- return this.push(singleOp(Op.EQ_CTX | Op.NOT, checkContextPropertyId(contextPropertyId)));
2711
- }
2712
- gt(bound) {
2713
- return this.push(singleOp(Op.GT, bound));
2714
- }
2715
- lt(bound) {
2716
- return this.push(singleOp(Op.LT, bound));
2717
- }
2718
- gte(bound) {
2719
- return this.push(singleOp(Op.GTE, bound));
2720
- }
2721
- lte(bound) {
2722
- return this.push(singleOp(Op.LTE, bound));
2723
- }
2724
- between(min, max) {
2725
- return this.push(rangeOp(Op.BETWEEN, BigInt(min), BigInt(max)));
2726
- }
2727
- isIn(values) {
2728
- return this.push(setOp(Op.IN, values));
2729
- }
2730
- notIn(values) {
2731
- return this.push(setOp(Op.IN | Op.NOT, values));
2732
- }
2733
- bitmaskAll(mask) {
2734
- return this.push(singleOp(Op.BITMASK_ALL, mask));
2735
- }
2736
- bitmaskAny(mask) {
2737
- return this.push(singleOp(Op.BITMASK_ANY, mask));
2738
- }
2739
- bitmaskNone(mask) {
2740
- return this.push(singleOp(Op.BITMASK_NONE, mask));
2741
- }
2742
- lengthEq(n) {
2743
- return this.push(singleOp(Op.LENGTH_EQ, n));
2744
- }
2745
- lengthGt(n) {
2746
- return this.push(singleOp(Op.LENGTH_GT, n));
2747
- }
2748
- lengthLt(n) {
2749
- return this.push(singleOp(Op.LENGTH_LT, n));
2750
- }
2751
- lengthGte(n) {
2752
- return this.push(singleOp(Op.LENGTH_GTE, n));
2753
- }
2754
- lengthLte(n) {
2755
- return this.push(singleOp(Op.LENGTH_LTE, n));
2756
- }
2757
- lengthBetween(min, max) {
2758
- return this.push(rangeOp(Op.LENGTH_BETWEEN, BigInt(min), BigInt(max)));
2759
- }
2760
- };
2761
- function arg(...steps) {
2762
- return new ConstraintBuilder(Scope.CALLDATA, encodePath(steps));
2763
- }
2764
- function msgSender() {
2765
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.MSG_SENDER]));
2766
- }
2767
- function msgValue() {
2768
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.MSG_VALUE]));
2769
- }
2770
- function blockTimestamp() {
2771
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.BLOCK_TIMESTAMP]));
2772
- }
2773
- function blockNumber() {
2774
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.BLOCK_NUMBER]));
2775
- }
2776
- function chainId() {
2777
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.CHAIN_ID]));
2778
- }
2779
- function txOrigin() {
2780
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.TX_ORIGIN]));
2781
- }
2782
- function baseFee() {
2783
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.BASE_FEE]));
2784
- }
2785
- function gasPrice() {
2786
- return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.GAS_PRICE]));
2787
- }
2788
- //#endregion
2789
2906
  //#region src/calldata-reader.ts
2790
2907
  function loadWord(callData, offset) {
2791
2908
  if (offset < 0 || offset + 32 > callData.length) return { code: "CALLDATA_OUT_OF_BOUNDS" };
2792
2909
  return callData.subarray(offset, offset + 32);
2793
2910
  }
2911
+ function readLength(callData, head) {
2912
+ const word = loadWord(callData, head);
2913
+ if (word instanceof Uint8Array) return toBigInt(word, 0);
2914
+ return word;
2915
+ }
2794
2916
  function readPointer(callData, head) {
2795
2917
  const word = loadWord(callData, head);
2796
2918
  if (word instanceof Uint8Array) {
@@ -2801,17 +2923,16 @@ function readPointer(callData, head) {
2801
2923
  }
2802
2924
  //#endregion
2803
2925
  //#region src/policy-enforcer.ts
2804
- const ABORT_VIOLATION_CODES = new Set([
2926
+ const abortViolationCodes = new Set([
2805
2927
  "CALLDATA_OUT_OF_BOUNDS",
2806
2928
  "ARRAY_INDEX_OUT_OF_BOUNDS",
2807
2929
  "NON_CANONICAL_VALUE",
2808
2930
  "QUANTIFIER_LIMIT_EXCEEDED"
2809
2931
  ]);
2810
- function addressToBigInt(hex) {
2811
- const bytes = hexToBytes(hex);
2812
- const padded = new Uint8Array(32);
2813
- padded.set(bytes, 32 - bytes.length);
2814
- return toBigInt(padded, 0);
2932
+ function contextValueToWord(value) {
2933
+ if (typeof value === "string") return BigInt(toAddress(value));
2934
+ if (BigInt.asUintN(256, value) !== value) throw new CallciumError("CONTEXT_VALUE_OVERFLOW", `Context value ${value} is outside the range a 32-byte word represents`);
2935
+ return value;
2815
2936
  }
2816
2937
  function check(policy, callData, context) {
2817
2938
  const { policy: decoded, data: policyBytes } = decodePolicy(policy);
@@ -2842,7 +2963,7 @@ function check(policy, callData, context) {
2842
2963
  const violation = evaluateRule(rule, policyBytes, callDataBytes, baseOffset, groupIndex, ruleIndex, context);
2843
2964
  if (violation !== null) {
2844
2965
  allViolations.push(violation);
2845
- if (ABORT_VIOLATION_CODES.has(violation.code)) return {
2966
+ if (abortViolationCodes.has(violation.code)) return {
2846
2967
  ok: false,
2847
2968
  violations: allViolations
2848
2969
  };
@@ -2875,10 +2996,14 @@ function evaluateRule(rule, policyBytes, callDataBytes, baseOffset, groupIndex,
2875
2996
  return evaluateCalldataRule(fieldBytes(policyBytes, rule.hint), callDataBytes, baseOffset, opCode, operandData, groupIndex, ruleIndex, rule.path.value, context);
2876
2997
  }
2877
2998
  function resolveContextOperand(operandData, context) {
2878
- const propInfo = lookupContextProperty(Number(toBigInt(operandData, 0)));
2879
- const value = context?.[propInfo.contextKey];
2880
- if (value === void 0) return { ctxTypeCode: propInfo.typeCode };
2881
- return typeof value === "string" ? addressToBigInt(value) : value;
2999
+ const propertyInfo = lookupContextProperty(Number(toBigInt(operandData, 0)));
3000
+ const value = context?.[propertyInfo.contextKey];
3001
+ if (value === void 0) return { ctxTypeCode: propertyInfo.typeCode };
3002
+ return contextValueToWord(value);
3003
+ }
3004
+ function referenceOperand(opCode, operandData, context) {
3005
+ if ((opCode & ~Op.NOT) !== Op.EQ_CTX) return void 0;
3006
+ return resolveContextOperand(operandData, context);
2882
3007
  }
2883
3008
  function readTargetBlock(hint, targetOffset) {
2884
3009
  return {
@@ -2905,8 +3030,8 @@ function chainResolve(hint, callData, base, hopsOffset, hopCount) {
2905
3030
  const meta = readU16(hint, hop + PolicyFormat.HINT_HOP_META_OFFSET);
2906
3031
  let elems = base;
2907
3032
  if ((meta & PolicyFormat.HINT_META_DYNAMIC_ARRAY) !== 0) {
2908
- const length = readPointer(callData, elems);
2909
- if (typeof length !== "number") return length;
3033
+ const length = readLength(callData, elems);
3034
+ if (typeof length !== "bigint") return length;
2910
3035
  if (index >= length) return { code: "ARRAY_INDEX_OUT_OF_BOUNDS" };
2911
3036
  elems += 32;
2912
3037
  }
@@ -2919,21 +3044,19 @@ function chainResolve(hint, callData, base, hopsOffset, hopCount) {
2919
3044
  }
2920
3045
  return base;
2921
3046
  }
2922
- function evalTarget(callData, target, block, opCode, operandData, context) {
3047
+ function evalTarget(callData, target, block, opCode, operandData, ctxOperand) {
2923
3048
  const typeCode = block.typeCode;
2924
3049
  if (isLengthValidType(typeCode)) {
2925
- if (!isLengthOp(opCode)) throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Value operator on a target without a scalar word.");
2926
- const length = readPointer(callData, target);
2927
- if (typeof length !== "number") return { error: length.code };
3050
+ const length = readLength(callData, target);
3051
+ if (typeof length !== "bigint") return { error: length.code };
2928
3052
  const stride = typeCode === TypeCode.DYNAMIC_ARRAY ? (block.targetMeta & PolicyFormat.HINT_META_STRIDE_MASK) * 32 : 1;
2929
3053
  const room = callData.length - (target + 32);
2930
- if (stride !== 0 && length > Math.floor(room / stride)) return { error: "CALLDATA_OUT_OF_BOUNDS" };
3054
+ if (stride !== 0 && length > BigInt(Math.floor(room / stride))) return { error: "CALLDATA_OUT_OF_BOUNDS" };
2931
3055
  return {
2932
3056
  passed: applyOperator(opCode, 0n, length, operandData, typeCode),
2933
- value: BigInt(length)
3057
+ value: length
2934
3058
  };
2935
3059
  }
2936
- if (classifyTypeCode(typeCode).typeClass !== "elementary") throw new CallciumError("OPERATOR_TARGET_MISMATCH", "Operator target does not carry a scalar word.");
2937
3060
  const word = loadWord(callData, target);
2938
3061
  if (!(word instanceof Uint8Array)) return { error: word.code };
2939
3062
  const value = toBigInt(word, 0);
@@ -2941,57 +3064,50 @@ function evalTarget(callData, target, block, opCode, operandData, context) {
2941
3064
  error: "NON_CANONICAL_VALUE",
2942
3065
  value
2943
3066
  };
2944
- if ((opCode & ~Op.NOT) === Op.EQ_CTX) {
2945
- const operand = resolveContextOperand(operandData, context);
2946
- if (typeof operand !== "bigint") return {
3067
+ if (ctxOperand !== void 0) {
3068
+ if (typeof ctxOperand !== "bigint") return {
2947
3069
  error: "MISSING_CONTEXT",
2948
- ctxTypeCode: operand.ctxTypeCode
3070
+ ctxTypeCode: ctxOperand.ctxTypeCode
2949
3071
  };
2950
3072
  return {
2951
- passed: value === operand !== ((opCode & Op.NOT) !== 0),
3073
+ passed: value === ctxOperand !== ((opCode & Op.NOT) !== 0),
2952
3074
  value,
2953
- ctxOperand: operand
3075
+ ctxOperand
2954
3076
  };
2955
3077
  }
2956
3078
  return {
2957
- passed: applyOperator(opCode, value, 32, operandData, typeCode),
3079
+ passed: applyOperator(opCode, value, 32n, operandData, typeCode),
2958
3080
  value
2959
3081
  };
2960
3082
  }
2961
- function navigationViolation(frame, code, elementIndex) {
3083
+ function ruleFields(frame) {
2962
3084
  return {
2963
3085
  group: frame.group,
2964
3086
  rule: frame.rule,
2965
- code,
2966
3087
  scope: Scope.CALLDATA,
2967
3088
  path: frame.path,
2968
3089
  opCode: frame.opCode,
2969
3090
  operandData: bytesToHex(frame.operandData),
2970
- typeCode: frame.typeCode,
3091
+ typeCode: frame.typeCode
3092
+ };
3093
+ }
3094
+ function navigationViolation(frame, code, elementIndex) {
3095
+ return {
3096
+ code,
3097
+ ...ruleFields(frame),
2971
3098
  ...elementIndex !== void 0 && { elementIndex }
2972
3099
  };
2973
3100
  }
2974
3101
  function targetViolation(frame, result, elementIndex) {
2975
3102
  if ("error" in result) {
2976
3103
  if (result.error === "MISSING_CONTEXT") return {
2977
- group: frame.group,
2978
- rule: frame.rule,
2979
3104
  code: result.error,
2980
- scope: Scope.CALLDATA,
2981
- path: frame.path,
2982
- opCode: frame.opCode,
2983
- operandData: bytesToHex(frame.operandData),
3105
+ ...ruleFields(frame),
2984
3106
  typeCode: result.ctxTypeCode
2985
3107
  };
2986
3108
  if (result.error === "NON_CANONICAL_VALUE") return {
2987
- group: frame.group,
2988
- rule: frame.rule,
2989
3109
  code: result.error,
2990
- scope: Scope.CALLDATA,
2991
- path: frame.path,
2992
- opCode: frame.opCode,
2993
- operandData: bytesToHex(frame.operandData),
2994
- typeCode: frame.typeCode,
3110
+ ...ruleFields(frame),
2995
3111
  resolvedValue: bigintToHex(result.value),
2996
3112
  ...elementIndex !== void 0 && { elementIndex }
2997
3113
  };
@@ -2999,14 +3115,8 @@ function targetViolation(frame, result, elementIndex) {
2999
3115
  }
3000
3116
  if (result.passed) return null;
3001
3117
  return {
3002
- group: frame.group,
3003
- rule: frame.rule,
3004
3118
  code: "VALUE_MISMATCH",
3005
- scope: Scope.CALLDATA,
3006
- path: frame.path,
3007
- opCode: frame.opCode,
3008
- operandData: bytesToHex(frame.operandData),
3009
- typeCode: frame.typeCode,
3119
+ ...ruleFields(frame),
3010
3120
  resolvedValue: bigintToHex(result.value),
3011
3121
  ...result.ctxOperand !== void 0 && { resolvedOperand: bigintToHex(result.ctxOperand) },
3012
3122
  ...elementIndex !== void 0 && { elementIndex }
@@ -3029,7 +3139,7 @@ function evaluateCalldataRule(hint, callDataBytes, baseOffset, opCode, operandDa
3029
3139
  };
3030
3140
  const base = chainResolve(hint, callDataBytes, baseOffset, hopsOffset, hopCount);
3031
3141
  if (typeof base !== "number") return navigationViolation(frame, base.code);
3032
- return targetViolation(frame, evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, context));
3142
+ return targetViolation(frame, evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, referenceOperand(opCode, operandData, context)));
3033
3143
  }
3034
3144
  function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, hopCount, opCode, operandData, groupIndex, ruleIndex, pathHex, context) {
3035
3145
  const frameOffset = hopsOffset + hopCount * PolicyFormat.HINT_HOP_SIZE;
@@ -3048,23 +3158,23 @@ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, h
3048
3158
  const chained = chainResolve(hint, callDataBytes, baseOffset, hopsOffset, hopCount);
3049
3159
  if (typeof chained !== "number") return navigationViolation(frame, chained.code);
3050
3160
  let elems = chained + readU32(hint, frameOffset);
3051
- let count = readU16(hint, frameOffset + PolicyFormat.HINT_FRAME_COUNT_OFFSET);
3052
- if (count === 0) {
3053
- const length = readPointer(callDataBytes, elems);
3054
- if (typeof length !== "number") return navigationViolation(frame, length.code);
3161
+ let count = BigInt(readU16(hint, frameOffset + PolicyFormat.HINT_FRAME_COUNT_OFFSET));
3162
+ if (count === 0n) {
3163
+ const length = readLength(callDataBytes, elems);
3164
+ if (typeof length !== "bigint") return navigationViolation(frame, length.code);
3055
3165
  count = length;
3056
3166
  elems += 32;
3057
3167
  }
3058
- if (count > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) return {
3168
+ if (count > BigInt(PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH)) return {
3059
3169
  group: groupIndex,
3060
3170
  rule: ruleIndex,
3061
3171
  code: "QUANTIFIER_LIMIT_EXCEEDED",
3062
3172
  scope: Scope.CALLDATA,
3063
3173
  path: pathHex,
3064
- resolvedValue: bigintToHex(BigInt(count))
3174
+ resolvedValue: bigintToHex(count)
3065
3175
  };
3066
3176
  const isUniversal = kind === PolicyFormat.HINT_KIND_ALL;
3067
- if (count === 0) {
3177
+ if (count === 0n) {
3068
3178
  if (isUniversal) return null;
3069
3179
  return {
3070
3180
  group: groupIndex,
@@ -3077,7 +3187,7 @@ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, h
3077
3187
  const meta = readU16(hint, frameOffset + PolicyFormat.HINT_FRAME_META_OFFSET);
3078
3188
  const elemStride = (meta & PolicyFormat.HINT_META_STRIDE_MASK) * 32;
3079
3189
  const elemIsDynamic = (meta & PolicyFormat.HINT_META_ELEM_DYNAMIC) !== 0;
3080
- let ctxOperand;
3190
+ const reference = referenceOperand(opCode, operandData, context);
3081
3191
  for (let elemIndex = 0; elemIndex < count; elemIndex++) {
3082
3192
  const slot = elems + elemIndex * elemStride;
3083
3193
  let elem = slot;
@@ -3092,68 +3202,54 @@ function evaluateQuantified(hint, callDataBytes, baseOffset, kind, hopsOffset, h
3092
3202
  if (typeof chainedElem !== "number") return navigationViolation(frame, chainedElem.code, elemIndex);
3093
3203
  base = chainedElem;
3094
3204
  }
3095
- const applied = evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, context);
3205
+ const applied = evalTarget(callDataBytes, base + block.targetDelta, block, opCode, operandData, reference);
3096
3206
  if ("error" in applied) return targetViolation(frame, applied, elemIndex);
3097
3207
  if (applied.passed) {
3098
3208
  if (!isUniversal) return null;
3099
3209
  } else if (isUniversal) return targetViolation(frame, applied, elemIndex);
3100
- ctxOperand = applied.ctxOperand;
3101
3210
  }
3102
3211
  if (isUniversal) return null;
3103
3212
  return {
3104
- group: groupIndex,
3105
- rule: ruleIndex,
3106
3213
  code: "VALUE_MISMATCH",
3107
- scope: Scope.CALLDATA,
3108
- path: pathHex,
3109
- opCode,
3110
- operandData: bytesToHex(operandData),
3111
- typeCode: block.typeCode,
3112
- ...ctxOperand !== void 0 && { resolvedOperand: bigintToHex(ctxOperand) }
3214
+ ...ruleFields(frame),
3215
+ ...typeof reference === "bigint" && { resolvedOperand: bigintToHex(reference) }
3113
3216
  };
3114
3217
  }
3115
3218
  function evaluateContextRule(pathBytes, opCode, operandData, groupIndex, ruleIndex, pathHex, context) {
3116
- const propertyId = readU16(pathBytes, 0);
3117
- if (propertyId > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", `Unknown context property ID 0x${propertyId.toString(16).padStart(4, "0")}`);
3118
- const propInfo = lookupContextProperty(propertyId);
3119
- const contextValue = context?.[propInfo.contextKey];
3120
- if (contextValue === void 0) return {
3219
+ const propertyInfo = lookupContextProperty(readU16(pathBytes, 0));
3220
+ const contextValue = context?.[propertyInfo.contextKey];
3221
+ const site = {
3121
3222
  group: groupIndex,
3122
3223
  rule: ruleIndex,
3123
- code: "MISSING_CONTEXT",
3124
3224
  scope: Scope.CONTEXT,
3125
- path: pathHex,
3126
- typeCode: propInfo.typeCode
3225
+ path: pathHex
3226
+ };
3227
+ if (contextValue === void 0) return {
3228
+ code: "MISSING_CONTEXT",
3229
+ ...site,
3230
+ typeCode: propertyInfo.typeCode
3127
3231
  };
3128
- let value;
3129
- if (typeof contextValue === "string") value = addressToBigInt(contextValue);
3130
- else value = contextValue;
3232
+ const value = contextValueToWord(contextValue);
3131
3233
  let result;
3132
3234
  let ctxOperand;
3133
3235
  if ((opCode & ~Op.NOT) === Op.EQ_CTX) {
3134
3236
  const operand = resolveContextOperand(operandData, context);
3135
3237
  if (typeof operand !== "bigint") return {
3136
- group: groupIndex,
3137
- rule: ruleIndex,
3138
3238
  code: "MISSING_CONTEXT",
3139
- scope: Scope.CONTEXT,
3140
- path: pathHex,
3239
+ ...site,
3141
3240
  opCode,
3142
3241
  operandData: bytesToHex(operandData),
3143
3242
  typeCode: operand.ctxTypeCode
3144
3243
  };
3145
3244
  result = value === operand !== ((opCode & Op.NOT) !== 0);
3146
3245
  ctxOperand = operand;
3147
- } else result = applyOperator(opCode, value, 32, operandData, propInfo.typeCode);
3246
+ } else result = applyOperator(opCode, value, 32n, operandData, propertyInfo.typeCode);
3148
3247
  if (!result) return {
3149
- group: groupIndex,
3150
- rule: ruleIndex,
3151
3248
  code: "VALUE_MISMATCH",
3152
- scope: Scope.CONTEXT,
3153
- path: pathHex,
3249
+ ...site,
3154
3250
  opCode,
3155
3251
  operandData: bytesToHex(operandData),
3156
- typeCode: propInfo.typeCode,
3252
+ typeCode: propertyInfo.typeCode,
3157
3253
  resolvedValue: bigintToHex(value),
3158
3254
  ...ctxOperand !== void 0 && { resolvedOperand: bigintToHex(ctxOperand) }
3159
3255
  };
@@ -3164,6 +3260,6 @@ const PolicyEnforcer = {
3164
3260
  enforce
3165
3261
  };
3166
3262
  //#endregion
3167
- export { CallciumError, ContextProperty, Descriptor, DescriptorCoder, Limits, 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 };
3263
+ 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, findContextProperty, gasPrice, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
3168
3264
 
3169
3265
  //# sourceMappingURL=index.js.map