SQL changes for trades, fees, resolutions, redemptions, migrations (insert) and user_pnl, user_positions, markets (upsert).
Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs

Package ref
polymarket-pnl@v2.0.1Run package
CLI
Run db_out from the command line.
substreams run polymarket-pnl@v2.0.1 db_out -e polygonsubstreams auth or directly on thegraph.market (see docs).README
Polymarket P&L
Real-time Profit & Loss tracking for Polymarket prediction markets on Polygon
Per-wallet positions, average-cost basis, realized and unrealized P&L, volume, fees, win rate, resolutions, redemptions and Protocol V2 migrations for Polymarket on Polygon. It covers CLOB v1, CLOB v2 and Protocol V2 (ExchangeV3 + PositionManager, pUSD collateral).
| Area | v1.x | v2.0.1 |
|---|---|---|
| Fills | map_order_fills produced no fills: its blockFilter joined terms with OR (the index query language uses ||) and used checksummed addresses (index keys are lowercase) | map_events uses a lowercase, ||-joined filter. All five exchanges, including ExchangeV3 0xe3333700… |
| Positions | TransferSingle from any ERC-1155 contract | TransferSingle and TransferBatch, only from Conditional Tokens 0x4D97… and PositionManager 0x006F54F7… |
| Collateral | USDC.e | USDC.e and pUSD (map_collateral_transfers) |
| Cost basis | none (avg price = last fill) | Average buy cost per (wallet, token), including splits. Realized P&L on sells, merges and redemptions |
| Stats | volume / win rate / first trade not tracked | volume, fees, trades, win/loss counts, win_rate, largest win/loss, first/last trade |
| New events | – | FeeCharged, CTF ConditionResolution, V2 ConditionResolved, UMA v3.1/v2 QuestionResolved, CTF PayoutRedemption, V2 PositionRedeemed, AutoRedeemer redemptions, V2 PositionMigrated (carries cost basis to the new position) |
| Ordering | HashSet (non-deterministic) | BTreeSet/BTreeMap (deterministic) |
| Excluded addresses | 2 | All Polymarket protocol contracts (exchanges, ledgers, modules, adapters, router, AutoRedeemer, UMA v3.1) |
pnl.v2. Module names changed: map_order_fills, map_token_transfers, map_usdc_transfers and the seven old stores become map_events, map_collateral_transfers, store_position_totals, map_migration_carry, store_migration_carry, map_realized, store_pnl_totals, store_pnl_max, store_pnl_min and store_latest_prices. map_user_pnl, map_market_stats and db_out keep their names, with new outputs.substreams-sink-sql setup again. Amounts are now raw 6-decimal base units (divide by 1e6). trades.amount is replaced by shares / collateral / fee.fees, resolutions, redemptions, migrations. New views: v_user_positions_live, v_user_pnl_live (unrealized P&L at the latest traded price).substreams run polymarket-pnl@v2.0.1 map_events -s -1000 -t 0 -e polygon.streamingfast.io:443
substreams run polymarket-pnl@v2.0.1 map_user_pnl -s -1000 -t 0 -e polygon.streamingfast.io:443
# Postgres (needed for full-history P&L)
substreams-sink-sql setup "psql://user:pass@localhost:5432/pnl?sslmode=disable" polymarket-pnl@v2.0.1
substreams-sink-sql run "psql://user:pass@localhost:5432/pnl?sslmode=disable" polymarket-pnl@v2.0.1
-- leaderboard (USD)
SELECT user_address, realized_pnl/1e6 AS realized, unrealized_pnl/1e6 AS unrealized, total_pnl/1e6 AS total,
total_volume/1e6 AS volume, win_rate
FROM v_user_pnl_live ORDER BY total_pnl DESC LIMIT 50;
sf.ethereum.type.v2.Block
├─ map_events ─────────────┬─ store_position_totals ─ map_migration_carry ─ store_migration_carry
│ ├─ map_realized ─┬─ store_pnl_totals / store_pnl_max / store_pnl_min
│ ├─ store_latest_prices
│ ├─ map_user_pnl ─┐
│ ├─ map_market_stats ─┤
│ └────────────────────┴─ db_out
└─ map_collateral_transfers
All modules start at block 33,605,403. The block filters use ethcommon:index_events.
OrderFilled belongs to its maker (the order owner). The taker order's own fill has the exchange as taker, so every participant is counted exactly once. From Polymarket ctf-exchange / ctf-exchange-v2 Trading.sol:
taking − fee (shares on a BUY, collateral on a SELL).makerAmountFilled + fee; a SELL receives takerAmountFilled − fee.win_rate = wins ÷ (wins + losses).PayoutRedemption reports one payout per condition. It is split across the burned tokens pro rata to amount. V2 PositionRedeemed is exact per position.0xf3cfb6a6…) appear as their own wallet.QuestionResolved is only read from the documented v3.1 / v2 adapters; CTF ConditionResolution is the canonical resolution.From https://docs.polymarket.com/resources/contracts. The v1 exchanges come from github.com/Polymarket/ctf-exchange. All of them are in src/addresses.rs.
| Contract | Address |
|---|---|
| CTF Exchange v1 / Neg Risk Exchange v1 | 0x4bfb41d5b3570defd03c39a9a4d8de6bd8b8982e / 0xC5d563A36AE78145C45a50134d48A1215220f80a |
| CTF Exchange v2 / Neg Risk Exchange v2 | 0xE111180000d2663C0091e4f400237545B87B996B / 0xe2222d279d744050d28e00520010520000310F59 |
| ExchangeV3 (Protocol V2) | 0xe3333700cA9d93003F00f0F71f8515005F6c00Aa |
| Conditional Tokens / PositionManager | 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 / 0x006F54F7F9A22e0000CC2Ab60031000000aE9FEF |
| pUSD / USDC.e | 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB / 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 |
| BinaryModule / NegRiskModule / CombinatorialModule | 0x1000008dD9001B968442c1000017eaE6E0dA00Ba / 0x200000900045e3B6259600682756002200028933 / 0x30000034706C7d8e12009DAB006Be20000c031A8 |
| AutoRedeemer / Router | 0xa1200000d0002264C9a1698e001292D00E1b00af / 0x12121212006e4CD160D18e3f00711DA5c3372600 |
| UmaCtfAdapter v3.1 / v2 | 0x157Ce2d672854c848c9b79C49a8Cc6cc89176a49 / 0x6A9D222616C90FcA5754cd1333cFD9b7fb6a4F74 |
substreams build # CLI >= 1.25; src/pb is generated (buffa)
cargo test --lib # unit tests: fill/fee semantics, TransferBatch, cost basis, formatting
MIT
Modules
SQL changes for trades, fees, resolutions, redemptions, migrations (insert) and user_pnl, user_positions, markets (upsert).
Output
sf.substreams.sink.database.v1.DatabaseChanges
Inputs
ERC-20 Transfer events of pUSD (current collateral) and USDC.e (legacy collateral).
Output
pnl.v2.EventsOne pass over each block. Fills (all five exchanges, attributed to the order owner), ERC-1155 TransferSingle + TransferBatch from Conditional Tokens and PositionManager only, FeeCharged, resolutions, redemptions, migrations, and the derived per-user cost-basis ledger.
Output
pnl.v2.EventsOutput
pnl.v2.MarketStatsCost basis moved from a CTF token to its PositionManager position on PositionMigrated.
Output
pnl.v2.RealizedEventsRealized P&L (proceeds - shares x average buy cost) for every sell, merge and redemption.
Output
pnl.v2.RealizedEventsPer-user P&L and per-position cost basis / unrealized P&L for every user and position touched in the block (BTreeSet order).
Output
pnl.v2.UserPnLUpdatesStore value
string
Update policy
set
Inputs
Store value
bigint
Update policy
add
Inputs
Store value
bigint
Update policy
max
Inputs
Store value
bigint
Update policy
min
Inputs
u:{user}:realized|volume|fees|trades|wins|losses, p:{user}:{token}:realized, m:{token}:volume|trades
Store value
bigint
Update policy
add
Inputs
p:{user}:{token}:bq|bc|q = cumulative bought shares, bought cost, held quantity.
Store value
bigint
Update policy
add
Inputs
Output
sf.substreams.ethereum.v1.Events
`index_events` sets the following keys on the block: * Event signatures evt_sig:0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef (signature in hex, prefixed by 0x) * Event address evt_addr:0x0123456789abcdef0123456789abcdef01234567 (address in hex, prefixed by 0x)
Output
sf.substreams.index.v1.Keys
Inputs