AI Agents in DeFi Lending: Aave, Compound, and Automated Collateral Management

DeFi lending protocols like Aave and Compound represent one of the most compelling use cases for autonomous AI agents. The protocols themselves are deterministic — interest rates adjust algorithmically, collateral ratios are transparently enforced, and liquidations happen automatically. But managing a lending position profitably requires continuous monitoring, rapid response to market moves, and cross-protocol optimization that no human can do around the clock. Agents can.

This guide covers the mechanics of decentralized lending, health factor management, liquidation risk modeling, automated collateral top-ups using the Purple Flea Wallet API, and yield optimization across Aave, Compound, and related protocols.

$12B+
Aave TVL (2026)
1.0
Liquidation threshold (health factor)
5-15%
Liquidation bonus (Aave)
<30s
Target health factor response time

Lending Protocol Mechanics

Both Aave and Compound operate on the same fundamental principle: users deposit assets as collateral and borrow against that collateral up to a protocol-defined limit. Interest rates adjust dynamically based on utilization — when more of the pool is borrowed, rates rise to attract more suppliers and deter more borrowers.

Core Concepts

Loan-to-Value (LTV): The maximum ratio of loan value to collateral value. For example, ETH on Aave has an LTV of 80%, meaning $1,000 of ETH collateral allows up to $800 in borrowing. This is the maximum borrow limit, not the liquidation threshold.

Liquidation Threshold (LT): The collateral ratio below which a position becomes eligible for liquidation. Always higher than LTV. For ETH on Aave, LT is typically 82.5%. This means you can borrow up to 80% LTV, but liquidation only triggers at 82.5% utilization.

Liquidation Bonus: The discount liquidators receive when buying collateral from an undercollateralized position. On Aave, this is typically 5-15% depending on the asset. The bonus ensures liquidators are economically motivated to act quickly.

Health Factor (HF): The primary risk metric. Calculated as:

Health Factor = (Σ Collateral_value × Liquidation_threshold) / Total_debt_value

When HF falls below 1.0, the position is liquidatable. The further above 1.0, the safer the position. Most experienced DeFi participants target HF > 1.5 as a buffer.

Safe
HF > 1.5
Caution
1.1 - 1.5
Liquidation Risk
HF < 1.1

Aave vs Compound: Key Differences

Feature Aave V3 Compound V3
Interest Rate Model Two-slope model (kink at optimal utilization) Per-market interest rate curves
Liquidation Model Partial (closes to safe HF) Fixed close factor (up to 50%)
Flash Loans Yes (0.09% fee) No native flash loans
E-Mode (efficiency mode) Yes (higher LTV for correlated assets) No
Isolation Mode Yes (limit exposure of new assets) No
Rewards Token stkAAVE + protocol revenue COMP token
Multi-chain Ethereum, Arbitrum, Polygon, Optimism, Base Ethereum, Base, Polygon
Oracle Chainlink (primary) Chainlink + TWAP

Interest Rate Dynamics

Aave uses a two-slope interest model with a kink at the optimal utilization rate (typically 80-90%). Below the kink, rates increase gradually with utilization. Above the kink, rates spike steeply to deter further borrowing and attract more liquidity.

If U < U_optimal:
Borrow_rate = Base_rate + (U / U_optimal) × Slope1

If U >= U_optimal:
Borrow_rate = Base_rate + Slope1 + ((U - U_optimal) / (1 - U_optimal)) × Slope2

For ETH on Aave: Base=0%, Slope1=3.8%, Slope2=80%, U_optimal=80%. At 90% utilization, borrow rate ≈ 3.8% + ((0.90-0.80)/(0.20)) × 80% = 3.8% + 40% = 43.8% APR. This steep jump is why high-utilization assets carry extreme borrow costs.

Health Factor Monitoring

A health factor monitoring agent has one primary job: detect when HF is approaching danger thresholds before liquidation can occur. The challenge is that HF can drop rapidly during market crashes — ETH dropping 20% in an hour is not unusual in crypto.

On-Chain Data Sources

from web3 import Web3
from eth_account import Account
import json
import asyncio
import httpx
import time

