Skip to main content
The Strategy Sentinel Agent is responsible for monitoring, analyzing, and managing every strategy within the MetaVault portfolio. It maintains safety, optimal performance, healthy leverage, and capital preservation through rule-based decision logic.

Overview

export async function getStrategySentinelAgent() {
  return new LlmAgent({
    name: "strategy_sentinel_agent",
    description: "A agent that monitors and manages the strategies in the Vault.",
    model: model,
    tools: [
      get_strategy_states,
      get_user_balances,
      get_vault_state,
      rebalance_vault,
      harvest_strategy,
      vault_deposit,
      vault_withdraw,
      check_liquidation_risk,
      auto_deleverage,
      get_token_prices,
      get_leverage_strategy_state,
      get_vault_apy,
      update_strategy_target_weights,
      toggle_leverage_strategy_pause,
      update_leverage_params
    ]
  })
}
Source: packages/agents/defi-portfolio/src/agents/sub-agents/strategy-sentinel-agent/agent.ts:22-180

Core Responsibilities

The Strategy Sentinel Agent must:
  1. Continuously monitor strategy health by pulling all on-chain balances, prices, risk metrics, and vault states
  2. Detect risks early including high LTV, excessive leverage, low liquidation buffer, and rapid price changes
  3. Use simulation tools to validate any corrective action before recommending it
  4. Evaluate actions using strategy rules, not intuition
  5. Recommend corrective actions such as reducing leverage, rebalancing, harvesting yield, or pausing strategies

Available Tools

Read & Monitoring Tools

Fetches strategy addresses, their balances, and target BPS allocations from the Strategy Router.Implementation:
export const get_strategy_states = createTool({
  name: "get_strategy_states",
  description: "Fetches strategy addresses, their balances, and target BPS allocations from the Strategy Router.",
  fn: async () => {
    const [strategies, balances, targets] = await chain_read(
      env.ROUTER_ADDRESS,
      StrategyRouterABI.abi,
      "getPortfolioState",
      []
    );
    return strategies.map((strat, i) => ({
      strategy: strat,
      raw: { balance: balances[i].toString(), targetBps: targets[i].toString() },
      human: { 
        balance: format18(balances[i]), 
        targetPercent: (Number(targets[i]) / 100).toFixed(2) + "%" 
      }
    }));
  }
});
Source: tools.ts:48-81Returns: Array of strategy objects with addresses, balances, and target allocations
Reads the vault’s global state including total assets, supply, and managed balance.Implementation:
export const get_vault_state = createTool({
  name: "get_vault_state",
  description: "Reads the vault's global state.",
  fn: async () => {
    const [totalAssets, totalSupply, totalManaged] = await Promise.all([
      chain_read(env.VAULT_ADDRESS, VaultABI.abi, "totalAssets", []),
      chain_read(env.VAULT_ADDRESS, VaultABI.abi, "totalSupply", []),
      chain_read(env.VAULT_ADDRESS, VaultABI.abi, "totalManagedAssets", []),
    ]);
    return { raw, human };
  }
});
Source: tools.ts:17-45Returns: Object with totalAssets, totalSupply, and totalManaged (both raw and human-readable)
Fetches vault share balance and withdrawable amount for a user.Schema:
z.object({
  user: z.string()
})
Returns: User’s shares and withdrawable assets (raw and human-readable)Source: tools.ts:85-114
Fetches real-time LINK and WETH prices from CoinGecko API (real market prices, not mock).Implementation:
export const get_token_prices = createTool({
  name: "get_token_prices",
  description: "Fetches real-time LINK and WETH prices from CoinGecko API.",
  fn: async () => {
    const response = await fetch(
      "https://api.coingecko.com/api/v3/simple/price?ids=chainlink,weth&vs_currencies=usd&include_24hr_change=true"
    );
    const data = await response.json();
    const linkPriceUSD = data.chainlink?.usd;
    const wethPriceUSD = data.weth?.usd;
    const linkPricePerWETH = wethPriceUSD / linkPriceUSD;
    
    return {
      raw: { linkPriceUSD, wethPriceUSD, linkPricePerWETH },
      human: { 
        linkPriceUSD: linkPriceUSD.toFixed(2),
        wethPriceUSD: wethPriceUSD.toFixed(2),
        link24hChange: link24hChange.toFixed(2) + "%",
        interpretation: `1 WETH = ${linkPricePerWETH.toFixed(2)} LINK`
      },
      source: "CoinGecko API (real market prices)"
    };
  }
});
Source: tools.ts:117-217Returns: USD prices for LINK and WETH, LINK/WETH ratio, and 24h price changesFallback: Falls back to CoinCap API if CoinGecko fails
Gets comprehensive state of the leverage strategy including deposited amount, borrowed WETH, LTV, pause status, and leverage parameters.Implementation:
export const get_leverage_strategy_state = createTool({
  name: "get_leverage_strategy_state",
  fn: async () => {
    const [leverageState, ltv, paused, maxDepth, borrowFactor] = await Promise.all([
      chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "getLeverageState", []),
      chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "getLTV", []),
      chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "paused", []),
      chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "maxDepth", []),
      chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "borrowFactor", []),
    ]);
    return { raw, human };
  }
});
Source: tools.ts:220-265Returns: Deposited amount, borrowed WETH, net exposure, LTV, pause status, maxDepth, and borrowFactor
Calculates the vault APY based on TVL growth over time.Implementation:
async function calculateVaultAPY(toolContext: ToolContext) {
  const stateKey = "session.vault_apy_state";
  const state = toolContext.state[stateKey];
  const lastTVL = state?.lastTVL || 0;
  const lastTs = state?.lastTimestamp || 0;
  const now = Math.floor(Date.now() / 1000);
  
  const tvl = await chain_read(env.VAULT_ADDRESS, VaultABI.abi, "totalManagedAssets", []);
  
  if (lastTVL === 0 || lastTs === 0) {
    toolContext.state[stateKey] = { lastTVL: tvl, lastTimestamp: now };
    return { apy: 0, readable: "0%", message: "APY baseline set (first run)." };
  }
  
  const dt = now - lastTs;
  const growth = (tvl - lastTVL) / lastTVL;
  const YEAR = 365 * 24 * 3600;
  const apy = (growth / dt) * YEAR;
  
  toolContext.state[stateKey] = { lastTVL: tvl, lastTimestamp: now };
  return { apy, readable: `${(apy * 100).toFixed(2)}%`, tvl, growth, dt };
}
Source: tools.ts:268-334Returns: APY percentage, TVL, growth rate, and time deltaNote: Uses stateful context to track TVL changes over time

Vault & Strategy Management Tools

Deposits LINK into the vault.Schema:
z.object({
  amount: z.string() // Human-readable LINK amount
})
Source: tools.ts:342-357
Withdraws shares from the vault.Schema:
z.object({
  shares: z.string() // Human-readable share amount
})
Source: tools.ts:360-370
Invokes the vault’s rebalance() function to reallocate funds according to target weights.Implementation:
export const rebalance_vault = createTool({
  name: "rebalance_vault",
  description: "Triggers vault rebalance().",
  fn: async () => {
    const tx = await chain_write(env.ROUTER_ADDRESS, StrategyRouterABI.abi, "rebalance", []);
    return tx.hash;
  }
});
Source: tools.ts:373-380
Triggers harvest() to send back the profits into the vault.Implementation:
export const harvest_strategy = createTool({
  name: "harvest_strategy",
  description: "Calls harvestAll().",
  fn: async () => {
    const tx = await chain_write(env.ROUTER_ADDRESS, StrategyRouterABI.abi, "harvestAll", []);
    return tx.hash;
  }
});
Source: tools.ts:383-390
Updates target allocation weights for strategies (in basis points, must sum to 10000).Schema:
z.object({
  leverageStrategyBps: z.number().min(0).max(10000),
  aaveStrategyBps: z.number().min(0).max(10000)
})
Implementation:
export const update_strategy_target_weights = createTool({
  fn: async ({ leverageStrategyBps, aaveStrategyBps }) => {
    if (leverageStrategyBps + aaveStrategyBps !== 10000) {
      throw new Error("Target weights must sum to 10000 (100%)");
    }
    const strategies = [env.STRATEGY_LEVERAGE_ADDRESS, env.STRATEGY_AAVE_ADDRESS];
    const bps = [leverageStrategyBps, aaveStrategyBps];
    const tx = await chain_write(env.ROUTER_ADDRESS, StrategyRouterABI.abi, "setStrategies", [strategies, bps]);
    return { tx: tx.hash, newWeights: { leverage: leverageStrategyBps, aave: aaveStrategyBps } };
  }
});
Source: tools.ts:439-462Use case: Adjust portfolio allocation between leverage and Aave strategies based on market conditionsDefault weights: Leverage: 80%, Aave: 20%
Pauses or unpauses the leverage strategy.Implementation:
export const toggle_leverage_strategy_pause = createTool({
  name: "toggle_leverage_strategy_pause",
  description: "Pauses or unpauses the leverage strategy. When paused, the strategy cannot invest or deleverage.",
  fn: async () => {
    const tx = await chain_write(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "togglePause", []);
    const paused = await chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "paused", []);
    return { tx: tx.hash, paused, status: paused ? "paused" : "active" };
  }
});
Source: tools.ts:465-485Use case: Pause when market conditions are unfavorable or risk is too high
Updates leverage strategy parameters (maxDepth and borrowFactor).Schema:
z.object({
  maxDepth: z.number().min(1).max(6), // Maximum leverage loop iterations
  borrowFactor: z.number().min(0).max(8000) // Basis points (e.g., 6000 = 60%)
})
Implementation:
export const update_leverage_params = createTool({
  description: "Updates leverage strategy parameters: maxDepth (1-6) and borrowFactor (0-8000).",
  fn: async ({ maxDepth, borrowFactor }) => {
    const tx = await chain_write(
      env.STRATEGY_LEVERAGE_ADDRESS,
      StrategyLeverageABI.abi,
      "setLeverageParams",
      [maxDepth, borrowFactor]
    );
    return { tx: tx.hash, newParams: { maxDepth, borrowFactor: borrowFactor / 100 + "%" } };
  }
});
Source: tools.ts:488-510Use case: Reduce values to decrease leverage risk when prices are volatile

