Skip to content

Commit aeefcdc

Browse files
Merge pull request #131 from 1nonlypiece/resolution
refactor: centralize fee management and resolution systems into dedicated modules
2 parents 56a1fbd + 40fbc7d commit aeefcdc

5 files changed

Lines changed: 1359 additions & 104 deletions

File tree

contracts/predictify-hybrid/src/errors.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pub enum Error {
8181
OraclePriceOutOfRange = 33,
8282
/// Oracle comparison operation failed
8383
OracleComparisonFailed = 34,
84+
/// Admin not set
85+
AdminNotSet = 50,
8486

8587
// ===== VALIDATION ERRORS (51-70) =====
8688
/// Invalid outcome specified for voting or resolution
@@ -199,7 +201,8 @@ impl Error {
199201
Error::InternalError
200202
| Error::StorageError
201203
| Error::ArithmeticError
202-
| Error::InvalidState => ErrorCategory::System,
204+
| Error::InvalidState
205+
| Error::AdminNotSet => ErrorCategory::System,
203206
}
204207
}
205208

@@ -266,6 +269,7 @@ impl Error {
266269
Error::StorageError => "Storage operation failed",
267270
Error::ArithmeticError => "Arithmetic overflow or underflow occurred",
268271
Error::InvalidState => "Invalid contract state",
272+
Error::AdminNotSet => "Admin not set in contract",
269273
}
270274
}
271275

@@ -319,6 +323,7 @@ impl Error {
319323
Error::StorageError => "STORAGE_ERROR",
320324
Error::ArithmeticError => "ARITHMETIC_ERROR",
321325
Error::InvalidState => "INVALID_STATE",
326+
Error::AdminNotSet => "ADMIN_NOT_SET",
322327
}
323328
}
324329

contracts/predictify-hybrid/src/lib.rs

Lines changed: 116 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ use types::ExtensionStats;
3636
// Fee management module
3737
pub mod fees;
3838
use fees::{FeeManager, FeeCalculator, FeeValidator, FeeUtils, FeeTracker, FeeConfigManager};
39+
use resolution::{OracleResolutionManager, MarketResolutionManager, MarketResolutionAnalytics, OracleResolutionAnalytics, ResolutionUtils};
40+
41+
pub mod resolution;
3942

4043
#[contract]
4144
pub struct PredictifyHybrid;
@@ -144,30 +147,10 @@ impl PredictifyHybrid {
144147

145148
// Finalize market after disputes
146149
pub fn finalize_market(env: Env, admin: Address, market_id: Symbol, outcome: String) {
147-
admin.require_auth();
148-
149-
// Verify admin
150-
let stored_admin: Address = env
151-
.storage()
152-
.persistent()
153-
.get(&Symbol::new(&env, "Admin"))
154-
.expect("Admin not set");
155-
156-
// Use error helper for admin validation
157-
errors::helpers::require_admin(&env, &admin, &stored_admin);
158-
159-
let mut market: Market = env
160-
.storage()
161-
.persistent()
162-
.get(&market_id)
163-
.expect("Market not found");
164-
165-
// Use error helper for outcome validation
166-
errors::helpers::require_valid_outcome(&env, &outcome, &market.outcomes);
167-
168-
// Set final outcome
169-
market.winning_outcome = Some(outcome);
170-
env.storage().persistent().set(&market_id, &market);
150+
match resolution::MarketResolutionManager::finalize_market(&env, &admin, &market_id, &outcome) {
151+
Ok(_) => (), // Success
152+
Err(e) => panic_with_error!(env, e),
153+
}
171154
}
172155

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

181164
// Fetch oracle result to determine market outcome
182165
pub fn fetch_oracle_result(env: Env, market_id: Symbol, oracle_contract: Address) -> String {
183-
// Get the market from storage
184-
let mut market: Market = env
185-
.storage()
186-
.persistent()
187-
.get(&market_id)
188-
.unwrap_or_else(|| {
189-
panic!("Market not found");
190-
});
191-
192-
// Check if the market has already been resolved
193-
if market.oracle_result.is_some() {
194-
panic_with_error!(env, Error::MarketAlreadyResolved);
166+
match resolution::OracleResolutionManager::fetch_oracle_result(&env, &market_id, &oracle_contract) {
167+
Ok(resolution) => resolution.oracle_result,
168+
Err(e) => panic_with_error!(env, e),
195169
}
170+
}
196171

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

203-
// Get the price from the appropriate oracle using the factory pattern
204-
let oracle = match OracleFactory::create_oracle(
205-
market.oracle_config.provider.clone(),
206-
oracle_contract,
207-
) {
208-
Ok(oracle) => oracle,
180+
// Resolves a market by combining oracle results and community votes
181+
pub fn resolve_market(env: Env, market_id: Symbol) -> String {
182+
match resolution::MarketResolutionManager::resolve_market(&env, &market_id) {
183+
Ok(resolution) => resolution.final_outcome,
209184
Err(e) => panic_with_error!(env, e),
210-
};
185+
}
186+
}
211187

212-
let price = match oracle.get_price(&env, &market.oracle_config.feed_id) {
213-
Ok(p) => p,
188+
// Resolve a dispute and determine final market outcome
189+
pub fn resolve_dispute(env: Env, admin: Address, market_id: Symbol) -> String {
190+
match DisputeManager::resolve_dispute(&env, market_id, admin) {
191+
Ok(resolution) => resolution.final_outcome,
214192
Err(e) => panic_with_error!(env, e),
215-
};
193+
}
194+
}
216195

217-
// Determine the outcome based on the price and threshold using OracleUtils
218-
let outcome = match OracleUtils::determine_outcome(
219-
price,
220-
market.oracle_config.threshold,
221-
&market.oracle_config.comparison,
222-
&env,
223-
) {
224-
Ok(result) => result,
225-
Err(e) => panic_with_error!(env, e),
226-
};
196+
// ===== RESOLUTION SYSTEM METHODS =====
227197

228-
// Store the result in the market
229-
market.oracle_result = Some(outcome.clone());
198+
// Get oracle resolution for a market
199+
pub fn get_oracle_resolution(env: Env, market_id: Symbol) -> Option<resolution::OracleResolution> {
200+
match OracleResolutionManager::get_oracle_resolution(&env, &market_id) {
201+
Ok(resolution) => resolution,
202+
Err(_) => None,
203+
}
204+
}
230205

231-
// Update the market in storage
232-
env.storage().persistent().set(&market_id, &market);
206+
// Get market resolution for a market
207+
pub fn get_market_resolution(env: Env, market_id: Symbol) -> Option<resolution::MarketResolution> {
208+
match MarketResolutionManager::get_market_resolution(&env, &market_id) {
209+
Ok(resolution) => resolution,
210+
Err(_) => None,
211+
}
212+
}
233213

234-
// Return the outcome
235-
outcome
214+
// Get resolution analytics
215+
pub fn get_resolution_analytics(env: Env) -> resolution::ResolutionAnalytics {
216+
match resolution::MarketResolutionAnalytics::calculate_resolution_analytics(&env) {
217+
Ok(analytics) => analytics,
218+
Err(_) => resolution::ResolutionAnalytics::default(),
219+
}
236220
}
237221

238-
// Allows users to dispute the market result by staking tokens
239-
pub fn dispute_result(env: Env, user: Address, market_id: Symbol, stake: i128) {
240-
match DisputeManager::process_dispute(&env, user, market_id, stake, None) {
241-
Ok(_) => (), // Success
242-
Err(e) => panic_with_error!(env, e),
222+
// Get oracle statistics
223+
pub fn get_oracle_stats(env: Env) -> resolution::OracleStats {
224+
match resolution::OracleResolutionAnalytics::get_oracle_stats(&env) {
225+
Ok(stats) => stats,
226+
Err(_) => resolution::OracleStats::default(),
243227
}
244228
}
245229

246-
// Resolves a market by combining oracle results and community votes
247-
pub fn resolve_market(env: Env, market_id: Symbol) -> String {
248-
// Get the market from storage
249-
let mut market = match MarketStateManager::get_market(&env, &market_id) {
250-
Ok(market) => market,
251-
Err(e) => panic_with_error!(env, e),
230+
// Validate resolution for a market
231+
pub fn validate_resolution(env: Env, market_id: Symbol) -> resolution::ResolutionValidation {
232+
let mut validation = resolution::ResolutionValidation {
233+
is_valid: true,
234+
errors: vec![&env],
235+
warnings: vec![&env],
236+
recommendations: vec![&env],
252237
};
253238

254-
// Validate market for resolution
255-
if let Err(e) = MarketValidator::validate_market_for_resolution(&env, &market) {
256-
panic_with_error!(env, e);
257-
}
258-
259-
// Retrieve the oracle result
260-
let oracle_result = match &market.oracle_result {
261-
Some(result) => result.clone(),
262-
None => panic_with_error!(env, Error::OracleUnavailable),
239+
// Get market
240+
let market = match MarketStateManager::get_market(&env, &market_id) {
241+
Ok(market) => market,
242+
Err(_) => {
243+
validation.is_valid = false;
244+
validation.errors.push_back(String::from_str(&env, "Market not found"));
245+
return validation;
246+
}
263247
};
264248

265-
// Calculate community consensus
266-
let community_consensus = MarketAnalytics::calculate_community_consensus(&market);
249+
// Check resolution state
250+
let state = resolution::ResolutionUtils::get_resolution_state(&env, &market);
251+
let (eligible, reason) = resolution::ResolutionUtils::get_resolution_eligibility(&env, &market);
267252

268-
// Determine final result using hybrid algorithm
269-
let final_result =
270-
MarketUtils::determine_final_result(&env, &oracle_result, &community_consensus);
253+
if !eligible {
254+
validation.is_valid = false;
255+
validation.errors.push_back(reason);
256+
}
271257

272-
// Set winning outcome
273-
MarketStateManager::set_winning_outcome(&mut market, final_result.clone());
258+
// Add recommendations based on state
259+
match state {
260+
resolution::ResolutionState::Active => {
261+
validation.recommendations.push_back(String::from_str(&env, "Market is active, wait for end time"));
262+
}
263+
resolution::ResolutionState::OracleResolved => {
264+
validation.recommendations.push_back(String::from_str(&env, "Oracle resolved, ready for market resolution"));
265+
}
266+
resolution::ResolutionState::MarketResolved => {
267+
validation.recommendations.push_back(String::from_str(&env, "Market already resolved"));
268+
}
269+
resolution::ResolutionState::Disputed => {
270+
validation.recommendations.push_back(String::from_str(&env, "Resolution disputed, consider admin override"));
271+
}
272+
resolution::ResolutionState::Finalized => {
273+
validation.recommendations.push_back(String::from_str(&env, "Resolution finalized"));
274+
}
275+
}
274276

275-
// Update the market in storage
276-
MarketStateManager::update_market(&env, &market_id, &market);
277+
validation
278+
}
277279

278-
// Return the final result
279-
final_result
280+
// Get resolution state for a market
281+
pub fn get_resolution_state(env: Env, market_id: Symbol) -> resolution::ResolutionState {
282+
match MarketStateManager::get_market(&env, &market_id) {
283+
Ok(market) => resolution::ResolutionUtils::get_resolution_state(&env, &market),
284+
Err(_) => resolution::ResolutionState::Active,
285+
}
280286
}
281287

282-
// Resolve a dispute and determine final market outcome
283-
pub fn resolve_dispute(env: Env, admin: Address, market_id: Symbol) -> String {
284-
match DisputeManager::resolve_dispute(&env, market_id, admin) {
285-
Ok(resolution) => resolution.final_outcome,
286-
Err(e) => panic_with_error!(env, e),
288+
// Check if market can be resolved
289+
pub fn can_resolve_market(env: Env, market_id: Symbol) -> bool {
290+
match MarketStateManager::get_market(&env, &market_id) {
291+
Ok(market) => resolution::ResolutionUtils::can_resolve_market(&env, &market),
292+
Err(_) => false,
293+
}
294+
}
295+
296+
// Calculate resolution time for a market
297+
pub fn calculate_resolution_time(env: Env, market_id: Symbol) -> u64 {
298+
match MarketStateManager::get_market(&env, &market_id) {
299+
Ok(market) => resolution::ResolutionUtils::calculate_resolution_time(&env, &market),
300+
Err(_) => 0,
287301
}
288302
}
289303

contracts/predictify-hybrid/src/markets.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use soroban_sdk::{token, vec, Address, Env, Map, String, Symbol, Vec};
1+
use soroban_sdk::{contracttype, token, vec, Address, Env, Map, String, Symbol, Vec};
22

33
use crate::errors::Error;
44
use crate::oracles::{OracleFactory, OracleUtils};
@@ -517,6 +517,7 @@ pub struct UserStats {
517517

518518
/// Community consensus statistics
519519
#[derive(Clone, Debug)]
520+
#[contracttype]
520521
pub struct CommunityConsensus {
521522
pub outcome: String,
522523
pub votes: u32,

0 commit comments

Comments
 (0)