Calldata Policy Engine

Change the rules, not the contract

Type-safe constraints on calldata, enforced onchain and offchain

Enforce onchain, offchain, or both

Two complete implementations, each standalone. Same API, same bytes, same verdict.

// function approve(address spender, uint256 amount)
bytes memory policy = PolicyBuilder
    .create("approve(address,uint256)")
    .add(arg(0).isIn(trustedSpenders))     // spender
    .add(arg(1).lte(uint256(1_000_000e6))) // amount
    .build();
// function approve(address spender, uint256 amount)
const policy = PolicyBuilder
    .create("approve(address,uint256)")
    .add(arg(0).isIn(trustedSpenders))       // spender
    .add(arg(1).lte(1_000_000n * 10n ** 6n)) // amount
    .build();

Encoded policy

60095ea7b30004020241200100020000006a0039010100000000000000000041070028111111111111111111111111111111111111111122222222222222222222222222222222222222220031010100010000000020000020050020000000000000000000000000000000000000000000000000000000e8d4a51000

Hover or tap a field

Advanced constraints

Reach into structs and arrays, compare against transaction context, and allow alternatives with OR.

// struct SwapParams { address tokenIn; address tokenOut; uint256 amount; }
// function swap(SwapParams params)
bytes memory policy = PolicyBuilder
    .create("swap((address,address,uint256))")
    .add(arg(0, 0).notIn(denied))  // params.tokenIn
    .add(arg(0, 1).notIn(denied))  // params.tokenOut
    .add(arg(0, 2).gt(uint256(0))) // params.amount
    .build();

Formal spec, Lean proofs

A Lean model defines the policy format and proves its guarantees.Both implementations pass the same test vectors.

Read the specification
Lean logo

Policy Tools

The tools run in the browser and use the TypeScript SDK.

Open the tools
Policy Builder

Build a policy visually, rule by rule.

Policy Inspector

Decode any policy blob, field by field.

Policy Enforcer

Test calldata against a policy.