Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions skills/uniswap-trading/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# uniswap-trading agent skill

An AI-agent integration for uniswap-python: a [SKILL.md](SKILL.md) following
the [Anthropic skill format](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/skills)
plus a subprocess-friendly CLI (`scripts/uniswap_agent.py`) that any agent
framework can call.

## Install into an agent

- **Claude Code**: symlink or copy this directory to `~/.claude/skills/uniswap-trading`
(or `.claude/skills/uniswap-trading` in a project). The skill loads on demand.
- **gptme**: add this directory to a configured lessons/skills path; the
`description` frontmatter drives matching.
- **Anything else** (LangChain, OpenAI tools, MCP): wrap
`scripts/uniswap_agent.py --json <command>` as a subprocess tool. The CLI is
the integration surface — stdout is one JSON object, exit codes are stable
(0 ok, 1 runtime error, 2 safety refusal).

## Demo

`./demo_sepolia.sh` runs read-only mainnet quotes (v3 + v4, verified working
against public RPCs) plus Sepolia status, and — once the library gains Sepolia
support — a real Sepolia swap with `DEMO_BROADCAST=1` and a funded key.

## Tests

```bash
python3 -m pytest skills/uniswap-trading/tests/ -q
```

No network needed — tests cover the safety gates and argument surface.

## Status

Skeleton (grant deliverable D3 groundwork). Verified working: v3 + v4
mainnet quotes over public RPCs, dry-run swap plans, all safety gates.
Cross-agent dogfood (Gordon, 2026-08-27): install clean, mainnet v3/v4 +
Polygon v3 quoting verified; his four corrections are folded in — human
amounts in JSON, deterministic v3-testnet refusal, branch-install setup
docs, double-armed mainnet broadcast (`UNISWAP_AGENT_ALLOW_MAINNET=1` +
`--allow-mainnet`).
TODO before production:

- [ ] **Sepolia enablement in the library** (blocks the D3 testnet tx demo):
v3 hardcodes the mainnet quoter/router in `uniswap.py`; the v4 maps in
`constants.py` have no `"sepolia"` entries. Official Sepolia v4
deployments (PoolManager `0xE03A1074c86CFeDd5C142C4F04F1a1536e203543`,
UniversalRouter `0x3A9D48AB9751398BbFa63ad67599Bb04e4BdF98b`, V4Quoter
`0x61b3f2011a92d183c7dbadbda940a7555ccf9227`, StateView
`0xe1dd9c3fa50edb962e442f60dfbc432e24537e4c`, PositionManager
`0x429ba70129df741B2Ca2a85BC3A2a3328e5c09b4`, Permit2
`0x000000000022D473030F116dDEE9F6B43aC78BA3`) cover 6 of the 8
contracts `Uniswap4.__init__` requires — `position_descriptor` and
`reserves_lens` still need deployments/addresses.
- [ ] v4 route/multi-hop support in `swap` (currently single-hop PoolKey)
- [ ] Wire tests into repo CI
- [ ] MCP server wrapper (optional distribution surface)
- [ ] Publish demo transcript + testnet tx hash in docs
95 changes: 95 additions & 0 deletions skills/uniswap-trading/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: uniswap-trading
description: Quote prices and execute Uniswap v2/v3/v4 swaps via uniswap-python. Use when asked to check on-chain token prices, get swap quotes, or trade tokens on Uniswap. Testnet-first — mainnet requires explicit opt-in. Do NOT use for CEX trading, fiat, or chains without Uniswap deployments.
---

# Uniswap Trading

