Skip to content

Repository files navigation

MoltWallet

Secure Ethereum wallet server with two-phase commit pattern for all fund-moving operations. Optionally integrates with Polymarket for prediction market trading on Polygon.

Features

Classic Wallet

  • Two-Phase Commit: All transactions require explicit prepare → confirm flow
  • API Key Authentication: SHA-256 hashed keys with read/write permissions
  • Rate Limiting: Per-key limits via Redis
  • Transaction Simulation: All operations validated via eth_call before storing
  • Private Key Isolation: Loaded once, never logged or exposed
  • Contract Allowlist: Enforced for vault/DEX interactions
  • ERC-20 Transfers & Approvals: Send tokens, approve spenders
  • DeFi: Uniswap V3 swaps, ERC-4626 vault deposits/withdrawals

Polymarket (Optional)

Enabled when POLYGON_RPC_URL is set. All endpoints are under /polymarket/.

  • CLOB Trading: Limit orders, market orders, cancel orders via Polymarket's off-chain CLOB
  • Market Discovery: Browse tags, events, and markets (Tags → Events → Markets hierarchy), view orderbooks, get midpoint prices (via Gamma + CLOB APIs)
  • Close Position: One-call convenience endpoint to close a position (auto-resolves size, token_id, best bid)
  • Setup Management: Batch-prepare all 7 Polygon approval transactions, derive CLOB credentials
  • CTF Inventory: Split USDC.e into YES/NO tokens, merge back, redeem resolved markets
  • Portfolio: View positions with token IDs, open orders, trade history, USDC.e balance on Polygon

Quick Start

1. Prerequisites

  • Python 3.11+
  • Redis server running locally or remotely
  • Ethereum RPC endpoint (Alchemy, Infura, or your own node)
  • A funded Ethereum wallet (for testing)
  • (Optional) Polygon RPC endpoint for Polymarket features

2. Installation

# Clone and enter directory
cd moltwallet

# Create virtual environment
python3 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -e ".[dev]"

3. Generate API Key

python scripts/generate_api_key.py --permission write

This outputs:

  • API Key: Give this to clients (store securely!)
  • Hash:Permission: Add this to your API_KEYS env var

4. Configure Environment

cp .env.example .env

Edit .env with your values:

# === Required (Classic Wallet) ===

# Your wallet's private key (WITH 0x prefix)
PRIVATE_KEY=0x...

# Ethereum RPC endpoint
RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY

# Chain ID (1 = mainnet, 11155111 = sepolia)
CHAIN_ID=1

# Redis connection
REDIS_URL=redis://localhost:6379/0

# API keys (from generate_api_key.py output)
API_KEYS=abc123hash:write,def456hash:read_only

# Optional: Allowed contracts for DeFi operations
ALLOWED_CONTRACTS=0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45

# === Optional (Polymarket) ===
# Set POLYGON_RPC_URL to enable all /polymarket/* endpoints.
# Without it, Polymarket routes are not registered.

POLYGON_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/YOUR_KEY

# CLOB credentials (optional — can also be derived via /polymarket/setup/credentials/create)
POLY_API_KEY=
POLY_API_SECRET=
POLY_API_PASSPHRASE=

# Trading guardrails
POLY_MAX_ORDER_SIZE=1000.0
POLY_MAX_PRICE_DEVIATION=0.10

5. Start Redis

# macOS
brew services start redis

# Or run directly
redis-server

6. Run Server

# Development
flask --app "app:create_app()" run --debug --port 8000

# Production
gunicorn --config gunicorn.conf.py "app:create_app()"

API Usage

Authentication

All endpoints (except /health) require the X-API-Key header:

curl -H "X-API-Key: your_api_key_here" http://localhost:8000/balance/eth

Two-Phase Commit Flow

Both classic wallet and Polymarket on-chain operations use the same prepare → confirm pattern.

Step 1: Prepare - Validate and simulate the transaction

