An open API service indexing awesome lists of open source software.

https://github.com/namankumar/private-defi-agent


https://github.com/namankumar/private-defi-agent

Last synced: 15 days ago
JSON representation

Awesome Lists containing this project

README

          

# private-defi-agent

A Python runtime for an LLM-owned cross-chain DeFi agent. The agent reads on-chain state, asks Claude to decide what to do next, and executes against Aleo and Polygon. It runs on a one-minute cron. State persists to disk between ticks, so restarts are transparent.

## How it works

Every tick runs the same loop:

```
observe → policy → LLM decides → validate → execute → persist
```

**Observe.** Read pool state from Aleo, yield position from Polygon (Morpho vault), participant registry from disk.

**Policy.** Deterministically compute which actions are legal given the current phase. The LLM never sees a blank canvas. It picks from a pre-computed set.

**LLM decides.** Claude receives the observation, the set of actions allowed in the current phase, and an economic summary. Returns one action with a reason, risk note, and expected outcome.

**Validate.** Guardrails re-check the decision before execution. If the action is illegal, the amount is missing, or a live operation is already in flight, the agent pauses for human review.

**Execute.** Aleo or Polygon adapter executes the chosen action on-chain.

**Persist.** State and decision written to JSON. Agent exits. Next cron tick starts fresh.

---

## Why agent

We chose state machines over prompt chains because the constraint space for DeFi execution is finite and enumerable. A policy engine can prove it won't produce an illegal trade. A prompt chain can make the same mistake in a new way every time. The model chooses from legal actions. The policy engine defines them.

A deterministic script could handle the happy path: bridge when funded, deposit to Morpho, withdraw when yield threshold is met. The LLM earns its keep in two places a script can't handle well:

**Economic reasoning under uncertainty.** Should the agent withdraw from Morpho now? That depends on current yield bps, gas price, expected bridge time, and how close the timeout is. These tradeoffs don't reduce to a threshold. Claude weighs them and explains the call, including what it's uncertain about.

**Ambiguous chain state.** What if a bridge confirmation is partial? What if pool total is above threshold but the bridge interface isn't deployed yet? A script throws an exception or proceeds blindly. The agent pauses, writes a human-readable reason, and waits for acknowledgment.

**Protocol extensibility.** A script encodes one strategy: deposit to Morpho, withdraw when yield hits the threshold. Adding a new protocol means rewriting the script. The agent separates the concerns differently: the policy engine defines what actions are legal, the LLM reasons about which one is wise. Adding a new protocol means adding new actions to the policy layer. The reasoning handles the rest. Polymarket is a compelling next case: the agent could evaluate prediction market odds, size positions against the yield baseline, and exit before resolution. Same architecture, different policy.

```json
// Example pause entry in the decision log:
{
"phase": "PAUSED_REVIEW",
"decision": "NOOP",
"result": "Guardrail rejection: Live bridge_out operation already in progress"
}
```

---

## The LLM interface

Claude receives a structured JSON prompt on every tick. Here is the full system prompt:

```
You are the operator of a cross-chain DeFi pool.

Choose exactly one action from LEGAL_ACTIONS.
Never invent balances, tx hashes, or phase transitions.
Never invent an action not present in LEGAL_ACTIONS.
Prefer NOOP over risky progression.
Return strict JSON with keys: action, reason, risk_note, expected_outcome.
```

"Prefer NOOP over risky progression" is a reliability constraint, not a style hint. It means the agent will wait through many NOOPs rather than take an aggressive action it's uncertain about.

**Example input** (what Claude receives):

```json
{
"goal": "Bridge pool funds to Polygon, deposit to Morpho for yield, bridge back with profit, distribute to depositors.",
"state": {
"phase": "FUNDS_ON_POLYGON",
"last_successful_action": "INITIATE_BRIDGE_OUT",
"last_observed_balances": {
"leo_pool": 0,
"evm_contract": 5000000,
"morpho_vault": 0
}
},
"observation": {
"pool_total_base_units": 0,
"evm_held_usdc_base_units": 5000000,
"evm_total_shares": 0,
"evm_current_value_base_units": 0,
"gas_price_gwei": 14
},
"legal_actions": ["NOOP", "DEPOSIT_TO_MORPHO"],
"payloads": {
"DEPOSIT_TO_MORPHO": { "amount_base_units": 5000000 }
},
"economic_summary": {
"bridge_eligible": true,
"unrealized_yield_bps": 0,
"withdraw_eligible": false
}
}
```

**Example output** (what Claude returns):

