Pre-release — The API surface may change. Unaudited.
Callcium LogoCallcium
Building Policies

Selectorless Policies

Validate raw ABI-encoded data without a function selector.

Policies created with PolicyBuilder.create(signature) are bound to a specific function selector — the first 4 bytes of calldata are matched against it before arguments are evaluated. Selectorless policies validate the ABI-encoded payload directly.

When to use

Selectorless policies are appropriate when the data being validated has no function selector prefix. Examples:

  • Signed payloads verified on-chain and passed as parameters to a relayer.
  • Cross-chain messages decoded and forwarded by a bridge contract.
  • Batch entries where each entry is an independently ABI-encoded parameter set.

Creating a selectorless policy

Use PolicyBuilder.createRaw() with a comma-separated list of ABI types instead of a function signature:

import { arg } from "callcium/Constraint.sol";
import { PolicyBuilder } from "callcium/PolicyBuilder.sol";

address[] memory recipients = new address[](2);
recipients[0] = TREASURY;
recipients[1] = MULTISIG;

bytes memory policy = PolicyBuilder
    .createRaw("address,uint256")
    .add(arg(0).isIn(recipients))          // recipient
    .add(arg(1).lte(uint256(1_000e18)))    // amount
    .build();

The full constraint API — arg(), context targets, isIn, notIn, bitmask, length operators, .or(), build(), buildUnsafe() — is identical to function-bound policies.

Passing data to enforce

PolicyEnforcer.enforce reads selectorless payloads from byte 0 — arg(0) resolves to the first byte of the data passed in.

Selector prefix

If the raw data has a function selector prefix, strip it before passing to enforce. Those 4 bytes are interpreted as the start of the first argument — no error is thrown.

Standard (selector-bound) policies are the inverse: the enforcer validates the first 4 bytes against the policy's selector and reads arguments from byte 4. The selector must be present.

On this page