curl -X POST http://localhost:8000/transactions/eth/prepare \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f5b1E2",
    "amount_eth": "0.01"
  }'

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "eth_transfer",
  "status": "pending",
  "expires_at": "2024-01-01T12:05:00Z",
  "summary": {
    "action": "ETH Transfer",
    "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f5b1E2",
    "amount": "0.01 ETH"
  },
  "simulation": {
    "success": true,
    "gas_used": 21000
  }
}

Step 2: Confirm - Sign and broadcast

curl -X POST http://localhost:8000/operations/550e8400-e29b-41d4-a716-446655440000/confirm \
  -H "X-API-Key: YOUR_KEY"

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "confirmed",
  "result": {
    "tx_hash": "0x...",
    "status": "broadcast"
  }
}

Classic Wallet Endpoints

Method Endpoint Permission Description
GET /health None Health check
GET /balance/eth read ETH balance
GET /balance/token/{address} read ERC-20 balance
GET /balance/wallet read Wallet address
GET /gas read Current gas prices
POST /transactions/eth/prepare write Prepare ETH transfer
POST /transactions/token/prepare write Prepare token transfer
POST /transactions/vault-shares/prepare write Prepare vault share transfer
POST /defi/approval/prepare write Prepare token approval
POST /defi/swap/prepare write Prepare DEX swap
GET /defi/swap/quote read Get swap quote
POST /defi/vault/deposit/prepare write Prepare vault deposit
POST /defi/vault/withdraw/prepare write Prepare vault withdrawal
GET /defi/vault/{address}/info read Vault information
GET /defi/approvals/{token}/{spender} read Check allowance
GET /operations/pending read List pending operations
GET /operations/{id} read Get operation details
POST /operations/{id}/confirm write Execute operation
POST /operations/{id}/cancel write Cancel operation
GET /transactions/tx/{hash} read Transaction status

Polymarket Endpoints

All Polymarket endpoints require POLYGON_RPC_URL to be set. Without it, these routes return 404.

Setup

Method Endpoint Permission Description
GET /polymarket/setup/status read Credential + RPC + allowance status
POST /polymarket/setup/allowances/prepare write Prepare all 7 approval txs
POST /polymarket/setup/credentials/create write Derive CLOB API credentials

Market Discovery

Polymarket data is organized in a hierarchy: Tags → Events → Markets.

  • Tags categorize topics (e.g. "Crypto", "Sports", "Politics"). Each has a numeric tag_id used for filtering.
  • Events are questions or topics that group related markets (e.g. "Where will Barron Trump attend College?").
  • Markets are individual tradeable outcomes within an event (e.g. "Will Barron attend Georgetown?", "Will Barron attend NYU?"). Each market has a conditionId and YES/NO token IDs used for trading.

The typical discovery flow is: browse tags → filter events by tag → pick an event → pick a market → get token IDs → trade.

Method Endpoint Permission Description
GET /polymarket/tags read List all tags with IDs for filtering
GET /polymarket/events read List events with nested markets (?tag_id=, ?limit=, ?offset=, ?active=, ?closed=)
GET /polymarket/events/{slug_or_id} read Single event by slug or numeric ID
GET /polymarket/markets read List individual markets (?tag_id=, ?limit=, ?offset=, ?active=, ?closed=)
GET /polymarket/markets/{condition_id_or_slug} read Market details (accepts 0x... conditionId or slug)
GET /polymarket/markets/{condition_id}/book read Orderbook (?token_id= required)
GET /polymarket/markets/{condition_id}/price read Midpoint price as float (?token_id= required)

Trading

Method Endpoint Permission Description
POST /polymarket/orders/limit/prepare write Prepare limit order
POST /polymarket/orders/market/prepare write Prepare market order
POST /polymarket/orders/cancel/prepare write Prepare cancel order(s)
POST /polymarket/orders/cancel-all/prepare write Prepare cancel-all
POST /polymarket/positions/close/prepare write Close a position (auto-resolves size, token_id, best bid)

