Skip to content

feat(oracle): add hardened Binance and Polygon sources#368

Draft
ByteYue wants to merge 7 commits into
mainfrom
codex/sports-score-oracle-poc
Draft

feat(oracle): add hardened Binance and Polygon sources#368
ByteYue wants to merge 7 commits into
mainfrom
codex/sports-score-oracle-poc

Conversation

@ByteYue

@ByteYue ByteYue commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR narrows and hardens the relayer-backed Oracle runtime around two capabilities:

  • Binance USD-M index-price klines as sourceType=3.
  • Finalized Polygon Polymarket CTF settlements as sourceType=6.

Sports/news HTTP adapters and the Hype adapter are removed from the runtime surface. Contract policy remains in the resolver layer; validators only fetch, validate, canonicalize, and submit deterministic bytes through the existing unsupported-JWK consensus path.

Binance index-price feed

The binance_index_kline_v1 adapter:

  • is inherently continuous and no longer has a one-shot/fixed-bucket mode;
  • rejects the legacy continuous URI parameter instead of allowing two nonce semantics;
  • requests exactly one closed bucket using startTime and endTime;
  • validates response open/close timestamps and bounded body size;
  • maps delivery nonce n to bucketStartMs + (n - 1) * intervalMs;
  • derives business round IDs from bucket time while preserving sequential NativeOracle delivery nonces;
  • reconciles the configured sequence against confirmed on-chain progress;
  • requires a new feedId when the bucket origin or interval changes;
  • keeps provider endpoints validator-local and rejects endpoint injection through task URIs.

The public market-data path does not require Binance API keys.

Why fixed mode was removed

The Oracle product implemented here is a long-running price feed. No governance task, E2E suite, or runtime caller required a one-shot Binance bucket. Keeping both modes allowed the same feedId to be reconfigured with a different interval while deriving roundId = bucketStartMs / intervalMs, which could make business round IDs move backwards even though delivery nonces continued increasing.

The follow-up commit d8cb206f6 (refactor(oracle): make Binance feeds continuous-only) removes that unreachable product branch and leaves one deterministic state machine. This addresses Galxe/gravity-audit#913.

This is an intentional pre-merge URI compatibility change: remove continuous=true or continuous=false from every Binance task URI before deploying this revision. The coordinated SDK E2E update is in Galxe/gravity-sdk#758.

Polygon / Polymarket settlement

The Polygon adapter:

  • requires chain ID 137 and an exact CTF/condition identity;
  • scans finalized logs with an exclusive block/log cursor;
  • bounds scan range, slot count, and payload size;
  • rejects malformed matching logs and multiple conflicting settlements;
  • resumes deterministically from confirmed NativeOracle state.

The cursor identity is (blockNumber, logIndex); delivery nonce remains a sequential transport nonce.

Consensus and execution safety

  • Unsupported-JWK payloads require canonical ABI decoding and byte-for-byte re-encoding.
  • Oracle callback gas is explicitly bounded.
  • Disabled RSA handling returns an error instead of panicking.
  • Logs redact configured endpoints and local paths.
  • Startup reconciles persisted relayer state with confirmed on-chain progress.

Validation

cargo test -p reth-pipe-exec-layer-relayer
cargo test -p reth-pipe-exec-layer-ext-v2 --lib

Results after the continuous-only change:

  • relayer: 40 passed, 3 ignored, 0 failed;
  • execution layer: 68 passed, 0 failed;
  • git diff --check: passed.

The ignored tests require explicitly configured external endpoints and are not part of the normal test gate.

Review boundaries

This is a production-candidate transport implementation, not a claim that every Polymarket UI market can be mirrored automatically. Production rollout still needs provider/RPC operations, monitoring and alerting, validator endpoint configuration, governance review, and product decisions around disputes and market voiding.

Cross-repo JWK API alignment

Commit 20af4ae4a2 pins gravity-api-types to gravity-aptos 10c4553b16, the revision containing the duplicate-observation aggregation fix. This is required so the reth relayer and SDK JWK consensus compile against one api-types identity; mixing the previous reth pin with the latest Aptos pin produces distinct Rust types with the same names at the relayer/consensus boundary.

Validation:

RUSTFLAGS="--cfg tokio_unstable" \
  cargo check -p reth-pipe-exec-layer-relayer --locked

The check passed. The coordinated SDK integration is Galxe/gravity-sdk#758, where the four-validator live Binance E2E passed and demonstrated a 3-of-4 voting-power QC followed by identical on-chain resolver state across all four RPCs.

