Skip to content
Merged
7 changes: 6 additions & 1 deletion contracts/predictify-hybrid/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ pub enum Error {
OraclePriceOutOfRange = 33,
/// Oracle comparison operation failed
OracleComparisonFailed = 34,
/// Admin not set
AdminNotSet = 50,

// ===== VALIDATION ERRORS (51-70) =====
/// Invalid outcome specified for voting or resolution
Expand Down Expand Up @@ -199,7 +201,8 @@ impl Error {
Error::InternalError
| Error::StorageError
| Error::ArithmeticError
| Error::InvalidState => ErrorCategory::System,
| Error::InvalidState
| Error::AdminNotSet => ErrorCategory::System,
}
}

Expand Down Expand Up @@ -266,6 +269,7 @@ impl Error {
Error::StorageError => "Storage operation failed",
Error::ArithmeticError => "Arithmetic overflow or underflow occurred",
Error::InvalidState => "Invalid contract state",
Error::AdminNotSet => "Admin not set in contract",
}
}

Expand Down Expand Up @@ -319,6 +323,7 @@ impl Error {
Error::StorageError => "STORAGE_ERROR",
Error::ArithmeticError => "ARITHMETIC_ERROR",
Error::InvalidState => "INVALID_STATE",
Error::AdminNotSet => "ADMIN_NOT_SET",
}
}

Expand Down
218 changes: 116 additions & 102 deletions contracts/predictify-hybrid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ use types::ExtensionStats;
// Fee management module
pub mod fees;
use fees::{FeeManager, FeeCalculator, FeeValidator, FeeUtils, FeeTracker, FeeConfigManager};
use resolution::{OracleResolutionManager, MarketResolutionManager, MarketResolutionAnalytics, OracleResolutionAnalytics, ResolutionUtils};

pub mod resolution;

