UNPKG

pmarket-cli

Version:
198 lines (139 loc) 8.15 kB
# CLAUDE.md This file provides context for AI assistants working on this codebase. ## Project Overview **pmarket-cli** is a command-line interface for trading on Polymarket, a prediction market platform. It allows users to list markets, buy/sell tokens, manage USDC allowances, and view order books. ## Tech Stack - **Runtime**: Node.js 22+ (ESM modules) - **Language**: TypeScript 5.6+ - **Build**: Plain `tsc` (no bundler) - **Testing**: Jest with ts-jest (ESM mode) - **Key Dependencies**: - `@polymarket/clob-client` - Polymarket API client - `ethers` v6 - Ethereum interactions - `commander` - CLI argument parsing - `better-sqlite3` - Local market data caching ## Architecture ### Entry Point - `src/main.ts` - Bootstrap, creates services, parses CLI args, executes strategy ### Services (`src/services/`) | Service | Purpose | |---------|---------| | `config.service.ts` | Loads config from `~/.pmarket-cli/config.json`, manages credentials | | `polymarket.service.ts` | Wraps `@polymarket/clob-client`, handles market/order operations | | `contract.service.ts` | Ethereum contract interactions (USDC allowance, position redemption) | | `cache.service.ts` | SQLite caching for market data (1hr TTL) | ### Strategy Pattern (`src/strategy/`) Each CLI command is implemented as a strategy: - `init-strategy.ts` - `-i` flag, initialize config with private key (onboarding) - `list-strategy.ts` - `-l` flag, lists markets (uses cache, fetches if expired) - `refresh-strategy.ts` - `-r` flag, refreshes the local market cache - `buy-strategy.ts` - `-b` flag, buy orders (token_id, size, price) - `sell-strategy.ts` - `-s` flag, sell orders (token_id, size, price) - `positions-strategy.ts` - `-p` flag, show current token positions with P&L - `allowance-strategy.ts` - `-a` flag, set USDC allowance - `order-book-strategy.ts` - `-o` flag, show order book - `cancel-all-strategy.ts` - `-c` flag, cancel all orders - `api-keys-strategy.ts` - `-k` flag, derive/show API keys - `redeem-strategy.ts` - `-w` flag, redeem winning positions from resolved markets `context.ts` determines which strategy to use based on CLI options. ### Configuration Files (User's machine) Located in `~/.pmarket-cli/`: - `config.json` - Just `{ "privateKey": "0x..." }` - `credentials.json` - Auto-generated API credentials - `cache.db` - SQLite database for market cache ## Key Design Decisions 1. **Simplified Config**: User only needs to provide `privateKey`. RPC defaults to public Polygon RPC, funder address is derived from private key, API keys are auto-generated. 2. **ESM Modules**: Uses `"type": "module"` in package.json. All imports use `.js` extension (TypeScript requirement for Node16 module resolution). 3. **No NestJS**: Removed in v0.8.0 modernization. Services are plain classes with constructor injection. 4. **Lazy Initialization**: `PolymarketService` lazily initializes the CLOB client on first API call via `ensureInitialized()`. 5. **SQLite Caching**: Markets are cached locally for 1 hour. Use `-r` flag to force refresh. ## Common Tasks ### Adding a New CLI Command 1. Create `src/strategy/new-strategy.ts` implementing `Strategy` interface 2. Add option to `src/program.ts` 3. Add case in `src/strategy/context.ts` `determineStrategy()` 4. Add test in `src/strategy/context.spec.ts` ### Modifying Market Data Market type is defined in `src/services/polymarket.service.ts` as `Market` interface. Cache schema is in `cache.service.ts`. ### Running Tests ```bash npm test # Run all tests npm test -- --watch # Watch mode npm test -- path/to/file # Single file ``` Tests use `@jest/globals` imports for ESM compatibility. ## Build & Test Commands ```bash npm run build # Compile TypeScript to dist/ npm test # Run Jest tests npm run lint # Run ESLint npm start # Run compiled CLI ``` ## Verification **IMPORTANT**: After every incremental change, always verify by running all three checks: ```bash npm run build && npm test && npm run lint ``` Do not consider a change complete until build, tests, and linting all pass. ## Important Files | File | Description | |------|-------------| | `package.json` | Dependencies, scripts, `"type": "module"` | | `tsconfig.json` | TypeScript config (ES2022, Node16 modules) | | `jest.config.cjs` | Jest config for ESM | | `src/main.ts` | Entry point | | `src/program.ts` | CLI argument definitions | | `scripts/create-config.js` | Postinstall script for config setup | ## Testing with Custom Config Directory **IMPORTANT**: When manually testing CLI commands, always use a test directory to avoid overwriting the user's real config: ```bash # Use a temp directory for testing PMARKET_CONFIG_DIR=/tmp/pmarket-test node dist/main.js -i 0xTEST_KEY # Or export for the session export PMARKET_CONFIG_DIR=/tmp/pmarket-test node dist/main.js -l "Bitcoin" ``` The `PMARKET_CONFIG_DIR` environment variable overrides the default `~/.pmarket-cli/` directory. ## Gotchas 1. **Import Extensions**: Always use `.js` in imports even for `.ts` files (Node16 module resolution) 2. **Jest ESM**: Tests must import from `@jest/globals` for `jest`, `describe`, `it`, `expect` 3. **better-sqlite3**: Native module, requires rebuild on Node version changes 4. **API Key Types**: `@polymarket/clob-client` returns `ApiKeyCreds` with `key`/`secret`/`passphrase` fields 5. **Testing**: ALWAYS use `PMARKET_CONFIG_DIR` env var when manually testing to avoid overwriting user's real config 6. **USDC.e vs USDC**: Polymarket ONLY works with USDC.e (bridged USDC at `0x2791...`), NOT native USDC (`0x3c49...`). This is the #1 cause of "insufficient balance" errors. 7. **Neg_risk Markets**: Some markets require allowance on NegRiskExchange and NegRiskAdapter in addition to CTFExchange 8. **Public RPC Rate Limits**: The default Polygon RPC has rate limits. The allowance command includes delays between transactions to avoid errors. ## External APIs - **Polymarket CLOB API**: `https://clob.polymarket.com/` - Trading operations - **Polymarket Data API**: `https://data-api.polymarket.com` - Positions, activity - **Polygon RPC** (default): `https://polygon-rpc.com` ## Important Contracts ### USDC.e (Bridged USDC) - REQUIRED **⚠️ Polymarket only works with USDC.e (bridged USDC), NOT native USDC!** | Token | Contract Address | Works? | |-------|------------------|--------| | **USDC.e (Bridged)** | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | ✅ YES | | USDC (Native) | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` | ❌ NO | ### Exchange Contracts (Need Allowance) Three contracts require USDC.e allowance for trading: | Contract | Address | Purpose | |----------|---------|---------| | **CTFExchange** | `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` | Regular markets | | **NegRiskExchange** | `0xC5d563A36AE78145C45a50134d48A1215220f80a` | Neg_risk markets | | **NegRiskAdapter** | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | Neg_risk markets | The `-a` command sets allowance for all three contracts with delays between transactions to avoid public RPC rate limiting. ### Redemption Contracts (Claiming Winnings) When a market resolves, winning positions can be redeemed for USDC.e via the `-w` flag. Two contracts are used depending on market type: | Contract | Address | Purpose | |----------|---------|---------| | **ConditionalTokens (CTF)** | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | Standard market redemption | | **NegRiskAdapter** | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | Neg_risk market redemption | The redeem command tries standard CTF redemption first, then falls back to NegRiskAdapter if that fails. For neg_risk markets, the NegRiskAdapter requires ERC-1155 approval on the ConditionalTokens contract (auto-handled). ## Signature Types The `@polymarket/clob-client` uses signature types for authentication: | Value | Type | Use Case | |-------|------|----------| | `0` | EOA | Regular Ethereum wallets (MetaMask, etc.) | | `1` | POLY_PROXY | Polymarket proxy wallets | | `2` | POLY_GNOSIS_SAFE | Gnosis Safe multisig | **This CLI uses `0` (EOA)** for regular wallet users.