Skip to main content
The @darkpool/resolver package runs two jobs in one Express process:
  1. OO settlement endpoint. HTTP service for atomic Pyth + redeem PTBs (used by the frontend when a market is ready to settle).
  2. Auto-settle keeper. Polymarket-style daemon that scans predict::PositionMinted events, groups by (oracle, manager, strike, isUp), and submits per-position predict::redeem_permissionless PTBs once an oracle expires.
Port: :8082. Configurable via RESOLVER_PORT.

Startup log

If you see RESOLVER_KEY is not set, the keeper runs in unsigned mode and never settles. Fix by ensuring .env lives at repo root and the value is a single-line suiprivkey1… string (no quotes).

Atomic settlement PTB

Oracle to auto-settle to permissionless redeem flow. Step 1: Pull Hermes VAA via the Pyth Sui SDK. Step 2: pyth::update_price_feeds consumes the hot-potato. Step 3: OracleSVI auto-settles inside Predict. Step 4: market_key::new or range_key::new per redeemer. Step 5: predict::redeem_permissionless per redeemer, which credits the manager. Step 6: predict_facade::attribute_redeem emits an event.

One PTB pulls a Hermes VAA, pushes Pyth, settles OracleSVI, then redeems each opted-in position and emits attribution. Per-position scoping isolates aborts.

If ENOKI_PRIVATE_KEY is set, the PTB is sponsored, user-perceived gas is zero. Falls back to the resolver’s own keypair paying gas if Enoki throws.

Routes

Auto-settle keeper

File: packages/resolver/src/autoSettleTick.ts. Runs every 60s. Algorithm:
  1. Page recent predict::PositionMinted events (up to 1000).
  2. Aggregate by (oracle, manager, strike, isUp). Unique positions.
  3. For each oracle: check expiry_ms, active, and settlement_price.
  4. If expired and not yet settled: submit one predict::redeem_permissionless per PTB (one position per tx so a single abort can’t sink the batch).
  5. Memoise tried tuples. Successes never re-fire; failures back off 30 min.
  6. Always self-pays. Enoki allow-list rejects Predict’s Move targets.
Log excerpt:

Files

Gotcha: don’t run two resolvers

A tsx watch reload can leave the old setInterval alive while a new one starts. You’ll see duplicate-digest log lines and validator sequence-number conflicts. Fix: ⌃C every resolver shell, pkill -f "resolver/src/index", restart one.