feat(oracle): add hardened Binance and Polygon sources#368
Conversation
753a6d1 to
2b46a42
Compare
TODO / Request changes: keep the price feed Binance-onlyThe current price-feed implementation is over-generalized for the agreed MVP scope. Gravity only needs one provider here: Binance USD-M Before this PR is merged, please simplify the production path:
Issue A future multi-provider oracle, if required, should be designed and reviewed separately with an explicit canonical aggregation specification. |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
[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; |
There was a problem hiding this comment.
[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.
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.
Summary
This PR narrows and hardens the relayer-backed Oracle runtime around two capabilities:
sourceType=3.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_v1adapter:continuousURI parameter instead of allowing two nonce semantics;startTimeandendTime;ntobucketStartMs + (n - 1) * intervalMs;feedIdwhen the bucket origin or interval changes;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
feedIdto be reconfigured with a different interval while derivingroundId = 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 addressesGalxe/gravity-audit#913.This is an intentional pre-merge URI compatibility change: remove
continuous=trueorcontinuous=falsefrom every Binance task URI before deploying this revision. The coordinated SDK E2E update is inGalxe/gravity-sdk#758.Polygon / Polymarket settlement
The Polygon adapter:
The cursor identity is
(blockNumber, logIndex); delivery nonce remains a sequential transport nonce.Consensus and execution safety
Validation
Results after the continuous-only change:
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
20af4ae4a2pinsgravity-api-typesto gravity-aptos10c4553b16, the revision containing the duplicate-observation aggregation fix. This is required so the reth relayer and SDK JWK consensus compile against oneapi-typesidentity; 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 --lockedThe 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.