@ByteYue ByteYue changed the title [codex] Mirror Polymarket settlements through oracle consensus [codex] Add relayer-backed oracle sources and Hype price feed Jun 30, 2026
@ByteYue ByteYue changed the title [codex] Add relayer-backed oracle sources and Hype price feed [codex] Harden Binance and Polygon oracle sources Jul 12, 2026
@ByteYue
ByteYue force-pushed the codex/sports-score-oracle-poc branch from 753a6d1 to 2b46a42 Compare July 14, 2026 08:01
@ByteYue ByteYue changed the title [codex] Harden Binance and Polygon oracle sources feat(oracle): add hardened Binance and Polygon sources Jul 14, 2026
@ByteYue

ByteYue commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

TODO / Request changes: keep the price feed Binance-only

The current price-feed implementation is over-generalized for the agreed MVP scope. Gravity only needs one provider here: Binance USD-M indexPriceKlines, using one finalized closed-kline bucket per round.

Before this PR is merged, please simplify the production path:

  • Support only provider=binance_index_kline_v1 for sourceType=3.
  • Remove production configuration for weight, minSourceCount, minTotalWeight, and aggregationMode; these multi-source aggregation knobs are not part of the current product requirement.
  • Do not construct a configurable multi-observation payload. One bucket should produce exactly one canonical Binance index-price observation.
  • Keep the deterministic checks that matter: exact pair/interval/bucket coordinates, aligned bucketStartMs, exact openTime/closeTime, closed-bucket readiness, response-size bounds, decimal scaling, and sequential delivery nonce.
  • If inline_fixture_v1 remains useful for unit tests, keep it test-only rather than as a production oracle provider.
  • Update the Rust tests, runbook, and E2E configuration to describe a single Binance source only.

Issue gravity-audit#912 is a symptom of the unnecessary generic threshold surface: reth accepts minTotalWeight=0 while the resolver rejects it. The preferred fix is to remove that configuration surface from this Binance-only MVP, not merely add another threshold check.

A future multi-provider oracle, if required, should be designed and reviewed separately with an explicit canonical aggregation specification.

@ByteYue ByteYue left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCKING / REQUEST CHANGES (GitHub does not allow the PR author to set the formal Request Changes state on their own PR.)

roundId must be independent of intervalMs. The execution-layer resolver orders latestPrice by this field, so bucketStartMs / intervalMs can regress after a fixed-task interval change and reject otherwise later deliveries. Please use roundId = bucketStartMs in both fixed and continuous derivation paths; keep deliveryNonce as transport order and resolvedAt = bucketEndMs. Update the canonical payload docs/tests and the gravity-sdk E2E round_id() helper/fixtures in the same coordinated change. This alters consensus payload bytes, so deployment also needs a coordinated validator activation boundary rather than a mixed-version rolling transition.

.checked_add(self.interval_ms)
.and_then(|value| value.checked_sub(1))
.ok_or_else(|| anyhow!("Binance continuous bucket end overflow"))?;
let round_id = bucket_start_ms / self.interval_ms;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[BLOCKING] This should be let round_id = bucket_start_ms;. Even though continuous startup currently rejects interval/origin history mismatches, retaining an interval-relative round here gives the same payload field different coordinate systems under different configurations and makes fixed/continuous semantics inconsistent. Keep the two modes canonicalized identically.

}
}
let grace_ms = parse_optional(task, "graceMs")?.unwrap_or(DEFAULT_BINANCE_GRACE_MS);
let round_id = bucket_start_ms / interval_ms;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[BLOCKING] This is the regression in gravity-audit#913: a later bucket after 1m -> 1h reconfiguration can produce a smaller round and be rejected by PriceFeedResolver as stale. Use bucket_start_ms directly. If two intervals need the same open time, they should use distinct feedIds (or a future explicit config epoch), not overload an interval-relative round number.

ByteYue added 4 commits July 22, 2026 14:18
Remove the unused one-shot Binance bucket mode and define binance_index_kline_v1 as a continuous price feed. Reject the legacy continuous query parameter, preserve a single nonce-to-bucket mapping, and require a new feedId when the bucket origin or interval changes.
Pin gravity-api-types to the Aptos revision that fixes duplicate JWK observation aggregation. This keeps the relayer ABI aligned with the SDK consensus crates for the four-validator live Binance E2E.
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