Matador Docs
API Reference

Policy Patterns

Common recipes and best practices for Matador policies.

This library contains copy-pasteable patterns for common use cases. Mix and match these snippets to build robust security policies for your smart accounts.

Financial Safety Rails

Fixed Token Spend

Restrict token transfers to one exact approved amount.

import "abis/ERC20.json" as Token;

permission SpendLimit -> 1.0.0 {
    parameters: {
        authorizer: address,
        token: address,
        maxAmount: uint256
    }

    pub fn authorize() -> bool {
        return authorizedBy(parameters.authorizer);
    }

    fn main() -> bool {
        if (context.target != parameters.token) {
            return false;
        }

        if (context.selector == Token.transfer) {
            return Token.transfer.value == parameters.maxAmount;
        }

        return false;
    }
}

One-Time Subscription Charge

Allow a specific service provider to charge a fixed fee once until the policy is reset or replaced.

permission Subscription -> 1.0.0 {
    parameters: {
        serviceProvider: address,
        fee: uint256
    }

    @persist let chargeCount: uint256 = 0;

    pub fn authorize() -> bool {
        return authorizedBy(parameters.serviceProvider);
    }

    fn main() -> bool {
        if (context.value != parameters.fee) {
            return false;
        }

        if (state.chargeCount >= 1) {
            return false;
        }

        state.chargeCount = state.chargeCount + 1;
        return true;
    }
}

Security & Access Control

Whitelist

Restrict interactions to a known list of safe contracts (e.g., official Uniswap routers).

permission SafeInteractions -> 1.0.0 {
    parameters: {
        authorizer: address,
        router: address,
        vault: address
    }

    pub fn authorize() -> bool {
        return authorizedBy(parameters.authorizer);
    }

    fn main() -> bool {
        if (context.target == parameters.router) {
            return true;
        }

        return context.target == parameters.vault;
    }
}

Circuit Breaker

Block all transactions if an emergency flag is set in the policy parameters.

permission EmergencyStop -> 1.0.0 {
    parameters: {
        authorizer: address,
        paused: bool
    }

    pub fn authorize() -> bool {
        return authorizedBy(parameters.authorizer);
    }

    fn main() -> bool {
        return parameters.paused == false;
    }
}

DeFi Automation

Swap with Fixed Slippage Floor

Allow an automated bot to execute swaps, but require the calldata minimum output amount to match the policy parameter.

import "abis/UniswapRouter.json" as Uniswap;

permission SafeSwap -> 1.0.0 {
    parameters: {
        authorizer: address,
        router: address,
        minAmountOut: uint256
    }

    pub fn authorize() -> bool {
        return authorizedBy(parameters.authorizer);
    }

    fn main() -> bool {
        if (context.target != parameters.router) {
            return false;
        }

        if (context.selector == Uniswap.exactInputSingle) {
            return Uniswap.exactInputSingle.params.amountOutMinimum >= parameters.minAmountOut;
        }

        return false;
    }
}

Math Precision

Matador's core arithmetic opcodes operate on uint256. For complex fixed-point math or price conversions, it is recommended to use a Custom Module or a helper contract rather than implementing complex math directly in DSL.

Flash Loan Prevention

Prevent the account from being used as a flash loan borrower by ensuring the transaction origin matches the sender.

permission NoFlashLoan -> 1.0.0 {
    parameters: {
        trustedCaller: address
    }

    pub fn authorize() -> bool {
        return authorizedBy(parameters.trustedCaller);
    }

    fn main() -> bool {
        return true;
    }
}

Pre/Post Value Invariant

Capture a value before execution and require the post-execution value to be no lower. This pattern requires an adapter with real pre/post lifecycle support, such as the Safe guard path.

permission ValueDoesNotDecrease -> 1.0.0 {
    parameters: {
        authorizer: address
    }

    @transient let valueBefore: uint256;
    @persist let accepted: uint256 = 0;

    pub fn authorize() -> bool {
        return authorizedBy(parameters.authorizer);
    }

    fn main() -> bool {
        if (context.phase == pre) {
            valueBefore = context.value;
            return true;
        }

        if (context.phase == post) {
            if (context.value >= valueBefore) {
                state.accepted = state.accepted + 1;
                return true;
            }

            return false;
        }

        return false;
    }
}

Kernel lifecycle installation

Kernel phase-aware and @transient policies require KernelMatador to be the validator's paired ERC-7579 hook and require the executeUserOp envelope. Installing the validator without that exact hook binding fails validation.

On this page