RedemptionWatcher monitors wallets for redeemable positions. It combines wallet position data from REST with real-time condition_resolution events from the oracle stream. When a market resolves, any tracked wallet holding that condition gets an instant alert with full payout details.
One class, one WebSocket connection, zero polling.
Install
Quick Start
How It Works
start(wallets)fetches positions for each wallet via REST (parallel), builds an internal index keyed bycondition_id, then subscribes to the oracle WebSocket stream- When a
condition_resolutionevent arrives, the watcher cross-referencesevent.condition_idagainst the position index - For each matched position, a
RedeemableAlertis emitted with wallet address, win/loss status, payout estimate, and full market metadata - After alerts fire, the resolved condition is evicted from memory. Positions that drop to zero size (full sell) are also evicted. This keeps memory bounded to only active, non-zero positions regardless of how long the watcher runs.
- If
trackPositionChangesis enabled (default), position sizes stay accurate in real-time via the wallets WebSocket stream. If a wallet enters a new market afterstart(), the watcher automatically picks it up from the enriched transfer event and adds it to the index. - A periodic REST refresh (default: every 5 minutes) re-fetches all wallet positions as a safety net, catching any positions the stream may have missed during brief disconnections.
condition_resolution is the moment positions become redeemable on the Conditional Tokens contract. For neg-risk markets (the majority on Polymarket), this fires in a separate transaction after the UMA resolution event. See Oracle Events for the full resolution lifecycle.Lifecycle
1. Construct
2. Register Handlers
Register handlers before callingstart() so you don’t miss the ready event.
3. Start
start() fetches positions for all wallets in parallel, indexes them by condition_id, subscribes to the oracle stream, and emits ready. Typical startup takes 100-300ms depending on wallet count.
Real output from start() with one wallet:
4. Add/Remove Wallets at Runtime
5. Query State
6. Close
Async Iterator
Consume alerts sequentially withfor await:
watcher.close() is called.
Alert Object
When acondition_resolution event matches a tracked position, the watcher emits a RedeemableAlert:
Tracked Positions
Each position loaded from the REST API is stored as aTrackedPosition:
positionsFor():
The Event That Triggers Alerts
The watcher listens forcondition_resolution oracle events. Here’s a real one from a Dota 2 esports market:
tokenId in event.token_ids, checks the corresponding index in event.payouts. If payouts[index] > 0, the position is a winner and pays out $1 × size.
Configuration
trackPositionChanges
When enabled (default), the watcher subscribes to the wallets WebSocket stream. This does two things:
- Size tracking — updates position sizes in real-time as ERC-1155 transfers occur. If a user buys or sells tokens after
start(), thesizefield stays accurate for payout calculations. - New market discovery — when a wallet enters a market the watcher hasn’t seen before, the enriched transfer event carries full market metadata (
condition_id,market_title,outcomes,token_ids). The watcher creates a new tracked position automatically. No gaps.
refreshInterval
Periodic REST re-fetch of all wallet positions. Acts as a safety net in case a transfer event is missed due to a brief WebSocket disconnection, ensuring the watcher eventually picks up any positions the stream didn’t catch.
Default: 300_000 (5 minutes). Set to 0 to disable, or lower (e.g. 60_000) if you need faster recovery.
Full Example: Notification Service
Memory Management
The watcher is designed to run indefinitely with bounded memory, even when tracking thousands of wallets.- Resolved conditions are evicted after alerts fire. A condition only resolves once, so there’s nothing left to watch.
- Zero-size positions are evicted when a wallet fully sells out of a market. If they buy back in, the position is re-created from the next
position_changeevent or the periodic REST refresh. - The periodic REST refresh (default: every 5 minutes) acts as a safety net. Even if a stream event is missed, the refresh catches it within one cycle.
API Reference
Constructor
Methods
| Method | Returns | Description |
|---|---|---|
start(wallets) | Promise<void> | Fetch positions, subscribe to streams, begin watching |
addWallets(wallets) | Promise<void> | Add wallets at runtime, fetch their positions |
removeWallets(wallets) | void | Remove wallets, clean up state |
positionsFor(wallet) | TrackedPosition[] | Get tracked positions for one wallet |
close() | void | Unsubscribe, disconnect, clean up |
Properties
| Property | Type | Description |
|---|---|---|
wallets | string[] | All tracked wallet addresses |
conditions | string[] | All tracked condition IDs |
size | number | Total tracked position count |
Events
| Event | Payload | Description |
|---|---|---|
alert | RedeemableAlert | A tracked position’s condition resolved |
ready | (none) | Positions loaded and streams connected |
error | Error | REST fetch failed or stream error |

