Matador Docs
Tutorials

Advanced Policy Authoring

Build a complex, multi-branched security policy for a Yield Farming Bot.

In this tutorial, we will construct a production-grade policy for an automated Yield Farming Bot.

The Scenario

We have a bot that manages a delta-neutral position on Aave. It needs permission to:

  1. Rebalance: Adjust the borrow/supply ratio to maintain a target Health Factor.
  2. Harvest: Claim rewards (e.g., AAVE tokens) and swap them to USDC.
  3. Emergency: Unwind the position if the market crashes.

Constraints:

  • The bot cannot withdraw collateral to its own wallet (only to the Smart Account).
  • Swaps must use the official Uniswap Router.
  • Flash loans are strictly forbidden.

Implementation

Imports & Parameters

We start by importing the necessary ABIs and defining our runtime parameters.

import "abis/AavePool.json" as Aave;
import "abis/UniswapV3Router.json" as Uniswap;
import "abis/ERC20.json" as Token;

permission YieldManager -> 1.0.0 {
    parameters: {
        pool: address,
        router: address,
        rewardsController: address,
        minHealthFactor: uint256
    }
    // ...

Branching Logic

The bot performs three distinct types of actions. Use explicit control flow so each allowed selector is visible to reviewers.

    fn main() -> bool {
        // Path 1: Rebalance
        // Path 2: Harvest & Swap
        // Path 3: Emergency Unwind
        return false;
    }
}

Path 1: Rebalance

Allow supplying collateral or borrowing assets, but enforce a health check post-execution.

if (context.target == parameters.pool) {
    if (context.selector == Aave.supply) {
        return Aave.supply.onBehalfOf == parameters.account;
    } else if (context.selector == Aave.borrow) {
        return Aave.borrow.amount == parameters.maxBorrowAmount;
    } else if (context.selector == Aave.repay) {
        return Aave.repay.onBehalfOf == parameters.account;
    }
}

Path 2: Harvest & Swap

Allow claiming rewards and swapping them to stablecoins.

if (context.target == parameters.router) {
    if (context.selector == Uniswap.exactInputSingle) {
        if (Uniswap.exactInputSingle.params.recipient != parameters.account) {
            return false;
        }

        return Uniswap.exactInputSingle.params.amountOutMinimum == parameters.minAmountOut;
    }
}

Path 3: Emergency Unwind

If the health factor drops below a critical threshold (e.g. 1.1), allow any action on the Aave pool (including full withdrawal) to save the position.

if (context.target == parameters.pool) {
    if (context.selector == Aave.withdraw) {
        return Aave.withdraw.to == parameters.account;
    }
}

Complete Policy

import "abis/AavePool.json" as Aave;
import "abis/UniswapV3Router.json" as Uniswap;

permission YieldManager -> 1.0.0 {
    parameters: {
        pool: address,
        router: address,
        account: address,
        maxBorrowAmount: uint256,
        minAmountOut: uint256
    }

    fn main() -> bool {
        if (context.target == parameters.pool) {
            if (context.selector == Aave.supply) {
                return Aave.supply.onBehalfOf == parameters.account;
            } else if (context.selector == Aave.borrow) {
                return Aave.borrow.amount == parameters.maxBorrowAmount;
            } else if (context.selector == Aave.repay) {
                return Aave.repay.onBehalfOf == parameters.account;
            } else if (context.selector == Aave.withdraw) {
                return Aave.withdraw.to == parameters.account;
            }
        }

        if (context.target == parameters.router) {
            if (context.selector == Uniswap.exactInputSingle) {
                if (Uniswap.exactInputSingle.params.recipient != parameters.account) {
                    return false;
                }

                return Uniswap.exactInputSingle.params.amountOutMinimum == parameters.minAmountOut;
            }
        }

        return false;
    }
}

Optimization Tip

Place the most frequent operation (e.g., Rebalance) first in the any block. The interpreter short-circuits as soon as a condition matches, saving gas.

On this page