CTF Inventory

Method Endpoint Permission Description
POST /polymarket/ctf/split/prepare write Split USDC.e → YES + NO tokens
POST /polymarket/ctf/merge/prepare write Merge YES + NO → USDC.e
POST /polymarket/ctf/redeem/prepare write Redeem resolved market tokens

Portfolio & Balance

Method Endpoint Permission Description
GET /polymarket/portfolio read Positions with token IDs from Data API
GET /polymarket/orders/open read Open orders from CLOB
GET /polymarket/trades read Trade history from CLOB
GET /polymarket/balance read USDC.e balance on Polygon

Polymarket Trading Example

# 1. Check setup status
curl -H "X-API-Key: YOUR_KEY" http://localhost:8000/polymarket/setup/status

# 2. Derive CLOB credentials (one-time)
curl -X POST http://localhost:8000/polymarket/setup/credentials/create \
  -H "X-API-Key: YOUR_KEY"

# 3. Prepare all approval transactions (one-time)
curl -X POST http://localhost:8000/polymarket/setup/allowances/prepare \
  -H "X-API-Key: YOUR_KEY"
# → Returns 7 operation IDs; confirm each via /operations/{id}/confirm

# 4. Discover tags for filtering
curl -H "X-API-Key: YOUR_KEY" http://localhost:8000/polymarket/tags

# 5. Browse events by tag (grouped markets by topic)
curl -H "X-API-Key: YOUR_KEY" \
  "http://localhost:8000/polymarket/events?tag_id=1013&closed=false&limit=5"

# 6. Get a single event by slug
curl -H "X-API-Key: YOUR_KEY" \
  "http://localhost:8000/polymarket/events/some-event-slug"

# 7. Get price (returns a plain float)
curl -H "X-API-Key: YOUR_KEY" \
  "http://localhost:8000/polymarket/markets/CONDITION_ID/price?token_id=TOKEN_ID"

# 8. Place a limit order
curl -X POST http://localhost:8000/polymarket/orders/limit/prepare \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "condition_id": "0xabc...",
    "token_id": "12345...",
    "side": "BUY",
    "price": 0.55,
    "size": 100
  }'
# → Returns operation ID

# 9. Confirm the order
curl -X POST http://localhost:8000/operations/OPERATION_ID/confirm \
  -H "X-API-Key: YOUR_KEY"
# → Order posted to CLOB

# 10. Check portfolio (positions include token_id)
curl -H "X-API-Key: YOUR_KEY" http://localhost:8000/polymarket/portfolio

# 11. Close a position (auto-resolves size, token_id, and best bid)
curl -X POST http://localhost:8000/polymarket/positions/close/prepare \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"condition_id": "0xabc...", "outcome": "Yes"}'
# → Returns operation ID for a SELL limit order; confirm via /operations/{id}/confirm

Testing with a Real Wallet

Option 1: Sepolia Testnet (Recommended for Testing)

  1. Get Sepolia ETH from a faucet:

  2. Configure for Sepolia:

    RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
    CHAIN_ID=11155111

Option 2: Mainnet (Real Funds)

WARNING: Use a dedicated wallet with limited funds for testing.

  1. Create a new wallet (e.g., via MetaMask)
  2. Fund it with a small amount of ETH
  3. Export the private key
  4. Configure .env with mainnet settings

Test Commands

# Check health
curl http://localhost:8000/health

# Get wallet address
curl -H "X-API-Key: YOUR_KEY" http://localhost:8000/balance/wallet

# Check ETH balance
curl -H "X-API-Key: YOUR_KEY" http://localhost:8000/balance/eth

# Check gas prices
curl -H "X-API-Key: YOUR_KEY" http://localhost:8000/gas

# Prepare a small ETH transfer
curl -X POST http://localhost:8000/transactions/eth/prepare \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to_address": "0xYOUR_OTHER_ADDRESS",
    "amount_eth": "0.001"
  }'