```json
{
"action": "DEPOSIT_TO_MORPHO",
"reason": "Funds confirmed on Polygon (5 USDC). Bridge is complete. Depositing to Morpho is the correct next step — no reason to hold idle.",
"risk_note": "Deposit is irreversible until the withdraw cycle completes. Gas at 14 gwei is acceptable. Morpho share price risk is present but expected.",
"expected_outcome": "Phase transitions to MORPHO_DEPOSITED. Morpho shares minted. Yield accrual begins."
}
```

The `reason`, `risk_note`, and `expected_outcome` fields are persisted to `agent_state.json` on every tick, creating a full audit trail of the agent's reasoning.

---

## Phase state machine

```
IDLE
└─► POOL_REGISTERED
└─► POOL_FUNDED
└─► BRIDGE_OUT_PENDING
└─► FUNDS_ON_POLYGON
└─► MORPHO_DEPOSITED
└─► WITHDRAW_PENDING
└─► BRIDGE_BACK_PENDING
└─► READY_TO_DISTRIBUTE
└─► COMPLETED

PAUSED_REVIEW ◄── reachable from any phase on guardrail failure or timeout
```

Phase transitions are driven by on-chain observation, not by the LLM. The agent never trusts its own previous state over what the chain actually shows.

---

## Reliability

Several independent layers prevent catastrophic failure:

| Layer | Mechanism |
|---|---|
| Constrained decisions | LLM picks from pre-computed legal actions only |
| Guardrail validation | Decision re-validated before execution |
| Heuristic fallback | If Anthropic is unavailable, deterministic priority order runs instead |
| PAUSED_REVIEW phase | Ambiguous state or rejection → pause + human acknowledgment |
| Timeout detection | Pending operations that exceed `BRIDGE_TIMEOUT_SECS` trigger a pause |
| File lock | Cron overlaps rejected. Only one agent run at a time. |
| Safe reads | Chain reads that fail return zero/none rather than crashing |
| File-based state | `agent_state.json` is human-inspectable and editable mid-run |

---

## Decision trace

`docs/sample-run.json` contains a realistic end-to-end run showing the agent progressing from IDLE through MORPHO_DEPOSITED, including one NOOP tick while waiting for bridge confirmation. Each entry shows the phase, legal actions offered, Claude's choice, and the result.

---

## Install

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

Requires the [Leo CLI](https://developer.aleo.org/leo/installation) for account generation.

## Quick start (dry run, no funds)

```bash
AGENT_LLM_MODE=heuristic python3 -m agent status
AGENT_LLM_MODE=heuristic python3 -m agent run
cat agent_state.json
```

`heuristic` mode runs without Anthropic configured. The agent executes the same loop using a deterministic priority order instead of Claude.

## Environment

```bash
# LLM
ANTHROPIC_API_KEY=
ANTHROPIC_MODEL=claude-sonnet-4-5
AGENT_LLM_MODE=auto # auto | anthropic | heuristic

# Aleo
LEO_PROGRAM_ID= # your Leo program ID
LEO_POOL_ID= # your pool ID
ALEO_API_BASE=https://api.explorer.provable.com/v2
ALEO_NETWORK=mainnet
ENABLE_ALEO_MUTATIONS=false # keep false until Leo contract is ready

# Polygon
POLYGON_RPC_URL=
EVM_CONTRACT_ADDRESS=
POLYGON_PRIVATE_KEY=

# Thresholds
MIN_BRIDGE_OUT_AMOUNT=500000000 # 500 USDC (6 decimals)
MIN_NET_YIELD_BPS=50
BRIDGE_TIMEOUT_SECS=1800
```

## CLI

```bash
python3 -m agent run # full controller pass
python3 -m agent status # read-only snapshot, no LLM call
python3 -m agent resume # clear pause state after review
python3 -m agent acknowledge-pause # mark pause reviewed
python3 -m agent withdraw-request # trigger admin withdraw
python3 -m agent force-withdraw # from MORPHO_DEPOSITED only
python3 -m agent force-distribute # from READY_TO_DISTRIBUTE only
```

## Cron

```bash
* * * * * cd /path/to/repo && python3 -m agent run >> /tmp/agent.log 2>&1
```

File lock prevents overlapping invocations.

## Tests

```bash
python3 -m unittest discover -s tests -t .
```

## Current limitations

- Bridge-out, receive-state, and distribute are pending Leo contract updates. Aleo writes disabled by default.
- `agent_state.json` is designed for single-operator use; multi-operator would need a proper store
- In-memory rate limiting on API reads; no retry backoff on chain RPC failures yet