# Aave V3 Pool ABI (minimal — getUserAccountData)
AAVE_POOL_ABI = json.loads('''[
  {
    "inputs": [{"internalType": "address", "name": "user", "type": "address"}],
    "name": "getUserAccountData",
    "outputs": [
      {"name": "totalCollateralBase", "type": "uint256"},
      {"name": "totalDebtBase", "type": "uint256"},
      {"name": "availableBorrowsBase", "type": "uint256"},
      {"name": "currentLiquidationThreshold", "type": "uint256"},
      {"name": "ltv", "type": "uint256"},
      {"name": "healthFactor", "type": "uint256"}
    ],
    "stateMutability": "view",
    "type": "function"
  }
]''')

# Aave V3 Pool address (Ethereum mainnet)
AAVE_POOL = "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"

class AaveHealthMonitor:
    def __init__(self, rpc_url: str, user_address: str,
                 warning_hf: float = 1.4,
                 critical_hf: float = 1.15):
        self.w3 = Web3(Web3.HTTPProvider(rpc_url))
        self.user = Web3.to_checksum_address(user_address)
        self.pool = self.w3.eth.contract(
            address=Web3.to_checksum_address(AAVE_POOL),
            abi=AAVE_POOL_ABI
        )
        self.warning_hf = warning_hf
        self.critical_hf = critical_hf
        self.last_hf = None
        self.alert_callbacks = []

    def get_account_data(self) -> dict:
        """Fetch current account data from Aave V3."""
        result = self.pool.functions.getUserAccountData(self.user).call()
        # All values in base currency units (8 decimals for USD)
        # healthFactor has 18 decimals
        collateral_usd = result[0] / 1e8
        debt_usd = result[1] / 1e8
        available_borrows_usd = result[2] / 1e8
        liq_threshold = result[3] / 1e4  # basis points → decimal
        ltv = result[4] / 1e4
        health_factor = result[5] / 1e18

        return {
            'collateral_usd': collateral_usd,
            'debt_usd': debt_usd,
            'available_borrows_usd': available_borrows_usd,
            'liquidation_threshold': liq_threshold,
            'ltv': ltv,
            'health_factor': health_factor,
            'utilization_pct': (debt_usd / (collateral_usd * liq_threshold)
                                if collateral_usd > 0 else 0) * 100
        }

    async def monitor_loop(self, poll_interval_s: float = 15.0):
        """Poll health factor continuously, trigger alerts on threshold breach."""
        while True:
            try:
                data = self.get_account_data()
                hf = data['health_factor']
                prev = self.last_hf
                self.last_hf = hf

                if hf < self.critical_hf:
                    severity = 'CRITICAL'
                    for cb in self.alert_callbacks:
                        await cb(severity, hf, data)
                elif hf < self.warning_hf:
                    severity = 'WARNING'
                    for cb in self.alert_callbacks:
                        await cb(severity, hf, data)

                # Adaptive polling: poll more frequently as HF approaches critical
                if hf < 1.25:
                    poll_interval_s = 5.0
                elif hf < 1.5:
                    poll_interval_s = 10.0
                else:
                    poll_interval_s = 30.0

            except Exception as e:
                print(f"Monitor error: {e}")

            await asyncio.sleep(poll_interval_s)

Price Impact Projection

Rather than waiting for HF to drop to the threshold, a sophisticated agent projects future health factor under different price scenarios. This enables pre-emptive action.

def project_health_factor(
    collateral_usd: float,
    debt_usd: float,
    liq_threshold: float,
    collateral_price_change_pct: float,
    debt_price_change_pct: float = 0.0  # debt in stablecoins = 0 change
) -> float:
    """
    Project health factor after given price changes.
    collateral_price_change_pct: e.g. -20 means ETH drops 20%
    """
    new_collateral = collateral_usd * (1 + collateral_price_change_pct / 100)
    new_debt = debt_usd * (1 + debt_price_change_pct / 100)
    if new_debt == 0:
        return float('inf')
    return (new_collateral * liq_threshold) / new_debt


def compute_liquidation_price(
    collateral_units: float,
    collateral_price: float,
    debt_usd: float,
    liq_threshold: float
) -> float:
    """
    Price at which collateral must fall to trigger liquidation (HF = 1.0).
    """
    # HF = (collateral_units × price × liq_threshold) / debt = 1
    # price = debt / (collateral_units × liq_threshold)
    return debt_usd / (collateral_units * liq_threshold)


# Example: 10 ETH at $3,000 = $30,000 collateral, $18,000 USDC debt
data = {
    'collateral_units': 10,
    'current_price': 3000,
    'debt_usd': 18000,
    'liq_threshold': 0.825  # ETH Aave LT
}

