Skip to content

feat: add 'watchAddress' and 'unwatchAddress' for a real-time address watching system#55

Merged
realfishsam merged 24 commits intopmxt-dev:mainfrom
psyberck:feat/address-watch
Mar 13, 2026
Merged

feat: add 'watchAddress' and 'unwatchAddress' for a real-time address watching system#55
realfishsam merged 24 commits intopmxt-dev:mainfrom
psyberck:feat/address-watch

Conversation

@psyberck
Copy link
Contributor

@psyberck psyberck commented Mar 6, 2026

What this PR adds (Close Issue #50 )

A real-time address watching system for prediction market wallets - the equivalent of CCXT Pro's watchOrderBook but for user's positions, trades, and balances.

New abstractions (core/src/subscriber)

subscriber/base.ts - Core interfaces:

  • SubscriptionOption — 'trades' | 'positions' | 'balances'
  • SubscribedAddressSnapshot — unified snapshot of trades, positions, balances for an address
  • SubscribedActivityBuilder — converts raw event data into a partial snapshot; allows exchange-specific logic to decode on-chain events directly without a REST roundtrip
  • BaseSubscriber — interface for pluggable polling/push backends

subscriber/watcher.ts - AddressWatcher orchestrates the CCXT Pro pattern:

  • First watch() call returns the initial snapshot immediately; subsequent calls block until something changes (same pattern as watchOrderBook)
  • Hybrid fetch: activity builder runs first on event data; only missing types fall back to REST/RPC
  • getChanged() computes incremental diffs — only new trade IDs, positions whose size changed, balances whose total changed are dispatched; empty diffs are suppressed
  • unwatch() / close() for lifecycle management

subscriber/external/goldsky.ts — Goldsky subgraph subscriber:

  • GoldSkySubscriber — polls one or more Goldsky GraphQL endpoints on a configurable interval, with per-address AbortController to cancel stale in-flight requests
  • POLYMARKET_DEFAULT_SUBSCRIPTION — orchestrates three parallel + one sequential subgraph query:
  • Trades: two indexed queries (maker + taker) merged by ID to avoid the or filter statement timeout
  • Positions: PNL subgraph (userPositions) → extract tokenIds → tokenIdConditions for market metadata (avoids the unindexed user + asset_in join that causes timeouts on
    userBalances)
  • All queries use orderBy: id to avoid timeouts on unindexed sort columns
  • LIMITLESS_DEFAULT_SUBSCRIPTION — two parallel from/to queries for USDC transfers, same or-split pattern
  • buildPolymarketActivity, buildPolymarketPositionsActivity, buildLimitlessBalanceActivity — activity builders that decode subgraph payloads into typed Trade[], Position[],
    Balance[]

Exchange changes

Polymarket (watchAddress + unwatchAddress)

  • trades — Goldsky CTF Exchange OrderFilled events
  • positions — Goldsky PNL subgraph and tokenIdConditions
  • balances — on-chain Polygon RPC, USDC.e at 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
  • fetchPositions(address?) and fetchBalance(address?) now accept an optional address and skip ensureAuth() when offered

Key design

  • BaseSubscriber offers basic interfaces to be plugable into AddressWatcher so users can easily replace GoldSkySubscriber with others.
  • Indexer is always faster than exchange's restful APIs, so here goldsky is integrated. But goldsky is HTTP-only GraphQL; no Websocket subscription protocol available. (The graph supports websocket subscription with more complicated customized setup)
  • Incremental diff dispatch: Callers receive only what changed (new trades, moved positions, shifted balances) and no need to diff on the caller side

Testing (core/examples/social)

  • pytest unit tests passed
  • Contains track_whale.ts and track_whale.py two testing examples
  • Sometimes goldsky subgraph graphql might timeout, users can replace the default graphql endpoint with self-deployed goldsky project endpoint
  • On-chain rpc for balance calls failed locally, code remains untouched in the production
Screenshot from 2026-03-06 10-39-54

zihao.xue and others added 24 commits March 1, 2026 21:25
- Fix SubscribedAddressSnapshot type bugs (positions/balances typed as list[Trade])
- Rewrite track_whale scripts to use actual SDKs instead of raw generated client
- Fix Python SDK watch_address/unwatch_address serialization (bypass broken generated model)
- Fix Python SDK _convert_subscription_snapshot to convert nested objects
- Fix TS example runtime errors (rank type cast, missing paren, null safety)
- Fix truncated apiKey JSDoc and mislabeled @param in watcher
@realfishsam realfishsam merged commit 46e311c into pmxt-dev:main Mar 13, 2026
@psyberck
Copy link
Contributor Author

@realfishsam Thanks for your fixes and merging this PR. I will continue watching issues to look for more I can contribute :)

@realfishsam
Copy link
Contributor

@realfishsam Thanks for your fixes and merging this PR. I will continue watching issues to look for more I can contribute :)

Thanks. I'm super grateful. Please write me on discord so I can give you the contributor role!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants