Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 111 additions & 79 deletions contracts/predictify-hybrid/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
#![no_std]

extern crate alloc;
extern crate wee_alloc;

#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
use soroban_sdk::{
contract, contractimpl, panic_with_error, symbol_short, Address, Env, Map, String, Symbol, Vec,
};

// Module declarations - all modules enabled
// ===== MODULE DECLARATIONS =====
mod admin;
mod config;
mod disputes;
Expand All @@ -25,11 +23,18 @@ mod voting;
// Re-export commonly used items
pub use errors::Error;
pub use types::*;

// Basic imports (only import what we're sure exists)
use admin::AdminInitializer;

use soroban_sdk::{
contract, contractimpl, panic_with_error, Address, Env, Map, String, Symbol, Vec,
};
// Predictify Hybrid Contract
//
// This contract provides a comprehensive prediction market system with:
// - Oracle integration for automated market resolution
// - Community voting and consensus mechanisms
// - Dispute resolution and escalation systems
// - Fee management and analytics
// - Admin controls and configuration management

#[contract]
pub struct PredictifyHybrid;
Expand All @@ -39,14 +44,15 @@ const FEE_PERCENTAGE: i128 = 2; // 2% fee for the platform

#[contractimpl]
impl PredictifyHybrid {
/// Initialize the contract with an admin
pub fn initialize(env: Env, admin: Address) {
match AdminInitializer::initialize(&env, &admin) {
Ok(_) => (), // Success
Err(e) => panic_with_error!(env, e),
}
}

// Create a market
/// Create a new prediction market
pub fn create_market(
env: Env,
admin: Address,
Expand All @@ -64,13 +70,11 @@ impl PredictifyHybrid {
.persistent()
.get(&Symbol::new(&env, "Admin"))
.unwrap_or_else(|| {
panic!("Admin not set");
panic_with_error!(env, Error::Unauthorized);
});


if admin != stored_admin {
panic_with_error!(env, Error::Unauthorized);

}

// Validate inputs
Expand All @@ -82,20 +86,25 @@ impl PredictifyHybrid {
panic_with_error!(env, Error::InvalidQuestion);
}

// Validate oracle configuration
if let Err(e) = oracle_config.validate(&env) {
panic_with_error!(env, e);
}

// Generate a unique market ID
let counter_key = Symbol::new(&env, "MarketCounter");
let counter: u32 = env.storage().persistent().get(&counter_key).unwrap_or(0);
let new_counter = counter + 1;
env.storage().persistent().set(&counter_key, &new_counter);

let market_id = Symbol::new(&env, "market");
// Create market ID using symbol short notation
let market_id = symbol_short!("market");

// Calculate end time
let seconds_per_day: u64 = 24 * 60 * 60;
let duration_seconds: u64 = (duration_days as u64) * seconds_per_day;
let end_time: u64 = env.ledger().timestamp() + duration_seconds;


// Create a new market
let market = Market {
admin: admin.clone(),
Expand Down Expand Up @@ -123,11 +132,15 @@ impl PredictifyHybrid {
market_id
}


// Allows users to vote on a market outcome by staking tokens
/// Allow users to vote on a market outcome by staking tokens
pub fn vote(env: Env, user: Address, market_id: Symbol, outcome: String, stake: i128) {
user.require_auth();

// Validate stake amount
if stake <= 0 {
panic_with_error!(env, Error::InsufficientStake);
}

let mut market: Market = env
.storage()
.persistent()
Expand All @@ -136,12 +149,16 @@ impl PredictifyHybrid {
panic_with_error!(env, Error::MarketNotFound);
});


// Check if the market is still active
if env.ledger().timestamp() >= market.end_time {
panic_with_error!(env, Error::MarketClosed);
}

// Check market state
if market.state != MarketState::Active {
panic_with_error!(env, Error::MarketClosed);
}

// Validate outcome
let outcome_exists = market.outcomes.iter().any(|o| o == outcome);
if !outcome_exists {
Expand All @@ -154,15 +171,15 @@ impl PredictifyHybrid {
}

// Store the vote and stake
market.votes.set(user.clone(), outcome);
market.votes.set(user.clone(), outcome.clone());
market.stakes.set(user.clone(), stake);
market.total_staked += stake;

env.storage().persistent().set(&market_id, &market);
}

// Claim winnings
pub fn claim_winnings(env: Env, user: Address, market_id: Symbol) {
/// Allow users to claim winnings from resolved markets
pub fn claim_winnings(env: Env, user: Address, market_id: Symbol) -> i128 {
user.require_auth();

let mut market: Market = env
Expand Down Expand Up @@ -192,44 +209,43 @@ impl PredictifyHybrid {

let user_stake = market.stakes.get(user.clone()).unwrap_or(0);

// Calculate payout if user won
if &user_outcome == winning_outcome {
let payout = if &user_outcome == winning_outcome {
// Calculate total winning stakes
let mut winning_total = 0;
for (voter, outcome) in market.votes.iter() {
if &outcome == winning_outcome {
winning_total += market.stakes.get(voter.clone()).unwrap_or(0);
}

}


if winning_total > 0 {
let user_share = (user_stake * (PERCENTAGE_DENOMINATOR - FEE_PERCENTAGE))
/ PERCENTAGE_DENOMINATOR;
let total_pool = market.total_staked;
let _payout = (user_share * total_pool) / winning_total;

// In a real implementation, transfer tokens here
// For now, we just mark as claimed
(user_share * total_pool) / winning_total
} else {
0
}
}
} else {
0 // User didn't win
};

// Mark as claimed
market.claimed.set(user.clone(), true);
env.storage().persistent().set(&market_id, &market);

payout
}

// Get market information
/// Get market information
pub fn get_market(env: Env, market_id: Symbol) -> Option<Market> {
env.storage().persistent().get(&market_id)
}

// Manually resolve a market (admin only)
/// Manually resolve a market (admin only)
pub fn resolve_market_manual(env: Env, admin: Address, market_id: Symbol, winning_outcome: String) {
admin.require_auth();


// Verify admin
let stored_admin: Address = env
.storage()
Expand All @@ -241,10 +257,8 @@ impl PredictifyHybrid {

if admin != stored_admin {
panic_with_error!(env, Error::Unauthorized);

}


let mut market: Market = env
.storage()
.persistent()
Expand All @@ -258,71 +272,89 @@ impl PredictifyHybrid {
panic_with_error!(env, Error::MarketClosed);
}

// Check if market is already resolved
if market.winning_outcome.is_some() {
panic_with_error!(env, Error::MarketAlreadyResolved);
}

// Validate winning outcome
let outcome_exists = market.outcomes.iter().any(|o| o == winning_outcome);
if !outcome_exists {
panic_with_error!(env, Error::InvalidOutcome);
}



// Set winning outcome
market.winning_outcome = Some(winning_outcome);
// Set winning outcome and update state
market.winning_outcome = Some(winning_outcome.clone());
market.state = MarketState::Resolved;
env.storage().persistent().set(&market_id, &market);
}

/// Fetch oracle result for a market
pub fn fetch_oracle_result(
env: Env,
market_id: Symbol,
oracle_contract: Address,
) -> Result<String, Error> {
// Get the market from storage
let market = env.storage().persistent().get::<Symbol, Market>(&market_id)
.ok_or(Error::MarketNotFound)?;
/// Get market vote information for a user
pub fn get_user_vote(env: Env, market_id: Symbol, user: Address) -> Option<(String, i128)> {
let market: Market = env.storage().persistent().get(&market_id)?;

// Validate market state
if market.oracle_result.is_some() {
return Err(Error::MarketAlreadyResolved);
}
let outcome = market.votes.get(user.clone())?;
let stake = market.stakes.get(user).unwrap_or(0);


// Check if market has ended
let current_time = env.ledger().timestamp();
if current_time < market.end_time {
return Err(Error::MarketClosed);

}
Some((outcome, stake))
}

/// Get market statistics
pub fn get_market_stats(env: Env, market_id: Symbol) -> Option<(i128, u32, bool)> {
let market: Market = env.storage().persistent().get(&market_id)?;

// Get oracle result using the resolution module
let oracle_resolution = resolution::OracleResolutionManager::fetch_oracle_result(&env, &market_id, &oracle_contract)?;
let total_staked = market.total_staked;
let total_voters = market.votes.len();
let is_resolved = market.winning_outcome.is_some();

Ok(oracle_resolution.oracle_result)
Some((total_staked, total_voters, is_resolved))
}

/// Resolve a market automatically using oracle and community consensus
pub fn resolve_market(env: Env, market_id: Symbol) -> Result<(), Error> {
// Use the resolution module to resolve the market
let _resolution = resolution::MarketResolutionManager::resolve_market(&env, &market_id)?;
Ok(())
/// Check if market has ended but not resolved
pub fn needs_resolution(env: Env, market_id: Symbol) -> bool {
let market: Market = match env.storage().persistent().get(&market_id) {
Some(m) => m,
None => return false,
};

let current_time = env.ledger().timestamp();
current_time >= market.end_time && market.winning_outcome.is_none()
}

/// Get resolution analytics
pub fn get_resolution_analytics(env: Env) -> Result<resolution::ResolutionAnalytics, Error> {
resolution::MarketResolutionAnalytics::calculate_resolution_analytics(&env)
/// Get all outcomes for a market
pub fn get_market_outcomes(env: Env, market_id: Symbol) -> Option<Vec<String>> {
let market: Market = env.storage().persistent().get(&market_id)?;
Some(market.outcomes)
}

/// Get market analytics
pub fn get_market_analytics(env: Env, market_id: Symbol) -> Result<markets::MarketStats, Error> {
let market = env.storage().persistent().get::<Symbol, Market>(&market_id)
.ok_or(Error::MarketNotFound)?;

// Calculate market statistics
let stats = markets::MarketAnalytics::get_market_stats(&market);
/// Check if user has already voted
pub fn has_user_voted(env: Env, market_id: Symbol, user: Address) -> bool {
let market: Market = match env.storage().persistent().get(&market_id) {
Some(m) => m,
None => return false,
};

Ok(stats)
market.votes.get(user).is_some()
}

/// Get market end time
pub fn get_market_end_time(env: Env, market_id: Symbol) -> Option<u64> {
let market: Market = env.storage().persistent().get(&market_id)?;
Some(market.end_time)
}

/// Get market state
pub fn get_market_state(env: Env, market_id: Symbol) -> Option<MarketState> {
let market: Market = env.storage().persistent().get(&market_id)?;
Some(market.state)
}

/// Get total number of markets created
pub fn get_total_markets(env: Env) -> u32 {
let counter_key = Symbol::new(&env, "MarketCounter");
env.storage().persistent().get(&counter_key).unwrap_or(0)
}
}

mod test;
#[cfg(test)]
mod test;
Loading
Loading