mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-10 11:23:32 +08:00
147 lines
4.4 KiB
Markdown
147 lines
4.4 KiB
Markdown
---
|
|
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
|