Trade on Uniswap through [uniswap-python](https://github.com/uniswap-python/uniswap-python)
using a single CLI: `scripts/uniswap_agent.py`. All commands are safe to
explore — state-changing operations are **dry-run unless `--broadcast`** is
passed, and non-testnet chains are refused unless explicitly allowed.

## Setup

Requires uniswap-python **with this skill included** — until the next PyPI
release contains it, install from the PR branch:

```bash
pip install "uniswap-python @ git+https://github.com/TimeToBuildBob/uniswap-python.git@feat/agent-skill-skeleton"
# after the skill ships in a release: pip install "uniswap-python>=0.8.1"
```

Then:

```bash
export PROVIDER=https://ethereum-sepolia-rpc.publicnode.com # any JSON-RPC endpoint
export UNISWAP_AGENT_ADDRESS=0xYourWallet # for balances/swaps
export UNISWAP_AGENT_PRIVATE_KEY=0x... # ONLY for --broadcast
```

Never echo `UNISWAP_AGENT_PRIVATE_KEY` or write it to disk. Read-only
commands (status, quote) work with just `PROVIDER`.

## Commands

```bash
SKILL_DIR=$(dirname "$0") # or the directory containing this SKILL.md

# Connectivity + wallet overview
python3 "$SKILL_DIR/scripts/uniswap_agent.py" --json status

# Quote: how much USDC for 0.01 WETH? (amounts in wei/base units)
python3 "$SKILL_DIR/scripts/uniswap_agent.py" --json quote WETH USDC 10000000000000000

# Dry-run swap (prints plan: quote, min-out after slippage; sends nothing)
python3 "$SKILL_DIR/scripts/uniswap_agent.py" --json swap WETH USDC 10000000000000000

# Real swap on testnet (requires key; approve the router first for ERC-20 input)
python3 "$SKILL_DIR/scripts/uniswap_agent.py" --json approve WETH --broadcast
python3 "$SKILL_DIR/scripts/uniswap_agent.py" --json swap WETH USDC 10000000000000000 --broadcast

# Uniswap v4 (single-hop; pool identified by fee + tick spacing)
python3 "$SKILL_DIR/scripts/uniswap_agent.py" --json --version 4 \
quote WETH USDC 10000000000000000 --fee 3000 --tick-spacing 60
```

On Sepolia the symbols `ETH`, `WETH`, `USDC` resolve automatically; on other
chains pass 0x token addresses.

## Safety rules (enforced by the CLI, exit code 2 on refusal)

1. Read-only commands (status, quote, balance) work on any chain. Broadcasting
outside known testnets (Sepolia, Arbitrum/Base/Optimism Sepolia) is
**double-armed**: it requires BOTH `UNISWAP_AGENT_ALLOW_MAINNET=1` in the
environment AND `--allow-mainnet` on the specific call. Never set the
environment arm yourself — that is the human operator's standing decision;
the flag is your per-call confirmation.
2. Nothing is signed or sent without `--broadcast`.
3. Slippage above 5% is refused (`UNISWAP_AGENT_MAX_SLIPPAGE` to override).
4. v3 on testnets is refused deterministically (exit 2) — the library's v3
contracts are mainnet-hardcoded; don't retry, use a production network or
v4.

## Current network coverage (uniswap-python 0.8.0)

Quotes work today on the ~18 production networks configured in the library
(mainnet, base, arbitrum, optimism, polygon, ...). Sepolia broadcasting is
pending library-side enablement: the v3 client hardcodes mainnet contract
addresses, and the v4 address maps don't include Sepolia yet. Until then, use
mainnet for read-only quoting and expect `status` (but not `quote`/`swap`) to
work on Sepolia.

## Interpreting output

With `--json`, results are a single JSON object on stdout. Errors go to
stderr as `{"error": ..., "kind": "safety"|"runtime"}`; exit code 2 means a
safety guard refused (do not retry with workarounds — report to the human),
1 means a runtime error (bad pool, no liquidity, RPC down — often worth one
retry or a different fee tier).

Amounts are integers in the token's base units (wei for ETH/WETH: 1 ETH =
10^18). Where token decimals are readable on-chain, outputs also carry
advisory `<field>_human` (decimal string) and `decimals_<field>` companions —
prefer those when showing humans, but treat the integer fields as the source
of truth (the human fields are omitted when a decimals lookup fails).
43 changes: 43 additions & 0 deletions skills/uniswap-trading/demo_sepolia.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Demo for the uniswap-trading agent skill.
#
# Part 1 (works today): read-only quoting against mainnet — no key needed.
# Part 2 (Sepolia broadcast): blocked on library-side Sepolia enablement
# (v3 hardcodes mainnet contracts; v4 address maps lack Sepolia). Once
# uniswap-python configures Sepolia, run with:
# UNISWAP_AGENT_ADDRESS=0x... UNISWAP_AGENT_PRIVATE_KEY=0x... DEMO_BROADCAST=1
set -euo pipefail

SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AGENT="python3 $SKILL_DIR/scripts/uniswap_agent.py --json"

MAINNET_RPC="${MAINNET_RPC:-https://ethereum-rpc.publicnode.com}"
SEPOLIA_RPC="${SEPOLIA_RPC:-https://ethereum-sepolia-rpc.publicnode.com}"

# 0.001 WETH in wei
QTY=1000000000000000

echo "== [mainnet, read-only] status =="
PROVIDER=$MAINNET_RPC $AGENT status

echo "== [mainnet, read-only] v3 quote: 0.001 WETH -> USDC =="
PROVIDER=$MAINNET_RPC $AGENT quote WETH USDC "$QTY"

echo "== [mainnet, read-only] v4 quote: 0.001 ETH -> USDC =="
PROVIDER=$MAINNET_RPC $AGENT --version 4 quote ETH USDC "$QTY" --fee 500 --tick-spacing 10

echo "== [mainnet] dry-run swap plan (nothing is sent) =="
PROVIDER=$MAINNET_RPC $AGENT swap WETH USDC "$QTY"

echo "== [sepolia] status =="
PROVIDER=$SEPOLIA_RPC $AGENT status

if [ "${DEMO_BROADCAST:-0}" = "1" ]; then
echo "== [sepolia] approve WETH (broadcast) =="
PROVIDER=$SEPOLIA_RPC $AGENT approve WETH --broadcast
echo "== [sepolia] swap 0.001 WETH -> USDC (broadcast) =="
PROVIDER=$SEPOLIA_RPC $AGENT swap WETH USDC "$QTY" --broadcast
else
echo "(Sepolia swap skipped: needs DEMO_BROADCAST=1, a funded key, and"
echo " library-side Sepolia contract enablement — see README status list)"
fi
Loading
Loading