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 ./outThis 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
- Valid Run: Bot sends a swap for 500 USDC. Success.
- Invalid Run: Bot sends a swap for 2000 USDC. Revert: Policy condition failed.
- Invalid Run: Bot tries to call
sushiRouter. Revert: Target mismatch.
You now have a trustless DCA bot that cannot drain your wallet!