Pre-release — The API surface may change. Unaudited.

Programmable Calldata Validation Engine

Define type-safe constraints on ABI-encoded data.Update validation rules without redeploying contracts.

Get Started

Build policies with a fluent Solidity API

// 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();
// 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(sanctioned)) // params.tokenIn
    .add(arg(0, 1).notIn(sanctioned)) // params.tokenOut
    .add(arg(0, 2).gt(uint256(0)))     // params.amount
    .build();
// struct Transaction { address to; uint256 value; }
// function multiSend(Transaction[] calls)
bytes memory policy = PolicyBuilder
    .create("multiSend((address,uint256)[])")
    .add(arg(0).lengthBetween(1, 50))            // calls.length
    .add(arg(0, Path.ALL, 0).notIn(sanctioned))  // calls[*].to
    .add(arg(0, Path.ALL, 1).lte(uint256(1e18))) // calls[*].value
    .build();
// function transfer(address to, uint256 amount)
bytes memory policy = PolicyBuilder
    .create("transfer(address,uint256)")
    .add(msgSender().isIn(operators))    // caller
    .add(arg(0).notIn(sanctioned))       // to
    .add(arg(1).lte(uint256(100e18)))    // amount
    .build();
// function supply(address asset, uint256 amount)
bytes memory policy = PolicyBuilder
    .create("supply(address,uint256)")
    .add(msgSender().eq(OPERATOR))   // caller
    .or()
    .add(arg(0).isIn(allowedAssets)) // asset
    .build();