Policy Inspection
Decode and introspect a policy blob.
A policy blob is an opaque byte string at the contract boundary. The SDK provides two entry points on PolicyCoder for inspecting one: a structured form for logical manipulation, and a byte-span-annotated form for tooling that needs to highlight positions in the raw bytes. Both reject a blob that fails storage validation or whose descriptor is invalid.
PolicyCoder.decode
PolicyCoder.decode(blob: Hex) → PolicyData returns the structured representation of a policy. Use it when the goal is to read, modify, or re-encode the policy itself, not the bytes it occupies.
import { PolicyCoder, Scope } from "@callcium/sdk";
const data = PolicyCoder.decode(policy);
console.log(data.isSelectorless); // false
console.log(data.selector); // 0x095ea7b3
console.log(data.groups.length); // number of OR groups
for (const group of data.groups) {
for (const rule of group) {
if (rule.scope === Scope.LOOP) console.log(rule.quantifier, rule.path, rule.body);
else console.log(rule.scope, rule.path, rule.operator);
}
}Groups and their rules are in the policy's normal form, which orders them by their encoding, so indices do not follow .add() and .or() order. Constraints with one quantifier over one array form a single loop rule.
PolicyData has the following shape:
type PolicyData = {
isSelectorless: boolean;
selector: Hex;
descriptor: Hex;
groups: Rule[][];
};
type Rule = BodyRule | LoopRule;
/** A test or a context test. */
type BodyRule = {
scope: typeof Scope.CONTEXT | typeof Scope.CALLDATA;
/** A context test's path is its property ID. */
path: Hex;
hint?: Hex;
/** opCode(1) || each operand as a 32-byte word, a context property as its ID. */
operator: Hex;
};
/** A loop: its body over each element of the array at `path`. */
type LoopRule = {
scope: typeof Scope.LOOP;
quantifier: QuantifierCode;
path: Hex;
hint?: Hex;
/** Each body test's path starts at the element. */
body: BodyRule[];
};Re-encoding a decoded policy yields its normal form, so a blob build() produced round-trips byte for byte:
import { PolicyCoder } from "@callcium/sdk";
const data = PolicyCoder.decode(policy);
const reencoded = PolicyCoder.encode(data);
// reencoded === policyPolicyCoder.inspect
PolicyCoder.inspect(blob: Hex) → DecodedPolicy returns a structural tree with every field wrapped in a Field<T> carrying its byte span, hints split into their fields, and operators parsed. Use it when the tool needs to point to byte ranges in the raw blob: hex viewers, diff renderers, audit reports, or debugger UIs.
import { PolicyCoder } from "@callcium/sdk";
const decoded = PolicyCoder.inspect(policy);
console.log(decoded.selector.value, decoded.selector.span); // selector hex and its byte range
console.log(decoded.groupCount.value); // number of groups
for (const group of decoded.groups) {
for (const rule of group.rules) {
const tests = "body" in rule ? rule.body : [rule];
for (const test of tests) {
console.log(test.operator.opCode, test.path.value, test.data.span);
}
}
}Each Field<T> is { value: T, span: { start, end } }, where start and end are byte offsets into the original blob. The tree follows the binary layout:
DecodedPolicy: the header (major,minor,isSelectorless), selector, descriptor, and groups.DecodedGroup: one OR group with its rule count, byte size, and rules.DecodedTest: a test with its scope, path, hint, operator code, and operand bytes, plustargetTypeand the parsedoperator.DecodedLoop: a loop with its quantifier, array path, frame hint, and theDecodedTestentries of its body. Narrow a rule with"body" in rule.DecodedHintandDecodedFrameHint: a hint's hops and its target or array.DecodedOperator: the comparison, its negation, the operand type, and either the constants as 32-byte words (values) or the context property the operand names (contextPropertyId).DecodedDescriptor: the descriptor's version, parameter count, and oneDecodedNodetree per parameter.
DescriptorCoder.inspect(descriptor) returns the same DecodedDescriptor for a descriptor on its own, and DescriptorCoder.toTypes(descriptor) recovers its type list.
Inspecting rule paths
Rule paths are BE16-packed step sequences. Use parsePathSteps to decode one into plain numbers:
import { parsePathSteps } from "@callcium/sdk";
parsePathSteps("0x00000002"); // [0, 2] — arg 0, field 2Path steps are argument, struct field, and array indices. A stored path holds no quantifier step: a loop rule carries its quantifier in its own field, and its body tests' paths start at the element. Quantifier.ALL and Quantifier.ANY appear only in paths given to arg().
Full method signatures and type exports are in the PolicyCoder reference.