Skip to content

feat: weighted median oracle aggregation#806

Open
Davichi-1 wants to merge 1 commit into
Predictify-org:masterfrom
Davichi-1:feature/weighted-median
Open

feat: weighted median oracle aggregation#806
Davichi-1 wants to merge 1 commit into
Predictify-org:masterfrom
Davichi-1:feature/weighted-median

Conversation

@Davichi-1

Copy link
Copy Markdown

Overview

This PR adds a configurable per-oracle weighted median aggregation primitive to contracts/predictify-hybrid/src/oracles.rs so oracle readings can be combined using explicit, per-source weights. Each source contributes a caller-configured weight, and the result is the weighted median — which is robust to outliers (a single mis-reporting source cannot drag the result far, regardless of its weight). A weight of 0 lets an operator disable a source via configuration without removing it from the list.

Note: the repo already had a confidence-weighted three-oracle resolver (OracleResolutionManager::resolve_with_median), where weights are derived automatically from each oracle's confidence interval. That does not provide configurable per-source weights, which is exactly what issue #704 asks for. This PR fills that gap with a focused, reusable primitive in the file named by the issue.

Related Issue

Closes #704

Changes

⚙️ Weighted Median Aggregation

  • [ADD] contracts/predictify-hybrid/src/oracles.rsWeightedOraclePrice { price, weight }

  • Defined a #[contracttype] struct pairing an oracle price with its configurable per-source weight.

  • A weight of 0 excludes the reading from aggregation (disable-via-config) without removing it from the list.

  • Added a new(price, weight) constructor.

  • [ADD] contracts/predictify-hybrid/src/oracles.rsOracleUtils::weighted_median(env, readings)

  • Aggregates Vec<WeightedOraclePrice> and returns the (lower) weighted median price.

  • Sorts (price, weight) pairs ascending and returns the price at which twice the cumulative weight first reaches the total weight (division-free crossing test).

  • Overflow-safe: all weight accumulation uses checked i128 arithmetic; no unwrap() on the production path.

  • Outlier-robust and input-order independent; returns Error::InvalidInput when no reading carries a positive weight (empty list or all-zero weights).

  • Full /// rustdoc with formula, error table, tie-break convention, overflow-safety notes, and a doctest.

🧪 Tests

  • [ADD] contracts/predictify-hybrid/src/oracles.rs#[cfg(test)] mod weighted_median_tests
  • Added 11 focused unit tests: high-weight dominance, equal-weight parity with a plain median (odd and even/lower-middle), single reading, zero-weight skipping, input-order independence, exact-half tie-break to the lower price, dominant weight outvoting extreme outliers, large-value (i128::MAX) overflow safety, and empty / all-zero rejection.

📚 Documentation

  • [MODIFY] API_DOCUMENTATION.md
  • Documented the new WeightedOraclePrice struct and OracleUtils::weighted_median entry under Oracle Management.

Verification Results

The predictify-hybrid crate has pre-existing, unrelated native build errors on master (e.g. an undeclared performance_benchmarks module in lib.rs), so the full crate cannot be compiled/tested natively today. The new code was verified to add zero new errors/warnings (build diagnostics identical before and after: 259 errors / 162 warnings → 259 / 162), and the new struct + function + test module were compiled and run verbatim against the real soroban-sdk 25.3.1 in an isolated crate — all 11 tests pass.

Acceptance Criteria Status
Aggregates oracle readings using configurable per-source weights
Uses a weighted median (outlier-robust)
weight = 0 disables a source via configuration
Overflow-safe math; no unwrap() on production paths
Returns Error::InvalidInput on empty / all-zero-weight input
Focused unit tests added (11) and passing
Clear rustdoc on the public API
Visible API change documented (API_DOCUMENTATION.md)
Introduces no new compiler errors/warnings

Add a configurable per-source weighted median primitive in oracles.rs
to aggregate oracle readings where each source carries an explicit,
configurable weight (issue Predictify-org#704).

- New `WeightedOraclePrice { price, weight }` type; weight 0 disables a
  source via configuration without removing it from the list.
- New `OracleUtils::weighted_median(env, readings)` returning the lower
  weighted median. Overflow-safe checked i128 math, no unwrap() on the
  production path, outlier-robust, and input-order independent.
- 11 focused unit tests covering dominance, equal-weight parity with a
  plain median, zero-weight skipping, exact-half tie-break, large-value
  overflow safety, and empty/all-zero rejection.
- Document the new API in API_DOCUMENTATION.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@drips-wave

drips-wave Bot commented Jun 30, 2026

Copy link
Copy Markdown

@Davichi-1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

Add per-oracle weighted median aggregation

1 participant