Matador Docs
Guides

Security Best Practices

Guidelines for authoring secure policies and managing permissions.

Matador policies act as the "programmable firewall" for your smart accounts. While the interpreter is robust, the security of your system ultimately depends on the logic of the policies you write.

Core Design Principles

Principle of Least Privilege

Grant only the exact permissions needed for a specific task.

  • Bad: Whitelisting an entire protocol router address.
  • Good: Restricting access to a specific router address AND a specific function selector (e.g., swap).

Fail Closed

Design your policies so that any ambiguity results in a revert. Matador enforces this by default: if an opcode is unknown or a check fails, the transaction is blocked.

Logic Grouping

Prefer all (AND) blocks over any (OR) blocks. Use any only when explicitly defining alternative valid paths (e.g., "Allow Swap OR Allow Revoke").

Common Pitfalls

1. Calldata Manipulation

When inspecting calldata, a malicious actor might try to manipulate unchecked arguments.

Vulnerability Example

Checking amountOutMinimum but ignoring recipient in a swap could allow an attacker to swap your tokens but send the proceeds to their own wallet.

Mitigation: Always verify critical parameters like recipient, tokenIn, and tokenOut.

// Secure Swap Policy
when: {
    all {
        Uniswap.exactInputSingle,
        context.args.params.recipient == context.caller, // Enforce self-custody
        context.args.params.tokenIn == parameters.allowedToken
    }
}

2. Rate Limit Key Collision

The ratelimit opcode uses a 32-byte key to track state.

  • Risk: Reusing the string "daily-limit" across multiple policies will cause them to share the same counter.
  • Fix: Use unique, descriptive keys or hash the policy ID into the key.
// Unique key per policy/action
ratelimit(1 days, 1000 ether, "policy-123-daily-spend")

3. Reentrancy & External Calls

CUSTOM_CHECK and STATE_VARIABLE_CHECK perform calls to external contracts.

Mitigation:

  • Treat external data as untrusted.
  • Use enforceView where possible to prevent state changes during validation.
  • Only whitelist trusted oracles and validators.

Operational Security

Compiler Verification

Always verify that the bytecode you are signing matches your source code.

  1. Source Control: Store .matador files in git.
  2. CI Verification: Run matador-policy-cli compile in CI and compare the output hash with the on-chain bytecode.
  3. Human Review: Use the instructions field in the compiler output to sanity-check the generated opcodes.

Monitoring

Set up alerts for PermissionViolation events.

  • Spikes: A sudden spike in violations often indicates a broken bot or an active exploit attempt.
  • Root Cause: Decode the opcode in the error to identify exactly which check failed.

Audit Checklist

Before deploying a policy to mainnet, ensure you can answer "Yes" to these questions:

  • Does the policy restrict the target address?
  • Does the policy restrict the function selector?
  • Are all critical calldata arguments (recipient, amount) validated?
  • Are numeric limits (allowance, slippage) set to safe bounds?
  • Is the rate-limit key unique to this use case?
  • Has the policy been tested against both valid and invalid transactions in Foundry?

On this page