@callcium/sdk 0.12.0 → 0.13.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/src/constants.ts CHANGED
@@ -7,6 +7,7 @@ import { CallciumError } from "./errors";
7
7
  /** Binary layout constants for the Callcium descriptor format. */
8
8
  export const DescriptorFormat = {
9
9
  VERSION: 0x02,
10
+ HEADER_PARAMCOUNT_OFFSET: 1,
10
11
  HEADER_SIZE: 2,
11
12
  TYPECODE_SIZE: 1,
12
13
  COMPOSITE_META_SIZE: 3,
@@ -28,27 +29,23 @@ export const DescriptorFormat = {
28
29
  // Policy format
29
30
  ///////////////////////////////////////////////////////////////////////////
30
31
 
31
- /** Byte width of a rule's data length field. */
32
- const RULE_DATALENGTH_SIZE = 2;
33
-
34
- /** Byte width of one path step. */
35
- const PATH_STEP_SIZE = 2;
36
-
37
- /** Byte overhead of a rule header, excluding the path, the hint block, and the operator payload. */
38
- const RULE_FIXED_OVERHEAD = 7;
39
-
40
- /** Highest value a big-endian field of `byteWidth` bytes holds. */
32
+ /** Highest value a big-endian field of `byteWidth` bytes can represent. */
41
33
  export function fieldMax(byteWidth: number): number {
42
34
  return 256 ** byteWidth - 1;
43
35
  }
44
36
 
37
+ /** Lowest minor version, the minor of a blob that uses no extension. */
38
+ const MINOR = 0;
39
+
45
40
  /** Binary layout constants and normative limits for the Callcium policy format. */
46
41
  export const PolicyFormat = {
47
- VERSION: 0x02,
48
- VERSION_MASK: 0x0f,
49
- FLAG_NO_SELECTOR: 0x10,
50
- RESERVED_MASK: 0xe0,
51
- HEADER_SIZE: 1,
42
+ MAJOR: 3,
43
+ MINOR,
44
+ SUPPORTED_MINOR: MINOR,
45
+ MAJOR_SHIFT: 5,
46
+ MINOR_SHIFT: 1,
47
+ MINOR_MASK: 0x0f,
48
+ FLAG_NO_SELECTOR: 0x01,
52
49
  SELECTOR_OFFSET: 1,
53
50
  SELECTOR_SIZE: 4,
54
51
  DESC_LENGTH_OFFSET: 5,
@@ -56,18 +53,20 @@ export const PolicyFormat = {
56
53
  DESC_OFFSET: 7,
57
54
  GROUP_COUNT_SIZE: 1,
58
55
  GROUP_RULECOUNT_SIZE: 2,
59
- GROUP_SIZE_SIZE: 4,
60
56
  GROUP_HEADER_SIZE: 6,
61
57
  RULE_SIZE_SIZE: 2,
62
58
  RULE_SCOPE_OFFSET: 2,
59
+ RULE_SCOPE_SIZE: 1,
63
60
  RULE_DEPTH_OFFSET: 3,
61
+ RULE_DEPTH_SIZE: 1,
64
62
  RULE_PATH_OFFSET: 4,
65
- PATH_STEP_SIZE,
66
- PATH_STEP_MAX: fieldMax(PATH_STEP_SIZE),
63
+ PATH_STEP_SIZE: 2,
64
+ PROPERTY_ID_SIZE: 2,
65
+ RULE_QUANTIFIER_OFFSET: 3,
66
+ RULE_QUANTIFIER_SIZE: 1,
67
+ LOOP_DEPTH_OFFSET: 4,
67
68
  RULE_OPCODE_SIZE: 1,
68
- RULE_DATALENGTH_SIZE,
69
- RULE_FIXED_OVERHEAD,
70
- RULE_MIN_SIZE: RULE_FIXED_OVERHEAD + PATH_STEP_SIZE,
69
+ RULE_DATALENGTH_SIZE: 2,
71
70
  HINT_HEADER_SIZE: 1,
72
71
  HINT_HOP_SIZE: 8,
73
72
  HINT_HOP_INDEX_OFFSET: 4,
@@ -78,15 +77,10 @@ export const PolicyFormat = {
78
77
  HINT_TARGET_SIZE: 7,
79
78
  HINT_TARGET_META_OFFSET: 4,
80
79
  HINT_TARGET_TYPECODE_OFFSET: 6,
81
- HINT_KIND_NONE: 0x0,
82
- HINT_KIND_ALL: 0x1,
83
- HINT_KIND_ANY: 0x2,
84
- HINT_KIND_MAX: 0x2,
85
- HINT_KIND_SHIFT: 6,
86
80
  HINT_HOP_COUNT_MASK: 0x3f,
87
- HINT_SUFFIX_RESERVED_MASK: 0xc0,
81
+ HINT_HEADER_RESERVED_MASK: 0xc0,
88
82
  HINT_NO_INDEX: 0xffff,
89
- HINT_INDEX_RESERVED: 0xfffe,
83
+ PATH_MARKER_BASE: 0xff00,
90
84
  HINT_META_ELEM_DYNAMIC: 0x8000,
91
85
  HINT_META_DYNAMIC_ARRAY: 0x4000,
92
86
  HINT_META_RESERVED_MASK: 0x3000,
@@ -94,7 +88,6 @@ export const PolicyFormat = {
94
88
  // Normative limits.
95
89
  MAX_PATH_DEPTH: 32,
96
90
  MAX_QUANTIFIED_ARRAY_LENGTH: 256,
97
- MAX_SET_MEMBERS: Math.floor(fieldMax(RULE_DATALENGTH_SIZE) / 32),
98
91
  } as const satisfies Record<string, number>;
99
92
 
100
93
  ///////////////////////////////////////////////////////////////////////////
@@ -113,7 +106,7 @@ type CodeMap<T extends readonly { readonly key: string; readonly code: number }[
113
106
  * Render a wire code as fixed-width hex.
114
107
  * @param code - Code value.
115
108
  * @param byteWidth - Byte width of the field the code occupies.
116
- * @returns The code as `0x`-prefixed hex, or its decimal form when no such field holds it.
109
+ * @returns The code as `0x`-prefixed hex, or its decimal form when no such field can represent it.
117
110
  */
118
111
  export function formatCode(code: number, byteWidth: number): string {
119
112
  if (!Number.isInteger(code) || code < 0 || code > fieldMax(byteWidth)) return String(code);
@@ -128,37 +121,6 @@ export function buildCodeMap<T extends readonly { readonly key: string; readonly
128
121
  return Object.fromEntries(table.map((entry) => [entry.key, entry.code])) as CodeMap<T>;
129
122
  }
130
123
 
131
- ///////////////////////////////////////////////////////////////////////////
132
- // Scope codes
133
- ///////////////////////////////////////////////////////////////////////////
134
-
135
- const SCOPE_TABLE = [
136
- { key: "CONTEXT", code: 0x00, label: "context" },
137
- { key: "CALLDATA", code: 0x01, label: "calldata" },
138
- ] as const;
139
-
140
- /** Rule scope discriminant: context (EVM environment) vs. calldata (ABI payload). */
141
- export const Scope = buildCodeMap(SCOPE_TABLE);
142
-
143
- /** Display metadata for a scope code. */
144
- export type ScopeInfo = { label: string };
145
-
146
- const scopeByCode: ReadonlyMap<number, ScopeInfo> = new Map<number, ScopeInfo>(
147
- SCOPE_TABLE.map((entry) => [entry.code, { label: entry.label }]),
148
- );
149
-
150
- /**
151
- * Map a scope code to its display label.
152
- * @param code - Scope byte value.
153
- * @returns Display metadata for the scope.
154
- * @throws {CallciumError} If the code is not a recognised scope.
155
- */
156
- export function lookupScope(code: number): ScopeInfo {
157
- const info = scopeByCode.get(code);
158
- if (!info) throw new CallciumError("INVALID_SCOPE", `Unknown scope value ${code}`);
159
- return info;
160
- }
161
-
162
124
  ///////////////////////////////////////////////////////////////////////////
163
125
  // Type code ranges
164
126
  ///////////////////////////////////////////////////////////////////////////
@@ -233,17 +195,96 @@ export function lookupContextProperty(code: number): ContextPropertyInfo {
233
195
  if (!info) {
234
196
  throw new CallciumError(
235
197
  "UNKNOWN_CONTEXT_PROPERTY",
236
- `Unknown context property ID ${formatCode(code, PATH_STEP_SIZE)}`,
198
+ `Unknown context property ID ${formatCode(code, PolicyFormat.PROPERTY_ID_SIZE)}`,
237
199
  );
238
200
  }
239
201
  return info;
240
202
  }
241
203
 
204
+ ///////////////////////////////////////////////////////////////////////////
205
+ // Scope codes
206
+ ///////////////////////////////////////////////////////////////////////////
207
+
208
+ const SCOPE_TABLE = [
209
+ { key: "CONTEXT", code: 0x00, label: "context" },
210
+ { key: "CALLDATA", code: 0x01, label: "calldata" },
211
+ { key: "LOOP", code: 0x02, label: "loop" },
212
+ ] as const;
213
+
214
+ /** Rule scope discriminant: context (EVM environment), calldata (ABI payload) or loop (array elements). */
215
+ export const Scope = buildCodeMap(SCOPE_TABLE);
216
+
217
+ /** A defined scope code. */
218
+ export type ScopeCode = (typeof SCOPE_TABLE)[number]["code"];
219
+
220
+ /** Display metadata for a scope code. */
221
+ export type ScopeInfo = { label: string };
222
+
223
+ const scopeByCode: ReadonlyMap<number, ScopeInfo> = new Map<number, ScopeInfo>(
224
+ SCOPE_TABLE.map((entry) => [entry.code, { label: entry.label }]),
225
+ );
226
+
227
+ /** Check whether a scope byte is a defined scope code. */
228
+ export function isScopeCode(code: number): code is ScopeCode {
229
+ return scopeByCode.has(code);
230
+ }
231
+
232
+ /**
233
+ * Map a scope code to its display label.
234
+ * @param code - Scope byte value.
235
+ * @returns Display metadata for the scope.
236
+ * @throws {CallciumError} If the code is not a recognised scope.
237
+ */
238
+ export function lookupScope(code: number): ScopeInfo {
239
+ const info = scopeByCode.get(code);
240
+ if (!info) throw new CallciumError("UNKNOWN_SCOPE", `Unknown scope value ${code}`);
241
+ return info;
242
+ }
243
+
244
+ ///////////////////////////////////////////////////////////////////////////
245
+ // Quantifier codes
246
+ ///////////////////////////////////////////////////////////////////////////
247
+
248
+ const QUANTIFIER_TABLE = [
249
+ { key: "ALL", code: 0x01, label: "all" },
250
+ { key: "ANY", code: 0x02, label: "any" },
251
+ ] as const;
252
+
253
+ /** Loop quantifier: the body holds for every element (`ALL`) or for some element (`ANY`). */
254
+ export const LoopQuantifier = buildCodeMap(QUANTIFIER_TABLE);
255
+
256
+ /** A defined loop quantifier code. */
257
+ export type QuantifierCode = (typeof QUANTIFIER_TABLE)[number]["code"];
258
+
259
+ /** Display metadata for a loop quantifier code. */
260
+ export type QuantifierInfo = { label: string };
261
+
262
+ const quantifierByCode: ReadonlyMap<number, QuantifierInfo> = new Map<number, QuantifierInfo>(
263
+ QUANTIFIER_TABLE.map((entry) => [entry.code, { label: entry.label }]),
264
+ );
265
+
266
+ /** Check whether a quantifier byte is a defined quantifier code. */
267
+ export function isQuantifierCode(code: number): code is QuantifierCode {
268
+ return quantifierByCode.has(code);
269
+ }
270
+
271
+ /**
272
+ * Map a loop quantifier code to its display label.
273
+ * @param code - Quantifier byte value.
274
+ * @returns Display metadata for the quantifier.
275
+ * @throws {CallciumError} If the code is not a recognised quantifier.
276
+ */
277
+ export function lookupQuantifier(code: number): QuantifierInfo {
278
+ const info = quantifierByCode.get(code);
279
+ if (!info) throw new CallciumError("UNKNOWN_QUANTIFIER", `Unknown quantifier value ${code}`);
280
+ return info;
281
+ }
282
+
242
283
  ///////////////////////////////////////////////////////////////////////////
243
284
  // Operator codes
244
285
  ///////////////////////////////////////////////////////////////////////////
245
286
 
246
- /** Operand count category for operator data validation. */
287
+ /** Operand count category of an operator. */
247
288
  export type Operands = "single" | "range" | "variadic";
248
289
 
249
290
  /** @internal */
@@ -255,24 +296,33 @@ const OP_TABLE = [
255
296
  { key: "LTE", code: 0x05, label: "<=", operands: "single" },
256
297
  { key: "BETWEEN", code: 0x06, label: "between", operands: "range" },
257
298
  { key: "IN", code: 0x07, label: "in", operands: "variadic" },
258
- { key: "EQ_CTX", code: 0x08, label: "== ctx", operands: "single" },
259
- { key: "GT_CTX", code: 0x09, label: "> ctx", operands: "single" },
260
- { key: "LT_CTX", code: 0x0a, label: "< ctx", operands: "single" },
261
- { key: "GTE_CTX", code: 0x0b, label: ">= ctx", operands: "single" },
262
- { key: "LTE_CTX", code: 0x0c, label: "<= ctx", operands: "single" },
263
299
  { key: "BITMASK_ALL", code: 0x10, label: "bitmask all", operands: "single" },
264
300
  { key: "BITMASK_ANY", code: 0x11, label: "bitmask any", operands: "single" },
265
301
  { key: "BITMASK_NONE", code: 0x12, label: "bitmask none", operands: "single" },
266
- { key: "LENGTH_EQ", code: 0x20, label: "length ==", operands: "single" },
267
- { key: "LENGTH_GT", code: 0x21, label: "length >", operands: "single" },
268
- { key: "LENGTH_LT", code: 0x22, label: "length <", operands: "single" },
269
- { key: "LENGTH_GTE", code: 0x23, label: "length >=", operands: "single" },
270
- { key: "LENGTH_LTE", code: 0x24, label: "length <=", operands: "single" },
271
- { key: "LENGTH_BETWEEN", code: 0x25, label: "length between", operands: "range" },
302
+ { key: "LENGTH_EQ", code: 0x18, label: "length ==", operands: "single" },
303
+ { key: "LENGTH_GT", code: 0x19, label: "length >", operands: "single" },
304
+ { key: "LENGTH_LT", code: 0x1a, label: "length <", operands: "single" },
305
+ { key: "LENGTH_GTE", code: 0x1b, label: "length >=", operands: "single" },
306
+ { key: "LENGTH_LTE", code: 0x1c, label: "length <=", operands: "single" },
307
+ { key: "LENGTH_BETWEEN", code: 0x1d, label: "length between", operands: "range" },
272
308
  ] as const;
273
309
 
274
- /** Callcium policy operator codes. */
275
- export const Op: CodeMap<typeof OP_TABLE> & { readonly NOT: 0x80 } = { ...buildCodeMap(OP_TABLE), NOT: 0x80 };
310
+ /** Callcium policy operator codes, with the NOT flag and the operand source fields. */
311
+ export const Op = { ...buildCodeMap(OP_TABLE), NOT: 0x80, SOURCE_SHIFT: 5, COMPARISON_MASK: 0x1f } as const;
312
+
313
+ /** A defined comparison code. */
314
+ export type ComparisonCode = (typeof OP_TABLE)[number]["code"];
315
+
316
+ const SOURCE_TABLE = [
317
+ { key: "CONSTANT", code: 0, labelSuffix: "" },
318
+ { key: "CONTEXT", code: 1, labelSuffix: " ctx" },
319
+ ] as const;
320
+
321
+ /** Operand source: constant values, or a context property. */
322
+ export const Source = buildCodeMap(SOURCE_TABLE);
323
+
324
+ /** A defined operand source code. */
325
+ export type SourceCode = (typeof SOURCE_TABLE)[number]["code"];
276
326
 
277
327
  /** Display metadata for an operator code. */
278
328
  export type OpInfo = { label: string; operands: Operands };
@@ -281,24 +331,44 @@ const opByCode: ReadonlyMap<number, OpInfo> = new Map<number, OpInfo>(
281
331
  OP_TABLE.map((entry) => [entry.code, { label: entry.label, operands: entry.operands }]),
282
332
  );
283
333
 
334
+ const labelSuffixBySource: ReadonlyMap<number, string> = new Map<number, string>(
335
+ SOURCE_TABLE.map((entry) => [entry.code, entry.labelSuffix]),
336
+ );
337
+
338
+ /** Check whether a comparison is a defined comparison code. */
339
+ export function isComparisonCode(code: number): code is ComparisonCode {
340
+ return opByCode.has(code);
341
+ }
342
+
343
+ /** Check whether a source is a defined operand source code. */
344
+ export function isSourceCode(code: number): code is SourceCode {
345
+ return labelSuffixBySource.has(code);
346
+ }
347
+
348
+ /** The comparison bits of an operator code. */
349
+ export function comparisonOf(opCode: number): number {
350
+ return opCode & Op.COMPARISON_MASK;
351
+ }
352
+
353
+ /** The operand source bits of an operator code. */
354
+ export function sourceOf(opCode: number): number {
355
+ return (opCode & ~Op.NOT) >> Op.SOURCE_SHIFT;
356
+ }
357
+
284
358
  /**
285
- * Map an operator code to its display label. Strips the NOT flag automatically.
286
- * @param code - Operator byte value (may include the NOT flag).
359
+ * Map an operator code to its display label. Strips the NOT flag automatically. A context source adds ` ctx`.
360
+ * @param code - Operator byte value (can include the NOT flag).
287
361
  * @returns Display metadata for the base operator.
288
362
  * @throws {CallciumError} If the base code is not a recognised operator.
289
363
  */
290
364
  export function lookupOp(code: number): OpInfo {
291
365
  const base = code & ~Op.NOT;
292
- const info = opByCode.get(base);
293
- if (!info)
366
+ const info = opByCode.get(comparisonOf(base));
367
+ const labelSuffix = labelSuffixBySource.get(sourceOf(base));
368
+ if (!info || labelSuffix === undefined)
294
369
  throw new CallciumError(
295
370
  "UNKNOWN_OPERATOR",
296
371
  `Unknown operator code ${formatCode(base, PolicyFormat.RULE_OPCODE_SIZE)}`,
297
372
  );
298
- return info;
299
- }
300
-
301
- /** Operand count category for an operator code, or undefined when unrecognised. */
302
- export function operandsOf(opBase: number): Operands | undefined {
303
- return opByCode.get(opBase)?.operands;
373
+ return { label: `${info.label}${labelSuffix}`, operands: info.operands };
304
374
  }
package/src/constraint.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { bytesToHex, hexToBytes, toAddress } from "./bytes";
2
- import { PolicyFormat, Op, Scope, ContextProperty, lookupContextProperty } from "./constants";
2
+ import { ContextProperty, lookupContextProperty, Op, Scope, Source } from "./constants";
3
3
  import { CallciumError } from "./errors";
4
4
  import { encodePath } from "./path";
5
5
 
6
- import type { Address, Hex, Constraint } from "./types";
6
+ import type { Address, Hex } from "./types";
7
+ import type { Constraint } from "./types";
7
8
 
8
9
  ///////////////////////////////////////////////////////////////////////////
9
10
  // Value encoding helpers
@@ -32,6 +33,17 @@ function addressBody(value: string): string {
32
33
  return toAddress(value).slice(2);
33
34
  }
34
35
 
36
+ /**
37
+ * Require an operand that reached the builder untyped to be a bigint and return it.
38
+ * @throws {CallciumError} When the operand is of another type, as a JavaScript `number` is.
39
+ */
40
+ function requireBigint(value: unknown): bigint {
41
+ if (typeof value !== "bigint") {
42
+ throw new CallciumError("MALFORMED_OPERAND", "Operand is not a bigint, a boolean or an address string");
43
+ }
44
+ return value;
45
+ }
46
+
35
47
  /** Convert a scalar value to a 32-byte big-endian word. */
36
48
  function encodeWord(value: ScalarValue): Uint8Array {
37
49
  const word = new Uint8Array(32);
@@ -47,6 +59,8 @@ function encodeWord(value: ScalarValue): Uint8Array {
47
59
  return word;
48
60
  }
49
61
 
62
+ requireBigint(value);
63
+
50
64
  // A negative operand occupies the word in two's complement, which is the signed target's encoding.
51
65
  let bigValue = checkOperandRange(value);
52
66
  for (let i = 31; i >= 0; i--) {
@@ -79,6 +93,9 @@ function rangeOp(opCode: number, min: bigint, max: bigint): Hex {
79
93
  return bytesToHex(buffer);
80
94
  }
81
95
 
96
+ /** The `source` bits of an operator code whose operand is a context property. */
97
+ const CONTEXT_SOURCE = Source.CONTEXT << Op.SOURCE_SHIFT;
98
+
82
99
  /**
83
100
  * Require an assigned context property ID and return it as an operand word.
84
101
  * @throws {CallciumError} When the ID names no assigned property.
@@ -95,8 +112,8 @@ function setOp(opCode: number, values: readonly ScalarValue[]): Hex {
95
112
  const words = values.map((value) => {
96
113
  if (typeof value === "bigint") return BigInt.asUintN(256, checkOperandRange(value));
97
114
  if (typeof value === "boolean") return value ? 1n : 0n;
98
- // String address.
99
- return BigInt("0x" + addressBody(value));
115
+ if (typeof value === "string") return BigInt("0x" + addressBody(value));
116
+ return requireBigint(value);
100
117
  });
101
118
 
102
119
  const deduped = [...new Set(words)].toSorted((a, b) => (a < b ? -1 : a > b ? 1 : 0));
@@ -104,9 +121,6 @@ function setOp(opCode: number, values: readonly ScalarValue[]): Hex {
104
121
  if (deduped.length === 0) {
105
122
  throw new CallciumError("EMPTY_SET", "Set must contain at least one value");
106
123
  }
107
- if (deduped.length > PolicyFormat.MAX_SET_MEMBERS) {
108
- throw new CallciumError("SET_TOO_LARGE", `Set must contain at most ${PolicyFormat.MAX_SET_MEMBERS} values`);
109
- }
110
124
 
111
125
  const buffer = new Uint8Array(1 + deduped.length * 32);
112
126
  buffer[0] = opCode;
@@ -149,19 +163,17 @@ export function readOperandExtremes(constraint: Constraint | ConstraintBuilder):
149
163
  * be passed directly to policy builders.
150
164
  */
151
165
  export class ConstraintBuilder<Operand extends ScalarValue = ScalarValue> implements Constraint {
152
- readonly scope: number;
166
+ readonly scope: Constraint["scope"];
153
167
  readonly path: Hex;
154
168
  readonly operators: Hex[];
155
169
  /**
156
- * Extremes of the numeric operands the value operators received, as written. Only the extremes can offend: an operand
157
- * folds into a target's range from below it or from above it, and whichever reaches furthest arrives first. Read it
158
- * through {@link readOperandExtremes}.
170
+ * Extremes of the numeric operands the value operators received, as written.
159
171
  * @internal
160
172
  */
161
173
  readonly operandExtremes: OperandExtremes;
162
174
 
163
175
  /** @internal */
164
- constructor(scope: number, path: Hex) {
176
+ constructor(scope: Constraint["scope"], path: Hex) {
165
177
  this.scope = scope;
166
178
  this.path = path;
167
179
  this.operators = [];
@@ -169,7 +181,7 @@ export class ConstraintBuilder<Operand extends ScalarValue = ScalarValue> implem
169
181
  }
170
182
 
171
183
  /**
172
- * Push a pre-encoded operator hex string and return this for chaining. `values` are the operands as written; the
184
+ * Push a pre-encoded operator hex string and return this for chaining. `values` are the operands as written. The
173
185
  * numeric ones join the record.
174
186
  */
175
187
  private push(opHex: Hex, values: readonly ScalarValue[] = []): this {
@@ -198,32 +210,32 @@ export class ConstraintBuilder<Operand extends ScalarValue = ScalarValue> implem
198
210
 
199
211
  /** Assert the value equals the context property `contextPropertyId`. */
200
212
  eqCtx(contextPropertyId: number): this {
201
- return this.push(singleOp(Op.EQ_CTX, contextOperand(contextPropertyId)));
213
+ return this.push(singleOp(Op.EQ | CONTEXT_SOURCE, contextOperand(contextPropertyId)));
202
214
  }
203
215
 
204
216
  /** Assert the value does not equal the context property `contextPropertyId`. */
205
217
  neqCtx(contextPropertyId: number): this {
206
- return this.push(singleOp(Op.EQ_CTX | Op.NOT, contextOperand(contextPropertyId)));
218
+ return this.push(singleOp(Op.EQ | CONTEXT_SOURCE | Op.NOT, contextOperand(contextPropertyId)));
207
219
  }
208
220
 
209
221
  /** Assert the value is greater than the context property `contextPropertyId`. */
210
222
  gtCtx(contextPropertyId: number): this {
211
- return this.push(singleOp(Op.GT_CTX, contextOperand(contextPropertyId)));
223
+ return this.push(singleOp(Op.GT | CONTEXT_SOURCE, contextOperand(contextPropertyId)));
212
224
  }
213
225
 
214
226
  /** Assert the value is less than the context property `contextPropertyId`. */
215
227
  ltCtx(contextPropertyId: number): this {
216
- return this.push(singleOp(Op.LT_CTX, contextOperand(contextPropertyId)));
228
+ return this.push(singleOp(Op.LT | CONTEXT_SOURCE, contextOperand(contextPropertyId)));
217
229
  }
218
230
 
219
231
  /** Assert the value is greater than or equal to the context property `contextPropertyId`. */
220
232
  gteCtx(contextPropertyId: number): this {
221
- return this.push(singleOp(Op.GTE_CTX, contextOperand(contextPropertyId)));
233
+ return this.push(singleOp(Op.GTE | CONTEXT_SOURCE, contextOperand(contextPropertyId)));
222
234
  }
223
235
 
224
236
  /** Assert the value is less than or equal to the context property `contextPropertyId`. */
225
237
  lteCtx(contextPropertyId: number): this {
226
- return this.push(singleOp(Op.LTE_CTX, contextOperand(contextPropertyId)));
238
+ return this.push(singleOp(Op.LTE | CONTEXT_SOURCE, contextOperand(contextPropertyId)));
227
239
  }
228
240
 
229
241
  /** Assert the value is greater than `bound`. */
@@ -260,8 +272,7 @@ export class ConstraintBuilder<Operand extends ScalarValue = ScalarValue> implem
260
272
 
261
273
  /**
262
274
  * Assert the value is a member of the set. Values are sorted and deduplicated before encoding.
263
- * @throws {CallciumError} If the set is empty, has more than MAX_SET_MEMBERS distinct values, or a value is not
264
- * encodable as an operand word.
275
+ * @throws {CallciumError} If the set is empty or a value is not encodable as an operand word.
265
276
  */
266
277
  isIn(values: readonly Operand[]): this {
267
278
  return this.push(setOp(Op.IN, values), values);
@@ -269,8 +280,7 @@ export class ConstraintBuilder<Operand extends ScalarValue = ScalarValue> implem
269
280
 
270
281
  /**
271
282
  * Assert the value is not a member of the set.
272
- * @throws {CallciumError} If the set is empty, has more than MAX_SET_MEMBERS distinct values, or a value is not
273
- * encodable as an operand word.
283
+ * @throws {CallciumError} If the set is empty or a value is not encodable as an operand word.
274
284
  */
275
285
  notIn(values: readonly Operand[]): this {
276
286
  return this.push(setOp(Op.IN | Op.NOT, values), values);
@@ -338,7 +348,7 @@ export class ConstraintBuilder<Operand extends ScalarValue = ScalarValue> implem
338
348
  ///////////////////////////////////////////////////////////////////////////
339
349
 
340
350
  /**
341
- * Target a calldata argument by path. Each argument is a big-endian uint16 step; further steps navigate nested types.
351
+ * Target a calldata argument by path. Each argument is a big-endian uint16 step. Further steps navigate nested types.
342
352
  */
343
353
  export function arg(p0: number, ...rest: number[]): ConstraintBuilder;
344
354
  export function arg(...steps: number[]): ConstraintBuilder {