Matador Docs
DeFi Automation

Tutorial - Real-World Integration

Deploying a safe Keeper Rail from scratch.

Tutorial: Deploying a Keeper Rail

In this tutorial, we will secure a Kernel Smart Account that is running an automated DCA (Dollar Cost Average) strategy.

Goal: Allow a bot to swap USDC for ETH on Uniswap V3, but restrict the amount to 1000 USDC per transaction to limit risk.

Prerequisites

  • A Kernel v3 Smart Account.
  • Node.js & Matador CLI installed.
  • A Keeper bot address (EOA).

Run this on a fork first

Keeper Rails are production controls. Validate your policy on a fork or testnet before granting permissions on mainnet.

Step 1: Write the Policy

Create a file named dca-policy.matador. We will use the permission syntax to define our constraints.

    import "abis/UniswapV3Router.json" as Uniswap;

    permission DCAGuard -> 1.0.0 {
        metadata: {
            description: "Limits DCA swaps to 1000 USDC on Uniswap"
        }

        parameters: {
            router: address,
            usdc: address,
            maxAmount: uint256
        }

        when: {
            // 1. Target Guard: Must call the authorized Router
            context.target == parameters.router,

            // 2. Function Guard: Must be exactInputSingle
            Uniswap.exactInputSingle(params: tuple),

            // 3. Argument Guard: AmountIn must be <= maxAmount
            calldata.params.amountIn <= parameters.maxAmount,

            // 4. Argument Guard: TokenIn must be USDC
            calldata.params.tokenIn == parameters.usdc
        }
    }

Step 2: Compile

Generate the bytecode using the Matador CLI.

matador compile dca-policy.matador --out ./out

This command produces dca-policy.json in the out directory, containing the compiled bytecode and ABI-encoded metadata.

Step 3: Enable the Module

On your Kernel account, you need to enable the Matador Executor Module. This module will act as the "Interpreter" for your policies.

import { getEnableModuleUserOp } from "@zerodev/sdk";

// Using permissionless.js / viem
const enableMatadorOp = await kernelClient.getEnableModuleUserOp({
    module: MATADOR_MODULE_ADDRESS
});
await kernelClient.sendUserOp(enableMatadorOp);

Step 4: Install the Policy

Now, register the policy on the module. We need to encode the installation data, which includes the policy bytecode and the initial parameters (Router address, USDC address, and limit).

import policyArtifact from './out/dca-policy.json';
import { encodeAbiParameters, parseAbiParameters } from 'viem';

// 1. Encode the arguments defined in 'parameters' block
const policyArgs = encodeAbiParameters(
    parseAbiParameters('address, address, uint256'),
    [
        "0xE592427A0AEce92De3Edee1F18E0157C05861564", // Uniswap Router
        "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        1000000000n // 1000 USDC (6 decimals)
    ]
);

// 2. Construct the install data
// Function: installPolicy(uint256 policyId, bytes bytecode, bytes args)
const installData = encodeFunctionData({
    abi: MatadorABI,
    functionName: 'installPolicy',
    args: [
        12345n, // A unique ID for this policy
        policyArtifact.bytecode,
        policyArgs
    ]
});

// 3. Send the UserOp to install
await kernelClient.sendUserOp({
    target: MATADOR_MODULE_ADDRESS,
    data: installData
});

Step 5: Authorize the Keeper

Finally, tell the module that your specific Keeper Bot is allowed to use this policy.

const authData = encodeFunctionData({
    abi: MatadorABI,
    functionName: 'grantRole',
    args: [
        12345n, // Policy ID
        KEEPER_BOT_ADDRESS
    ]
});

await kernelClient.sendUserOp({
    target: MATADOR_MODULE_ADDRESS,
    data: authData
});

Step 6: Run the Bot

Your bot sends a transaction to the Matador Module, not directly to Uniswap. The module runs the checks, then executes the swap.

// Bot code (EOA)
const executeData = encodeFunctionData({
    abi: MatadorABI,
    functionName: 'execute',
    args: [
        12345n, // Policy ID to use
        uniswapRouter, // Target
        0n, // Value
        swapCalldata // The actual swap data
    ]
});

// Send tx to the module (via the Smart Account or directly if module is executor)
// Note: Implementation depends on if you use 4337 or direct EOA calls.
// If direct EOA:
walletClient.sendTransaction({
    to: MATADOR_MODULE_ADDRESS,
    data: executeData
});

Verification

  1. Valid Run: Bot sends a swap for 500 USDC. Success.
  2. Invalid Run: Bot sends a swap for 2000 USDC. Revert: Policy condition failed.
  3. Invalid Run: Bot tries to call sushiRouter. Revert: Target mismatch.

You now have a trustless DCA bot that cannot drain your wallet!

On this page