# List pending operations
curl -H "X-API-Key: YOUR_KEY" http://localhost:8000/operations/pending

# Confirm an operation (replace with actual ID)
curl -X POST http://localhost:8000/operations/OPERATION_ID/confirm \
  -H "X-API-Key: YOUR_KEY"

# Or cancel it
curl -X POST http://localhost:8000/operations/OPERATION_ID/cancel \
  -H "X-API-Key: YOUR_KEY"

Project Structure

moltwallet/
├── app/
│   ├── __init__.py           # Flask app factory
│   ├── config.py             # Pydantic settings
│   ├── extensions.py         # Redis, rate limiter
│   ├── api/
│   │   ├── middleware/       # Auth, rate limiting, errors
│   │   ├── formatters/       # Response formatters (Polymarket)
│   │   ├── routes/           # API endpoints
│   │   │   ├── health.py         # Health check
│   │   │   ├── balance.py        # Balance queries
│   │   │   ├── gas.py            # Gas price endpoint
│   │   │   ├── transactions.py   # ETH/token transfer routes
│   │   │   ├── operations.py     # Operation confirm/cancel/list
│   │   │   ├── defi.py           # DeFi routes (swaps, vaults)
│   │   │   └── polymarket.py     # Polymarket routes (conditional)
│   │   └── schemas/          # Request/response models
│   ├── services/
│   │   ├── operation_service.py      # Two-phase commit + routing
│   │   ├── simulation_service.py     # eth_call simulation
│   │   ├── polymarket_service.py     # Polymarket trading logic
│   │   └── clob_auth.py             # CLOB credential management
│   ├── storage/              # Redis operations
│   ├── web3_ops/
│   │   ├── client.py         # Primary chain Web3 singleton
│   │   ├── polygon_client.py # Polygon Web3 singleton
│   │   ├── wallet.py         # Private key isolation
│   │   ├── eth_ops.py        # ETH transfer builder
│   │   ├── erc20_ops.py      # ERC-20 transfer/approval builders
│   │   ├── dex_ops.py        # Uniswap V3 swap builders
│   │   ├── erc4626_ops.py    # ERC-4626 vault operations
│   │   ├── erc1155_ops.py    # ERC-1155 operations (Polymarket)
│   │   ├── ctf_ops.py        # CTF split/merge/redeem (Polymarket)
│   │   └── contracts/abis/   # Contract ABIs and addresses
│   └── utils/                # Helpers
├── docs/
│   └── polymarket-learnings.md  # Polymarket bugs, API gotchas, patterns
├── scripts/
│   └── generate_api_key.py
├── tests/
│   ├── unit/
│   ├── integration/
│   └── e2e/
├── .env.example
├── pyproject.toml
├── requirements.txt          # Dependencies for Railway deployment
├── gunicorn.conf.py
├── Procfile                  # Process definition for Railway
└── nixpacks.toml             # Build config for Railway/Nixpacks

Deployment (Railway)

  1. Push to GitHub
  2. Connect repo to Railway
  3. Add Redis service
  4. Set environment variables
  5. Deploy

The project includes pre-configured deployment files:

  • nixpacks.toml - Build configuration
  • requirements.txt - Python dependencies
  • Procfile - Gunicorn process definition

Note for Polymarket: Polymarket blocks US IPs. Deploy to a non-US region if using Polymarket features.

Security Notes

  • Never commit .env - it contains your private key
  • Use a dedicated wallet - don't use your main wallet
  • Start with testnets - validate everything works first
  • Monitor operations - pending ops expire after 5 minutes
  • Rate limits apply - 100 reads/min, 20 writes/min per key

Running Tests

# Unit tests (no external deps)
pytest tests/unit/ -v

# With coverage
pytest tests/unit/ --cov=app --cov-report=html

License

MIT

About

Wallet for Mewtwo

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages