This guide walks you through building a Polymarket copy-trading bot. A Turbo pipeline decodes on-chainDocumentation Index
Fetch the complete documentation index at: https://docs.goldsky.com/llms.txt
Use this file to discover all available pages before exploring further.
OrderFilled events, filters them to a configurable set of wallets, and webhooks each fill into a Compose app that signs and submits the same trade to the Polymarket CLOB. Winning shares auto-redeem on a 5-minute cron.
It demonstrates the Turbo → Compose webhook pattern, cron triggers, sponsored-gas wallets, ctx.fetch for external APIs, and collection-based state.
The example targets Polymarket V2 (cutover 2026-04-28). V2 introduced new Exchange contracts on Polygon, switched collateral from USDC.e to pUSD (a 1:1 ERC-20 backed by USDC.e), and ships a new SDK at
@polymarket/clob-client-v2. Funding still happens in USDC.e — the setup_approvals task wraps it into pUSD via the Collateral Onramp.How it works
- Polygon emits
OrderFilledfrom the V2 CTF Exchange (0xE111…996B) and V2 NegRisk Exchange (0xe222…0F59) contracts - Turbo pipeline decodes fills, keeps only trades where maker or taker is in your watched list, and posts each to a Compose HTTP task
copy_tradeparses the fill, checks the wallet’s pUSD balance, looks up market metadata, signs a V2 FAK (Fill-and-Kill) order, and submits it via the Fly.io proxy (Polymarket’s CLOB is geo-blocked from US hosts)redeemruns every 5 minutes, polls Polymarket’s data API for redeemable positions, and callsredeemPositionson-chain
Prerequisites
- Goldsky CLI installed
- A Compose API token from your Goldsky project (for the webhook auth secret)
- An EOA private key for the bot’s wallet — no Polymarket UI onboarding or proxy wallet required
- USDC.e (
0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) on Polygon to fund the bot — thesetup_approvalstask wraps it into pUSD before trading - A list of wallets you want to mirror (selecting them is out of scope for this guide)
Project structure
Step 1: Set up the project
Clone the example repository and install dependencies:node_modules/, so npm install is required before goldsky compose start or deploy.
Step 2: Pick wallets to copy and update both configs
The pipeline pre-filters on-chain events to the watched list, so the same addresses must appear in two places. Incompose.yaml:
pipeline/polymarket-ctf-events.yaml, update the watched_fills transform:
Step 3: Set the wallet private key
The Compose app signs CLOB orders as an EOA. No Polymarket proxy wallet is involved:Step 4: Create the webhook auth secret
The Turbo webhook authenticates to your Compose app with a Goldsky-level secret. This is a one-time setup per project:Step 5: Deploy the app and pipeline
Step 6: Fund the wallet
Send USDC.e to the EOA that corresponds to the private key from step 3. Compose sponsors gas on all on-chain calls, so you do not need MATIC in the wallet.Step 7: Grant approvals and wrap collateral
V2 markets settle in pUSD, so the bot needs to (a) approve the Collateral Onramp to spend USDC.e, (b) wrap the wallet’s USDC.e balance into pUSD, and (c) approve pUSD + ConditionalTokens to both V2 Exchanges. Thesetup_approvals task does all of this in one call. Compose sponsors every transaction:
Understanding the code
Parsing a fill
V2’sOrderFilled event encodes the maker’s side as a uint8 (0 = BUY, 1 = SELL) and exposes a single tokenId instead of the V1 maker/taker asset-id pair. The whale we want to copy can be the maker or the taker — the taker takes the opposite side:
On-chain balance as source of truth
Before every BUY, the task reads pUSD balance directly from Polygon rather than keeping a local counter. pUSD has 6 decimals (matching USDC.e):Signing and submitting via ctx.fetch
Polymarket’s @polymarket/clob-client-v2 SDK uses axios for HTTP, which fails under Compose’s task runtime because task binaries run without --allow-net. The template reuses the SDK’s pure signing utilities (local crypto only) and routes every HTTP call through ctx.fetch. The version: 2 argument selects the V2 Exchange domain and order struct (V2 fees are computed on-chain, so they are no longer signed):
ctx.fetch is host-mediated, which means it has network access even though the task itself does not. This is the general pattern for calling external APIs from a Compose task — see calling external APIs for more.
Why the Fly.io proxy
Polymarket’s CLOB API geo-blocks the US. Compose tasks run fromus-west. The template points CLOB_HOST at a shared Goldsky-hosted Fly.io proxy in Amsterdam that forwards every request from an EU IP. If you want to isolate yourself from the shared proxy, deploy your own copy of fly-polymarket-proxy and update CLOB_HOST in compose.yaml.
Key Compose features used
- Turbo → Compose webhook pattern — a pipeline sinks decoded on-chain events directly to an HTTP task via a
COMPOSE_WEBHOOK_AUTHsecret ctx.fetch— all outbound HTTP (CLOB, Gamma, Polymarket data API, Polygon RPC) goes through Compose’s host-mediated fetchctx.evm.walletwithsponsorGas: true— approvals and redemptions use a Compose-sponsored wallet so the EOA does not need MATIC- Cron triggers — the
redeemtask runs every 5 minutes via0 */5 * * * * ctx.collection—positionsandtradescollections persist bot state across invocations- Compose secrets — the wallet private key is stored via
goldsky compose secret set, not baked into the manifest
Customization
Trade size
The default is Polymarket’s $1 minimum notional. Raise it incompose.yaml:
Different markets
The pipeline filters by address only, so it picks up every fill on the CTF Exchange and NegRisk Exchange. To scope to a specific market type (e.g. a particular event’s outcomes), add atoken_id filter to the watched_fills transform.
Your own proxy
CLOB_HOST defaults to the Goldsky-hosted Fly proxy. To isolate from it, deploy fly-polymarket-proxy yourself and point CLOB_HOST at your deployment.