liq_price = compute_liquidation_price(
    data['collateral_units'], data['current_price'],
    data['debt_usd'], data['liq_threshold']
)
print(f"Liquidation price: ${liq_price:.2f}")
# Output: Liquidation price: $2181.82
# ETH needs to drop 27.3% from $3,000 to trigger liquidation

# Scenario analysis
for drop in [-10, -15, -20, -25, -30]:
    hf = project_health_factor(30000, 18000, 0.825, drop)
    print(f"ETH -{abs(drop)}%: HF = {hf:.3f}")
# ETH -10%: HF = 1.237
# ETH -15%: HF = 1.170
# ETH -20%: HF = 1.103
# ETH -25%: HF = 1.036
# ETH -30%: HF = 0.970 ← LIQUIDATION ZONE

Liquidation Risk and Mechanics

Understanding how liquidations actually work — and who profits from them — is essential for managing the risk from the borrower's perspective.

How Liquidations Execute

When a position's health factor falls below 1.0, any external address (the liquidator) can call the protocol's liquidationCall function. The liquidator:

  1. Repays part of the borrower's debt (up to 50% in Aave, 100% in some protocols)
  2. Receives an equivalent value of the borrower's collateral plus a liquidation bonus (5-15%)
  3. The bonus comes out of the borrower's collateral — the borrower loses more than their debt value
Real Cost of Liquidation: If your position is liquidated with a 10% bonus, you don't just lose the debt value — you lose the debt value plus 10%. On a $20,000 debt, that's a $22,000 hit to your collateral. Plus, you still owe any remaining debt. The effective cost is always worse than simply closing the position early.

MEV and Liquidation Competition

Liquidations are extremely competitive. As soon as a position's HF dips below 1.0, dozens of MEV bots race to liquidate it first. On Ethereum mainnet, this means paying significant priority fees. On L2s like Arbitrum, the competition is less intense but still present.

As a borrower, you cannot compete with liquidators in response speed. Your strategy must be to ensure your position never reaches HF = 1.0 in the first place.

Partial vs Full Liquidation

Aave V3 implements "close factor" — by default, a liquidator can only repay up to 50% of the debt in a single call when HF is between 0.95 and 1.0. When HF drops below 0.95, up to 100% can be liquidated. This means that if you fall slightly below 1.0, a partial liquidation may restore you above 1.0 — but this comes at significant cost (bonus paid to liquidator).

Automated Collateral Management

The primary agent function in DeFi lending is automated collateral top-up: when health factor drops below a warning threshold, automatically deposit additional collateral to restore the buffer. Purple Flea's multi-chain Wallet API is the tool that makes this possible across chains.

Collateral Top-Up Strategy

import asyncio
import httpx
from web3 import Web3

PF_BASE = "https://api.purpleflea.com"

# ERC-20 approve + Aave supply ABI (minimal)
ERC20_ABI = json.loads('''[
  {"inputs": [{"name": "spender","type": "address"},{"name": "amount","type": "uint256"}],
   "name": "approve","outputs": [{"type": "bool"}],"type": "function"},
  {"inputs": [{"name": "account","type": "address"}],
   "name": "balanceOf","outputs": [{"type": "uint256"}],"type": "function"}
]''')

AAVE_SUPPLY_ABI = json.loads('''[
  {"inputs": [
    {"name": "asset","type": "address"},
    {"name": "amount","type": "uint256"},
    {"name": "onBehalfOf","type": "address"},
    {"name": "referralCode","type": "uint16"}
  ],"name": "supply","type": "function"}
]''')

class CollateralManager:
    def __init__(
        self,
        rpc_url: str,
        private_key: str,
        pf_api_key: str,
        aave_pool_address: str,
        target_hf: float = 1.8,
        warning_hf: float = 1.4,
        critical_hf: float = 1.15
    ):
        self.w3 = Web3(Web3.HTTPProvider(rpc_url))
        self.account = Account.from_key(private_key)
        self.pf_api_key = pf_api_key
        self.pool_address = Web3.to_checksum_address(aave_pool_address)
        self.target_hf = target_hf
        self.warning_hf = warning_hf
        self.critical_hf = critical_hf
        self.http = httpx.AsyncClient(timeout=15.0)

    def compute_required_collateral(
        self,
        current_hf: float,
        collateral_usd: float,
        debt_usd: float,
        liq_threshold: float
    ) -> float:
        """
        Compute additional collateral (USD) needed to restore health factor
        to self.target_hf.
        HF = (collateral + additional) * liq_threshold / debt = target_hf
        additional = debt * target_hf / liq_threshold - collateral
        """
        required_collateral = (debt_usd * self.target_hf) / liq_threshold
        additional_needed = max(0.0, required_collateral - collateral_usd)
        # Add 5% buffer for price movement during transaction
        return additional_needed * 1.05

    async def get_pf_wallet_balance(self, chain: str, token: str) -> float:
        """Check Purple Flea wallet balance for available collateral funds."""
        resp = await self.http.get(
            f"{PF_BASE}/v1/wallet/balances",
            params={"chain": chain},
            headers={"X-API-Key": self.pf_api_key}
        )
        balances = resp.json()
        return float(balances.get(token, 0))

    async def withdraw_from_pf_wallet(
        self, token: str, amount: float, to_address: str, chain: str
    ) -> str:
        """Withdraw from Purple Flea wallet to on-chain address for collateral top-up."""
        resp = await self.http.post(
            f"{PF_BASE}/v1/wallet/withdraw",
            json={
                "token": token,
                "amount": amount,
                "destination": to_address,
                "chain": chain
            },
            headers={"X-API-Key": self.pf_api_key}
        )
        data = resp.json()
        return data["tx_hash"]

    def supply_collateral_to_aave(
        self,
        token_address: str,
        amount_wei: int
    ) -> str:
        """Supply ERC-20 token to Aave as collateral. Returns tx hash."""
        token = self.w3.eth.contract(
            address=Web3.to_checksum_address(token_address),
            abi=ERC20_ABI
        )
        pool = self.w3.eth.contract(
            address=self.pool_address,
            abi=AAVE_SUPPLY_ABI
        )

        # Step 1: Approve Aave pool to spend token
        nonce = self.w3.eth.get_transaction_count(self.account.address)
        approve_tx = token.functions.approve(
            self.pool_address, amount_wei
        ).build_transaction({
            'from': self.account.address,
            'nonce': nonce,
            'gas': 80000,
            'maxFeePerGas': self.w3.eth.gas_price * 2,
            'maxPriorityFeePerGas': Web3.to_wei(2, 'gwei'),
        })
        signed = self.account.sign_transaction(approve_tx)
        self.w3.eth.send_raw_transaction(signed.rawTransaction)
        self.w3.eth.wait_for_transaction_receipt(signed.hash, timeout=60)

        # Step 2: Supply to Aave
        supply_tx = pool.functions.supply(
            Web3.to_checksum_address(token_address),
            amount_wei,
            self.account.address,
            0  # referralCode
        ).build_transaction({
            'from': self.account.address,
            'nonce': nonce + 1,
            'gas': 200000,
            'maxFeePerGas': self.w3.eth.gas_price * 2,
            'maxPriorityFeePerGas': Web3.to_wei(2, 'gwei'),
        })
        signed2 = self.account.sign_transaction(supply_tx)
        tx_hash = self.w3.eth.send_raw_transaction(signed2.rawTransaction)
        self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
        return tx_hash.hex()

    async def handle_health_alert(
        self, severity: str, current_hf: float, account_data: dict
    ):
        """Main callback: triggered when health factor crosses warning threshold."""
        print(f"[{severity}] Health Factor: {current_hf:.3f}")
        print(f"  Collateral: ${account_data['collateral_usd']:,.0f}")
        print(f"  Debt: ${account_data['debt_usd']:,.0f}")

        needed_usd = self.compute_required_collateral(
            current_hf,
            account_data['collateral_usd'],
            account_data['debt_usd'],
            account_data['liquidation_threshold']
        )

        print(f"  Need ${needed_usd:,.0f} additional collateral to reach HF {self.target_hf}")

        # Check PF wallet for available USDC (can be swapped for ETH on-chain)
        usdc_balance = await self.get_pf_wallet_balance('ethereum', 'USDC')
        print(f"  PF Wallet USDC: ${usdc_balance:,.0f}")

        if usdc_balance >= needed_usd:
            # Withdraw from PF wallet and supply to Aave
            tx = await self.withdraw_from_pf_wallet(
                'USDC', needed_usd, self.account.address, 'ethereum'
            )
            print(f"  Withdrawal initiated: {tx}")
            # After confirmation, supply USDC to Aave
            USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
            amount_wei = int(needed_usd * 1e6)  # USDC has 6 decimals
            supply_tx = self.supply_collateral_to_aave(USDC_ADDRESS, amount_wei)
            print(f"  Collateral supplied: {supply_tx}")
        else:
            print(f"  INSUFFICIENT FUNDS — consider partial debt repayment instead")

Yield Optimization Across Protocols

Supply rates across DeFi lending protocols are not static — they fluctuate constantly based on utilization. An agent that moves assets to the highest-yielding protocol every few hours can meaningfully outperform a static depositor.

Rate Aggregation

import asyncio
import httpx
from dataclasses import dataclass

@dataclass
class LendingRate:
    protocol: str
    chain: str
    asset: str
    supply_apy: float
    borrow_apy: float
    utilization: float
    tvl_usd: float
    timestamp: float

async def fetch_aave_rates(client: httpx.AsyncClient, chain: str = "ethereum") -> list[LendingRate]:
    """Fetch current supply/borrow rates from Aave V3 subgraph."""
    SUBGRAPH_URLS = {
        "ethereum": "https://api.thegraph.com/subgraphs/name/aave/protocol-v3",
        "arbitrum": "https://api.thegraph.com/subgraphs/name/aave/protocol-v3-arbitrum",
        "base": "https://api.thegraph.com/subgraphs/name/aave/protocol-v3-base",
    }
    query = """
    {
      reserves(first: 20, where: {isActive: true}) {
        symbol
        liquidityRate
        variableBorrowRate
        utilizationRate
        totalLiquidity
        decimals
      }
    }
    """
    url = SUBGRAPH_URLS.get(chain, SUBGRAPH_URLS["ethereum"])
    resp = await client.post(url, json={"query": query})
    reserves = resp.json().get("data", {}).get("reserves", [])

    rates = []
    for r in reserves:
        # Aave returns rates as ray (27 decimal) values
        supply_apy = float(r["liquidityRate"]) / 1e27 * 100
        borrow_apy = float(r["variableBorrowRate"]) / 1e27 * 100
        util = float(r["utilizationRate"]) / 1e27 * 100
        rates.append(LendingRate(
            protocol="aave_v3",
            chain=chain,
            asset=r["symbol"],
            supply_apy=supply_apy,
            borrow_apy=borrow_apy,
            utilization=util,
            tvl_usd=0,  # compute from totalLiquidity × price
            timestamp=__import__('time').time()
        ))
    return rates

class YieldOptimizer:
    def __init__(self, pf_api_key: str,
                 rebalance_threshold_pct: float = 0.5,
                 min_tvl_usd: float = 1_000_000):
        self.pf_api_key = pf_api_key
        self.threshold = rebalance_threshold_pct
        self.min_tvl = min_tvl_usd
        self.client = httpx.AsyncClient(timeout=10.0)
        self.current_positions: dict = {}  # asset -> (protocol, chain, amount)

    async def find_best_rates(self, asset: str) -> list[LendingRate]:
        """Aggregate supply rates for an asset across all supported chains/protocols."""
        tasks = [
            fetch_aave_rates(self.client, chain)
            for chain in ['ethereum', 'arbitrum', 'base']
        ]
        all_results = await asyncio.gather(*tasks, return_exceptions=True)
        flat = []
        for r in all_results:
            if isinstance(r, list):
                flat.extend(r)

        relevant = [
            rate for rate in flat
            if rate.asset.upper() == asset.upper()
            and rate.tvl_usd >= self.min_tvl
        ]
        return sorted(relevant, key=lambda r: r.supply_apy, reverse=True)

    async def should_rebalance(self, asset: str) -> tuple[bool, LendingRate | None]:
        """Check if rebalancing to a better protocol is warranted."""
        rates = await self.find_best_rates(asset)
        if not rates:
            return False, None

        best = rates[0]
        current = self.current_positions.get(asset)

        if not current:
            return True, best  # No current position — deploy to best

        current_rate = current.get('apy', 0)
        improvement = best.supply_apy - current_rate

        # Only rebalance if improvement exceeds threshold (covers gas + friction)
        return improvement >= self.threshold, best
