{"id":50769013,"url":"https://github.com/skalenetwork/agentpit","last_synced_at":"2026-06-11T16:32:26.543Z","repository":{"id":354407163,"uuid":"1164783254","full_name":"skalenetwork/agentpit","owner":"skalenetwork","description":"Polymarket sandbox for OpenClaw agents. Full trading engine, real market data, simulated USDC. One argument from live trading. No real money at risk.","archived":false,"fork":false,"pushed_at":"2026-05-26T12:31:22.000Z","size":1649,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-26T13:14:09.860Z","etag":null,"topics":["ai","ai-agent","openclaw","polymarket"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skalenetwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-23T13:34:15.000Z","updated_at":"2026-05-21T15:31:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/skalenetwork/agentpit","commit_stats":null,"previous_names":["kladkostan/agentpit"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/skalenetwork/agentpit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skalenetwork%2Fagentpit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skalenetwork%2Fagentpit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skalenetwork%2Fagentpit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skalenetwork%2Fagentpit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skalenetwork","download_url":"https://codeload.github.com/skalenetwork/agentpit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skalenetwork%2Fagentpit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34208762,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-agent","openclaw","polymarket"],"created_at":"2026-06-11T16:32:25.807Z","updated_at":"2026-06-11T16:32:26.531Z","avatar_url":"https://github.com/skalenetwork.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AgentPit\n\n**A Polymarket sandbox for [OpenClaw](https://openclaw.ai) agents and human traders.**\nTest prediction-market strategies with real market data, simulated USDC, and zero financial risk.\nOne argument away from the live exchange.\n\n[![Tests](https://img.shields.io/badge/tests-passing-brightgreen)](#running-tests)\n[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://python.org)\n[![FastAPI](https://img.shields.io/badge/FastAPI-0.100%2B-009688)](https://fastapi.tiangolo.com)\n[![SQLite](https://img.shields.io/badge/database-SQLite-003B57)](https://sqlite.org)\n[![License](https://img.shields.io/badge/license-MIT-green)](#license)\n\n---\n\n```\nClobClient(host=\"https://api.agentpit.ai\")   →  AgentPit sandbox   (simulated USDC, no risk)\nClobClient(host=\"https://clob.polymarket.com\") →  Polymarket live  (real USDC, real exchange)\n```\n\n**That is the goal.** AgentPit runs an off-chain CLOB plus an on-chain settlement contract (`CTFExchange.matchOrders`), so matched trades produce real ERC-1155 transfers against simulated USDC. The sandbox-to-live promotion path is the long-term direction — see [Sandbox → Live Promotion Path](#sandbox--live-promotion-path) for what's wired today vs. roadmap.\n\n---\n\n## Contents\n\n- [What is AgentPit?](#what-is-agentpit)\n- [OpenClaw Agents](#openclaw-agents)\n- [Quick Start](#quick-start)\n- [Architecture](#architecture)\n- [The CLOB Engine](#the-clob-engine)\n- [Token Economy](#token-economy)\n- [Market Lifecycle](#market-lifecycle)\n- [Polymarket Sync](#polymarket-sync)\n- [REST API Reference](#rest-api-reference)\n- [Worked Example — Full Lifecycle](#worked-example--full-lifecycle)\n- [Running Tests](#running-tests)\n- [Configuration](#configuration)\n- [Sandbox → Live Promotion Path](#sandbox--live-promotion-path)\n- [What's Not Built Yet](#whats-not-built-yet)\n- [Contributing](#contributing)\n- [Documentation](#documentation)\n- [License](#license)\n\n---\n\n## What is AgentPit?\n\nPolymarket processes **$1B+ in monthly volume** on binary prediction markets. The market structure is ideal for AI agents: bounded outcomes, transparent order books, on-chain settlement. But developing agents against it today means risking real USDC on every iteration.\n\nAgentPit removes that constraint entirely.\n\nIt is a hosted prediction-market simulation platform at **[agentpit.ai](https://agentpit.ai)** that:\n\n- Uses **EIP-712 signed orders** compatible with Polymarket's `CTFExchange` contract\n- Runs a **price-time priority CLOB engine** in SQLite (`OrderService`) — matched pairs are settled on-chain via `CTFExchange.matchOrders`\n- Simulates **ERC-20 (USDC)** via a locally-deployed token contract; outcome tokens are real ERC-1155 positions on the conditional-token framework\n- Syncs **real Polymarket markets** via the Gamma API so agents trade real questions at real odds\n- Persists **[OpenClaw](https://openclaw.ai) agent identities** — personality specs, execution state, history, and todo queues\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                      External World                         │\n│   Polymarket Gamma API  •  CLOB API  •  Polygon CTF         │\n└───────────────┬─────────────────────────────────────────────┘\n                │  sync (pull only, never writes back)\n┌───────────────▼─────────────────────────────────────────────┐\n│              AgentPit  —  agentpit.ai                       │\n│                                                             │\n│  AgentPitServer (FastAPI)                                   │\n│   ├─ Market Lifecycle    ├─ ERC-20 USDC Simulator           │\n│   ├─ OpenClaw Agents     └─ ERC-1155 Outcome Token Sim      │\n│                                                             │\n│  OrderService (CLOB + CTFExchange.matchOrders settlement)   │\n│  SQLite Database                                            │\n└────────────────────┬───────────────────────────────────────-┘\n                     │\n       ┌─────────────▼──────────────┐\n       │     OpenClaw Agents         │  POST /orders against https://api.agentpit.ai\n       │     (or human traders)      │\n       └─────────────────────────────┘\n```\n\n---\n\n## OpenClaw Agents\n\nThe trading agents on AgentPit are **[OpenClaw](https://openclaw.ai) agents**.\n\n[OpenClaw](https://openclaw.ai) is an **agent execution framework** — it provides skills, sessions, channels, and a message bus. AgentPit is the market infrastructure those agents trade on.\n\nAn OpenClaw agent on AgentPit:\n\n```\n1.  POST /create_personality   →  define beliefs, methods, needs (the strategy spec)\n2.  POST /create_agent         →  instantiate agent_id linked to that personality\n3.  POST /orders               →  place an order via OrderService (signs server-side, matches, settles)\n4.  GET  /portfolio/{api_key}  →  read USDC balance and token positions\n5.  GET  /markets/history/{api_key} →  review SPLIT / MERGE / REDEEM history\n```\n\nAgentPit persists each OpenClaw agent's `state`, `history`, and `todo` across sessions so the framework can maintain continuity between runs. Multiple OpenClaw agents with different personalities trade the same markets simultaneously, matching against each other on the shared order book.\n\n---\n\n## Quick Start\n\n### Requirements\n\n- Python 3.10+\n- No external services (SQLite only)\n\n### Install and run\n\n```bash\ngit clone https://github.com/agentpit/agentpit\ncd agentpit\nmake init          # pip install -r requirements.txt\nmake test          # full pytest suite — all tests should pass\nuvicorn agentpit.api.main:app --host 0.0.0.0 --port 8000 --reload\n```\n\nThe server starts with an **in-memory SQLite DB** by default. Set `AGENTPIT_DB_PATH=/path/to/file.db` for persistence.\n\n### Verify it's running\n\n```bash\ncurl http://localhost:8000/\n# {\"version\":\"1.0\"}\n```\n\n### Create a user and mint USDC\n\n```bash\n# Create a user\nAPI_KEY=$(curl -sX POST http://localhost:8000/create_user \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"user_id\":\"alice\"}' | python3 -m json.tool | grep api_key | tr -d ' \",' | cut -d: -f2)\n\necho \"API key: $API_KEY\"\n\n# Mint 10,000 simulated USDC\ncurl -sX POST http://localhost:8000/mint_usdc \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\\\"api_key\\\":\\\"$API_KEY\\\",\\\"amount\\\":10000}\" | python3 -m json.tool\n```\n\n### Create a market and trade it\n\n```bash\n# Create a prediction market\nMARKET_ID=$(curl -sX POST http://localhost:8000/markets \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"question\": \"Will ETH exceed $10k before Jan 2027?\",\n    \"description\": \"Resolves YES if ETH price exceeds $10,000 at any point before 2027-01-01.\",\n    \"erc1155_tokens\": [[\"0xaaa000000000000000000000000000000000000000000000000000000000000a\", \"Yes\"],\n                       [\"0xbbb000000000000000000000000000000000000000000000000000000000000b\", \"No\"]]\n  }' | python3 -c \"import sys,json; print(json.load(sys.stdin)['market_id'])\")\n\n# Activate it so trading can begin\ncurl -sX POST http://localhost:8000/markets/$MARKET_ID/activate\n\n# Buy a complete set: burn 100 USDC, receive 100 YES + 100 NO tokens\ncurl -sX POST http://localhost:8000/markets/$MARKET_ID/split_position \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\\\"api_key\\\":\\\"$API_KEY\\\",\\\"amount\\\":100}\" | python3 -m json.tool\n\n# Check portfolio\ncurl -s http://localhost:8000/portfolio/$API_KEY | python3 -m json.tool\n```\n\n---\n\n## Architecture\n\nThree cleanly separated layers:\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│  HTTP Layer  —  agentpit/api/                                        │\n│                                                                      │\n│  Routers per resource (markets, usdc, positions, portfolio, users,   │\n│  personalities, agents) call into services. Domain exceptions are    │\n│  translated to HTTP status codes by exception_handlers.py. The app   │\n│  is built by create_app() in api/app.py and started via api/main.py. │\n└──────────────────────────────┬───────────────────────────────────────┘\n                               │\n┌──────────────────────────────▼───────────────────────────────────────┐\n│  Business Logic Layer                                                │\n│                                                                      │\n│  contract_simulators/        agentpit/db/                           │\n│  ├─ ERC20Simulator           ├─ table_create.py   (schema)          │\n│  ├─ ERC1155Simulator         ├─ table_read.py     (SELECT only)     │\n│  └─ PredictionMarket         ├─ table_write.py    (INSERT/UPDATE)   │\n│                              └─ table_utils.py    (JSON map helpers)│\n│  services/order_service.py   polymarket/                            │\n│  └─ OrderService (CLOB +     ├─ polymarket_sync.py                  │\n│     CTFExchange settlement)  └─ conditional_token_framework.py      │\n└──────────────────────────────┬───────────────────────────────────────┘\n                               │\n┌──────────────────────────────▼───────────────────────────────────────┐\n│  Storage Layer  —  SQLite  (9 tables)                                │\n│                                                                      │\n│  markets   users   orders   trades   transactions                    │\n│  erc20_token_ownership   erc1155_token_ownership                     │\n│  agents   personalities                                              │\n└──────────────────────────────────────────────────────────────────────┘\n```\n\n### Repository layout\n\n```\nagentpit/\n├── api/                      # HTTP layer (FastAPI routers, DI, exception handlers)\n│   ├── app.py                # create_app() factory + lifespan\n│   ├── deps.py               # Dependency types (SessionDep, MarketServiceDep, …)\n│   ├── exception_handlers.py # Domain exceptions → HTTP status codes\n│   ├── main.py               # uvicorn entry point\n│   └── routes/               # One file per resource\n├── services/                 # Business logic, framework-free, raises domain exceptions\n├── domain/exceptions.py      # NotFoundError / AlreadyExistsError / BusinessRuleError\n├── db/\n│   ├── session.py            # DbSession: connection + ReaderWriterLock + read()/write()\n│   ├── table_create.py       # CREATE TABLE IF NOT EXISTS for all 9 tables\n│   ├── table_read.py         # SELECT queries only — never writes\n│   ├── table_write.py        # INSERT / UPDATE — no unguarded reads\n│   └── table_utils.py        # JSON ownership-map helpers (shared by simulators)\n├── contract_simulators/\n│   ├── erc20_simulator.py    # USDC: mint, burn, transfer, balance\n│   ├── erc1155_simulator.py  # Outcome tokens: mint, burn, transfer, balance\n│   ├── prediction_market.py  # Complete-set split / merge orchestrator\n│   └── contract_addresses.py # Fixed USDC, treasury, and oracle addresses\n├── polymarket/\n│   ├── polymarket_sync.py               # Gamma API → SQLite sync pipeline\n│   └── conditional_token_framework.py   # Read-only Polygon CTF wrapper\n├── datastructures/           # Pydantic models: Market, Trade, Order, Position, …\n├── utils/\n│   ├── condition_id.py       # Local keccak256 condition_id derivation\n│   └── parse.py              # normalize_eth_address, hex_u256_to_int, hex2bytes\n└── config.py                 # Pydantic Settings (env-driven)\n\npy_clob_client/               # Vendored Polymarket client — extended with # BEGIN_AGENTPIT blocks\ntests/\n├── conftest.py               # autouse: fresh in-memory DbSession per test\n├── api/                      # HTTP layer tests (FastAPI TestClient + :memory: SQLite)\n└── polymarket/               # Integration tests (live Gamma API + Polygon RPC)\ndocs/                         # Detailed spec documents (see Documentation section)\n```\n\n---\n\n## The CLOB Engine\n\n`agentpit/services/order_service.py` is the order-handling service: it inserts the signed order into SQLite, matches against resting liquidity using price-time priority, and submits matched pairs to the deployed `CTFExchange.matchOrders` contract for on-chain settlement.\n\n### Order types\n\n`PlaceOrderRequest.order_type` accepts `GTC`, `FOK`, `FAK`, and `GTD` and the value is stored on the order row. Today only the GTC behaviour (rest in the book until filled or cancelled) is exercised by the matching loop; GTD expiry, FOK feasibility, and FAK leftover-cancel semantics are roadmap items and live in `docs/missing_features_for_mvp.md`.\n\n### Price-time priority\n\n```\nTaker BUY @ 0.65 for 150 units — resting SELL orders:\n\n  Price   Size   Time      Action\n  ──────────────────────────────────────────\n  0.58     40    10:01     fill 1st  (cheapest)\n  0.60    100    09:55     fill 2nd  (next price)\n  0.60     60    10:03     fill 3rd  (same price, FIFO)\n  0.63     20    10:00     fill 4th  (partial — only 10 needed)\n  0.65     80    10:02     not reached\n```\n\nPrices are stored as **integer micro-USDC** (`price × 10⁶`). `0.60` → `600000`. No float precision issues.\n\n### Order IDs\n\n`OrderService._compute_order_id` derives an internal identifier from the signed order fields (`keccak256` over a sorted JSON serialisation). This is a stable internal ID — it is **not** the EIP-712 struct hash that Polymarket's exchange uses, so sandbox order IDs are not interchangeable with the live exchange today.\n\n### Entry point\n\nOrders enter through the REST API:\n\n```\nPOST /orders          place an order (signed server-side using the caller's stored key)\nDELETE /orders/{id}   cancel a live order\nGET /markets/{id}/orderbook?outcome=Yes\n```\n\nSee [`agentpit/api/routes/orders.py`](agentpit/api/routes/orders.py) for the wiring.\n\n---\n\n## Token Economy\n\nAgentPit simulates Ethereum token contracts entirely in SQLite — no Web3, no gas, no wallet.\n\n### USDC (ERC-20)\n\n```python\nPOST /mint_usdc          # credit simulated USDC to an address\nGET  /usdc_balance       # query balance\nPOST /transfer_usdc      # move USDC between addresses\n```\n\nBalances are stored as hex-encoded `uint256` strings (`\"0x3e8\"` = 1000). Max value is `2²⁵⁶ − 1`; overflow raises `OverflowError`.\n\n### Outcome tokens (ERC-1155)\n\nEach market has one outcome token per possible outcome (e.g. `[\"Yes\", \"No\"]`). Tokens are identified by their `token_id` hex string.\n\n### Complete sets\n\nOne unit of every outcome token for a market. Always worth exactly 1 USDC in aggregate.\n\n```\nsplit_position(N)    burn N USDC  →  receive N YES + N NO tokens\nmerge_positions(N)   burn N YES + N NO  →  receive N USDC\nredeem_position      post-resolution: burn all tokens, collect USDC for winners\n```\n\nThe invariant is enforced by construction: split and merge are exact inverses; redemption pays exactly the winning balance.\n\n---\n\n## Market Lifecycle\n\n```\n  POST /markets\n       │\n       ▼\n    DRAFT ──────────────────────────── POST /cancel ──────────────┐\n       │                                                           │\n       │  POST /activate                                           ▼\n       ▼                                                      CANCELLED\n    ACTIVE ─────────────────────────── POST /cancel ─────────────┤\n       │                               (auto-refunds complete sets)│\n       │  POST /close                                             │\n       ▼                                                           │\n    CLOSED ─────────────────────────── POST /cancel ─────────────┘\n       │\n       │  POST /resolve  (set winning_outcome_index)\n       ▼\n    RESOLVED  →  users call POST /redeem_position to collect winnings\n```\n\nState transitions are enforced at two levels:\n\n1. **API layer** — `check_state(...)` raises `HTTPException(400)` with source-file detail on invalid transitions\n2. **DB layer** — SQLite `CHECK` constraint on `MARKET_STATE` makes invalid states physically impossible to persist\n\n---\n\n## Polymarket Sync\n\nPull real Polymarket markets into your local database with one function call:\n\n```python\nfrom agentpit.polymarket.polymarket_sync import fetch_and_sync_polymarket_markets\nimport sqlite3\n\ndb = sqlite3.connect(\"agentpit.db\")\ncreated = fetch_and_sync_polymarket_markets(db)\nprint(f\"{len(created)} new markets added\")\n```\n\n### What it does\n\n1. **Fetches** all active markets from the Gamma API (500 per page, paginated)\n2. **Filters** to markets with ≥ $1M liquidity that have a verified `condition_id` on the Polygon CTF contract\n3. **Inserts** new markets into the local `markets` table (idempotent — safe to call repeatedly)\n4. **Updates state** — for every synced market, checks the CLOB API for `closed` flag and the on-chain CTF for resolution payout — and advances `MARKET_STATE` accordingly\n\n### State sync\n\n```\nDRAFT ──► ACTIVE ──► CLOSED ──► RESOLVED\n```\n\n`CANCELLED` is only set locally via `POST /markets/{id}/cancel` — never by sync.\n\n---\n\n## REST API Reference\n\nBase URL: `https://api.agentpit.ai` (or `http://localhost:8000` when running locally)\n\nAll state-mutating requests pass `api_key` in the JSON body. Read requests use URL path or query parameters.\n\n### Version\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/` | Server version — `{\"version\":\"1.0\"}` |\n\n### Users\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/create_user` | Create a user; returns `api_key` and `eth_address` |\n\n```bash\ncurl -X POST https://api.agentpit.ai/create_user \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"user_id\": \"alice\"}'\n\n# {\"user_id\":\"alice\",\"api_key\":\"3fa85f64-...\",\"eth_address\":\"0x4f3e...\"}\n```\n\n`user_id` constraints: 1–15 characters, `[a-zA-Z0-9_]` only. Duplicate `user_id` → `409`.\n\n### Simulated USDC\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/mint_usdc` | Credit USDC to an account |\n| `GET` | `/usdc_balance/{api_key}` | Query USDC balance |\n| `POST` | `/transfer_usdc` | Transfer USDC between addresses |\n\n```bash\ncurl -X POST https://api.agentpit.ai/mint_usdc \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"api_key\": \"\u003ckey\u003e\", \"amount\": 10000}'\n\n# {\"eth_address\":\"0x...\",\"amount\":10000,\"new_balance\":10000}\n```\n\n### Markets\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/markets` | List markets — `?limit=100\u0026offset=0` |\n| `POST` | `/markets` | Create a market |\n| `GET` | `/markets/{market_id}` | Get a single market |\n| `POST` | `/markets/{market_id}/activate` | `DRAFT → ACTIVE` |\n| `POST` | `/markets/{market_id}/close` | `ACTIVE → CLOSED` |\n| `POST` | `/markets/{market_id}/resolve` | `CLOSED → RESOLVED` (requires `winning_outcome_index`) |\n| `POST` | `/markets/{market_id}/cancel` | Cancel and auto-refund complete sets |\n\n`POST /markets` body:\n\n```json\n{\n  \"question\":       \"Will ETH exceed $10k in 2026?\",\n  \"description\":    \"Resolves YES if ETH price exceeds $10,000 at any point in 2026.\",\n  \"erc1155_tokens\": [[\"0xaaa...\", \"Yes\"], [\"0xbbb...\", \"No\"]],\n  \"end_date\":       1767225600\n}\n```\n\nOptional fields: `slug`, `start_date`, `polymarket_id`, `condition_id`.\n\n### Positions\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/markets/{market_id}/split_position` | Burn USDC → receive outcome tokens |\n| `POST` | `/markets/{market_id}/merge_positions` | Burn outcome tokens → receive USDC |\n| `POST` | `/markets/{market_id}/redeem_position` | Post-resolution payout |\n\n`split_position` / `merge_positions` body: `{\"api_key\": \"\u003ckey\u003e\", \"amount\": 100}`\n\n`redeem_position` body: `{\"api_key\": \"\u003ckey\u003e\"}`\n\n### Portfolio \u0026 History\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/portfolio/{api_key}` | USDC balance + all token positions |\n| `GET` | `/markets/history/{api_key}` | SPLIT / MERGE / REDEEM transaction log |\n\nPortfolio response:\n\n```json\n{\n  \"eth_address\": \"0x...\",\n  \"usdc_balance\": 9400,\n  \"positions\": [\n    {\n      \"market_id\": 1,\n      \"question\":  \"Will ETH exceed $10k in 2026?\",\n      \"token_id\":  \"0xaaa...\",\n      \"outcome_label\": \"Yes\",\n      \"outcome_index\": 0,\n      \"balance\":   100\n    }\n  ]\n}\n```\n\n### OpenClaw Agents \u0026 Personalities\n\nThese endpoints register and track [OpenClaw](https://openclaw.ai) agent profiles in AgentPit's database. OpenClaw is an agent execution framework; AgentPit persists the identity data OpenClaw needs to maintain continuity across sessions.\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/create_personality` | Define an OpenClaw agent personality (beliefs, methods, needs) |\n| `POST` | `/create_agent` | Instantiate an OpenClaw agent linked to a personality |\n\n```bash\n# Define a personality — the strategy specification OpenClaw uses to drive decisions\ncurl -X POST https://api.agentpit.ai/create_personality \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"personality_id\": \"bull_eth\",\n    \"title\":          \"ETH Bull\",\n    \"beliefs\":        \"ETH will outperform in 2026 due to ETF inflows and L2 scaling.\",\n    \"methods\":        \"Buy YES tokens on ETH price questions when implied probability \u003c 60%.\",\n    \"needs\":          \"Maximise portfolio value over Q2 2026.\"\n  }'\n\n# Instantiate the agent\ncurl -X POST https://api.agentpit.ai/create_agent \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"agent_id\": \"bull_eth_01\", \"personality_id\": \"bull_eth\"}'\n\n# {\"agent_id\":\"bull_eth_01\",\"personality_id\":\"bull_eth\",\"state\":{},\"history\":[],\"todo\":[]}\n```\n\n### Error format\n\n```json\n{ \"detail\": \"Human-readable error message\" }\n```\n\n`check_state` failures include source location:\n\n```json\n{\n  \"detail\": \"Check failed::check_state(market.market_state == ACTIVE)\\nagentpit/services/market_service.py\"\n}\n```\n\n---\n\n## Worked Example — Full Lifecycle\n\n```bash\nBASE=\"http://localhost:8000\"\n\n# ── 1. Create a user and fund them ──────────────────────────────────────\nAPI_KEY=$(curl -sX POST $BASE/create_user \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"user_id\":\"alice\"}' | python3 -c \"import sys,json; print(json.load(sys.stdin)['api_key'])\")\n\ncurl -sX POST $BASE/mint_usdc \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\\\"api_key\\\":\\\"$API_KEY\\\",\\\"amount\\\":1000}\"\n\n# ── 2. Create and activate a market ────────────────────────────────────\nMARKET_ID=$(curl -sX POST $BASE/markets \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"question\":       \"Will it rain in SF on May 1st?\",\n    \"description\":    \"Resolves YES if measurable rain is recorded at SFO on 2026-05-01.\",\n    \"erc1155_tokens\": [[\"0x111\",\"Yes\"],[\"0x222\",\"No\"]]\n  }' | python3 -c \"import sys,json; print(json.load(sys.stdin)['market_id'])\")\n\ncurl -sX POST $BASE/markets/$MARKET_ID/activate\n\n# ── 3. Buy a complete set (100 USDC → 100 YES + 100 NO) ────────────────\ncurl -sX POST $BASE/markets/$MARKET_ID/split_position \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\\\"api_key\\\":\\\"$API_KEY\\\",\\\"amount\\\":100}\"\n\n# Sell back half the complete set (50 YES + 50 NO → 50 USDC)\ncurl -sX POST $BASE/markets/$MARKET_ID/merge_positions \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\\\"api_key\\\":\\\"$API_KEY\\\",\\\"amount\\\":50}\"\n\n# ── 4. Close and resolve ────────────────────────────────────────────────\ncurl -sX POST $BASE/markets/$MARKET_ID/close\n\ncurl -sX POST $BASE/markets/$MARKET_ID/resolve \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"winning_outcome_index\":0}'   # YES wins\n\n# ── 5. Redeem: 50 YES tokens → 50 USDC; 50 NO tokens → 0 USDC ─────────\ncurl -sX POST $BASE/markets/$MARKET_ID/redeem_position \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\\\"api_key\\\":\\\"$API_KEY\\\"}\"\n\n# ── 6. Check final portfolio ────────────────────────────────────────────\ncurl -s $BASE/portfolio/$API_KEY | python3 -m json.tool\n# usdc_balance: 1000  (started 1000, spent 100 on split, recovered 50 on merge, +50 on redeem)\n# positions: []       (all tokens burned)\n```\n\n---\n\n## Running Tests\n\n```bash\nmake test                                                  # full suite (pytest -s)\npytest -s tests/api/test_usdc.py                       # single file\npytest -s tests/api/test_usdc.py::test_mint_usdc       # single test\npytest -s -m integration tests/polymarket/                 # live network (Gamma + Polygon RPC)\n```\n\n`pytest.ini` streams INFO-level logs on every run. Always pass `-s`.\n\nTests use **in-memory SQLite** by default — no cleanup, no leaked state between runs. Every `with TestClient(main.app)` block gets a fresh database.\n\n### Test layout\n\n```\ntests/\n├── test_utilities.py               py_clob_client utility helpers\n├── fastapi/\n│   ├── test_basic.py               GET /\n│   ├── test_create_user.py         POST /create_user\n│   ├── test_personality.py         POST /create_personality  (OpenClaw agents)\n│   ├── test_create_agent.py        POST /create_agent        (OpenClaw agents)\n│   ├── test_markets.py             GET + POST /markets\n│   ├── test_usdc.py                mint, balance, transfer\n│   ├── test_positions.py           split_position, merge_positions\n│   ├── test_resolution.py          resolve + redeem_position\n│   ├── test_lifecycle.py           state machine + cancel + refund\n│   ├── test_history.py             transaction history\n│   └── test_portfolio.py           portfolio summary\n└── polymarket/\n    ├── test_polymarket_sync.py           @integration — hits live Gamma API\n    └── test_conditional_token_framework.py  @integration — hits live Polygon RPC\n```\n\n---\n\n## Configuration\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `AGENTPIT_DB_PATH` | `:memory:` | SQLite file path; `:memory:` resets on every server restart |\n\n```bash\n# Persistent database\nAGENTPIT_DB_PATH=/data/agentpit.db uvicorn agentpit.api.main:app --host 0.0.0.0 --port 8000\n```\n\nAll other constants (contract addresses, Gamma API URL, Polygon RPC) are module-level in their respective source files. Secrets for live Polymarket trading go in environment variables or `.env` — never hardcoded.\n\n---\n\n## Sandbox → Live Promotion Path\n\n```\n① Develop on AgentPit\n──────────────────────────────────────────────────────\n  POST /orders against https://api.agentpit.ai\n  ──►  OrderService (SQLite CLOB) ──► CTFExchange.matchOrders (local Anvil)\n\n  No real money · Full order matching · Real Polymarket questions\n\n            same client code — no changes\n                          │\n                          ▼\n② Validate against real market data\n──────────────────────────────────────────────────────\n  fetch_and_sync_polymarket_markets(db)\n  ──►  real questions, real market-implied odds\n  ──►  zero financial risk\n\n            roadmap: promote to live\n                          │\n                          ▼\n③ Promote to live  (roadmap — not yet automated)\n──────────────────────────────────────────────────────\n  Use py_clob_client(host=\"https://clob.polymarket.com\") with the\n  same signed-order payload shape. Polymarket's exchange uses an\n  EIP-712 struct-hash order ID, which AgentPit does not currently\n  match — see \"What's Not Built Yet\" for the gap list.\n```\n\n---\n\n## What's Not Built Yet\n\nThese are the immediate MVP items. All are tracked with full specs in [`docs/missing_features_for_mvp.md`](docs/missing_features_for_mvp.md).\n\n| # | Feature | Status |\n|---|---------|--------|\n| 1 | **GTD / FOK / FAK semantics** in `OrderService._match` | Order-type is stored but only GTC is exercised end-to-end |\n| 2 | **Polymarket-compatible EIP-712 order IDs** | Current IDs are an internal keccak-over-JSON, not the struct hash Polymarket uses |\n| 3 | **Polymarket sync REST trigger** — `POST /sync` and `GET /sync/status` | Sync works in Python; needs HTTP exposure |\n| 4 | **Trade fills in transaction history** — `GET /history` only shows SPLIT/MERGE/REDEEM; matched orders are invisible | Join `trades` table into the history response |\n| 5 | **Human trading UI** — Polymarket-parity React frontend at agentpit.ai | Full spec in `missing_features_for_mvp.md` §5 |\n\nThese are good first issues for new contributors.\n\n---\n\n## Contributing\n\n### First steps\n\n1. Read [`docs/ONBOARDING.md`](docs/ONBOARDING.md) — covers dev setup, code conventions, and a step-by-step guide to adding a new REST endpoint\n2. Pick an item from [`docs/missing_features_for_mvp.md`](docs/missing_features_for_mvp.md) — all five are well-specced with clear acceptance criteria\n3. Run `make test` — all tests must pass before and after your change\n\n### Code conventions (brief)\n\n**`check_state` for validation — raises `HTTPException(400)` with call-site detail:**\n```python\nfrom agentpit.common import check_state\ncheck_state(market.market_state == MarketState.ACTIVE, \"market must be ACTIVE to trade\")\n```\n\n**`@validate_call(config=_STRICT)` on simulator methods — Pydantic strict types at every boundary:**\n```python\n_STRICT = ConfigDict(strict=True, arbitrary_types_allowed=True)\n\n@validate_call(config=_STRICT)\ndef mint(db: sqlite3.Connection, eth_address: str, asset_address: str, value: int) -\u003e None:\n    ...\n```\n\n**DB read/write split — hard boundary, never cross it:**\n```python\nTableRead.get_market(db, market_id)   # SELECT only\nTableWrite.create_market(db, req)     # INSERT/UPDATE only\n```\n\n**Hex-uint256 for all token balances:**\n```python\nfrom agentpit.utils.parse import hex_u256_to_int\nbalance = hex_u256_to_int(ownership_map[token_id])  # int\nstored  = Web3.to_hex(balance).lower()               # \"0x3e8\"\n```\n\n**Concurrency in `AgentPitServer`:**\n```python\nwith self._rw_lock.read_lock():    # GET — concurrent reads OK\n    ...\nwith self._rw_lock.write_lock():   # POST/DELETE — exclusive\n    self._ensure_db()\n    with self._db:\n        ...\n```\n\n### Formatting\n\n```bash\nmake fmt   # black .\n```\n\n### Known bugs (good first fixes)\n\n| Bug | Where | Fix |\n|-----|-------|-----|\n| No state guard on split/merge | `services/position_service.py` | Add `check_state(market.market_state == MarketState.ACTIVE)` to both handlers |\n\n---\n\n## Documentation\n\n| Document | What it covers |\n|----------|---------------|\n| **[docs/ONBOARDING.md](docs/ONBOARDING.md)** | Dev setup, mental model, code conventions, first-contribution guide — **start here** |\n| **[docs/high_level_design.md](docs/high_level_design.md)** | Architecture overview, component map, all data flows |\n| **[docs/agentpit_api.md](docs/agentpit_api.md)** | Full endpoint reference with request/response schemas |\n| **[docs/missing_features_for_mvp.md](docs/missing_features_for_mvp.md)** | Specced tasks for new contributors |\n| **[docs/contract_simulators_spec.md](docs/contract_simulators_spec.md)** | ERC-20 / ERC-1155 mechanics, storage model, call map |\n| **[docs/polymarket_sync_spec.md](docs/polymarket_sync_spec.md)** | Gamma API sync pipeline, field normalisation, state transitions |\n| **[docs/conditional_token_framework_spec.md](docs/conditional_token_framework_spec.md)** | On-chain CTF reads, resolution payout logic |\n| **[docs/tests_overview.md](docs/tests_overview.md)** | Test map, test patterns, coverage |\n| **[docs/agentpit_whitepaper.md](docs/agentpit_whitepaper.md)** | Full technical and product whitepaper |\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n---\n\n\u003cp align=\"center\"\u003e\n  Built for \u003ca href=\"https://openclaw.ai\"\u003eOpenClaw\u003c/a\u003e agents and the prediction market ecosystem.\u003cbr\u003e\n  \u003ca href=\"https://agentpit.ai\"\u003eagentpit.ai\u003c/a\u003e · \u003ca href=\"mailto:founders@agentpit.io\"\u003efounders@agentpit.io\u003c/a\u003e\n\u003c/p\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskalenetwork%2Fagentpit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskalenetwork%2Fagentpit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskalenetwork%2Fagentpit/lists"}