Risk Management Tools

Checks leverage strategy liquidation risk.Implementation:
export const check_liquidation_risk = createTool({
  name: "check_liquidation_risk",
  description: "Checks leverage strategy liquidation risk.",
  fn: async () => {
    const [deposited, borrowed] = await Promise.all([
      chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "deposited", []),
      chain_read(env.STRATEGY_LEVERAGE_ADDRESS, StrategyLeverageABI.abi, "borrowedWETH", []),
    ]);
    
    const ltv = borrowed / deposited;
    
    return {
      ltv,
      safe: ltv < 0.70,
      warning: ltv >= 0.70 && ltv < 0.80,
      critical: ltv >= 0.80,
    };
  }
});
Source: tools.ts:397-421Risk thresholds:
  • Safe: LTV < 70%
  • Warning: 70% ≤ LTV < 80%
  • Critical: LTV ≥ 80%
Repays debt to reduce liquidation risk.Implementation:
export const auto_deleverage = createTool({
  name: "auto_deleverage",
  description: "Repay debt to reduce liquidation risk.",
  fn: async () => {
    const tx = await chain_write(
      env.ROUTER_ADDRESS,
      StrategyRouterABI.abi,
      "triggerDeleverage",
      [env.STRATEGY_LEVERAGE_ADDRESS, 10]
    );
    return tx.hash;
  }
});
Source: tools.ts:424-436Note: Uses fixed deleverage amount of 10

Decision Making Guidelines

Price-Based Decision Making

The agent ALWAYS checks LINK and WETH prices using get_token_prices before making leverage strategy decisions. Price monitoring logic:
  • Monitor price movements and volatility to assess risk
  • When LINK/WETH price ratio changes significantly (>10%):
    • If prices are volatile or dropping rapidly → pause leverage strategy or reduce leverage params
    • If prices are stable and favorable → can maintain or increase leverage
  • Use price data to inform target weight adjustments between strategies
Source: agent.ts:95-101

Leverage Strategy State Management

The agent regularly checks leverage strategy state using get_leverage_strategy_state. Management rules:
  • Monitor LTV, borrowed amounts, and pause status
  • When LTV exceeds 70% or prices are volatile → consider pausing or reducing leverage
  • Adjust leverage parameters (maxDepth, borrowFactor) based on market conditions:
    • High volatility → reduce maxDepth and borrowFactor
    • Stable markets → can maintain or slightly increase
Source: agent.ts:103-109

Target Weight Management

Default target weights:
  • Leverage: 80%
  • Aave: 20%
Adjustment criteria:
  • Market conditions favor one strategy over another
  • Risk profile changes (e.g., reduce leverage strategy weight during high volatility)
  • Price movements suggest reallocation is prudent
Process:
  1. Adjust target weights using update_strategy_target_weights
  2. Call rebalance_vault to execute the reallocation
Source: agent.ts:111-118

Risk Detection

The agent detects risks early, including:
  • High LTV
  • Excessive leverage
  • Low buffer to liquidation
  • Rapid price changes
  • Divergence from target allocations
Source: agent.ts:120-130

Behavioral Principles

The Strategy Sentinel Agent follows strict behavioral principles:
  1. Be precise, risk-aware, and rule-driven
  2. Prefer safety over yield
  3. Explain reasoning based solely on tool outputs
  4. Chain multiple tools to reach correct conclusions
  5. Never assume or invent on-chain values
  6. Never perform an unsafe or unnecessary action
Source: agent.ts:152-160

Example Workflows

Risk Assessment Workflow

1. get_token_prices() → Check current LINK and WETH prices
2. get_leverage_strategy_state() → Check LTV and leverage params
3. check_liquidation_risk() → Assess risk level
4. If LTV > 70%:
   - auto_deleverage() → Reduce debt
   OR
   - update_leverage_params(maxDepth: 3, borrowFactor: 5000) → Reduce leverage
   OR
   - toggle_leverage_strategy_pause() → Pause strategy

Rebalancing Workflow

1. get_strategy_states() → Check current allocations
2. get_token_prices() → Check market conditions
3. If reallocation needed:
   - update_strategy_target_weights(leverageStrategyBps: 6000, aaveStrategyBps: 4000)
   - rebalance_vault() → Execute rebalancing

Harvesting Workflow

1. get_vault_state() → Check total managed assets
2. get_vault_apy() → Check yield generation
3. harvest_strategy() → Collect profits from strategies

Build docs developers (and LLMs) love