Skip to content

Replace CoinGecko outcome inference with on-chain oracle resolution#63

Merged
heyitsStylez merged 1 commit into
mainfrom
authoritative-outcome-resolution
May 16, 2026
Merged

Replace CoinGecko outcome inference with on-chain oracle resolution#63
heyitsStylez merged 1 commit into
mainfrom
authoritative-outcome-resolution

Conversation

@heyitsStylez

Copy link
Copy Markdown
Owner

Problem

Two bugs prevented expired PUT/CALL positions from resolving to ASSIGNED or CALLED:

Bug 1 — HSFC correction loop missing. When a Hypersurface trade was synced as OPEN and later expired, subsequent syncs skipped it entirely (already in the synced set). autoDetectOutcomes was never called again, so the trade stayed OPEN indefinitely.

Bug 2 — CoinGecko as settlement oracle. autoDetectOutcomes fetched the CoinGecko historical daily-close price for the expiry date and compared it to the strike. Three failure modes:

  • Price mismatch: Rysk settles at 8:00 UTC using a Chainlink oracle; CoinGecko returns a daily close — volatile underlyings differ enough to misclassify ITM vs OTM
  • Rate-limiting: CoinGecko free tier drops requests under load; failed fetches permanently left trades as EXPIRED with no retry
  • No retry path: autoDetectOutcomes was not wired into any periodic retry; a single failure was final

Solution

Replace CoinGecko inference with authoritative on-chain data, separately for each platform.

Rysk — expiry-prices endpoint (resolveRyskOutcomes)

GET /api/expiry-prices/999/{underlyingAddress} returns { expiryTimestamp: rawPriceAt1e8 } — the exact Chainlink oracle price the Rysk protocol used at settlement. One call per unique underlying token covers all strikes and expiries for that asset.

Resolution:

  • PUT: settlementPrice ≤ strikePrice → ASSIGNED, else EXPIRED
  • CALL: settlementPrice ≥ strikePrice → CALLED, else EXPIRED
  • Matched by txHash (authoritative). Runs every sync — self-correcting over prior CoinGecko misclassifications.

Hypersurface — positions + redeemActions (resolveHsfcOutcomes)

A second Goldsky GQL query fetches short positions (amount_lt:"0") and checks redeemActions. Non-empty = option buyer exercised = ASSIGNED (PUT) or CALLED (CALL). Empty = expired worthless. No price comparison needed.

Matched by platform=HSFC + asset + expiry + strike (within $0.01) + type. Only updates OPEN/EXPIRED outcomes — CLOSED trades are never touched.

autoDetectOutcomes (CoinGecko) — retained for stale-detection only

Still called from autoLoadChain's boot-time stale-detection loop for locally-known OPEN trades whose expiry passed while the app was closed. For Rysk and HSFC, the oracle resolvers will correct anything CoinGecko guessed once the sync runs. For platform='SPOT' (manual entries), CoinGecko remains the only path.

Changes

File What changed
src/js/18-chain-sync.js Added fetchRyskExpiryPrices, resolveRyskOutcomes, resolveHsfcOutcomes. Refactored fetchHsfcGoldsky(url, address)fetchHsfcGoldsky(url, gqlBody) to share transport between trades and positions GQL calls. Added HSFC OPEN→EXPIRED correction loop (mirroring Rysk's existing one). Removed autoDetectOutcomes calls from syncRysk and syncHypersurface.
api/chain-sync.js Added type=expiry-prices handler for Rysk source, with Ethereum address validation (/^0x[0-9a-fA-F]{40}$/).
test/integration/chain-sync-outcomes.test.js 14 new integration tests covering all four PUT/CALL × OTM/ITM combinations for both platforms, plus CLOSED-not-touched, future-expiry ignored, unknown asset skipped, non-SETTLED skipped, and fetch-failure-no-crash cases.
docs/adr/0005-authoritative-outcome-resolution.md New ADR documenting the decision, alternatives considered, and consequences.
docs/architecture.md Updated ChainAPI edges and notes to reflect the two-call-per-platform pattern and clarify CoinGecko is spot prices only.
CLAUDE.md Updated 18-chain-sync.js file map entry; rewrote chain sync section.

Test plan

  • npm test passes (99/99, +14 new tests)
  • python3 build.py --check passes
  • Husky pre-commit hook passed on commit
  • Live sync against wallet 0xe94a312B9e8B4B5117aEB485dd749c3547aC06C2 — verify known cases after deploy:
    • WHYPE PUT strike=30.5, expiry=1765526400 → ASSIGNED
    • USOL PUT strike=140, expiry=1764316800 → ASSIGNED
    • WHYPE CALL strike=41.5, expiry=1778227200 → CALLED
    • UETH CALL strike=2300, expiry=1776412800 → CALLED
    • kHYPE CALL strike=38, expiry=1775808000 → CALLED

🤖 Generated with Claude Code

Rysk outcomes now resolved via /api/expiry-prices (Chainlink settlement
price); Hypersurface outcomes via redeemActions on the Goldsky positions
subgraph. Fixes two bugs: HSFC trades stuck as OPEN after expiry, and
CoinGecko daily-close price misclassifying ITM vs OTM options.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented May 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperwheel Ready Ready Preview, Comment May 16, 2026 2:32am

@heyitsStylez heyitsStylez merged commit 5363b18 into main May 16, 2026
4 checks passed
@heyitsStylez heyitsStylez deleted the authoritative-outcome-resolution branch May 16, 2026 02:32
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.

1 participant