Important: Always account for gas costs when rebalancing. A 0.3% APY improvement on a $5,000 position earns ~$15/year. If gas for the rebalance costs $20, it's not worth doing. Set minimum position sizes for rebalancing ($10,000+ recommended on mainnet, $1,000+ on L2s).

Compound V3 (Comet) Specifics

Compound V3 takes a different architectural approach than Aave. Rather than pooling all assets together, each "Comet" market is centered around a single base asset (e.g., USDC). Collateral assets are deposited but do not earn interest — only the base asset earns supply APY.

Compound V3 Key Differences for Agents

  • Single borrow asset per market: You can only borrow the base asset (USDC/USDT/ETH) — not arbitrary tokens
  • Collateral earns no interest: Unlike Aave, depositing ETH as collateral on Compound earns 0% — it only enables borrowing
  • Liquidation is immediate: No grace period. Once collateral value falls below the borrow amount × liquidation factor, liquidation is instant
  • Interest is accrued per second: Compound V3 uses per-second interest accrual, making rate calculations more precise
  • COMP rewards: Supply and borrow positions accumulate COMP tokens, adding to effective yield
# Compound V3 Comet ABI (minimal)
COMET_ABI = json.loads('''[
  {
    "inputs": [{"name": "account","type": "address"}],
    "name": "borrowBalanceOf",
    "outputs": [{"type": "uint256"}],
    "stateMutability": "view","type": "function"
  },
  {
    "inputs": [{"name": "account","type": "address"}],
    "name": "collateralBalanceOf",
    "outputs": [{"type": "uint128"}],
    "stateMutability": "view","type": "function"
  },
  {
    "inputs": [{"name": "account","type": "address"}],
    "name": "isLiquidatable",
    "outputs": [{"type": "bool"}],
    "stateMutability": "view","type": "function"
  },
  {
    "inputs": [],
    "name": "getUtilization",
    "outputs": [{"type": "uint256"}],
    "stateMutability": "view","type": "function"
  }
]''')

COMET_USDC_MAINNET = "0xc3d688B66703497DAA19211EEdff47f25384cdc3"

def check_compound_liquidatable(w3: Web3, user: str) -> bool:
    comet = w3.eth.contract(
        address=Web3.to_checksum_address(COMET_USDC_MAINNET),
        abi=COMET_ABI
    )
    return comet.functions.isLiquidatable(
        Web3.to_checksum_address(user)
    ).call()

def get_compound_utilization(w3: Web3) -> float:
    comet = w3.eth.contract(
        address=Web3.to_checksum_address(COMET_USDC_MAINNET),
        abi=COMET_ABI
    )
    util_raw = comet.functions.getUtilization().call()
    return util_raw / 1e18  # returns 0-1 as 18-decimal fixed point

Full Agent System: Monitor + Protect + Optimize

Combining health monitoring, automated collateral top-ups, and yield optimization into a single agent loop creates a fully autonomous DeFi position manager.

import asyncio
import logging
import time

logging.basicConfig(level=logging.INFO)
log = logging.getLogger("defi_agent")

class DeFiLendingAgent:
    """
    Autonomous DeFi lending agent that:
    1. Monitors health factor across Aave and Compound
    2. Auto-tops-up collateral when HF approaches critical levels
    3. Periodically rebalances supply to highest-yield protocol
    4. Uses Purple Flea Wallet API as capital reservoir
    """

    def __init__(
        self,
        rpc_url: str,
        private_key: str,
        pf_api_key: str,
        aave_user: str,
        compound_user: str,
        target_hf: float = 1.8,
        yield_rebalance_interval_h: float = 6.0
    ):
        self.monitor = AaveHealthMonitor(rpc_url, aave_user, warning_hf=1.4, critical_hf=1.15)
        self.collateral_mgr = CollateralManager(
            rpc_url, private_key, pf_api_key,
            AAVE_POOL, target_hf=target_hf
        )
        self.yield_optimizer = YieldOptimizer(pf_api_key)
        self.monitor.alert_callbacks.append(self.collateral_mgr.handle_health_alert)
        self.rebalance_interval = yield_rebalance_interval_h * 3600
        self.last_rebalance = 0.0
        self.pf_api_key = pf_api_key

    async def run(self):
        log.info("DeFi Lending Agent starting...")

        # Run monitor and rebalancer concurrently
        await asyncio.gather(
            self.monitor.monitor_loop(poll_interval_s=15.0),
            self._yield_rebalance_loop()
        )

    async def _yield_rebalance_loop(self):
        while True:
            now = time.time()
            if now - self.last_rebalance >= self.rebalance_interval:
                try:
                    await self._check_and_rebalance_yields()
                    self.last_rebalance = now
                except Exception as e:
                    log.error(f"Rebalance error: {e}")
            await asyncio.sleep(300)  # check every 5 minutes

    async def _check_and_rebalance_yields(self):
        for asset in ['USDC', 'USDT', 'DAI']:
            should, best_rate = await self.yield_optimizer.should_rebalance(asset)
            if should and best_rate:
                log.info(
                    f"Rebalancing {asset} to {best_rate.protocol} "
                    f"on {best_rate.chain}: {best_rate.supply_apy:.2f}% APY"
                )
                # Implementation: withdraw from current, bridge if needed, deposit to best
                # Use Purple Flea Wallet API for cross-chain transfers
                pass

