--- name: llm-trading-agent-security description: Security patterns for autonomous trading agents with wallet or transaction authority. Covers prompt injection, spend limits, pre-send simulation, circuit breakers, MEV protection, and key handling. origin: ECC direct-port adaptation version: "1.0.0" --- # LLM Trading Agent Security Autonomous trading agents have a harsher threat model than normal LLM apps: an injection or bad tool path can turn directly into asset loss. ## When to Use - Building an AI agent that signs and sends transactions - Auditing a trading bot or on-chain execution assistant - Designing wallet key management for an agent - Giving an LLM access to order placement, swaps, or treasury operations ## How It Works Layer the defenses. No single check is enough. Treat prompt hygiene, spend policy, simulation, execution limits, and wallet isolation as independent controls. ## Examples ### Treat prompt injection as a financial attack ```python import re INJECTION_PATTERNS = [ r'ignore (previous|all) instructions', r'new (task|directive|instruction)', r'system prompt', r'send .{0,50} to 0x[0-9a-fA-F]{40}', r'transfer .{0,50} to', r'approve .{0,50} for', ] def sanitize_onchain_data(text: str) -> str: for pattern in INJECTION_PATTERNS: if re.search(pattern, text, re.IGNORECASE): raise ValueError(f"Potential prompt injection: {text[:100]}") return text ``` Do not blindly inject token names, pair labels, webhooks, or social feeds into an execution-capable prompt. ### Hard spend limits ```python from decimal import Decimal MAX_SINGLE_TX_USD = Decimal("500") MAX_DAILY_SPEND_USD = Decimal("2000") class SpendLimitError(Exception): pass class SpendLimitGuard: def check_and_record(self, usd_amount: Decimal) -> None: if usd_amount > MAX_SINGLE_TX_USD: raise SpendLimitError(f"Single tx ${usd_amount} exceeds max ${MAX_SINGLE_TX_USD}") daily = self._get_24h_spend() if daily + usd_amount > MAX_DAILY_SPEND_USD: raise SpendLimitError(f"Daily limit: ${daily} + ${usd_amount} > ${MAX_DAILY_SPEND_USD}") self._record_spend(usd_amount) ``` ### Simulate before sending ```python class SlippageError(Exception): pass async def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str: sim_result = await self.w3.eth.call(tx) if expected_min_out is None: raise ValueError("min_amount_out is required before send") actual_out = decode_uint256(sim_result) if actual_out < expected_min_out: raise SlippageError(f"Simulation: {actual_out} < {expected_min_out}") signed = self.account.sign_transaction(tx) return await self.w3.eth.send_raw_transaction(signed.raw_transaction) ``` ### Circuit breaker ```python class TradingCircuitBreaker: MAX_CONSECUTIVE_LOSSES = 3 MAX_HOURLY_LOSS_PCT = 0.05 def check(self, portfolio_value: float) -> None: if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES: self.halt("Too many consecutive losses") if self.hour_start_value <= 0: self.halt("Invalid hour_start_value") return hourly_pnl = (portfolio_value - self.hour_start_value) / self.hour_start_value if hourly_pnl < -self.MAX_HOURLY_LOSS_PCT: self.halt(f"Hourly PnL {hourly_pnl:.1%} below threshold") ``` ### Wallet isolation ```python import os from eth_account import Account private_key = os.environ.get("TRADING_WALLET_PRIVATE_KEY") if not private_key: raise EnvironmentError("TRADING_WALLET_PRIVATE_KEY not set") account = Account.from_key(private_key) ``` Use a dedicated hot wallet with only the required session funds. Never point the agent at a primary treasury wallet. ### MEV and deadline protection ```python import time PRIVATE_RPC = "https://rpc.flashbots.net" MAX_SLIPPAGE_BPS = {"stable": 10, "volatile": 50} deadline = int(time.time()) + 60 ``` ## Pre-Deploy Checklist - External data is sanitized before entering the LLM context - Spend limits are enforced independently from model output - Transactions are simulated before send - `min_amount_out` is mandatory - Circuit breakers halt on drawdown or invalid state - Keys come from env or a secret manager, never code or logs - Private mempool or protected routing is used when appropriate - Slippage and deadlines are set per strategy - All agent decisions are audit-logged, not just successful sends