#[contract]
pub struct PredictifyHybrid;
Expand Down Expand Up @@ -144,30 +147,10 @@ impl PredictifyHybrid {

// Finalize market after disputes
pub fn finalize_market(env: Env, admin: Address, market_id: Symbol, outcome: String) {
admin.require_auth();

// Verify admin
let stored_admin: Address = env
.storage()
.persistent()
.get(&Symbol::new(&env, "Admin"))
.expect("Admin not set");

// Use error helper for admin validation
errors::helpers::require_admin(&env, &admin, &stored_admin);

let mut market: Market = env
.storage()
.persistent()
.get(&market_id)
.expect("Market not found");

// Use error helper for outcome validation
errors::helpers::require_valid_outcome(&env, &outcome, &market.outcomes);

// Set final outcome
market.winning_outcome = Some(outcome);
env.storage().persistent().set(&market_id, &market);
match resolution::MarketResolutionManager::finalize_market(&env, &admin, &market_id, &outcome) {
Ok(_) => (), // Success
Err(e) => panic_with_error!(env, e),
}
}

// Allows users to vote on a market outcome by staking tokens
Expand All @@ -180,110 +163,141 @@ impl PredictifyHybrid {

// Fetch oracle result to determine market outcome
pub fn fetch_oracle_result(env: Env, market_id: Symbol, oracle_contract: Address) -> String {
// Get the market from storage
let mut market: Market = env
.storage()
.persistent()
.get(&market_id)
.unwrap_or_else(|| {
panic!("Market not found");
});

// Check if the market has already been resolved
if market.oracle_result.is_some() {
panic_with_error!(env, Error::MarketAlreadyResolved);
match resolution::OracleResolutionManager::fetch_oracle_result(&env, &market_id, &oracle_contract) {
Ok(resolution) => resolution.oracle_result,
Err(e) => panic_with_error!(env, e),
}
}

// Check if the market ended (we can only fetch oracle result after market ends)
let current_time = env.ledger().timestamp();
if current_time < market.end_time {
panic_with_error!(env, Error::MarketClosed);
// Allows users to dispute the market result by staking tokens
pub fn dispute_result(env: Env, user: Address, market_id: Symbol, stake: i128) {
match DisputeManager::process_dispute(&env, user, market_id, stake, None) {
Ok(_) => (), // Success
Err(e) => panic_with_error!(env, e),
}
}

// Get the price from the appropriate oracle using the factory pattern
let oracle = match OracleFactory::create_oracle(
market.oracle_config.provider.clone(),
oracle_contract,
) {
Ok(oracle) => oracle,
// Resolves a market by combining oracle results and community votes
pub fn resolve_market(env: Env, market_id: Symbol) -> String {
match resolution::MarketResolutionManager::resolve_market(&env, &market_id) {
Ok(resolution) => resolution.final_outcome,
Err(e) => panic_with_error!(env, e),
};
}
}

let price = match oracle.get_price(&env, &market.oracle_config.feed_id) {
Ok(p) => p,
// Resolve a dispute and determine final market outcome
pub fn resolve_dispute(env: Env, admin: Address, market_id: Symbol) -> String {
match DisputeManager::resolve_dispute(&env, market_id, admin) {
Ok(resolution) => resolution.final_outcome,
Err(e) => panic_with_error!(env, e),
};
}
}

// Determine the outcome based on the price and threshold using OracleUtils
let outcome = match OracleUtils::determine_outcome(
price,
market.oracle_config.threshold,
&market.oracle_config.comparison,
&env,
) {
Ok(result) => result,
Err(e) => panic_with_error!(env, e),
};
// ===== RESOLUTION SYSTEM METHODS =====

// Store the result in the market
market.oracle_result = Some(outcome.clone());
// Get oracle resolution for a market
pub fn get_oracle_resolution(env: Env, market_id: Symbol) -> Option<resolution::OracleResolution> {
match OracleResolutionManager::get_oracle_resolution(&env, &market_id) {
Ok(resolution) => resolution,
Err(_) => None,
}
}

// Update the market in storage
env.storage().persistent().set(&market_id, &market);
// Get market resolution for a market
pub fn get_market_resolution(env: Env, market_id: Symbol) -> Option<resolution::MarketResolution> {
match MarketResolutionManager::get_market_resolution(&env, &market_id) {
Ok(resolution) => resolution,
Err(_) => None,
}
}

// Return the outcome
outcome
// Get resolution analytics
pub fn get_resolution_analytics(env: Env) -> resolution::ResolutionAnalytics {
match resolution::MarketResolutionAnalytics::calculate_resolution_analytics(&env) {
Ok(analytics) => analytics,
Err(_) => resolution::ResolutionAnalytics::default(),
}
}

// Allows users to dispute the market result by staking tokens
pub fn dispute_result(env: Env, user: Address, market_id: Symbol, stake: i128) {
match DisputeManager::process_dispute(&env, user, market_id, stake, None) {
Ok(_) => (), // Success
Err(e) => panic_with_error!(env, e),
// Get oracle statistics
pub fn get_oracle_stats(env: Env) -> resolution::OracleStats {
match resolution::OracleResolutionAnalytics::get_oracle_stats(&env) {
Ok(stats) => stats,
Err(_) => resolution::OracleStats::default(),
}
}

// Resolves a market by combining oracle results and community votes
pub fn resolve_market(env: Env, market_id: Symbol) -> String {
// Get the market from storage
let mut market = match MarketStateManager::get_market(&env, &market_id) {
Ok(market) => market,
Err(e) => panic_with_error!(env, e),
// Validate resolution for a market
pub fn validate_resolution(env: Env, market_id: Symbol) -> resolution::ResolutionValidation {
let mut validation = resolution::ResolutionValidation {
is_valid: true,
errors: vec![&env],
warnings: vec![&env],
recommendations: vec![&env],
};

// Validate market for resolution
if let Err(e) = MarketValidator::validate_market_for_resolution(&env, &market) {
panic_with_error!(env, e);
}

// Retrieve the oracle result
let oracle_result = match &market.oracle_result {
Some(result) => result.clone(),
None => panic_with_error!(env, Error::OracleUnavailable),
// Get market
let market = match MarketStateManager::get_market(&env, &market_id) {
Ok(market) => market,
Err(_) => {
validation.is_valid = false;
validation.errors.push_back(String::from_str(&env, "Market not found"));
return validation;
}
};

// Calculate community consensus
let community_consensus = MarketAnalytics::calculate_community_consensus(&market);
// Check resolution state
let state = resolution::ResolutionUtils::get_resolution_state(&env, &market);
let (eligible, reason) = resolution::ResolutionUtils::get_resolution_eligibility(&env, &market);

// Determine final result using hybrid algorithm
let final_result =
MarketUtils::determine_final_result(&env, &oracle_result, &community_consensus);
if !eligible {
validation.is_valid = false;
validation.errors.push_back(reason);
}

// Set winning outcome
MarketStateManager::set_winning_outcome(&mut market, final_result.clone());
// Add recommendations based on state
match state {
resolution::ResolutionState::Active => {
validation.recommendations.push_back(String::from_str(&env, "Market is active, wait for end time"));
}
resolution::ResolutionState::OracleResolved => {
validation.recommendations.push_back(String::from_str(&env, "Oracle resolved, ready for market resolution"));
}
resolution::ResolutionState::MarketResolved => {
validation.recommendations.push_back(String::from_str(&env, "Market already resolved"));
}
resolution::ResolutionState::Disputed => {
validation.recommendations.push_back(String::from_str(&env, "Resolution disputed, consider admin override"));
}
resolution::ResolutionState::Finalized => {
validation.recommendations.push_back(String::from_str(&env, "Resolution finalized"));
}
}

// Update the market in storage
MarketStateManager::update_market(&env, &market_id, &market);
validation
}

// Return the final result
final_result
// Get resolution state for a market
pub fn get_resolution_state(env: Env, market_id: Symbol) -> resolution::ResolutionState {
match MarketStateManager::get_market(&env, &market_id) {
Ok(market) => resolution::ResolutionUtils::get_resolution_state(&env, &market),
Err(_) => resolution::ResolutionState::Active,
}
}

// Resolve a dispute and determine final market outcome
pub fn resolve_dispute(env: Env, admin: Address, market_id: Symbol) -> String {
match DisputeManager::resolve_dispute(&env, market_id, admin) {
Ok(resolution) => resolution.final_outcome,
Err(e) => panic_with_error!(env, e),
// Check if market can be resolved
pub fn can_resolve_market(env: Env, market_id: Symbol) -> bool {
match MarketStateManager::get_market(&env, &market_id) {
Ok(market) => resolution::ResolutionUtils::can_resolve_market(&env, &market),
Err(_) => false,
}
}

// Calculate resolution time for a market
pub fn calculate_resolution_time(env: Env, market_id: Symbol) -> u64 {
match MarketStateManager::get_market(&env, &market_id) {
Ok(market) => resolution::ResolutionUtils::calculate_resolution_time(&env, &market),
Err(_) => 0,
}
}

Expand Down
3 changes: 2 additions & 1 deletion contracts/predictify-hybrid/src/markets.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use soroban_sdk::{token, vec, Address, Env, Map, String, Symbol, Vec};
use soroban_sdk::{contracttype, token, vec, Address, Env, Map, String, Symbol, Vec};

use crate::errors::Error;
use crate::oracles::{OracleFactory, OracleUtils};
Expand Down Expand Up @@ -517,6 +517,7 @@ pub struct UserStats {

/// Community consensus statistics
#[derive(Clone, Debug)]
#[contracttype]
pub struct CommunityConsensus {
pub outcome: String,
pub votes: u32,
Expand Down
Loading
Loading