# Start the agent
async def main():
    agent = DeFiLendingAgent(
        rpc_url="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
        private_key="0x...",
        pf_api_key="pf_live_...",
        aave_user="0x...",
        compound_user="0x...",
        target_hf=1.8,
        yield_rebalance_interval_h=6.0
    )
    await agent.run()

asyncio.run(main())

Risk Considerations and Safety Nets

Automated DeFi position management carries unique risks that agents must handle explicitly.

Smart Contract Risk

Aave and Compound are battle-tested but not risk-free. Agents should monitor protocol governance proposals and pause activity if a major upgrade or security incident is announced. Track governance forums and the protocol's Discord/Twitter for emergency notifications.

Oracle Manipulation Risk

If the price oracle is manipulated (flash loan attack), health factors can be spoofed and mass liquidations triggered. Mitigate by: using protocols with multi-oracle redundancy, avoiding highly concentrated positions in less-liquid assets, setting maximum position sizes.

Gas Price Spikes

During periods of extreme market stress (exactly when you need to top up collateral), gas prices can spike 10-50x. Always maintain enough ETH for gas costs. Use L2s (Aave on Arbitrum/Base) where gas costs $0.01-0.10 rather than $5-50.

Circuit Breakers

class AgentCircuitBreaker:
    def __init__(self, max_daily_spend_usd: float = 5000):
        self.max_daily = max_daily_spend_usd
        self.daily_spent = 0.0
        self.day_start = time.time()
        self.paused = False
        self.pause_reason = ""

    def _reset_if_new_day(self):
        if time.time() - self.day_start > 86400:
            self.daily_spent = 0.0
            self.day_start = time.time()
            if self.paused and "daily limit" in self.pause_reason:
                self.paused = False

    def can_spend(self, amount_usd: float) -> bool:
        self._reset_if_new_day()
        if self.paused:
            return False
        if self.daily_spent + amount_usd > self.max_daily:
            self.paused = True
            self.pause_reason = "daily limit exceeded"
            log.warning(f"Circuit breaker: daily spend limit ${self.max_daily} reached")
            return False
        return True

    def record_spend(self, amount_usd: float):
        self.daily_spent += amount_usd
        log.info(f"Daily spend: ${self.daily_spent:.0f} / ${self.max_daily:.0f}")
Getting Started: New agents can use the Purple Flea Faucet to get free funds to experiment with DeFi lending on testnets. The Wallet API handles multi-chain asset management, making it easy to fund positions across Ethereum, Arbitrum, and Base. Register at purpleflea.com/register.

Summary: The AI Advantage in DeFi Lending

AI agents have clear structural advantages in DeFi lending management:

  • 24/7 monitoring: Health factors can drop to critical levels at 3am on a Saturday. Agents never sleep.
  • Sub-minute response: Market crashes that might require collateral top-up within minutes are handled automatically, before human intervention is even possible.
  • Continuous yield optimization: Checking rates across 6 chains and 4 protocols every 6 hours and rebalancing when beneficial is impractical for humans but trivial for agents.
  • Emotionless execution: Agents don't panic-sell collateral or freeze during market crashes — they execute the pre-defined strategy consistently.

The Purple Flea platform provides agents with the infrastructure to execute these strategies: a Wallet API for multi-chain capital management, a Trading API for hedging positions, and a Faucet for risk-free initial exploration. Start with small positions, validate your monitoring thresholds against historical data, and scale gradually as confidence builds.