diff --git a/contracts/hello-world/src/lib.rs b/contracts/hello-world/src/lib.rs index 07874a4e..1f284fcc 100644 --- a/contracts/hello-world/src/lib.rs +++ b/contracts/hello-world/src/lib.rs @@ -29,11 +29,11 @@ use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec}; /// # let env = Env::default(); /// # let contract_id = env.register(Contract, ()); /// # let client = hello_world::ContractClient::new(&env, &contract_id); -/// +/// /// // Call the hello function /// let name = String::from_str(&env, "World"); /// let greeting = client.hello(&name); -/// +/// /// // greeting will be ["Hello", "World"] /// assert_eq!(greeting.len(), 2); /// ``` @@ -98,22 +98,22 @@ impl Contract { /// # let env = Env::default(); /// # let contract_id = env.register(Contract, ()); /// # let client = hello_world::ContractClient::new(&env, &contract_id); - /// + /// /// // Basic greeting /// let name = String::from_str(&env, "Alice"); /// let result = client.hello(&name); - /// + /// /// // Verify the result /// assert_eq!(result, vec![ /// &env, /// String::from_str(&env, "Hello"), /// String::from_str(&env, "Alice") /// ]); - /// + /// /// // Different names produce different greetings /// let dev_greeting = client.hello(&String::from_str(&env, "Developer")); /// let world_greeting = client.hello(&String::from_str(&env, "World")); - /// + /// /// // Both contain "Hello" as the first element /// assert_eq!(dev_greeting.get(0).unwrap(), String::from_str(&env, "Hello")); /// assert_eq!(world_greeting.get(0).unwrap(), String::from_str(&env, "Hello")); diff --git a/contracts/predictify-hybrid/src/admin.rs b/contracts/predictify-hybrid/src/admin.rs index 55367d6e..b60c4630 100644 --- a/contracts/predictify-hybrid/src/admin.rs +++ b/contracts/predictify-hybrid/src/admin.rs @@ -2,13 +2,13 @@ extern crate alloc; use soroban_sdk::{contracttype, vec, Address, Env, Map, String, Symbol, Vec}; // use alloc::string::ToString; // Unused import +use crate::config::{ConfigManager, ConfigUtils, ContractConfig, Environment}; use crate::errors::Error; +use crate::events::EventEmitter; +use crate::extensions::ExtensionManager; +use crate::fees::{FeeConfig, FeeManager}; use crate::markets::MarketStateManager; -use crate::fees::{FeeManager, FeeConfig}; -use crate::config::{ConfigManager, ContractConfig, Environment, ConfigUtils}; use crate::resolution::MarketResolutionManager; -use crate::extensions::ExtensionManager; -use crate::events::EventEmitter; /// Admin management system for Predictify Hybrid contract /// @@ -145,7 +145,7 @@ impl AdminInitializer { /// # use predictify_hybrid::admin::AdminInitializer; /// # let env = Env::default(); /// # let admin_address = Address::generate(&env); - /// + /// /// match AdminInitializer::initialize(&env, &admin_address) { /// Ok(()) => { /// println!("Contract initialized successfully"); @@ -188,26 +188,13 @@ impl AdminInitializer { .set(&Symbol::new(env, "Admin"), admin); // Set default admin role - AdminRoleManager::assign_role( - env, - admin, - AdminRole::SuperAdmin, - admin, - )?; + AdminRoleManager::assign_role(env, admin, AdminRole::SuperAdmin, admin)?; // Emit admin initialization event EventEmitter::emit_admin_initialized(env, admin); // Log admin action - AdminActionLogger::log_action( - env, - admin, - "initialize", - None, - Map::new(env), - true, - None, - )?; + AdminActionLogger::log_action(env, admin, "initialize", None, Map::new(env), true, None)?; Ok(()) } @@ -245,7 +232,7 @@ impl AdminInitializer { /// # use predictify_hybrid::config::Environment; /// # let env = Env::default(); /// # let admin_address = Address::generate(&env); - /// + /// /// // Initialize for mainnet deployment /// match AdminInitializer::initialize_with_config( /// &env, @@ -336,7 +323,7 @@ impl AdminInitializer { /// # use predictify_hybrid::admin::AdminInitializer; /// # let env = Env::default(); /// # let proposed_admin = Address::generate(&env); - /// + /// /// // Validate before initialization /// match AdminInitializer::validate_initialization_params(&env, &proposed_admin) { /// Ok(()) => { @@ -370,10 +357,7 @@ impl AdminInitializer { /// Always call this function before `initialize()` or `initialize_with_config()` /// to prevent failed initialization attempts that could leave the contract /// in an inconsistent state. - pub fn validate_initialization_params( - env: &Env, - admin: &Address, - ) -> Result<(), Error> { + pub fn validate_initialization_params(env: &Env, admin: &Address) -> Result<(), Error> { AdminValidator::validate_admin_address(env, admin)?; AdminValidator::validate_contract_not_initialized(env)?; Ok(()) @@ -418,7 +402,7 @@ impl AdminAccessControl { /// # use predictify_hybrid::admin::{AdminAccessControl, AdminPermission}; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Check if admin can create markets /// match AdminAccessControl::validate_permission( /// &env, @@ -498,7 +482,7 @@ impl AdminAccessControl { /// # use predictify_hybrid::admin::AdminAccessControl; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Authenticate admin before sensitive operation /// match AdminAccessControl::require_admin_auth(&env, &admin) { /// Ok(()) => { @@ -583,7 +567,7 @@ impl AdminAccessControl { /// # use predictify_hybrid::admin::AdminAccessControl; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Validate admin for market creation /// match AdminAccessControl::validate_admin_for_action( /// &env, @@ -669,7 +653,7 @@ impl AdminAccessControl { /// /// ```rust /// # use predictify_hybrid::admin::{AdminAccessControl, AdminPermission}; - /// + /// /// // Map action string to permission /// match AdminAccessControl::map_action_to_permission("create_market") { /// Ok(permission) => { @@ -680,7 +664,7 @@ impl AdminAccessControl { /// println!("Invalid action: {:?}", e); /// } /// } - /// + /// /// // Handle invalid action /// match AdminAccessControl::map_action_to_permission("invalid_action") { /// Ok(_) => unreachable!(), @@ -779,7 +763,7 @@ impl AdminRoleManager { /// # let env = Env::default(); /// # let super_admin = Address::generate(&env); /// # let new_admin = Address::generate(&env); - /// + /// /// // Assign MarketAdmin role to a new admin /// match AdminRoleManager::assign_role( /// &env, @@ -827,7 +811,7 @@ impl AdminRoleManager { ) -> Result<(), Error> { // Use a simple fixed key for admin role storage let key = Symbol::new(env, "admin_role"); - + // Check if this is the first admin role assignment (bootstrapping) if !env.storage().persistent().has(&key) { // No admin role assigned yet, allow bootstrapping without permission check @@ -897,7 +881,7 @@ impl AdminRoleManager { /// # use predictify_hybrid::admin::{AdminRoleManager, AdminRole}; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Get admin role for permission checking /// match AdminRoleManager::get_admin_role(&env, &admin) { /// Ok(AdminRole::SuperAdmin) => { @@ -938,7 +922,7 @@ impl AdminRoleManager { pub fn get_admin_role(env: &Env, admin: &Address) -> Result { // Use a simple fixed key for admin role storage let key = Symbol::new(env, "admin_role"); - + let assignment: AdminRoleAssignment = env .storage() .persistent() @@ -987,25 +971,25 @@ impl AdminRoleManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::admin::{AdminRoleManager, AdminRole, AdminPermission}; /// # let env = Env::default(); - /// + /// /// // Check if MarketAdmin can create markets /// let can_create = AdminRoleManager::has_permission( /// &env, /// &AdminRole::MarketAdmin, /// &AdminPermission::CreateMarket /// ).unwrap(); - /// + /// /// if can_create { /// println!("MarketAdmin can create markets"); /// } - /// + /// /// // Check if ReadOnlyAdmin can update fees /// let can_update_fees = AdminRoleManager::has_permission( /// &env, /// &AdminRole::ReadOnlyAdmin, /// &AdminPermission::UpdateFees /// ).unwrap(); - /// + /// /// assert!(!can_update_fees); // ReadOnlyAdmin cannot update fees /// ``` /// @@ -1063,23 +1047,23 @@ impl AdminRoleManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::admin::{AdminRoleManager, AdminRole, AdminPermission}; /// # let env = Env::default(); - /// + /// /// // Get all permissions for SuperAdmin /// let super_permissions = AdminRoleManager::get_permissions_for_role( /// &env, /// &AdminRole::SuperAdmin /// ); - /// + /// /// println!("SuperAdmin has {} permissions", super_permissions.len()); /// assert!(super_permissions.contains(&AdminPermission::Initialize)); /// assert!(super_permissions.contains(&AdminPermission::EmergencyActions)); - /// + /// /// // Get permissions for MarketAdmin /// let market_permissions = AdminRoleManager::get_permissions_for_role( /// &env, /// &AdminRole::MarketAdmin /// ); - /// + /// /// assert!(market_permissions.contains(&AdminPermission::CreateMarket)); /// assert!(!market_permissions.contains(&AdminPermission::UpdateFees)); /// ``` @@ -1155,10 +1139,7 @@ impl AdminRoleManager { AdminPermission::CollectFees, AdminPermission::ViewAnalytics, ], - AdminRole::ReadOnlyAdmin => soroban_sdk::vec![ - env, - AdminPermission::ViewAnalytics, - ], + AdminRole::ReadOnlyAdmin => soroban_sdk::vec![env, AdminPermission::ViewAnalytics,], } } @@ -1177,7 +1158,7 @@ impl AdminRoleManager { // Use a simple fixed key for admin role storage let key = Symbol::new(env, "admin_role"); - + let mut assignment: AdminRoleAssignment = env .storage() .persistent() @@ -1232,7 +1213,7 @@ impl AdminFunctions { /// # let env = Env::default(); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "problematic_market"); - /// + /// /// // Close a problematic market /// match AdminFunctions::close_market(&env, &admin, &market_id) { /// Ok(()) => { @@ -1273,11 +1254,7 @@ impl AdminFunctions { /// /// This is a powerful admin function that should be used carefully. /// Only admins with CloseMarket permission can execute this function. - pub fn close_market( - env: &Env, - admin: &Address, - market_id: &Symbol, - ) -> Result<(), Error> { + pub fn close_market(env: &Env, admin: &Address, market_id: &Symbol) -> Result<(), Error> { // Validate admin permissions AdminAccessControl::validate_admin_for_action(env, admin, "close_market")?; @@ -1292,7 +1269,10 @@ impl AdminFunctions { // Log admin action let mut params = Map::new(env); - params.set(String::from_str(env, "market_id"), String::from_str(env, "market_id")); + params.set( + String::from_str(env, "market_id"), + String::from_str(env, "market_id"), + ); AdminActionLogger::log_action(env, admin, "close_market", None, params, true, None)?; Ok(()) @@ -1335,7 +1315,7 @@ impl AdminFunctions { /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "disputed_market"); /// # let outcome = String::from_str(&env, "Yes"); - /// + /// /// // Finalize a disputed market with admin decision /// match AdminFunctions::finalize_market(&env, &admin, &market_id, &outcome) { /// Ok(()) => { @@ -1394,9 +1374,20 @@ impl AdminFunctions { // Log admin action let mut params = Map::new(env); - params.set(String::from_str(env, "market_id"), String::from_str(env, "market_id")); + params.set( + String::from_str(env, "market_id"), + String::from_str(env, "market_id"), + ); params.set(String::from_str(env, "outcome"), outcome.clone()); - AdminActionLogger::log_action(env, admin, "finalize_market", Some(String::from_str(env, "market_id")), params, true, None)?; + AdminActionLogger::log_action( + env, + admin, + "finalize_market", + Some(String::from_str(env, "market_id")), + params, + true, + None, + )?; Ok(()) } @@ -1439,7 +1430,7 @@ impl AdminFunctions { /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "active_market"); /// # let reason = String::from_str(&env, "Low participation, extending for more votes"); - /// + /// /// // Extend market by 7 days due to low participation /// match AdminFunctions::extend_market_duration( /// &env, @@ -1504,14 +1495,34 @@ impl AdminFunctions { AdminAccessControl::validate_admin_for_action(env, admin, "extend_market")?; // Extend market using extension manager - ExtensionManager::extend_market_duration(env, admin.clone(), market_id.clone(), additional_days, reason.clone())?; + ExtensionManager::extend_market_duration( + env, + admin.clone(), + market_id.clone(), + additional_days, + reason.clone(), + )?; // Log admin action let mut params = Map::new(env); - params.set(String::from_str(env, "market_id"), String::from_str(env, "market_id")); - params.set(String::from_str(env, "additional_days"), String::from_str(env, "additional_days")); + params.set( + String::from_str(env, "market_id"), + String::from_str(env, "market_id"), + ); + params.set( + String::from_str(env, "additional_days"), + String::from_str(env, "additional_days"), + ); params.set(String::from_str(env, "reason"), reason.clone()); - AdminActionLogger::log_action(env, admin, "extend_market", Some(String::from_str(env, "market_id")), params, true, None)?; + AdminActionLogger::log_action( + env, + admin, + "extend_market", + Some(String::from_str(env, "market_id")), + params, + true, + None, + )?; Ok(()) } @@ -1555,7 +1566,7 @@ impl AdminFunctions { /// # creation_fee: 1000000, // 1 XLM /// # min_stake: 100000, // 0.1 XLM /// # }; - /// + /// /// // Update platform fees /// match AdminFunctions::update_fee_config(&env, &admin, &new_config) { /// Ok(updated_config) => { @@ -1612,8 +1623,14 @@ impl AdminFunctions { // Log admin action let mut params = Map::new(env); - params.set(String::from_str(env, "platform_fee"), String::from_str(env, "platform_fee")); - params.set(String::from_str(env, "creation_fee"), String::from_str(env, "creation_fee")); + params.set( + String::from_str(env, "platform_fee"), + String::from_str(env, "platform_fee"), + ); + params.set( + String::from_str(env, "creation_fee"), + String::from_str(env, "creation_fee"), + ); AdminActionLogger::log_action(env, admin, "update_fees", None, params, true, None)?; Ok(updated_config) @@ -1660,7 +1677,7 @@ impl AdminFunctions { /// # max_outcomes_per_market: 10, /// # oracle_timeout_seconds: 3600, /// # }; - /// + /// /// // Update contract configuration for mainnet /// match AdminFunctions::update_contract_config(&env, &admin, &new_config) { /// Ok(()) => { @@ -1762,7 +1779,7 @@ impl AdminFunctions { /// # use predictify_hybrid::admin::AdminFunctions; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Reset configuration to defaults after problematic changes /// match AdminFunctions::reset_config_to_defaults(&env, &admin) { /// Ok(default_config) => { @@ -1825,10 +1842,7 @@ impl AdminFunctions { /// - Update fee structures if needed /// - Verify oracle integrations work correctly /// - Test market creation and resolution - pub fn reset_config_to_defaults( - env: &Env, - admin: &Address, - ) -> Result { + pub fn reset_config_to_defaults(env: &Env, admin: &Address) -> Result { // Validate admin permissions AdminAccessControl::validate_admin_for_action(env, admin, "reset_config")?; @@ -1899,7 +1913,7 @@ impl AdminValidator { /// # use predictify_hybrid::admin::AdminValidator; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Validate admin address before operations /// match AdminValidator::validate_admin_address(&env, &admin) { /// Ok(()) => { @@ -1960,7 +1974,7 @@ impl AdminValidator { /// # use soroban_sdk::Env; /// # use predictify_hybrid::admin::AdminValidator; /// # let env = Env::default(); - /// + /// /// // Check if contract can be initialized /// match AdminValidator::validate_contract_not_initialized(&env) { /// Ok(()) => { @@ -1992,7 +2006,7 @@ impl AdminValidator { /// # use predictify_hybrid::admin::{AdminValidator, AdminInitializer}; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Safe initialization pattern /// AdminValidator::validate_contract_not_initialized(&env)?; /// AdminInitializer::initialize_contract(&env, &admin)?; @@ -2006,10 +2020,7 @@ impl AdminValidator { /// - Consider it a potential security incident /// - Provide clear error messages to legitimate callers pub fn validate_contract_not_initialized(env: &Env) -> Result<(), Error> { - let admin_exists = env - .storage() - .persistent() - .has(&Symbol::new(env, "Admin")); + let admin_exists = env.storage().persistent().has(&Symbol::new(env, "Admin")); if admin_exists { return Err(Error::InvalidState); @@ -2064,7 +2075,7 @@ impl AdminValidator { /// # String::from_str(&env, "outcome"), /// # String::from_str(&env, "Yes") /// # ); - /// + /// /// // Validate parameters for market finalization /// match AdminValidator::validate_action_parameters( /// &env, @@ -2117,7 +2128,7 @@ impl AdminValidator { /// # let admin = Address::generate(&env); /// # let action = "close_market"; /// # let params = Map::new(&env); - /// + /// /// // Validate before logging /// AdminValidator::validate_action_parameters(&env, action, ¶ms)?; /// AdminActionLogger::log_action(&env, &admin, action, None, params, true, None)?; @@ -2138,25 +2149,30 @@ impl AdminValidator { ) -> Result<(), Error> { match action { "close_market" => { - let market_id = parameters.get(String::from_str(env, "market_id")) + let market_id = parameters + .get(String::from_str(env, "market_id")) .ok_or(Error::InvalidInput)?; if market_id.is_empty() { return Err(Error::InvalidInput); } } "finalize_market" => { - let market_id = parameters.get(String::from_str(env, "market_id")) + let market_id = parameters + .get(String::from_str(env, "market_id")) .ok_or(Error::InvalidInput)?; - let outcome = parameters.get(String::from_str(env, "outcome")) + let outcome = parameters + .get(String::from_str(env, "outcome")) .ok_or(Error::InvalidInput)?; if market_id.is_empty() || outcome.is_empty() { return Err(Error::InvalidInput); } } "extend_market" => { - let market_id = parameters.get(String::from_str(env, "market_id")) + let market_id = parameters + .get(String::from_str(env, "market_id")) .ok_or(Error::InvalidInput)?; - let additional_days = parameters.get(String::from_str(env, "additional_days")) + let additional_days = parameters + .get(String::from_str(env, "additional_days")) .ok_or(Error::InvalidInput)?; if market_id.is_empty() || additional_days.is_empty() { return Err(Error::InvalidInput); @@ -2245,7 +2261,7 @@ impl AdminActionLogger { /// # String::from_str(&env, "outcome"), /// # String::from_str(&env, "Yes") /// # ); - /// + /// /// // Log successful market finalization /// match AdminActionLogger::log_action( /// &env, @@ -2370,13 +2386,13 @@ impl AdminActionLogger { /// # use soroban_sdk::Env; /// # use predictify_hybrid::admin::AdminActionLogger; /// # let env = Env::default(); - /// + /// /// // Retrieve recent admin actions for audit /// match AdminActionLogger::get_admin_actions(&env, 50) { /// Ok(actions) => { /// println!("Found {} admin actions", actions.len()); /// for action in actions { - /// println!("Action: {} by {:?} at {}", + /// println!("Action: {} by {:?} at {}", /// action.action, action.admin, action.timestamp); /// } /// }, @@ -2400,10 +2416,10 @@ impl AdminActionLogger { /// ```rust /// // Time-based partitioning /// let partition_key = format!("actions_{}", timestamp / PARTITION_SIZE); - /// + /// /// // Admin-based indexing /// let admin_index = format!("admin_actions_{}", admin); - /// + /// /// // Action type indexing /// let type_index = format!("action_type_{}", action_type); /// ``` @@ -2461,14 +2477,14 @@ impl AdminActionLogger { /// # use predictify_hybrid::admin::AdminActionLogger; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// // Get actions performed by a specific admin /// match AdminActionLogger::get_admin_actions_for_admin(&env, &admin, 25) { /// Ok(actions) => { /// println!("Admin performed {} actions", actions.len()); /// for action in actions { - /// println!("{}: {} ({})", - /// action.timestamp, + /// println!("{}: {} ({})", + /// action.timestamp, /// action.action, /// if action.success { "Success" } else { "Failed" } /// ); @@ -2498,7 +2514,7 @@ impl AdminActionLogger { /// // Store actions with admin-based indexing /// let admin_key = format!("admin_{}_{}", admin, timestamp); /// env.storage().persistent().set(&admin_key, &action); - /// + /// /// // Maintain admin action count /// let count_key = format!("admin_count_{}", admin); /// let current_count: u32 = env.storage().persistent().get(&count_key).unwrap_or(0); @@ -2591,25 +2607,51 @@ impl AdminUtils { AdminRole::MarketAdmin => String::from_str(&soroban_sdk::Env::default(), "MarketAdmin"), AdminRole::ConfigAdmin => String::from_str(&soroban_sdk::Env::default(), "ConfigAdmin"), AdminRole::FeeAdmin => String::from_str(&soroban_sdk::Env::default(), "FeeAdmin"), - AdminRole::ReadOnlyAdmin => String::from_str(&soroban_sdk::Env::default(), "ReadOnlyAdmin"), + AdminRole::ReadOnlyAdmin => { + String::from_str(&soroban_sdk::Env::default(), "ReadOnlyAdmin") + } } } /// Get permission name pub fn get_permission_name(permission: &AdminPermission) -> String { match permission { - AdminPermission::Initialize => String::from_str(&soroban_sdk::Env::default(), "Initialize"), - AdminPermission::CreateMarket => String::from_str(&soroban_sdk::Env::default(), "CreateMarket"), - AdminPermission::CloseMarket => String::from_str(&soroban_sdk::Env::default(), "CloseMarket"), - AdminPermission::FinalizeMarket => String::from_str(&soroban_sdk::Env::default(), "FinalizeMarket"), - AdminPermission::ExtendMarket => String::from_str(&soroban_sdk::Env::default(), "ExtendMarket"), - AdminPermission::UpdateFees => String::from_str(&soroban_sdk::Env::default(), "UpdateFees"), - AdminPermission::UpdateConfig => String::from_str(&soroban_sdk::Env::default(), "UpdateConfig"), - AdminPermission::ResetConfig => String::from_str(&soroban_sdk::Env::default(), "ResetConfig"), - AdminPermission::CollectFees => String::from_str(&soroban_sdk::Env::default(), "CollectFees"), - AdminPermission::ManageDisputes => String::from_str(&soroban_sdk::Env::default(), "ManageDisputes"), - AdminPermission::ViewAnalytics => String::from_str(&soroban_sdk::Env::default(), "ViewAnalytics"), - AdminPermission::EmergencyActions => String::from_str(&soroban_sdk::Env::default(), "EmergencyActions"), + AdminPermission::Initialize => { + String::from_str(&soroban_sdk::Env::default(), "Initialize") + } + AdminPermission::CreateMarket => { + String::from_str(&soroban_sdk::Env::default(), "CreateMarket") + } + AdminPermission::CloseMarket => { + String::from_str(&soroban_sdk::Env::default(), "CloseMarket") + } + AdminPermission::FinalizeMarket => { + String::from_str(&soroban_sdk::Env::default(), "FinalizeMarket") + } + AdminPermission::ExtendMarket => { + String::from_str(&soroban_sdk::Env::default(), "ExtendMarket") + } + AdminPermission::UpdateFees => { + String::from_str(&soroban_sdk::Env::default(), "UpdateFees") + } + AdminPermission::UpdateConfig => { + String::from_str(&soroban_sdk::Env::default(), "UpdateConfig") + } + AdminPermission::ResetConfig => { + String::from_str(&soroban_sdk::Env::default(), "ResetConfig") + } + AdminPermission::CollectFees => { + String::from_str(&soroban_sdk::Env::default(), "CollectFees") + } + AdminPermission::ManageDisputes => { + String::from_str(&soroban_sdk::Env::default(), "ManageDisputes") + } + AdminPermission::ViewAnalytics => { + String::from_str(&soroban_sdk::Env::default(), "ViewAnalytics") + } + AdminPermission::EmergencyActions => { + String::from_str(&soroban_sdk::Env::default(), "EmergencyActions") + } } } } @@ -2653,16 +2695,12 @@ impl AdminTesting { // Note: In test environments, timestamp can be 0, so we skip this validation // In production, you might want to add env parameter to enable this check - + Ok(()) } /// Simulate admin action - pub fn simulate_admin_action( - env: &Env, - admin: &Address, - action: &str, - ) -> Result<(), Error> { + pub fn simulate_admin_action(env: &Env, admin: &Address, action: &str) -> Result<(), Error> { // Log test action AdminActionLogger::log_action( env, @@ -2701,7 +2739,7 @@ impl Default for AdminAnalytics { #[cfg(test)] mod tests { use super::*; - use soroban_sdk::testutils::{Address as _,}; + use soroban_sdk::testutils::Address as _; #[test] fn test_admin_initializer_initialize() { @@ -2714,7 +2752,8 @@ mod tests { assert!(AdminInitializer::initialize(&env, &admin).is_ok()); // Verify admin is stored - let stored_admin: Address = env.storage() + let stored_admin: Address = env + .storage() .persistent() .get(&Symbol::new(&env, "Admin")) .unwrap(); @@ -2737,7 +2776,8 @@ mod tests { &env, &admin, &AdminPermission::CreateMarket - ).is_ok()); + ) + .is_ok()); }); } @@ -2758,7 +2798,8 @@ mod tests { &new_admin, AdminRole::MarketAdmin, &admin - ).is_ok()); + ) + .is_ok()); // Verify role assignment let role = AdminRoleManager::get_admin_role(&env, &new_admin).unwrap(); @@ -2781,7 +2822,7 @@ mod tests { // For now, just test the permission mapping and validation without auth let permission = AdminAccessControl::map_action_to_permission("close_market").unwrap(); assert_eq!(permission, AdminPermission::CloseMarket); - + // Test that the admin has the required permission assert!(AdminAccessControl::validate_permission(&env, &admin, &permission).is_ok()); }); @@ -2819,4 +2860,4 @@ mod tests { assert_eq!(role_assignment.role, AdminRole::MarketAdmin); assert!(role_assignment.is_active); } -} \ No newline at end of file +} diff --git a/contracts/predictify-hybrid/src/config.rs b/contracts/predictify-hybrid/src/config.rs index fb2015f6..01fc4a54 100644 --- a/contracts/predictify-hybrid/src/config.rs +++ b/contracts/predictify-hybrid/src/config.rs @@ -169,10 +169,10 @@ pub const ORACLE_STATS_STORAGE_KEY: &str = "OracleStats"; /// /// ```rust /// # use predictify_hybrid::config::Environment; -/// +/// /// // Environment comparison and selection /// let current_env = Environment::Mainnet; -/// +/// /// match current_env { /// Environment::Development => { /// println!("Using development settings with relaxed rules"); @@ -200,7 +200,7 @@ pub enum Environment { /// - Permissive market creation limits /// - Debug-friendly error messages Development, - + /// Testnet environment that mirrors production settings. /// /// Characteristics: @@ -210,7 +210,7 @@ pub enum Environment { /// - Comprehensive testing capabilities /// - Integration testing support Testnet, - + /// Production mainnet environment with full security. /// /// Characteristics: @@ -220,7 +220,7 @@ pub enum Environment { /// - Maximum security features /// - Audit-ready configuration Mainnet, - + /// Custom environment for specialized deployments. /// /// Characteristics: @@ -261,7 +261,7 @@ pub enum Environment { /// # use predictify_hybrid::config::{NetworkConfig, Environment}; /// # let env = Env::default(); /// # let contract_addr = Address::generate(&env); -/// +/// /// // Create mainnet network configuration /// let mainnet_config = NetworkConfig { /// environment: Environment::Mainnet, @@ -270,8 +270,8 @@ pub enum Environment { /// network_id: String::from_str(&env, "mainnet"), /// contract_address: contract_addr, /// }; -/// -/// println!("Configured for {} environment", +/// +/// println!("Configured for {} environment", /// match mainnet_config.environment { /// Environment::Mainnet => "production", /// Environment::Testnet => "testing", @@ -288,7 +288,7 @@ pub struct NetworkConfig { /// This determines which network-specific settings and validation /// rules are applied throughout the contract. pub environment: Environment, - + /// Stellar network passphrase for transaction signing. /// /// Standard passphrases: @@ -296,7 +296,7 @@ pub struct NetworkConfig { /// - Testnet: "Test SDF Network ; September 2015" /// - Development: Custom passphrase for local networks pub passphrase: String, - + /// RPC endpoint URL for blockchain interactions. /// /// Examples: @@ -304,7 +304,7 @@ pub struct NetworkConfig { /// - Testnet: "https://horizon-testnet.stellar.org" /// - Development: "http://localhost:8000" pub rpc_url: String, - + /// Network identifier for configuration management. /// /// Used for: @@ -313,7 +313,7 @@ pub struct NetworkConfig { /// - Deployment tracking /// - Environment validation pub network_id: String, - + /// The deployed contract's address on this network. /// /// This address is used for: @@ -350,7 +350,7 @@ pub struct NetworkConfig { /// /// ```rust /// # use predictify_hybrid::config::FeeConfig; -/// +/// /// // Create a balanced fee configuration /// let fee_config = FeeConfig { /// platform_fee_percentage: 250, // 2.5% of winnings @@ -360,7 +360,7 @@ pub struct NetworkConfig { /// collection_threshold: 50_000_000, // Collect at 5 XLM /// fees_enabled: true, // Fees are active /// }; -/// +/// /// // Calculate platform fee for a 100 XLM payout /// let payout = 1_000_000_000; // 100 XLM /// let platform_fee = (payout * fee_config.platform_fee_percentage) / 10000; @@ -379,7 +379,7 @@ pub struct FeeConfig { /// /// Range: 0-1000 (0% to 10%) pub platform_fee_percentage: i128, - + /// Fixed fee required to create a new prediction market (in stroops). /// /// This fee prevents spam market creation and covers: @@ -392,25 +392,25 @@ pub struct FeeConfig { /// - Testnet: 10_000_000 (1 XLM) /// - Mainnet: 50_000_000 (5 XLM) pub creation_fee: i128, - + /// Minimum fee amount that can be charged (in stroops). /// /// Ensures fees are meaningful and cover basic operational costs. /// Prevents dust fees that could clog the system. pub min_fee_amount: i128, - + /// Maximum fee amount that can be charged (in stroops). /// /// Protects users from excessive fees on large markets. /// Provides predictable cost ceiling for high-value markets. pub max_fee_amount: i128, - + /// Threshold amount for automatic fee collection (in stroops). /// /// When accumulated fees reach this threshold, they are automatically /// collected to reduce gas costs and improve efficiency. pub collection_threshold: i128, - + /// Global flag to enable or disable all fee collection. /// /// When false: @@ -446,7 +446,7 @@ pub struct FeeConfig { /// /// ```rust /// # use predictify_hybrid::config::VotingConfig; -/// +/// /// // Create balanced voting configuration /// let voting_config = VotingConfig { /// min_vote_stake: 1_000_000, // 0.1 XLM minimum vote @@ -457,7 +457,7 @@ pub struct FeeConfig { /// high_activity_threshold: 100, // 100+ votes = high activity /// dispute_extension_hours: 24, // 24 hour dispute window /// }; -/// +/// /// // Check if market qualifies as large /// let market_volume = 500_000_000; // 50 XLM /// let is_large_market = market_volume >= voting_config.large_market_threshold; @@ -474,25 +474,25 @@ pub struct VotingConfig { /// - Testnet: 1_000_000 (0.1 XLM) /// - Mainnet: 5_000_000 (0.5 XLM) pub min_vote_stake: i128, - + /// Minimum stake required to initiate a dispute (in stroops). /// /// Higher than voting stake to prevent frivolous disputes. /// Should be meaningful but not prohibitive for legitimate disputes. pub min_dispute_stake: i128, - + /// Maximum dispute threshold that can be required (in stroops). /// /// Caps dispute costs to ensure accessibility while maintaining /// serious commitment from disputers. pub max_dispute_threshold: i128, - + /// Base dispute threshold for standard markets (in stroops). /// /// Starting point for dispute cost calculations. /// May be adjusted based on market size and activity. pub base_dispute_threshold: i128, - + /// Total stake threshold that defines a "large" market (in stroops). /// /// Large markets may have: @@ -500,7 +500,7 @@ pub struct VotingConfig { /// - Extended resolution periods /// - Additional validation requirements pub large_market_threshold: i128, - + /// Vote count threshold that defines "high activity" markets. /// /// High activity markets may receive: @@ -508,7 +508,7 @@ pub struct VotingConfig { /// - Enhanced dispute mechanisms /// - Additional monitoring pub high_activity_threshold: u32, - + /// Additional hours added to resolution period when disputed. /// /// Provides time for: @@ -545,7 +545,7 @@ pub struct VotingConfig { /// /// ```rust /// # use predictify_hybrid::config::MarketConfig; -/// +/// /// // Create production market configuration /// let market_config = MarketConfig { /// max_duration_days: 365, // Up to 1 year markets @@ -555,18 +555,18 @@ pub struct VotingConfig { /// max_question_length: 500, // 500 character questions /// max_outcome_length: 100, // 100 character outcomes /// }; -/// +/// /// // Validate a market proposal /// let question = "Will Bitcoin reach $100,000 by end of 2024?"; /// let outcomes = vec!["Yes", "No"]; /// let duration = 90; // 3 months -/// +/// /// let valid_question = question.len() <= market_config.max_question_length as usize; -/// let valid_outcomes = outcomes.len() >= market_config.min_outcomes as usize && +/// let valid_outcomes = outcomes.len() >= market_config.min_outcomes as usize && /// outcomes.len() <= market_config.max_outcomes as usize; -/// let valid_duration = duration >= market_config.min_duration_days && +/// let valid_duration = duration >= market_config.min_duration_days && /// duration <= market_config.max_duration_days; -/// +/// /// println!("Market valid: {}", valid_question && valid_outcomes && valid_duration); /// ``` #[derive(Clone, Debug)] @@ -582,7 +582,7 @@ pub struct MarketConfig { /// /// Typical values: 30-365 days pub max_duration_days: u32, - + /// Minimum required market duration in days. /// /// Ensures markets have sufficient time for: @@ -592,7 +592,7 @@ pub struct MarketConfig { /// /// Typical values: 1-7 days pub min_duration_days: u32, - + /// Maximum number of possible outcomes per market. /// /// Limits complexity while supporting diverse market types: @@ -605,13 +605,13 @@ pub struct MarketConfig { /// - UI complexity /// - Resolution difficulty pub max_outcomes: u32, - + /// Minimum number of required outcomes per market. /// /// Typically 2 to ensure meaningful prediction markets. /// Single-outcome markets don't provide prediction value. pub min_outcomes: u32, - + /// Maximum length for market questions in characters. /// /// Balances between: @@ -622,7 +622,7 @@ pub struct MarketConfig { /// /// Typical range: 200-1000 characters pub max_question_length: u32, - + /// Maximum length for outcome descriptions in characters. /// /// Keeps outcome descriptions: @@ -661,7 +661,7 @@ pub struct MarketConfig { /// /// ```rust /// # use predictify_hybrid::config::ExtensionConfig; -/// +/// /// // Create extension configuration /// let extension_config = ExtensionConfig { /// max_extension_days: 30, // Up to 30 days per extension @@ -669,12 +669,12 @@ pub struct MarketConfig { /// fee_per_day: 1_000_000, // 0.1 XLM per day /// max_total_extensions: 3, // Maximum 3 extensions per market /// }; -/// +/// /// // Calculate extension cost /// let extension_days = 7; /// let total_cost = extension_days as i128 * extension_config.fee_per_day; /// println!("7-day extension costs: {} stroops", total_cost); // 7,000,000 stroops -/// +/// /// // Check if extension is valid /// let current_extensions = 2; /// let can_extend = current_extensions < extension_config.max_total_extensions && @@ -692,13 +692,13 @@ pub struct ExtensionConfig { /// - Development: 7-14 days /// - Production: 14-30 days pub max_extension_days: u32, - + /// Minimum number of days required for an extension. /// /// Ensures extensions are meaningful and worth the administrative /// overhead. Typically 1-3 days minimum. pub min_extension_days: u32, - + /// Fee charged per day of extension (in stroops). /// /// This fee: @@ -711,7 +711,7 @@ pub struct ExtensionConfig { /// - Testnet: 1_000_000 (0.1 XLM/day) /// - Mainnet: 5_000_000 (0.5 XLM/day) pub fee_per_day: i128, - + /// Maximum number of extensions allowed per market. /// /// Prevents indefinite market extensions while allowing @@ -746,7 +746,7 @@ pub struct ExtensionConfig { /// /// ```rust /// # use predictify_hybrid::config::ResolutionConfig; -/// +/// /// // Create balanced resolution configuration /// let resolution_config = ResolutionConfig { /// min_confidence_score: 0, // 0% minimum confidence @@ -755,15 +755,15 @@ pub struct ExtensionConfig { /// community_weight_percentage: 30, // Community has 30% influence /// min_votes_for_consensus: 5, // Need at least 5 votes /// }; -/// +/// /// // Calculate weighted resolution /// let oracle_confidence = 85; // Oracle 85% confident /// let community_confidence = 92; // Community 92% confident -/// -/// let weighted_confidence = +/// +/// let weighted_confidence = /// (oracle_confidence * resolution_config.oracle_weight_percentage + /// community_confidence * resolution_config.community_weight_percentage) / 100; -/// +/// /// println!("Final confidence: {}%", weighted_confidence); // 87% /// ``` #[derive(Clone, Debug)] @@ -774,13 +774,13 @@ pub struct ResolutionConfig { /// Represents the lowest confidence level that can be assigned /// to a market resolution. Usually 0 to allow full range. pub min_confidence_score: u32, - + /// Maximum allowed confidence score (typically 100). /// /// Represents the highest confidence level that can be assigned /// to a market resolution. Usually 100 for percentage-based scoring. pub max_confidence_score: u32, - + /// Percentage weight given to oracle data in hybrid resolution. /// /// Determines how much influence oracle data has in the final @@ -791,7 +791,7 @@ pub struct ResolutionConfig { /// - Balanced approach: 50-60% /// - Community focused: 30-40% pub oracle_weight_percentage: u32, - + /// Percentage weight given to community voting in hybrid resolution. /// /// Determines how much influence community consensus has in the @@ -802,7 +802,7 @@ pub struct ResolutionConfig { /// - Resistance to oracle manipulation /// - Democratic decision making pub community_weight_percentage: u32, - + /// Minimum number of community votes required for valid consensus. /// /// Ensures community input is meaningful and representative. @@ -839,18 +839,18 @@ pub struct ResolutionConfig { /// /// ```rust /// # use predictify_hybrid::config::OracleConfig; -/// +/// /// // Create production oracle configuration /// let oracle_config = OracleConfig { /// max_price_age: 3600, // 1 hour maximum data age /// retry_attempts: 3, // Try up to 3 times /// timeout_seconds: 30, // 30 second timeout per attempt /// }; -/// +/// /// // Calculate total maximum wait time /// let max_wait_time = oracle_config.retry_attempts as u64 * oracle_config.timeout_seconds; /// println!("Maximum oracle wait: {} seconds", max_wait_time); // 90 seconds -/// +/// /// // Check if data is fresh enough /// let data_age = 1800; // 30 minutes old /// let is_fresh = data_age <= oracle_config.max_price_age; @@ -869,7 +869,7 @@ pub struct OracleConfig { /// - Standard markets: 1800-3600 seconds (30-60 minutes) /// - Long-term markets: 3600-7200 seconds (1-2 hours) pub max_price_age: u64, - + /// Number of retry attempts for failed oracle requests. /// /// Provides resilience against: @@ -880,7 +880,7 @@ pub struct OracleConfig { /// /// Typical values: 2-5 retries pub retry_attempts: u32, - + /// Timeout duration for each oracle request (in seconds). /// /// Balances between: @@ -932,19 +932,19 @@ pub struct OracleConfig { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::{ConfigManager, Environment}; /// # let env = Env::default(); -/// +/// /// // Get environment-specific configurations /// let dev_config = ConfigManager::get_development_config(&env); /// let mainnet_config = ConfigManager::get_mainnet_config(&env); -/// +/// /// // Compare environment differences /// println!("Dev creation fee: {} stroops", dev_config.fees.creation_fee); /// println!("Mainnet creation fee: {} stroops", mainnet_config.fees.creation_fee); -/// +/// /// // Access nested configuration /// println!("Oracle timeout: {} seconds", dev_config.oracle.timeout_seconds); /// println!("Max market duration: {} days", dev_config.market.max_duration_days); -/// +/// /// // Validate environment consistency /// assert_eq!(dev_config.network.environment, Environment::Development); /// assert_eq!(mainnet_config.network.environment, Environment::Mainnet); @@ -983,37 +983,37 @@ pub struct ContractConfig { /// Defines which Stellar network the contract operates on, /// connection parameters, and environment-specific settings. pub network: NetworkConfig, - + /// Economic model and fee structure configuration. /// /// Controls platform fees, creation costs, limits, and /// collection thresholds for sustainable operation. pub fees: FeeConfig, - + /// Voting and dispute mechanism configuration. /// /// Governs community participation, stake requirements, /// and dispute resolution processes. pub voting: VotingConfig, - + /// Market creation and structure configuration. /// /// Defines constraints for market creation including /// duration limits, outcome counts, and content restrictions. pub market: MarketConfig, - + /// Market duration extension configuration. /// /// Controls how markets can be extended beyond their /// original duration, including fees and limits. pub extension: ExtensionConfig, - + /// Market resolution and confidence scoring configuration. /// /// Defines how markets are resolved using hybrid oracle /// and community consensus mechanisms. pub resolution: ResolutionConfig, - + /// Oracle integration and reliability configuration. /// /// Controls how the contract interacts with external @@ -1062,14 +1062,14 @@ pub struct ContractConfig { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::{ConfigManager, Environment}; /// # let env = Env::default(); -/// +/// /// // Get environment-appropriate configurations /// let dev_config = ConfigManager::get_development_config(&env); /// let mainnet_config = ConfigManager::get_mainnet_config(&env); -/// +/// /// // Store configuration in contract /// ConfigManager::store_config(&env, &mainnet_config).unwrap(); -/// +/// /// // Retrieve stored configuration /// let stored_config = ConfigManager::get_config(&env).unwrap(); /// assert_eq!(stored_config.network.environment, Environment::Mainnet); @@ -1107,16 +1107,16 @@ impl ConfigManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::{ConfigManager, Environment}; /// # let env = Env::default(); - /// + /// /// // Get development configuration /// let dev_config = ConfigManager::get_development_config(&env); - /// + /// /// // Verify development characteristics /// assert_eq!(dev_config.network.environment, Environment::Development); /// assert!(dev_config.fees.creation_fee < 10_000_000); // Less than 1 XLM /// assert!(dev_config.oracle.timeout_seconds <= 30); // Quick timeouts - /// - /// println!("Development config ready with {} second oracle timeout", + /// + /// println!("Development config ready with {} second oracle timeout", /// dev_config.oracle.timeout_seconds); /// ``` /// @@ -1193,20 +1193,20 @@ impl ConfigManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::{ConfigManager, Environment}; /// # let env = Env::default(); - /// + /// /// // Get testnet configuration /// let testnet_config = ConfigManager::get_testnet_config(&env); - /// + /// /// // Verify testnet characteristics /// assert_eq!(testnet_config.network.environment, Environment::Testnet); - /// assert_eq!(testnet_config.network.passphrase.to_string(), + /// assert_eq!(testnet_config.network.passphrase.to_string(), /// "Test SDF Network ; September 2015"); - /// + /// /// // Compare with development settings /// let dev_config = ConfigManager::get_development_config(&env); /// assert!(testnet_config.fees.creation_fee >= dev_config.fees.creation_fee); - /// - /// println!("Testnet config with {} XLM creation fee", + /// + /// println!("Testnet config with {} XLM creation fee", /// testnet_config.fees.creation_fee / 10_000_000); /// ``` /// @@ -1286,20 +1286,20 @@ impl ConfigManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::{ConfigManager, Environment}; /// # let env = Env::default(); - /// + /// /// // Get mainnet configuration /// let mainnet_config = ConfigManager::get_mainnet_config(&env); - /// + /// /// // Verify mainnet characteristics /// assert_eq!(mainnet_config.network.environment, Environment::Mainnet); - /// assert_eq!(mainnet_config.network.passphrase.to_string(), + /// assert_eq!(mainnet_config.network.passphrase.to_string(), /// "Public Global Stellar Network ; September 2015"); - /// + /// /// // Check production-grade settings /// assert!(mainnet_config.fees.creation_fee >= 10_000_000); // At least 1 XLM /// assert!(mainnet_config.fees.platform_fee_percentage >= 200); // At least 2% - /// - /// println!("Mainnet config with {}% platform fee", + /// + /// println!("Mainnet config with {}% platform fee", /// mainnet_config.fees.platform_fee_percentage / 100); /// ``` /// @@ -1385,15 +1385,15 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get default fee configuration /// let fee_config = ConfigManager::get_default_fee_config(); - /// + /// /// // Verify default values /// assert_eq!(fee_config.platform_fee_percentage, 2); // 2% /// assert_eq!(fee_config.creation_fee, 10_000_000); // 1 XLM /// assert!(fee_config.fees_enabled); - /// + /// /// println!("Default platform fee: {}%", fee_config.platform_fee_percentage); /// ``` /// @@ -1445,15 +1445,15 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get mainnet fee configuration /// let mainnet_fees = ConfigManager::get_mainnet_fee_config(); /// let default_fees = ConfigManager::get_default_fee_config(); - /// + /// /// // Compare mainnet vs default /// assert!(mainnet_fees.platform_fee_percentage > default_fees.platform_fee_percentage); /// assert!(mainnet_fees.creation_fee > default_fees.creation_fee); - /// + /// /// println!("Mainnet creation fee: {} XLM", mainnet_fees.creation_fee / 10_000_000); /// ``` /// @@ -1509,14 +1509,14 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get default voting configuration /// let voting_config = ConfigManager::get_default_voting_config(); - /// + /// /// // Check accessibility /// assert_eq!(voting_config.min_vote_stake, 1_000_000); // 0.1 XLM /// assert_eq!(voting_config.dispute_extension_hours, 24); - /// + /// /// // Calculate dispute cost for standard market /// let dispute_cost = voting_config.base_dispute_threshold; /// println!("Base dispute cost: {} XLM", dispute_cost / 10_000_000); @@ -1575,16 +1575,16 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get mainnet voting configuration /// let mainnet_voting = ConfigManager::get_mainnet_voting_config(); /// let default_voting = ConfigManager::get_default_voting_config(); - /// + /// /// // Compare mainnet vs default security /// assert!(mainnet_voting.min_dispute_stake > default_voting.min_dispute_stake); /// assert!(mainnet_voting.dispute_extension_hours > default_voting.dispute_extension_hours); - /// - /// println!("Mainnet dispute stake: {} XLM", + /// + /// println!("Mainnet dispute stake: {} XLM", /// mainnet_voting.min_dispute_stake / 10_000_000); /// ``` /// @@ -1641,19 +1641,19 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get default market configuration /// let market_config = ConfigManager::get_default_market_config(); - /// + /// /// // Validate a market proposal /// let question = "Will Bitcoin reach $100,000 by end of 2024?"; /// let outcomes = vec!["Yes", "No"]; /// let duration_days = 90; - /// + /// /// let valid_question = question.len() <= market_config.max_question_length as usize; /// let valid_outcomes = outcomes.len() >= market_config.min_outcomes as usize; /// let valid_duration = duration_days <= market_config.max_duration_days; - /// + /// /// assert!(valid_question && valid_outcomes && valid_duration); /// ``` /// @@ -1707,19 +1707,19 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get default extension configuration /// let ext_config = ConfigManager::get_default_extension_config(); - /// + /// /// // Calculate extension cost /// let extension_days = 7; /// let total_cost = extension_days as i128 * ext_config.fee_per_day; - /// + /// /// // Check if extension is allowed /// let current_extensions = 2; /// let can_extend = current_extensions < ext_config.max_total_extensions && /// extension_days <= ext_config.max_extension_days; - /// + /// /// println!("7-day extension costs: {} XLM", total_cost / 10_000_000); /// assert!(can_extend); /// ``` @@ -1773,16 +1773,16 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get default resolution configuration /// let resolution_config = ConfigManager::get_default_resolution_config(); - /// + /// /// // Check hybrid weighting /// assert_eq!(resolution_config.oracle_weight_percentage, 60); /// assert_eq!(resolution_config.community_weight_percentage, 40); - /// assert_eq!(resolution_config.oracle_weight_percentage + + /// assert_eq!(resolution_config.oracle_weight_percentage + /// resolution_config.community_weight_percentage, 100); - /// + /// /// // Verify confidence thresholds /// assert!(resolution_config.min_confidence_score >= 70); /// println!("Minimum confidence required: {}%", resolution_config.min_confidence_score); @@ -1844,17 +1844,17 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get default oracle configuration /// let oracle_config = ConfigManager::get_default_oracle_config(); - /// + /// /// // Check data freshness requirements /// assert_eq!(oracle_config.max_price_age, 3600); // 1 hour - /// + /// /// // Verify reliability settings /// assert_eq!(oracle_config.retry_attempts, 3); /// assert_eq!(oracle_config.timeout_seconds, 30); - /// + /// /// // Calculate maximum total time for oracle resolution /// let max_resolution_time = oracle_config.retry_attempts * oracle_config.timeout_seconds; /// println!("Maximum oracle resolution time: {} seconds", max_resolution_time); @@ -1921,17 +1921,17 @@ impl ConfigManager { /// /// ```rust /// # use predictify_hybrid::config::ConfigManager; - /// + /// /// // Get mainnet oracle configuration /// let mainnet_oracle = ConfigManager::get_mainnet_oracle_config(); /// let default_oracle = ConfigManager::get_default_oracle_config(); - /// + /// /// // Compare mainnet vs default strictness /// assert!(mainnet_oracle.max_price_age < default_oracle.max_price_age); /// assert!(mainnet_oracle.retry_attempts > default_oracle.retry_attempts); /// assert!(mainnet_oracle.timeout_seconds > default_oracle.timeout_seconds); - /// - /// println!("Mainnet data freshness: {} minutes", + /// + /// println!("Mainnet data freshness: {} minutes", /// mainnet_oracle.max_price_age / 60); /// ``` /// @@ -1980,13 +1980,13 @@ impl ConfigManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::ConfigManager; /// # let env = Env::default(); - /// + /// /// // Create and store development configuration /// let dev_config = ConfigManager::get_development_config(&env); /// let result = ConfigManager::store_config(&env, &dev_config); - /// + /// /// assert!(result.is_ok()); - /// + /// /// // Verify storage by retrieving /// let retrieved_config = ConfigManager::get_config(&env).unwrap(); /// assert_eq!(retrieved_config.network.environment, dev_config.network.environment); @@ -2034,19 +2034,19 @@ impl ConfigManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::{ConfigManager, Environment}; /// # let env = Env::default(); - /// + /// /// // Store a configuration first /// let testnet_config = ConfigManager::get_testnet_config(&env); /// ConfigManager::store_config(&env, &testnet_config).unwrap(); - /// + /// /// // Retrieve the stored configuration /// let current_config = ConfigManager::get_config(&env).unwrap(); - /// + /// /// // Verify it matches what was stored /// assert_eq!(current_config.network.environment, Environment::Testnet); - /// assert_eq!(current_config.fees.platform_fee_percentage, + /// assert_eq!(current_config.fees.platform_fee_percentage, /// testnet_config.fees.platform_fee_percentage); - /// + /// /// println!("Current environment: {:?}", current_config.network.environment); /// ``` /// @@ -2094,19 +2094,19 @@ impl ConfigManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::ConfigManager; /// # let env = Env::default(); - /// + /// /// // Get current configuration /// let mut current_config = ConfigManager::get_development_config(&env); /// ConfigManager::store_config(&env, ¤t_config).unwrap(); - /// + /// /// // Modify fee settings /// current_config.fees.platform_fee_percentage = 3; // Increase to 3% /// current_config.fees.creation_fee = 15_000_000; // Increase to 1.5 XLM - /// + /// /// // Update stored configuration /// let result = ConfigManager::update_config(&env, ¤t_config); /// assert!(result.is_ok()); - /// + /// /// // Verify update /// let updated_config = ConfigManager::get_config(&env).unwrap(); /// assert_eq!(updated_config.fees.platform_fee_percentage, 3); @@ -2152,18 +2152,18 @@ impl ConfigManager { /// # use soroban_sdk::Env; /// # use predictify_hybrid::config::{ConfigManager, Environment}; /// # let env = Env::default(); - /// + /// /// // Store a custom configuration first /// let mainnet_config = ConfigManager::get_mainnet_config(&env); /// ConfigManager::store_config(&env, &mainnet_config).unwrap(); - /// + /// /// // Reset to development defaults /// let reset_config = ConfigManager::reset_to_defaults(&env).unwrap(); - /// + /// /// // Verify reset to development environment /// assert_eq!(reset_config.network.environment, Environment::Development); /// assert_eq!(reset_config.fees.platform_fee_percentage, 2); // Default 2% - /// + /// /// // Confirm it's stored /// let stored_config = ConfigManager::get_config(&env).unwrap(); /// assert_eq!(stored_config.network.environment, Environment::Development); diff --git a/contracts/predictify-hybrid/src/disputes.rs b/contracts/predictify-hybrid/src/disputes.rs index 0fee650d..d1fc36be 100644 --- a/contracts/predictify-hybrid/src/disputes.rs +++ b/contracts/predictify-hybrid/src/disputes.rs @@ -33,7 +33,7 @@ use soroban_sdk::{contracttype, symbol_short, Address, Env, Map, String, Symbol, /// # let env = Env::default(); /// # let user = Address::generate(&env); /// # let market_id = Symbol::new(&env, "market_123"); -/// +/// /// let dispute = Dispute { /// user: user.clone(), /// market_id: market_id.clone(), @@ -42,7 +42,7 @@ use soroban_sdk::{contracttype, symbol_short, Address, Env, Map, String, Symbol, /// reason: Some(String::from_str(&env, "Oracle data appears incorrect")), /// status: DisputeStatus::Active, /// }; -/// +/// /// // Dispute is now active and awaiting community voting /// assert_eq!(dispute.status, DisputeStatus::Active); /// ``` @@ -86,15 +86,15 @@ pub struct Dispute { /// /// ```rust /// # use predictify_hybrid::disputes::DisputeStatus; -/// +/// /// // Check if dispute can still receive votes /// let status = DisputeStatus::Active; /// let can_vote = matches!(status, DisputeStatus::Active); /// assert!(can_vote); -/// +/// /// // Check if dispute is finalized /// let final_status = DisputeStatus::Resolved; -/// let is_final = matches!(final_status, +/// let is_final = matches!(final_status, /// DisputeStatus::Resolved | DisputeStatus::Rejected | DisputeStatus::Expired /// ); /// assert!(is_final); @@ -106,7 +106,7 @@ pub struct Dispute { /// - `Active` → `Resolved` (community upholds dispute) /// - `Active` → `Rejected` (community rejects dispute) /// - `Active` → `Expired` (insufficient voting participation) -/// +/// /// Invalid transitions: /// - Any final status → Any other status (disputes are immutable once resolved) /// @@ -142,7 +142,7 @@ pub enum DisputeStatus { /// /// ```rust /// # use predictify_hybrid::disputes::DisputeStats; -/// +/// /// let stats = DisputeStats { /// total_disputes: 3, /// total_dispute_stakes: 50_000_000, // 5 XLM total @@ -150,11 +150,11 @@ pub enum DisputeStatus { /// resolved_disputes: 2, /// unique_disputers: 3, /// }; -/// +/// /// // Calculate average stake per dispute /// let avg_stake = stats.total_dispute_stakes / stats.total_disputes as i128; /// assert_eq!(avg_stake, 16_666_666); // ~1.67 XLM average -/// +/// /// // Check market controversy level /// let controversy_ratio = stats.total_disputes as f64 / 10.0; // Assume 10 total participants /// println!("Market controversy: {:.1}%", controversy_ratio * 100.0); @@ -204,7 +204,7 @@ pub struct DisputeStats { /// # use soroban_sdk::{Env, Symbol, String}; /// # use predictify_hybrid::disputes::DisputeResolution; /// # let env = Env::default(); -/// +/// /// let resolution = DisputeResolution { /// market_id: Symbol::new(&env, "btc_100k"), /// final_outcome: String::from_str(&env, "No"), @@ -213,10 +213,10 @@ pub struct DisputeStats { /// dispute_impact: 25, // 25% change from original oracle result /// resolution_timestamp: env.ledger().timestamp(), /// }; -/// +/// /// // Verify hybrid resolution weights sum to 100% /// assert_eq!(resolution.oracle_weight + resolution.community_weight, 100); -/// +/// /// // Check if community significantly influenced outcome /// let community_influenced = resolution.dispute_impact > 20; /// assert!(community_influenced); @@ -276,7 +276,7 @@ pub struct DisputeResolution { /// # let env = Env::default(); /// # let voter = Address::generate(&env); /// # let dispute_id = Symbol::new(&env, "dispute_123"); -/// +/// /// let vote = DisputeVote { /// user: voter.clone(), /// dispute_id: dispute_id.clone(), @@ -285,7 +285,7 @@ pub struct DisputeResolution { /// timestamp: env.ledger().timestamp(), /// reason: Some(String::from_str(&env, "Oracle data contradicts reliable sources")), /// }; -/// +/// /// // Vote supports the dispute with economic backing /// assert!(vote.vote); /// assert!(vote.stake > 0); @@ -347,7 +347,7 @@ pub struct DisputeVote { /// # use soroban_sdk::{Env, Symbol}; /// # use predictify_hybrid::disputes::{DisputeVoting, DisputeVotingStatus}; /// # let env = Env::default(); -/// +/// /// let voting = DisputeVoting { /// dispute_id: Symbol::new(&env, "dispute_123"), /// voting_start: env.ledger().timestamp(), @@ -359,12 +359,12 @@ pub struct DisputeVote { /// total_against_stake: 20_000_000, // 2.0 XLM /// status: DisputeVotingStatus::Active, /// }; -/// +/// /// // Calculate voting metrics /// let participation_rate = voting.total_votes as f64 / 100.0; // Assume 100 eligible voters /// let stake_ratio = voting.total_support_stake as f64 / voting.total_against_stake as f64; -/// -/// println!("Participation: {:.1}%, Stake ratio: {:.2}", +/// +/// println!("Participation: {:.1}%, Stake ratio: {:.2}", /// participation_rate * 100.0, stake_ratio); /// ``` /// @@ -420,17 +420,17 @@ pub struct DisputeVoting { /// /// ```rust /// # use predictify_hybrid::disputes::DisputeVotingStatus; -/// +/// /// // Check if voting is still accepting votes /// let status = DisputeVotingStatus::Active; /// let can_vote = matches!(status, DisputeVotingStatus::Active); /// assert!(can_vote); -/// +/// /// // Check if voting has concluded /// let final_status = DisputeVotingStatus::Completed; -/// let is_concluded = matches!(final_status, -/// DisputeVotingStatus::Completed | -/// DisputeVotingStatus::Expired | +/// let is_concluded = matches!(final_status, +/// DisputeVotingStatus::Completed | +/// DisputeVotingStatus::Expired | /// DisputeVotingStatus::Cancelled /// ); /// assert!(is_concluded); @@ -442,7 +442,7 @@ pub struct DisputeVoting { /// - `Active` → `Completed` (successful voting completion) /// - `Active` → `Expired` (insufficient participation) /// - `Active` → `Cancelled` (administrative termination) -/// +/// /// Invalid transitions: /// - Any final status → Any other status (voting outcomes are immutable) /// @@ -482,17 +482,17 @@ pub enum DisputeVotingStatus { /// # use predictify_hybrid::disputes::DisputeEscalation; /// # let env = Env::default(); /// # let user = Address::generate(&env); -/// +/// /// let escalation = DisputeEscalation { /// dispute_id: Symbol::new(&env, "dispute_456"), /// escalated_by: user.clone(), -/// escalation_reason: String::from_str(&env, +/// escalation_reason: String::from_str(&env, /// "Voting resulted in exact tie, need admin decision"), /// escalation_timestamp: env.ledger().timestamp(), /// escalation_level: 1, // Admin review /// requires_admin_review: true, /// }; -/// +/// /// // Escalation requires admin intervention /// assert!(escalation.requires_admin_review); /// assert_eq!(escalation.escalation_level, 1); @@ -555,7 +555,7 @@ pub struct DisputeEscalation { /// # let mut winners = Vec::new(&env); /// # winners.push_back(Address::generate(&env)); /// # winners.push_back(Address::generate(&env)); -/// +/// /// let distribution = DisputeFeeDistribution { /// dispute_id: Symbol::new(&env, "dispute_789"), /// total_fees: 30_000_000, // 3 XLM total @@ -565,11 +565,11 @@ pub struct DisputeEscalation { /// distribution_timestamp: env.ledger().timestamp(), /// fees_distributed: true, /// }; -/// +/// /// // Calculate reward ratio /// let reward_ratio = distribution.loser_stake as f64 / distribution.winner_stake as f64; /// println!("Winners receive {:.1}% bonus", reward_ratio * 100.0); -/// +/// /// // Verify distribution completed /// assert!(distribution.fees_distributed); /// ``` @@ -695,7 +695,7 @@ pub struct TimeoutAnalytics { /// # let user = Address::generate(&env); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "market_123"); -/// +/// /// // User disputes a market result /// let result = DisputeManager::process_dispute( /// &env, @@ -704,7 +704,7 @@ pub struct TimeoutAnalytics { /// 10_000_000, // 1 XLM stake /// Some(String::from_str(&env, "Oracle data appears incorrect")) /// ); -/// +/// /// // Admin resolves the dispute after community voting /// let resolution = DisputeManager::resolve_dispute( /// &env, @@ -760,17 +760,17 @@ impl DisputeManager { /// # let env = Env::default(); /// # let user = Address::generate(&env); /// # let market_id = Symbol::new(&env, "btc_price_market"); - /// + /// /// // User disputes oracle result with reasoning /// let result = DisputeManager::process_dispute( /// &env, /// user.clone(), /// market_id.clone(), /// 15_000_000, // 1.5 XLM stake - /// Some(String::from_str(&env, + /// Some(String::from_str(&env, /// "Oracle price differs significantly from major exchanges")) /// ); - /// + /// /// match result { /// Ok(()) => println!("Dispute successfully created"), /// Err(e) => println!("Dispute failed: {:?}", e), @@ -871,20 +871,20 @@ impl DisputeManager { /// # let env = Env::default(); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "disputed_market"); - /// + /// /// // Admin resolves dispute after voting period /// let resolution = DisputeManager::resolve_dispute( /// &env, /// market_id.clone(), /// admin.clone() /// ).unwrap(); - /// + /// /// // Check resolution details /// println!("Final outcome: {}", resolution.final_outcome.to_string()); /// println!("Oracle weight: {}%", resolution.oracle_weight); /// println!("Community weight: {}%", resolution.community_weight); /// println!("Dispute impact: {}%", resolution.dispute_impact); - /// + /// /// // Verify weights sum to 100% /// assert_eq!(resolution.oracle_weight + resolution.community_weight, 100); /// ``` @@ -986,21 +986,21 @@ impl DisputeManager { /// # use predictify_hybrid::disputes::DisputeManager; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "analyzed_market"); - /// + /// /// // Get dispute statistics for analysis /// let stats = DisputeManager::get_dispute_stats(&env, market_id).unwrap(); - /// + /// /// // Analyze dispute activity /// println!("Total disputes: {}", stats.total_disputes); /// println!("Total stakes: {} XLM", stats.total_dispute_stakes / 10_000_000); /// println!("Unique disputers: {}", stats.unique_disputers); - /// + /// /// // Calculate engagement metrics /// let avg_stake = if stats.total_disputes > 0 { /// stats.total_dispute_stakes / stats.total_disputes as i128 /// } else { 0 }; /// println!("Average stake per dispute: {} XLM", avg_stake / 10_000_000); - /// + /// /// // Check market controversy level /// let controversy_ratio = stats.total_disputes as f64 / 100.0; // Assume 100 participants /// if controversy_ratio > 0.1 { @@ -1052,10 +1052,10 @@ impl DisputeManager { /// # use predictify_hybrid::disputes::{DisputeManager, DisputeStatus}; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "disputed_market"); - /// + /// /// // Get all disputes for detailed analysis /// let disputes = DisputeManager::get_market_disputes(&env, market_id).unwrap(); - /// + /// /// // Analyze dispute patterns /// for dispute in disputes.iter() { /// println!("Dispute by: {}", dispute.user.to_string()); @@ -1066,12 +1066,12 @@ impl DisputeManager { /// println!("Reason: {}", reason.to_string()); /// } /// } - /// + /// /// // Filter by status /// let active_disputes: Vec<_> = disputes.iter() /// .filter(|d| matches!(d.status, DisputeStatus::Active)) /// .collect(); - /// + /// /// println!("Active disputes: {}", active_disputes.len()); /// ``` /// @@ -1123,14 +1123,14 @@ impl DisputeManager { /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "market_123"); /// # let user = Address::generate(&env); - /// + /// /// // Check if user can dispute (hasn't disputed before) /// let has_disputed = DisputeManager::has_user_disputed( - /// &env, - /// market_id.clone(), + /// &env, + /// market_id.clone(), /// user.clone() /// ).unwrap(); - /// + /// /// if has_disputed { /// println!("User has already disputed this market"); /// // Show dispute status instead of dispute option @@ -1138,7 +1138,7 @@ impl DisputeManager { /// println!("User can dispute this market"); /// // Show dispute creation interface /// } - /// + /// /// // Validation before allowing dispute creation /// if !has_disputed { /// // Proceed with dispute creation logic @@ -1190,14 +1190,14 @@ impl DisputeManager { /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "staked_market"); /// # let user = Address::generate(&env); - /// + /// /// // Get user's dispute stake /// let stake = DisputeManager::get_user_dispute_stake( /// &env, /// market_id.clone(), /// user.clone() /// ).unwrap(); - /// + /// /// if stake > 0 { /// println!("User has {} XLM staked in disputes", stake / 10_000_000); /// @@ -1268,7 +1268,7 @@ impl DisputeManager { /// # let voter = Address::generate(&env); /// # let market_id = Symbol::new(&env, "disputed_market"); /// # let dispute_id = Symbol::new(&env, "dispute_456"); - /// + /// /// // Vote to support the dispute /// let result = DisputeManager::vote_on_dispute( /// &env, @@ -1279,12 +1279,12 @@ impl DisputeManager { /// 5_000_000, // 0.5 XLM voting power /// Some(String::from_str(&env, "Oracle data contradicts multiple sources")) /// ); - /// + /// /// match result { /// Ok(()) => println!("Vote successfully recorded"), /// Err(e) => println!("Vote failed: {:?}", e), /// } - /// + /// /// // Vote to reject the dispute /// let other_voter = Address::generate(&env); /// let reject_result = DisputeManager::vote_on_dispute( @@ -1394,13 +1394,13 @@ impl DisputeManager { /// # use predictify_hybrid::disputes::DisputeManager; /// # let env = Env::default(); /// # let dispute_id = Symbol::new(&env, "completed_dispute"); - /// + /// /// // Calculate outcome after voting period ends /// let outcome = DisputeManager::calculate_dispute_outcome( - /// &env, + /// &env, /// dispute_id.clone() /// ).unwrap(); - /// + /// /// if outcome { /// println!("Dispute upheld - oracle result overturned"); /// // Community believes oracle was incorrect @@ -1470,25 +1470,25 @@ impl DisputeManager { /// # use predictify_hybrid::disputes::DisputeManager; /// # let env = Env::default(); /// # let dispute_id = Symbol::new(&env, "resolved_dispute"); - /// + /// /// // Distribute fees after dispute resolution /// let distribution = DisputeManager::distribute_dispute_fees( /// &env, /// dispute_id.clone() /// ).unwrap(); - /// + /// /// // Check distribution results - /// println!("Total fees distributed: {} XLM", + /// println!("Total fees distributed: {} XLM", /// distribution.total_fees / 10_000_000); - /// println!("Winners: {} addresses", + /// println!("Winners: {} addresses", /// distribution.winner_addresses.len()); - /// println!("Winner stake: {} XLM", + /// println!("Winner stake: {} XLM", /// distribution.winner_stake / 10_000_000); - /// println!("Loser stake (rewards): {} XLM", + /// println!("Loser stake (rewards): {} XLM", /// distribution.loser_stake / 10_000_000); - /// + /// /// // Calculate reward ratio - /// let reward_ratio = distribution.loser_stake as f64 / + /// let reward_ratio = distribution.loser_stake as f64 / /// distribution.winner_stake as f64; /// println!("Winners receive {:.1}% bonus", reward_ratio * 100.0); /// ``` @@ -1571,16 +1571,16 @@ impl DisputeManager { /// # let env = Env::default(); /// # let user = Address::generate(&env); /// # let dispute_id = Symbol::new(&env, "tied_dispute"); - /// + /// /// // Escalate a dispute with exact vote tie /// let escalation = DisputeManager::escalate_dispute( /// &env, /// user.clone(), /// dispute_id.clone(), - /// String::from_str(&env, + /// String::from_str(&env, /// "Voting resulted in exact tie with equal stakes on both sides") /// ).unwrap(); - /// + /// /// // Check escalation details /// println!("Escalated by: {}", escalation.escalated_by.to_string()); /// println!("Escalation level: {}", escalation.escalation_level); @@ -1678,7 +1678,8 @@ impl DisputeManager { DisputeValidator::validate_admin_permissions(env, &admin)?; // Validate timeout hours - if timeout_hours == 0 || timeout_hours > 720 { // Max 30 days + if timeout_hours == 0 || timeout_hours > 720 { + // Max 30 days return Err(Error::InvalidTimeoutHours); } @@ -1713,7 +1714,7 @@ impl DisputeManager { pub fn check_dispute_timeout(env: &Env, dispute_id: Symbol) -> Result { let timeout = DisputeUtils::get_dispute_timeout(env, &dispute_id)?; let current_time = env.ledger().timestamp(); - + Ok(current_time >= timeout.expires_at) } @@ -1729,7 +1730,7 @@ impl DisputeManager { // Get timeout configuration let mut timeout = DisputeUtils::get_dispute_timeout(env, &dispute_id)?; - + // Update timeout status timeout.status = DisputeTimeoutStatus::AutoResolved; DisputeUtils::store_dispute_timeout(env, &dispute_id, &timeout)?; @@ -1765,7 +1766,7 @@ impl DisputeManager { ) -> Result { // Get dispute voting data let voting_data = DisputeUtils::get_dispute_voting(env, &dispute_id)?; - + // Determine outcome based on stake-weighted voting let outcome = if voting_data.total_support_stake > voting_data.total_against_stake { String::from_str(env, "Support") @@ -1780,20 +1781,19 @@ impl DisputeManager { outcome, resolution_method: String::from_str(env, "Timeout Auto-Resolution"), resolution_timestamp: env.ledger().timestamp(), - reason: String::from_str(env, "Dispute timeout expired - automatic resolution based on stake-weighted voting"), + reason: String::from_str( + env, + "Dispute timeout expired - automatic resolution based on stake-weighted voting", + ), }; Ok(timeout_outcome) } /// Emit timeout event - pub fn emit_timeout_event( - env: &Env, - dispute_id: Symbol, - outcome: String, - ) -> Result<(), Error> { + pub fn emit_timeout_event(env: &Env, dispute_id: Symbol, outcome: String) -> Result<(), Error> { let timeout = DisputeUtils::get_dispute_timeout(env, &dispute_id)?; - + crate::events::EventEmitter::emit_dispute_timeout_expired( env, &dispute_id, @@ -1828,7 +1828,8 @@ impl DisputeManager { DisputeValidator::validate_admin_permissions(env, &admin)?; // Validate additional hours - if additional_hours == 0 || additional_hours > 168 { // Max 7 days extension + if additional_hours == 0 || additional_hours > 168 { + // Max 7 days extension return Err(Error::InvalidTimeoutHours); } @@ -1906,10 +1907,8 @@ impl DisputeValidator { /// Validate admin permissions pub fn validate_admin_permissions(env: &Env, admin: &Address) -> Result<(), Error> { - let stored_admin: Option
= env - .storage() - .persistent() - .get(&Symbol::new(env, "Admin")); + let stored_admin: Option
= + env.storage().persistent().get(&Symbol::new(env, "Admin")); match stored_admin { Some(stored_admin) => { @@ -2066,7 +2065,8 @@ impl DisputeValidator { return Err(Error::InvalidTimeoutHours); } - if timeout_hours > 720 { // Max 30 days + if timeout_hours > 720 { + // Max 30 days return Err(Error::InvalidTimeoutHours); } @@ -2074,12 +2074,15 @@ impl DisputeValidator { } /// Validate dispute timeout extension parameters - pub fn validate_dispute_timeout_extension_parameters(additional_hours: u32) -> Result<(), Error> { + pub fn validate_dispute_timeout_extension_parameters( + additional_hours: u32, + ) -> Result<(), Error> { if additional_hours == 0 { return Err(Error::InvalidTimeoutHours); } - if additional_hours > 168 { // Max 7 days extension + if additional_hours > 168 { + // Max 7 days extension return Err(Error::InvalidTimeoutHours); } @@ -2276,10 +2279,10 @@ impl DisputeUtils { pub fn get_dispute_votes(env: &Env, dispute_id: &Symbol) -> Result, Error> { // This is a simplified implementation - in a real system you'd need to track all votes let votes = Vec::new(env); - + // Get the voting data to access stored votes let _voting_data = Self::get_dispute_voting(env, dispute_id)?; - + // In a real implementation, you would iterate through stored vote keys // For now, return empty vector as this would require tracking vote keys separately Ok(votes) @@ -2384,7 +2387,6 @@ impl DisputeUtils { vote: bool, stake: i128, ) { - // In a real implementation, this would emit an event // For now, we'll just store it in persistent storage let event_key = symbol_short!("vote_evt"); @@ -2399,7 +2401,6 @@ impl DisputeUtils { _dispute_id: &Symbol, distribution: &DisputeFeeDistribution, ) { - // In a real implementation, this would emit an event // For now, we'll just store it in persistent storage let event_key = symbol_short!("fee_event"); @@ -2468,7 +2469,7 @@ impl DisputeUtils { pub fn check_expired_timeouts(env: &Env) -> Vec { let mut expired_disputes = Vec::new(env); let current_time = env.ledger().timestamp(); - + // This is a simplified implementation // In a real system, you would iterate through all timeouts and check expiration // For now, return empty vector @@ -2757,7 +2758,9 @@ pub mod testing { } /// Validate timeout outcome structure - pub fn validate_timeout_outcome_structure(outcome: &DisputeTimeoutOutcome) -> Result<(), Error> { + pub fn validate_timeout_outcome_structure( + outcome: &DisputeTimeoutOutcome, + ) -> Result<(), Error> { if outcome.resolution_timestamp == 0 { return Err(Error::InvalidInput); } diff --git a/contracts/predictify-hybrid/src/errors.rs b/contracts/predictify-hybrid/src/errors.rs index 88ecf804..cdd779be 100644 --- a/contracts/predictify-hybrid/src/errors.rs +++ b/contracts/predictify-hybrid/src/errors.rs @@ -34,7 +34,7 @@ use soroban_sdk::contracterror; /// /// ```rust /// # use predictify_hybrid::errors::Error; -/// +/// /// // Handle specific error types /// fn handle_market_operation_result(result: Result<(), Error>) { /// match result { @@ -53,7 +53,7 @@ use soroban_sdk::contracterror; /// } /// } /// } -/// +/// /// // Get error information /// let error = Error::MarketClosed; /// println!("Error Code: {}", error.code()); // "MARKET_CLOSED" @@ -191,26 +191,26 @@ impl Error { /// /// ```rust /// # use predictify_hybrid::errors::Error; - /// + /// /// // Display user-friendly error messages /// let error = Error::InsufficientStake; /// println!("Operation failed: {}", error.description()); /// // Output: "Operation failed: Insufficient stake amount" - /// + /// /// // Use in error handling for user interfaces /// fn display_error_to_user(error: Error) { /// let message = format!("Error: {}", error.description()); /// // Display message in UI /// println!("{}", message); /// } - /// + /// /// // Compare different error descriptions /// let errors = vec![ /// Error::MarketNotFound, /// Error::MarketClosed, /// Error::AlreadyVoted, /// ]; - /// + /// /// for error in errors { /// println!("{}: {}", error.code(), error.description()); /// } @@ -290,12 +290,12 @@ impl Error { /// /// ```rust /// # use predictify_hybrid::errors::Error; - /// + /// /// // Use for structured logging /// let error = Error::OracleUnavailable; /// println!("ERROR_CODE={} MESSAGE={}", error.code(), error.description()); /// // Output: "ERROR_CODE=ORACLE_UNAVAILABLE MESSAGE=Oracle is unavailable" - /// + /// /// // Use for API error responses /// fn create_api_error_response(error: Error) -> String { /// format!( @@ -309,7 +309,7 @@ impl Error { /// error as u32 /// ) /// } - /// + /// /// // Use for error categorization /// fn categorize_error(error: Error) -> &'static str { /// match error.code() { @@ -319,11 +319,11 @@ impl Error { /// _ => "General Error", /// } /// } - /// + /// /// // Use for monitoring and alerting /// fn should_alert(error: Error) -> bool { - /// matches!(error.code(), - /// "ORACLE_UNAVAILABLE" | + /// matches!(error.code(), + /// "ORACLE_UNAVAILABLE" | /// "DISPUTE_FEE_DISTRIBUTION_FAILED" | /// "ADMIN_NOT_SET" /// ) @@ -386,4 +386,3 @@ impl Error { } } } - diff --git a/contracts/predictify-hybrid/src/events.rs b/contracts/predictify-hybrid/src/events.rs index 7916e5d2..810c61ca 100644 --- a/contracts/predictify-hybrid/src/events.rs +++ b/contracts/predictify-hybrid/src/events.rs @@ -3,9 +3,8 @@ extern crate alloc; // use alloc::string::ToString; // Removed to fix Display/ToString trait errors use soroban_sdk::{contracttype, symbol_short, vec, Address, Env, Map, String, Symbol, Vec}; - -use crate::errors::Error; use crate::config::Environment; +use crate::errors::Error; // Define AdminRole locally since it's not available in the crate root #[derive(Clone, Debug, Eq, PartialEq)] @@ -48,7 +47,7 @@ pub enum AdminRole { /// # use predictify_hybrid::events::MarketCreatedEvent; /// # let env = Env::default(); /// # let admin = Address::generate(&env); -/// +/// /// // Market creation event data /// let event = MarketCreatedEvent { /// market_id: Symbol::new(&env, "btc_50k_2024"), @@ -62,7 +61,7 @@ pub enum AdminRole { /// end_time: 1735689600, // Dec 31, 2024 /// timestamp: env.ledger().timestamp(), /// }; -/// +/// /// // Event provides complete market context /// println!("New market: {}", event.question.to_string()); /// println!("Market ID: {}", event.market_id.to_string()); @@ -123,7 +122,7 @@ pub struct MarketCreatedEvent { /// # use predictify_hybrid::events::VoteCastEvent; /// # let env = Env::default(); /// # let voter = Address::generate(&env); -/// +/// /// // Vote casting event data /// let event = VoteCastEvent { /// market_id: Symbol::new(&env, "btc_50k_2024"), @@ -132,7 +131,7 @@ pub struct MarketCreatedEvent { /// stake: 10_000_000, // 1.0 XLM /// timestamp: env.ledger().timestamp(), /// }; -/// +/// /// // Event provides complete voting context /// println!("Vote cast by: {}", event.voter.to_string()); /// println!("Market: {}", event.market_id.to_string()); @@ -198,7 +197,7 @@ pub struct VoteCastEvent { /// # use soroban_sdk::{Env, Symbol, String}; /// # use predictify_hybrid::events::OracleResultEvent; /// # let env = Env::default(); -/// +/// /// // Oracle result event for Bitcoin price market /// let event = OracleResultEvent { /// market_id: Symbol::new(&env, "btc_50k_2024"), @@ -210,7 +209,7 @@ pub struct VoteCastEvent { /// comparison: String::from_str(&env, "gte"), // greater than or equal /// timestamp: env.ledger().timestamp(), /// }; -/// +/// /// // Event provides complete oracle context /// println!("Oracle result: {}", event.result.to_string()); /// println!("Price fetched: ${}", event.price / 100000000); @@ -283,7 +282,7 @@ pub struct OracleResultEvent { /// # use soroban_sdk::{Env, Symbol, String}; /// # use predictify_hybrid::events::MarketResolvedEvent; /// # let env = Env::default(); -/// +/// /// // Market resolution event for Bitcoin price market /// let event = MarketResolvedEvent { /// market_id: Symbol::new(&env, "btc_50k_2024"), @@ -294,13 +293,13 @@ pub struct OracleResultEvent { /// confidence_score: 95, // 95% confidence /// timestamp: env.ledger().timestamp(), /// }; -/// +/// /// // Event provides complete resolution context /// println!("Market resolved: {}", event.market_id.to_string()); /// println!("Final outcome: {}", event.final_outcome.to_string()); /// println!("Resolution method: {}", event.resolution_method.to_string()); /// println!("Confidence: {}%", event.confidence_score); -/// +/// /// // Check consensus alignment /// let consensus_aligned = event.oracle_result == event.community_consensus; /// println!("Oracle-Community alignment: {}", consensus_aligned); @@ -370,22 +369,22 @@ pub struct MarketResolvedEvent { /// # use predictify_hybrid::events::DisputeCreatedEvent; /// # let env = Env::default(); /// # let disputer = Address::generate(&env); -/// +/// /// // Dispute creation event /// let event = DisputeCreatedEvent { /// market_id: Symbol::new(&env, "btc_50k_2024"), /// disputer: disputer.clone(), /// stake: 50_000_000, // 5.0 XLM dispute stake -/// reason: Some(String::from_str(&env, +/// reason: Some(String::from_str(&env, /// "Oracle price appears incorrect - multiple exchanges show different value")), /// timestamp: env.ledger().timestamp(), /// }; -/// +/// /// // Event provides complete dispute context /// println!("Dispute created by: {}", event.disputer.to_string()); /// println!("Market disputed: {}", event.market_id.to_string()); /// println!("Stake amount: {} XLM", event.stake / 10_000_000); -/// +/// /// if let Some(reason) = &event.reason { /// println!("Dispute reason: {}", reason.to_string()); /// } @@ -453,7 +452,7 @@ pub struct DisputeCreatedEvent { /// # let winner1 = Address::generate(&env); /// # let winner2 = Address::generate(&env); /// # let loser1 = Address::generate(&env); -/// +/// /// // Dispute resolution event /// let event = DisputeResolvedEvent { /// market_id: Symbol::new(&env, "btc_50k_2024"), @@ -463,7 +462,7 @@ pub struct DisputeCreatedEvent { /// fee_distribution: 25_000_000, // 2.5 XLM distributed to winners /// timestamp: env.ledger().timestamp(), /// }; -/// +/// /// // Event provides complete resolution context /// println!("Dispute resolved: {}", event.market_id.to_string()); /// println!("Outcome: {}", event.outcome.to_string()); @@ -994,12 +993,7 @@ impl EventEmitter { } /// Emit admin action logged event - pub fn emit_admin_action_logged( - env: &Env, - admin: &Address, - action: &str, - success: &bool, - ) { + pub fn emit_admin_action_logged(env: &Env, admin: &Address, action: &str, success: &bool) { let event = AdminActionEvent { admin: admin.clone(), action: String::from_str(env, action), @@ -1022,19 +1016,18 @@ impl EventEmitter { } /// Emit config initialized event - pub fn emit_config_initialized( - env: &Env, - admin: &Address, - environment: &Environment, - ) { + pub fn emit_config_initialized(env: &Env, admin: &Address, environment: &Environment) { let event = ConfigInitializedEvent { admin: admin.clone(), - environment: String::from_str(env, match environment { - Environment::Development => "Development", - Environment::Testnet => "Testnet", - Environment::Mainnet => "Mainnet", - Environment::Custom => "Custom", - }), + environment: String::from_str( + env, + match environment { + Environment::Development => "Development", + Environment::Testnet => "Testnet", + Environment::Mainnet => "Mainnet", + Environment::Custom => "Custom", + }, + ), timestamp: env.ledger().timestamp(), }; @@ -1050,11 +1043,14 @@ impl EventEmitter { ) { let event = AdminRoleEvent { admin: admin.clone(), - role: String::from_str(env, match role { - AdminRole::Owner => "Owner", - AdminRole::Admin => "Admin", - AdminRole::Moderator => "Moderator", - }), + role: String::from_str( + env, + match role { + AdminRole::Owner => "Owner", + AdminRole::Admin => "Admin", + AdminRole::Moderator => "Moderator", + }, + ), assigned_by: assigned_by.clone(), timestamp: env.ledger().timestamp(), }; @@ -1063,11 +1059,7 @@ impl EventEmitter { } /// Emit admin role deactivated event - pub fn emit_admin_role_deactivated( - env: &Env, - admin: &Address, - deactivated_by: &Address, - ) { + pub fn emit_admin_role_deactivated(env: &Env, admin: &Address, deactivated_by: &Address) { let event = AdminRoleEvent { admin: admin.clone(), role: String::from_str(env, "deactivated"), @@ -1079,11 +1071,7 @@ impl EventEmitter { } /// Emit market closed event - pub fn emit_market_closed( - env: &Env, - market_id: &Symbol, - admin: &Address, - ) { + pub fn emit_market_closed(env: &Env, market_id: &Symbol, admin: &Address) { let event = MarketClosedEvent { market_id: market_id.clone(), admin: admin.clone(), @@ -1094,12 +1082,7 @@ impl EventEmitter { } /// Emit market finalized event - pub fn emit_market_finalized( - env: &Env, - market_id: &Symbol, - admin: &Address, - outcome: &String, - ) { + pub fn emit_market_finalized(env: &Env, market_id: &Symbol, admin: &Address, outcome: &String) { let event = MarketFinalizedEvent { market_id: market_id.clone(), admin: admin.clone(), @@ -1440,7 +1423,6 @@ impl EventValidator { // Remove empty check for Symbol since it doesn't have is_empty method // Market ID validation is handled by the Symbol type itself - if event.additional_days == 0 { return Err(Error::InvalidInput); } @@ -1498,7 +1480,6 @@ impl EventHelpers { /// Create event context string pub fn create_event_context(env: &Env, context_parts: &Vec) -> String { - let mut context = String::from_str(env, ""); for (i, part) in context_parts.iter().enumerate() { if i > 0 { @@ -1510,7 +1491,6 @@ impl EventHelpers { } } context - } /// Validate event timestamp @@ -1787,26 +1767,22 @@ impl EventDocumentation { String::from_str(env, "EventEmitter::emit_market_created(env, market_id, question, outcomes, admin, end_time)"), ); examples.set( - String::from_str(&env, "EmitVoteCast"), String::from_str( &env, "EventEmitter::emit_vote_cast(env, market_id, voter, outcome, stake)", ), - ); examples.set( String::from_str(env, "GetMarketEvents"), String::from_str(env, "EventLogger::get_market_events(env, market_id)"), ); examples.set( - String::from_str(&env, "ValidateEvent"), String::from_str( &env, "EventValidator::validate_market_created_event(&event)", ), - ); examples diff --git a/contracts/predictify-hybrid/src/extensions.rs b/contracts/predictify-hybrid/src/extensions.rs index d4258f9e..f0a6efe0 100644 --- a/contracts/predictify-hybrid/src/extensions.rs +++ b/contracts/predictify-hybrid/src/extensions.rs @@ -68,7 +68,7 @@ const MAX_TOTAL_EXTENSIONS: u32 = crate::config::MAX_TOTAL_EXTENSIONS; /// # let env = Env::default(); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "btc_market"); -/// +/// /// // Extend market by 7 days /// let result = ExtensionManager::extend_market_duration( /// &env, @@ -77,18 +77,18 @@ const MAX_TOTAL_EXTENSIONS: u32 = crate::config::MAX_TOTAL_EXTENSIONS; /// 7, // Additional days /// String::from_str(&env, "Extended due to high community interest") /// ); -/// +/// /// match result { /// Ok(()) => println!("Market successfully extended by 7 days"), /// Err(e) => println!("Extension failed: {:?}", e), /// } -/// +/// /// // Check extension history /// let history = ExtensionManager::get_market_extension_history( -/// &env, +/// &env, /// market_id.clone() /// ).unwrap(); -/// +/// /// println!("Total extensions: {}", history.len()); /// ``` pub struct ExtensionManager; @@ -125,7 +125,7 @@ impl ExtensionManager { /// # let env = Env::default(); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "crypto_prediction"); - /// + /// /// // Extend market for additional data collection /// let result = ExtensionManager::extend_market_duration( /// &env, @@ -134,7 +134,7 @@ impl ExtensionManager { /// 14, // Two weeks extension /// String::from_str(&env, "Oracle data delayed, need more time for accurate resolution") /// ); - /// + /// /// match result { /// Ok(()) => { /// println!("Market extended successfully"); @@ -245,24 +245,24 @@ impl ExtensionManager { /// # use predictify_hybrid::extensions::ExtensionManager; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "btc_market"); - /// + /// /// // Get complete extension history /// let history = ExtensionManager::get_market_extension_history( /// &env, /// market_id.clone() /// ).unwrap(); - /// + /// /// // Analyze extension patterns /// println!("Total extensions: {}", history.len()); - /// + /// /// let mut total_days = 0u32; /// let mut total_fees = 0i128; - /// + /// /// for extension in history.iter() { /// total_days += extension.additional_days; /// total_fees += extension.fee_amount; /// - /// println!("Extension: {} days by {} (fee: {} XLM)", + /// println!("Extension: {} days by {} (fee: {} XLM)", /// extension.additional_days, /// extension.admin.to_string(), /// extension.fee_amount / 10_000_000); @@ -271,7 +271,7 @@ impl ExtensionManager { /// println!("Reason: {}", reason.to_string()); /// } /// } - /// + /// /// println!("Total extended days: {}", total_days); /// println!("Total fees paid: {} XLM", total_fees / 10_000_000); /// ``` @@ -323,13 +323,13 @@ impl ExtensionManager { /// # use predictify_hybrid::extensions::ExtensionManager; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "crypto_market"); - /// + /// /// // Get extension statistics /// let stats = ExtensionManager::get_extension_stats( /// &env, /// market_id.clone() /// ).unwrap(); - /// + /// /// // Display extension status /// println!("Extension Statistics for Market: {}", market_id.to_string()); /// println!("─────────────────────────────────────────"); @@ -337,17 +337,17 @@ impl ExtensionManager { /// println!("Total days extended: {}", stats.total_extension_days); /// println!("Maximum extension days: {}", stats.max_extension_days); /// println!("Can extend further: {}", stats.can_extend); - /// println!("Extension fee per day: {} XLM", + /// println!("Extension fee per day: {} XLM", /// stats.extension_fee_per_day / 10_000_000); - /// + /// /// // Calculate remaining extension capacity /// let remaining_days = stats.max_extension_days - stats.total_extension_days; /// println!("Remaining extension capacity: {} days", remaining_days); - /// + /// /// // Estimate cost for maximum extension /// if stats.can_extend && remaining_days > 0 { /// let max_cost = (remaining_days as i128) * stats.extension_fee_per_day; - /// println!("Cost to use remaining capacity: {} XLM", + /// println!("Cost to use remaining capacity: {} XLM", /// max_cost / 10_000_000); /// } /// ``` @@ -417,14 +417,14 @@ impl ExtensionManager { /// # let env = Env::default(); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "prediction_market"); - /// + /// /// // Check extension eligibility /// let can_extend = ExtensionManager::can_extend_market( /// &env, /// market_id.clone(), /// admin.clone() /// ).unwrap(); - /// + /// /// if can_extend { /// println!("Market can be extended by this admin"); /// @@ -490,31 +490,31 @@ impl ExtensionManager { /// /// ```rust /// # use predictify_hybrid::extensions::ExtensionManager; - /// + /// /// // Calculate fees for different extension periods /// let one_day_fee = ExtensionManager::calculate_extension_fee(1); /// let one_week_fee = ExtensionManager::calculate_extension_fee(7); /// let one_month_fee = ExtensionManager::calculate_extension_fee(30); - /// + /// /// println!("Extension Fee Calculator"); /// println!("─────────────────────────"); /// println!("1 day: {} XLM", one_day_fee / 10_000_000); /// println!("1 week: {} XLM", one_week_fee / 10_000_000); /// println!("1 month: {} XLM", one_month_fee / 10_000_000); - /// + /// /// // Calculate cost per day /// let daily_rate = one_day_fee; /// println!("Daily rate: {} XLM", daily_rate / 10_000_000); - /// + /// /// // Verify linear pricing /// assert_eq!(one_week_fee, daily_rate * 7); /// assert_eq!(one_month_fee, daily_rate * 30); - /// + /// /// // Budget planning example /// let budget_xlm = 50; // 50 XLM budget /// let budget_stroops = budget_xlm * 10_000_000; /// let max_days = budget_stroops / daily_rate; - /// println!("With {} XLM budget, can extend up to {} days", + /// println!("With {} XLM budget, can extend up to {} days", /// budget_xlm, max_days); /// ``` /// @@ -750,10 +750,12 @@ mod tests { env.as_contract(&contract_id, || { // Test valid extension days - assert!( - ExtensionValidator::validate_extension_conditions(&env, &symbol_short!("test"), 5) - .is_err() - ); // Market doesn't exist + assert!(ExtensionValidator::validate_extension_conditions( + &env, + &symbol_short!("test"), + 5 + ) + .is_err()); // Market doesn't exist // Test invalid extension days assert_eq!( diff --git a/contracts/predictify-hybrid/src/fees.rs b/contracts/predictify-hybrid/src/fees.rs index bfe1e7c8..27f909c1 100644 --- a/contracts/predictify-hybrid/src/fees.rs +++ b/contracts/predictify-hybrid/src/fees.rs @@ -72,7 +72,7 @@ pub const MARKET_SIZE_LARGE: i128 = 10_000_000_000; // 1000 XLM /// # use soroban_sdk::Env; /// # use predictify_hybrid::fees::FeeConfig; /// # let env = Env::default(); -/// +/// /// // Standard fee configuration /// let config = FeeConfig { /// platform_fee_percentage: 200, // 2.00% (basis points) @@ -82,12 +82,12 @@ pub const MARKET_SIZE_LARGE: i128 = 10_000_000_000; // 1000 XLM /// collection_threshold: 100_000_000, // 10 XLM threshold /// fees_enabled: true, /// }; -/// +/// /// // Calculate platform fee for 50 XLM stake /// let stake_amount = 500_000_000; // 50 XLM /// let platform_fee = (stake_amount * config.platform_fee_percentage) / 10_000; /// println!("Platform fee: {} XLM", platform_fee / 10_000_000); -/// +/// /// // Check if fees are collectible /// if config.fees_enabled && stake_amount >= config.collection_threshold { /// println!("Fees can be collected"); @@ -147,7 +147,7 @@ pub struct FeeConfig { /// # use soroban_sdk::Env; /// # use predictify_hybrid::fees::FeeTier; /// # let env = Env::default(); -/// +/// /// let tier = FeeTier { /// min_size: 0, /// max_size: 100_000_000, // 10 XLM @@ -187,7 +187,7 @@ pub struct FeeTier { /// # use soroban_sdk::Env; /// # use predictify_hybrid::fees::ActivityAdjustment; /// # let env = Env::default(); -/// +/// /// let adjustment = ActivityAdjustment { /// activity_level: 50, /// fee_multiplier: 110, // 10% increase @@ -225,7 +225,7 @@ pub struct ActivityAdjustment { /// # use soroban_sdk::Env; /// # use predictify_hybrid::fees::FeeCalculationFactors; /// # let env = Env::default(); -/// +/// /// let factors = FeeCalculationFactors { /// base_fee_percentage: 200, // 2% /// size_multiplier: 110, // 10% increase @@ -273,7 +273,7 @@ pub struct FeeCalculationFactors { /// # use soroban_sdk::Env; /// # use predictify_hybrid::fees::FeeHistory; /// # let env = Env::default(); -/// +/// /// let history = FeeHistory { /// market_id: Symbol::new(&env, "market_123"), /// timestamp: env.ledger().timestamp(), @@ -324,7 +324,7 @@ pub struct FeeHistory { /// # use predictify_hybrid::fees::FeeCollection; /// # let env = Env::default(); /// # let admin = Address::generate(&env); -/// +/// /// // Fee collection record /// let collection = FeeCollection { /// market_id: Symbol::new(&env, "btc_prediction"), @@ -333,14 +333,14 @@ pub struct FeeHistory { /// timestamp: env.ledger().timestamp(), /// fee_percentage: 200, // 2% fee rate used /// }; -/// +/// /// // Analyze collection details /// println!("Fee Collection Report"); /// println!("Market: {}", collection.market_id.to_string()); /// println!("Amount: {} XLM", collection.amount / 10_000_000); /// println!("Collected by: {}", collection.collected_by.to_string()); /// println!("Fee rate: {}%", collection.fee_percentage as f64 / 100.0); -/// +/// /// // Calculate original stake from fee /// let original_stake = (collection.amount * 10_000) / collection.fee_percentage; /// println!("Original stake: {} XLM", original_stake / 10_000_000); @@ -396,7 +396,7 @@ pub struct FeeCollection { /// # use soroban_sdk::{Env, Map, String, Vec}; /// # use predictify_hybrid::fees::{FeeAnalytics, FeeCollection}; /// # let env = Env::default(); -/// +/// /// // Fee analytics example /// let analytics = FeeAnalytics { /// total_fees_collected: 1_000_000_000, // 100 XLM total @@ -405,27 +405,27 @@ pub struct FeeCollection { /// collection_history: Vec::new(&env), // Historical records /// fee_distribution: Map::new(&env), // Distribution by market size /// }; -/// +/// /// // Display analytics summary /// println!("Fee System Analytics"); /// println!("═══════════════════════════════════════"); -/// println!("Total fees collected: {} XLM", +/// println!("Total fees collected: {} XLM", /// analytics.total_fees_collected / 10_000_000); /// println!("Markets with fees: {}", analytics.markets_with_fees); -/// println!("Average per market: {} XLM", +/// println!("Average per market: {} XLM", /// analytics.average_fee_per_market / 10_000_000); -/// +/// /// // Calculate fee collection rate /// if analytics.markets_with_fees > 0 { /// let collection_efficiency = (analytics.markets_with_fees as f64 / 100.0) * 100.0; /// println!("Collection efficiency: {:.1}%", collection_efficiency); /// } -/// +/// /// // Analyze fee distribution /// println!("Fee distribution by market category:"); /// for (category, amount) in analytics.fee_distribution.iter() { -/// println!(" {}: {} XLM", -/// category.to_string(), +/// println!(" {}: {} XLM", +/// category.to_string(), /// amount / 10_000_000); /// } /// ``` @@ -489,7 +489,7 @@ pub struct FeeAnalytics { /// # use soroban_sdk::{Env, String, Vec}; /// # use predictify_hybrid::fees::{FeeValidationResult, FeeBreakdown}; /// # let env = Env::default(); -/// +/// /// // Fee validation result example /// let validation = FeeValidationResult { /// is_valid: false, @@ -507,7 +507,7 @@ pub struct FeeAnalytics { /// user_payout_amount: 980_000_000, // 98 XLM /// }, /// }; -/// +/// /// // Process validation results /// if validation.is_valid { /// println!("Fee validation passed"); @@ -517,7 +517,7 @@ pub struct FeeAnalytics { /// for error in validation.errors.iter() { /// println!(" - {}", error.to_string()); /// } -/// println!("Suggested amount: {} XLM", +/// println!("Suggested amount: {} XLM", /// validation.suggested_amount / 10_000_000); /// } /// ``` @@ -575,7 +575,7 @@ pub struct FeeValidationResult { /// /// ```rust /// # use predictify_hybrid::fees::FeeBreakdown; -/// +/// /// // Fee breakdown for 100 XLM stake at 2% fee /// let breakdown = FeeBreakdown { /// total_staked: 1_000_000_000, // 100 XLM total stake @@ -584,7 +584,7 @@ pub struct FeeValidationResult { /// platform_fee: 20_000_000, // 2 XLM platform fee /// user_payout_amount: 980_000_000, // 98 XLM after fees /// }; -/// +/// /// // Display breakdown to user /// println!("Fee Calculation Breakdown"); /// println!("─────────────────────────────────────────"); @@ -593,11 +593,11 @@ pub struct FeeValidationResult { /// println!("Fee Amount: {} XLM", breakdown.fee_amount / 10_000_000); /// println!("Platform Fee: {} XLM", breakdown.platform_fee / 10_000_000); /// println!("User Payout: {} XLM", breakdown.user_payout_amount / 10_000_000); -/// +/// /// // Verify calculation accuracy /// let expected_fee = (breakdown.total_staked * breakdown.fee_percentage) / 10_000; /// assert_eq!(breakdown.fee_amount, expected_fee); -/// +/// /// let expected_payout = breakdown.total_staked - breakdown.fee_amount; /// assert_eq!(breakdown.user_payout_amount, expected_payout); /// ``` @@ -664,21 +664,21 @@ pub struct FeeBreakdown { /// # let env = Env::default(); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "btc_market"); -/// +/// /// // Collect fees from a resolved market /// let collected_amount = FeeManager::collect_fees( /// &env, /// admin.clone(), /// market_id.clone() /// ).unwrap(); -/// +/// /// println!("Collected {} XLM in fees", collected_amount / 10_000_000); -/// +/// /// // Get fee analytics /// let analytics = FeeManager::get_fee_analytics(&env).unwrap(); -/// println!("Total platform fees: {} XLM", +/// println!("Total platform fees: {} XLM", /// analytics.total_fees_collected / 10_000_000); -/// +/// /// // Validate market fees /// let validation = FeeManager::validate_market_fees(&env, &market_id).unwrap(); /// if validation.is_valid { @@ -837,8 +837,12 @@ impl FeeManager { /// Get fee history for a specific market pub fn get_fee_history(env: &Env, market_id: Symbol) -> Result, Error> { let history_key = Symbol::new(env, "fee_history"); - - match env.storage().persistent().get::>(&history_key) { + + match env + .storage() + .persistent() + .get::>(&history_key) + { Some(history) => Ok(history), None => Ok(Vec::new(env)), } @@ -985,7 +989,11 @@ impl FeeCalculator { } /// Adjust fee by activity level - pub fn adjust_fee_by_activity(env: &Env, market_id: Symbol, activity_level: u32) -> Result { + pub fn adjust_fee_by_activity( + env: &Env, + market_id: Symbol, + activity_level: u32, + ) -> Result { let market = crate::markets::MarketStateManager::get_market(env, &market_id)?; let base_fee = Self::calculate_dynamic_fee(&market)?; @@ -1037,12 +1045,15 @@ impl FeeCalculator { } /// Get fee calculation factors for a market - pub fn get_fee_calculation_factors(env: &Env, market_id: Symbol) -> Result { + pub fn get_fee_calculation_factors( + env: &Env, + market_id: Symbol, + ) -> Result { let market = crate::markets::MarketStateManager::get_market(env, &market_id)?; - + // Get base fee tier let tier = Self::get_fee_tier_by_market_size(env, market.total_staked)?; - + // Calculate activity level let vote_count = market.votes.len() as u32; let activity_level = if vote_count >= ACTIVITY_LEVEL_HIGH { @@ -1061,19 +1072,19 @@ impl FeeCalculator { } else if tier.tier_name == String::from_str(env, "Medium") { 100 // No change } else if tier.tier_name == String::from_str(env, "Small") { - 95 // 5% decrease + 95 // 5% decrease } else if tier.tier_name == String::from_str(env, "Micro") { - 90 // 10% decrease + 90 // 10% decrease } else { 100 }; let activity_multiplier = if activity_level == String::from_str(env, "High") { - 120 // 20% increase + 120 // 20% increase } else if activity_level == String::from_str(env, "Medium") { - 110 // 10% increase + 110 // 10% increase } else if activity_level == String::from_str(env, "Low") { - 105 // 5% increase + 105 // 5% increase } else if activity_level == String::from_str(env, "Very Low") { 100 // No change } else { @@ -1083,7 +1094,9 @@ impl FeeCalculator { let complexity_factor = 100; // No complexity adjustment for now // Calculate final fee percentage - let final_fee_percentage = (tier.fee_percentage * size_multiplier * activity_multiplier * complexity_factor) / (100 * 100 * 100); + let final_fee_percentage = + (tier.fee_percentage * size_multiplier * activity_multiplier * complexity_factor) + / (100 * 100 * 100); // Ensure final fee is within limits let final_fee_percentage = if final_fee_percentage < MIN_FEE_PERCENTAGE { @@ -1114,10 +1127,8 @@ pub struct FeeValidator; impl FeeValidator { /// Validate admin permissions pub fn validate_admin_permissions(env: &Env, admin: &Address) -> Result<(), Error> { - let stored_admin: Option
= env - .storage() - .persistent() - .get(&Symbol::new(env, "Admin")); + let stored_admin: Option
= + env.storage().persistent().get(&Symbol::new(env, "Admin")); match stored_admin { Some(stored_admin) => { @@ -1331,7 +1342,6 @@ impl FeeTracker { /// Record creation fee pub fn record_creation_fee(env: &Env, _admin: &Address, amount: i128) -> Result<(), Error> { - // Record creation fee in analytics let creation_key = symbol_short!("creat_fee"); let current_total: i128 = env.storage().persistent().get(&creation_key).unwrap_or(0); @@ -1572,7 +1582,7 @@ pub mod testing { FeeTier { min_size: 0, max_size: 100_000_000, // 10 XLM - fee_percentage: 150, // 1.5% + fee_percentage: 150, // 1.5% tier_name: String::from_str(env, "Small"), } } @@ -1589,10 +1599,10 @@ pub mod testing { /// Create test fee calculation factors pub fn create_test_fee_calculation_factors(env: &Env) -> FeeCalculationFactors { FeeCalculationFactors { - base_fee_percentage: 200, // 2% - size_multiplier: 100, // No change - activity_multiplier: 110, // 10% increase - complexity_factor: 100, // No change + base_fee_percentage: 200, // 2% + size_multiplier: 100, // No change + activity_multiplier: 110, // 10% increase + complexity_factor: 100, // No change final_fee_percentage: 220, // 2.2% market_size_tier: String::from_str(env, "Medium"), activity_level: String::from_str(env, "Medium"), @@ -1773,17 +1783,17 @@ mod tests { #[test] fn test_dynamic_fee_tier_calculation() { let env = Env::default(); - + // Test small market tier let small_tier = FeeCalculator::get_fee_tier_by_market_size(&env, 50_000_000).unwrap(); assert_eq!(small_tier.fee_percentage, 100); // 1.0% assert_eq!(small_tier.tier_name, String::from_str(&env, "Micro")); - + // Test medium market tier let medium_tier = FeeCalculator::get_fee_tier_by_market_size(&env, 500_000_000).unwrap(); assert_eq!(medium_tier.fee_percentage, 150); // 1.5% assert_eq!(medium_tier.tier_name, String::from_str(&env, "Small")); - + // Test large market tier let large_tier = FeeCalculator::get_fee_tier_by_market_size(&env, 5_000_000_000).unwrap(); assert_eq!(large_tier.fee_percentage, 200); // 2.0% @@ -1793,7 +1803,7 @@ mod tests { #[test] fn test_fee_calculation_factors() { let env = Env::default(); - + // Test the structure creation let factors = testing::create_test_fee_calculation_factors(&env); assert_eq!(factors.base_fee_percentage, 200); @@ -1806,10 +1816,13 @@ mod tests { fn test_fee_history_creation() { let env = Env::default(); let market_id = Symbol::new(&env, "test_market"); - + let history = testing::create_test_fee_history(&env, market_id); assert_eq!(history.old_fee_percentage, 200); assert_eq!(history.new_fee_percentage, 220); - assert_eq!(history.reason, String::from_str(&env, "Activity level increased")); + assert_eq!( + history.reason, + String::from_str(&env, "Activity level increased") + ); } } diff --git a/contracts/predictify-hybrid/src/integration_test.rs b/contracts/predictify-hybrid/src/integration_test.rs index 47dc4bf1..3fc7ad4c 100644 --- a/contracts/predictify-hybrid/src/integration_test.rs +++ b/contracts/predictify-hybrid/src/integration_test.rs @@ -1,14 +1,14 @@ #![cfg(test)] use super::*; +use alloc::format; use soroban_sdk::{ testutils::{Address as _, Ledger, LedgerInfo}, token::StellarAssetClient, vec, String, Symbol, }; -use alloc::format; /// Simplified Integration Test Suite for Predictify Hybrid Contract -/// +/// /// This module provides basic integration tests covering: /// - Market creation and voting /// - Basic market lifecycle @@ -75,9 +75,14 @@ impl IntegrationTestSuite { } } - pub fn create_market(&mut self, question: &str, outcomes: Vec, duration_days: u32) -> Symbol { + pub fn create_market( + &mut self, + question: &str, + outcomes: Vec, + duration_days: u32, + ) -> Symbol { let client = PredictifyHybridClient::new(&self.env, &self.contract_id); - + self.env.mock_all_auths(); let market_id = client.create_market( &self.admin, @@ -110,7 +115,7 @@ impl IntegrationTestSuite { pub fn advance_time(&self, days: u32) { let current_ledger = self.env.ledger(); let new_timestamp = current_ledger.timestamp() + (days as u64 * 24 * 60 * 60); - + self.env.ledger().set(LedgerInfo { timestamp: new_timestamp, protocol_version: current_ledger.protocol_version(), @@ -136,11 +141,11 @@ impl IntegrationTestSuite { pub fn resolve_market(&self, market_id: &Symbol) -> Result<(), Error> { let client = PredictifyHybridClient::new(&self.env, &self.contract_id); self.env.mock_all_auths(); - + // Get the market to determine the correct outcome to use let market = self.get_market(market_id); let winning_outcome = market.outcomes.get(0).unwrap().clone(); // Use first outcome as default - + // Use manual resolution instead of automatic oracle resolution client.resolve_market_manual(&self.admin, market_id, &winning_outcome); Ok(()) @@ -156,7 +161,7 @@ impl IntegrationTestSuite { #[test] fn test_complete_market_lifecycle() { let mut test_suite = IntegrationTestSuite::setup(5); - + // Step 1: Create a market let market_id = test_suite.create_market( "Will BTC reach $30,000 by end of year?", @@ -170,10 +175,10 @@ fn test_complete_market_lifecycle() { // Step 2: Multiple users vote test_suite.vote_on_market(&test_suite.get_user(0), &market_id, "yes", 100_0000000); // 100 XLM - test_suite.vote_on_market(&test_suite.get_user(1), &market_id, "yes", 50_0000000); // 50 XLM - test_suite.vote_on_market(&test_suite.get_user(2), &market_id, "no", 75_0000000); // 75 XLM - test_suite.vote_on_market(&test_suite.get_user(3), &market_id, "yes", 25_0000000); // 25 XLM - test_suite.vote_on_market(&test_suite.get_user(4), &market_id, "no", 60_0000000); // 60 XLM + test_suite.vote_on_market(&test_suite.get_user(1), &market_id, "yes", 50_0000000); // 50 XLM + test_suite.vote_on_market(&test_suite.get_user(2), &market_id, "no", 75_0000000); // 75 XLM + test_suite.vote_on_market(&test_suite.get_user(3), &market_id, "yes", 25_0000000); // 25 XLM + test_suite.vote_on_market(&test_suite.get_user(4), &market_id, "no", 60_0000000); // 60 XLM // Step 3: Verify market state let market = test_suite.get_market(&market_id); @@ -201,7 +206,7 @@ fn test_complete_market_lifecycle() { #[test] fn test_multi_user_market_scenarios() { let mut test_suite = IntegrationTestSuite::setup(10); - + // Create multiple markets let market_1 = test_suite.create_market( "Market 1: BTC price prediction", @@ -226,14 +231,24 @@ fn test_multi_user_market_scenarios() { // Users vote on multiple markets for i in 0..10 { let user = test_suite.get_user(i); - + // Vote on market 1 let outcome_1 = if i % 2 == 0 { "above_30k" } else { "below_30k" }; - test_suite.vote_on_market(&user, &market_1, outcome_1, ((i + 1) * 10) as i128 * 1_0000000); + test_suite.vote_on_market( + &user, + &market_1, + outcome_1, + ((i + 1) * 10) as i128 * 1_0000000, + ); // Vote on market 2 let outcome_2 = if i % 3 == 0 { "above_2k" } else { "below_2k" }; - test_suite.vote_on_market(&user, &market_2, outcome_2, ((i + 1) * 5) as i128 * 1_0000000); + test_suite.vote_on_market( + &user, + &market_2, + outcome_2, + ((i + 1) * 5) as i128 * 1_0000000, + ); } // Verify all markets have votes @@ -262,11 +277,11 @@ fn test_multi_user_market_scenarios() { #[should_panic(expected = "Error(Contract, #101)")] // MarketNotFound fn test_error_scenario_integration() { let mut test_suite = IntegrationTestSuite::setup(2); - + // Test 1: Try to vote on non-existent market let client = PredictifyHybridClient::new(&test_suite.env, &test_suite.contract_id); let non_existent_market = Symbol::new(&test_suite.env, "non_existent"); - + test_suite.env.mock_all_auths(); client.vote( &test_suite.get_user(0), @@ -280,7 +295,7 @@ fn test_error_scenario_integration() { #[test] fn test_stress_test_multiple_markets() { let mut test_suite = IntegrationTestSuite::setup(20); - + // Create 5 markets simultaneously let mut market_ids = Vec::new(&test_suite.env); for i in 0..5 { @@ -300,10 +315,14 @@ fn test_stress_test_multiple_markets() { for user_index in 0..20 { let user = test_suite.get_user(user_index); for (market_index, market_id) in market_ids.iter().enumerate() { - let outcome = if (user_index + market_index) % 2 == 0 { "outcome_a" } else { "outcome_b" }; + let outcome = if (user_index + market_index) % 2 == 0 { + "outcome_a" + } else { + "outcome_b" + }; let stake = ((user_index + market_index + 1) * 5) as i128 * 1_0000000; - - test_suite.vote_on_market(&user, &market_id, outcome, stake); + + test_suite.vote_on_market(&user, &market_id, outcome, stake); } } @@ -327,4 +346,4 @@ fn test_stress_test_multiple_markets() { let market = test_suite.get_market(&market_id); assert_eq!(market.state, MarketState::Resolved); } -} \ No newline at end of file +} diff --git a/contracts/predictify-hybrid/src/lib.rs b/contracts/predictify-hybrid/src/lib.rs index 4fcde8b8..e0c87d69 100644 --- a/contracts/predictify-hybrid/src/lib.rs +++ b/contracts/predictify-hybrid/src/lib.rs @@ -26,14 +26,14 @@ mod voting; mod integration_test; // Re-export commonly used items +use admin::AdminInitializer; pub use errors::Error; pub use types::*; -use admin::AdminInitializer; +use alloc::format; use soroban_sdk::{ contract, contractimpl, panic_with_error, Address, Env, Map, String, Symbol, Vec, }; -use alloc::format; #[contract] pub struct PredictifyHybrid; @@ -68,7 +68,7 @@ impl PredictifyHybrid { /// # use predictify_hybrid::PredictifyHybrid; /// # let env = Env::default(); /// # let admin_address = Address::generate(&env); - /// + /// /// // Initialize the contract with an admin /// PredictifyHybrid::initialize(env.clone(), admin_address); /// ``` @@ -118,7 +118,7 @@ impl PredictifyHybrid { /// # use predictify_hybrid::{PredictifyHybrid, OracleConfig, OracleType}; /// # let env = Env::default(); /// # let admin = Address::generate(&env); - /// + /// /// let question = String::from_str(&env, "Will Bitcoin reach $100,000 by 2024?"); /// let outcomes = vec![ /// String::from_str(&env, "Yes"), @@ -130,7 +130,7 @@ impl PredictifyHybrid { /// asset_code: Some(String::from_str(&env, "BTC")), /// threshold_value: Some(100000), /// }; - /// + /// /// let market_id = PredictifyHybrid::create_market( /// env.clone(), /// admin, @@ -165,10 +165,8 @@ impl PredictifyHybrid { panic!("Admin not set"); }); - if admin != stored_admin { panic_with_error!(env, Error::Unauthorized); - } // Validate inputs @@ -193,7 +191,6 @@ impl PredictifyHybrid { 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(), @@ -221,7 +218,6 @@ impl PredictifyHybrid { market_id } - /// Allows users to vote on a market outcome by staking tokens. /// /// This function enables users to participate in prediction markets by voting @@ -252,7 +248,7 @@ impl PredictifyHybrid { /// # let env = Env::default(); /// # let user = Address::generate(&env); /// # let market_id = Symbol::new(&env, "market_1"); - /// + /// /// // Vote "Yes" with 1000 token units stake /// PredictifyHybrid::vote( /// env.clone(), @@ -285,7 +281,6 @@ 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); @@ -338,7 +333,7 @@ impl PredictifyHybrid { /// # let env = Env::default(); /// # let user = Address::generate(&env); /// # let market_id = Symbol::new(&env, "resolved_market"); - /// + /// /// // Claim winnings from a resolved market /// PredictifyHybrid::claim_winnings( /// env.clone(), @@ -403,10 +398,8 @@ impl PredictifyHybrid { 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; @@ -451,7 +444,7 @@ impl PredictifyHybrid { /// # use predictify_hybrid::PredictifyHybrid; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "market_1"); - /// + /// /// match PredictifyHybrid::get_market(env.clone(), market_id) { /// Some(market) => { /// // Market found - access market data @@ -510,7 +503,7 @@ impl PredictifyHybrid { /// # let env = Env::default(); /// # let admin = Address::generate(&env); /// # let market_id = Symbol::new(&env, "market_1"); - /// + /// /// // Manually resolve market with "Yes" as winning outcome /// PredictifyHybrid::resolve_market_manual( /// env.clone(), @@ -538,10 +531,14 @@ impl PredictifyHybrid { /// /// This function requires admin privileges and should be used carefully. /// Manual resolutions should be transparent and follow established governance procedures. - pub fn resolve_market_manual(env: Env, admin: Address, market_id: Symbol, winning_outcome: String) { + 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() @@ -553,10 +550,8 @@ impl PredictifyHybrid { if admin != stored_admin { panic_with_error!(env, Error::Unauthorized); - } - let mut market: Market = env .storage() .persistent() @@ -570,21 +565,18 @@ impl PredictifyHybrid { panic_with_error!(env, Error::MarketClosed); } - // 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 and update state market.winning_outcome = Some(winning_outcome); market.state = MarketState::Resolved; env.storage().persistent().set(&market_id, &market); } - + /// Fetches oracle result for a market from external oracle contracts. /// /// This function retrieves prediction results from configured oracle sources @@ -619,7 +611,7 @@ impl PredictifyHybrid { /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "btc_market"); /// # let oracle_address = Address::generate(&env); - /// + /// /// match PredictifyHybrid::fetch_oracle_result( /// env.clone(), /// market_id, @@ -654,28 +646,33 @@ impl PredictifyHybrid { oracle_contract: Address, ) -> Result { // Get the market from storage - let market = env.storage().persistent().get::(&market_id) + let market = env + .storage() + .persistent() + .get::(&market_id) .ok_or(Error::MarketNotFound)?; - + // Validate market state if market.oracle_result.is_some() { return Err(Error::MarketAlreadyResolved); } - // Check if market has ended let current_time = env.ledger().timestamp(); if current_time < market.end_time { return Err(Error::MarketClosed); - } - + // Get oracle result using the resolution module - let oracle_resolution = resolution::OracleResolutionManager::fetch_oracle_result(&env, &market_id, &oracle_contract)?; - + let oracle_resolution = resolution::OracleResolutionManager::fetch_oracle_result( + &env, + &market_id, + &oracle_contract, + )?; + Ok(oracle_resolution.oracle_result) } - + /// Resolves a market automatically using oracle data and community consensus. /// /// This function implements the hybrid resolution algorithm that combines @@ -709,7 +706,7 @@ impl PredictifyHybrid { /// # use predictify_hybrid::PredictifyHybrid; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "ended_market"); - /// + /// /// match PredictifyHybrid::resolve_market(env.clone(), market_id) { /// Ok(()) => { /// // Market resolved successfully @@ -750,7 +747,7 @@ impl PredictifyHybrid { let _resolution = resolution::MarketResolutionManager::resolve_market(&env, &market_id)?; Ok(()) } - + /// Retrieves comprehensive analytics about market resolution performance. /// /// This function provides detailed statistics about how markets are being @@ -788,7 +785,7 @@ impl PredictifyHybrid { /// # use soroban_sdk::Env; /// # use predictify_hybrid::PredictifyHybrid; /// # let env = Env::default(); - /// + /// /// match PredictifyHybrid::get_resolution_analytics(env.clone()) { /// Ok(analytics) => { /// // Access resolution statistics @@ -830,7 +827,7 @@ impl PredictifyHybrid { pub fn get_resolution_analytics(env: Env) -> Result { resolution::MarketResolutionAnalytics::calculate_resolution_analytics(&env) } - + /// Retrieves comprehensive analytics and statistics for a specific market. /// /// This function provides detailed statistical analysis of a market including @@ -868,7 +865,7 @@ impl PredictifyHybrid { /// # use predictify_hybrid::PredictifyHybrid; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "market_1"); - /// + /// /// match PredictifyHybrid::get_market_analytics(env.clone(), market_id) { /// Ok(stats) => { /// // Access market statistics @@ -914,13 +911,19 @@ impl PredictifyHybrid { /// This function performs calculations on market data and may have /// computational overhead for markets with many participants. Consider /// caching results for frequently accessed markets. - pub fn get_market_analytics(env: Env, market_id: Symbol) -> Result { - let market = env.storage().persistent().get::(&market_id) + pub fn get_market_analytics( + env: Env, + market_id: Symbol, + ) -> Result { + let market = env + .storage() + .persistent() + .get::(&market_id) .ok_or(Error::MarketNotFound)?; - + // Calculate market statistics let stats = markets::MarketAnalytics::get_market_stats(&market); - + Ok(stats) } @@ -947,7 +950,9 @@ impl PredictifyHybrid { reason: Option, ) -> Result<(), Error> { user.require_auth(); - disputes::DisputeManager::vote_on_dispute(&env, user, market_id, dispute_id, vote, stake, reason) + disputes::DisputeManager::vote_on_dispute( + &env, user, market_id, dispute_id, vote, stake, reason, + ) } /// Resolve a dispute (admin only) @@ -957,7 +962,7 @@ impl PredictifyHybrid { market_id: Symbol, ) -> Result { admin.require_auth(); - + // Verify admin let stored_admin: Address = env .storage() @@ -977,7 +982,7 @@ impl PredictifyHybrid { /// Collect fees from a market (admin only) pub fn collect_fees(env: Env, admin: Address, market_id: Symbol) -> Result { admin.require_auth(); - + // Verify admin let stored_admin: Address = env .storage() @@ -1004,7 +1009,7 @@ impl PredictifyHybrid { fee_amount: i128, ) -> Result<(), Error> { admin.require_auth(); - + // Verify admin let stored_admin: Address = env .storage() @@ -1018,8 +1023,14 @@ impl PredictifyHybrid { panic_with_error!(env, Error::Unauthorized); } - extensions::ExtensionManager::extend_market_duration(&env, admin, market_id, additional_days, reason) + extensions::ExtensionManager::extend_market_duration( + &env, + admin, + market_id, + additional_days, + reason, + ) } } -mod test; \ No newline at end of file +mod test; diff --git a/contracts/predictify-hybrid/src/markets.rs b/contracts/predictify-hybrid/src/markets.rs index 9a85401d..e13825fd 100644 --- a/contracts/predictify-hybrid/src/markets.rs +++ b/contracts/predictify-hybrid/src/markets.rs @@ -18,7 +18,7 @@ use crate::types::*; // ===== MARKET CREATION ===== /// Market creation utilities for the Predictify prediction market platform. -/// +/// /// This struct provides methods to create different types of prediction markets /// with various oracle configurations. All market creation functions validate /// input parameters and handle fee processing automatically. @@ -85,7 +85,14 @@ impl MarketCreator { /// oracle_config /// ).expect("Market creation should succeed"); /// ``` - pub fn create_market(env: &Env, admin: Address, question: String, outcomes: Vec, duration_days: u32, oracle_config: OracleConfig) -> Result { + pub fn create_market( + env: &Env, + admin: Address, + question: String, + outcomes: Vec, + duration_days: u32, + oracle_config: OracleConfig, + ) -> Result { // Validate market parameters MarketValidator::validate_market_params(env, &question, &outcomes, duration_days)?; @@ -99,11 +106,19 @@ impl MarketCreator { let end_time = MarketUtils::calculate_end_time(env, duration_days); // Create market instance - let market = Market::new(env, admin.clone(), question, outcomes, end_time, oracle_config, MarketState::Active); - + let market = Market::new( + env, + admin.clone(), + question, + outcomes, + end_time, + oracle_config, + MarketState::Active, + ); + // Process market creation fee MarketUtils::process_creation_fee(env, &admin)?; - + // Store market env.storage().persistent().set(&market_id, &market); @@ -163,7 +178,16 @@ impl MarketCreator { /// String::from_str(&env, "gt") /// ).expect("Reflector market creation should succeed"); /// ``` - pub fn create_reflector_market(_env: &Env, admin: Address, question: String, outcomes: Vec, duration_days: u32, asset_symbol: String, threshold: i128, comparison: String) -> Result { + pub fn create_reflector_market( + _env: &Env, + admin: Address, + question: String, + outcomes: Vec, + duration_days: u32, + asset_symbol: String, + threshold: i128, + comparison: String, + ) -> Result { let oracle_config = OracleConfig { provider: OracleProvider::Reflector, feed_id: asset_symbol, @@ -171,7 +195,14 @@ impl MarketCreator { comparison, }; - Self::create_market(_env, admin, question, outcomes, duration_days, oracle_config) + Self::create_market( + _env, + admin, + question, + outcomes, + duration_days, + oracle_config, + ) } /// Creates a prediction market using Pyth Network oracle as the data source. @@ -227,7 +258,16 @@ impl MarketCreator { /// String::from_str(&env, "gte") /// ).expect("Pyth market creation should succeed"); /// ``` - pub fn create_pyth_market(_env: &Env, admin: Address, question: String, outcomes: Vec, duration_days: u32, feed_id: String, threshold: i128, comparison: String) -> Result { + pub fn create_pyth_market( + _env: &Env, + admin: Address, + question: String, + outcomes: Vec, + duration_days: u32, + feed_id: String, + threshold: i128, + comparison: String, + ) -> Result { let oracle_config = OracleConfig { provider: OracleProvider::Pyth, feed_id, @@ -235,7 +275,14 @@ impl MarketCreator { comparison, }; - Self::create_market(_env, admin, question, outcomes, duration_days, oracle_config) + Self::create_market( + _env, + admin, + question, + outcomes, + duration_days, + oracle_config, + ) } /// Creates a prediction market using Reflector oracle for specific asset types. @@ -291,8 +338,26 @@ impl MarketCreator { /// String::from_str(&env, "gt") /// ).expect("Asset market creation should succeed"); /// ``` - pub fn create_reflector_asset_market(_env: &Env, admin: Address, question: String, outcomes: Vec, duration_days: u32, asset_symbol: String, threshold: i128, comparison: String) -> Result { - Self::create_reflector_market(_env, admin, question, outcomes, duration_days, asset_symbol, threshold, comparison) + pub fn create_reflector_asset_market( + _env: &Env, + admin: Address, + question: String, + outcomes: Vec, + duration_days: u32, + asset_symbol: String, + threshold: i128, + comparison: String, + ) -> Result { + Self::create_reflector_market( + _env, + admin, + question, + outcomes, + duration_days, + asset_symbol, + threshold, + comparison, + ) } } @@ -372,7 +437,6 @@ impl MarketValidator { outcomes: &Vec, duration_days: u32, ) -> Result<(), Error> { - // Validate question is not empty if question.is_empty() { return Err(Error::InvalidQuestion); @@ -590,7 +654,6 @@ impl MarketValidator { outcome: &String, market_outcomes: &Vec, ) -> Result<(), Error> { - for valid_outcome in market_outcomes.iter() { if *outcome == valid_outcome { return Ok(()); @@ -838,7 +901,13 @@ impl MarketStateManager { /// // Save updated market /// MarketStateManager::update_market(&env, &market_id, &market); /// ``` - pub fn add_vote(market: &mut Market, user: Address, outcome: String, stake: i128, _market_id: Option<&Symbol>) { + pub fn add_vote( + market: &mut Market, + user: Address, + outcome: String, + stake: i128, + _market_id: Option<&Symbol>, + ) { MarketStateLogic::check_function_access_for_state("vote", market.state).unwrap(); market.votes.set(user.clone(), outcome); market.stakes.set(user.clone(), stake); @@ -904,18 +973,31 @@ impl MarketStateManager { /// /// MarketStateManager::update_market(&env, &market_id, &market); /// ``` - pub fn add_dispute_stake(market: &mut Market, user: Address, stake: i128, market_id: Option<&Symbol>) { + pub fn add_dispute_stake( + market: &mut Market, + user: Address, + stake: i128, + market_id: Option<&Symbol>, + ) { MarketStateLogic::check_function_access_for_state("dispute", market.state).unwrap(); let existing_stake = market.dispute_stakes.get(user.clone()).unwrap_or(0); market.dispute_stakes.set(user, existing_stake + stake); // State transition: Ended -> Disputed if market.state == MarketState::Ended { - MarketStateLogic::validate_state_transition(market.state, MarketState::Disputed).unwrap(); + MarketStateLogic::validate_state_transition(market.state, MarketState::Disputed) + .unwrap(); let old_state = market.state; market.state = MarketState::Disputed; let env = &market.votes.env(); - let owned_event_id = market_id.cloned().unwrap_or_else(|| Symbol::new(env, "unknown_market_id")); - MarketStateLogic::emit_state_change_event(env, &owned_event_id, old_state, market.state); + let owned_event_id = market_id + .cloned() + .unwrap_or_else(|| Symbol::new(env, "unknown_market_id")); + MarketStateLogic::emit_state_change_event( + env, + &owned_event_id, + old_state, + market.state, + ); } } @@ -1073,11 +1155,19 @@ impl MarketStateManager { market.winning_outcome = Some(outcome); // State transition: Ended/Disputed -> Resolved if market.state == MarketState::Ended || market.state == MarketState::Disputed { - MarketStateLogic::validate_state_transition(market.state, MarketState::Resolved).unwrap(); + MarketStateLogic::validate_state_transition(market.state, MarketState::Resolved) + .unwrap(); market.state = MarketState::Resolved; let env = &market.votes.env(); - let owned_event_id = market_id.cloned().unwrap_or_else(|| Symbol::new(env, "unknown_market_id")); - MarketStateLogic::emit_state_change_event(env, &owned_event_id, old_state, market.state); + let owned_event_id = market_id + .cloned() + .unwrap_or_else(|| Symbol::new(env, "unknown_market_id")); + MarketStateLogic::emit_state_change_event( + env, + &owned_event_id, + old_state, + market.state, + ); } } @@ -1140,8 +1230,15 @@ impl MarketStateManager { MarketStateLogic::validate_state_transition(market.state, MarketState::Closed).unwrap(); market.state = MarketState::Closed; let env = &market.votes.env(); - let owned_event_id = market_id.cloned().unwrap_or_else(|| Symbol::new(env, "unknown_market_id")); - MarketStateLogic::emit_state_change_event(env, &owned_event_id, old_state, market.state); + let owned_event_id = market_id + .cloned() + .unwrap_or_else(|| Symbol::new(env, "unknown_market_id")); + MarketStateLogic::emit_state_change_event( + env, + &owned_event_id, + old_state, + market.state, + ); } market.fee_collected = true; } @@ -1186,7 +1283,7 @@ impl MarketStateManager { /// // End time should be extended if needed /// let current_time = env.ledger().timestamp(); /// let expected_extension = current_time + (24 * 60 * 60); - /// + /// /// if original_end_time < expected_extension { /// assert_eq!(market.end_time, expected_extension); /// } else { @@ -1477,7 +1574,7 @@ impl MarketAnalytics { percentage: consensus_percentage, } } - + /// Calculates basic analytics for a market (placeholder implementation). /// /// This function provides a placeholder for basic market analytics calculation. @@ -1514,7 +1611,7 @@ impl MarketAnalytics { /// /// // Currently returns placeholder analytics /// let analytics = MarketAnalytics::calculate_basic_analytics(&market); - /// + /// /// // In future versions, this would provide detailed insights /// // println!("Market volatility: {}", analytics.volatility); /// // println!("Participation trend: {:?}", analytics.trend); @@ -1938,10 +2035,10 @@ pub struct MarketStats { /// let market_id = Symbol::new(&env, "resolved_market"); /// let market = MarketStateManager::get_market(&env, &market_id)?; /// let winning_outcome = String::from_str(&env, "Yes"); -/// +/// /// let winning_stats = MarketAnalytics::calculate_winning_stats(&market, &winning_outcome); /// let payout_multiplier = winning_stats.total_pool as f64 / winning_stats.winning_total as f64; -/// +/// /// println!("Winners: {} participants", winning_stats.winning_voters); /// println!("Payout multiplier: {:.2}x", payout_multiplier); /// ``` @@ -1985,14 +2082,14 @@ pub struct WinningStats { /// let user = Address::generate(&env); /// let market_id = Symbol::new(&env, "market_123"); /// let market = MarketStateManager::get_market(&env, &market_id)?; -/// +/// /// let user_stats = MarketAnalytics::get_user_stats(&market, &user); -/// +/// /// if user_stats.has_voted { /// println!("User voted for: {:?}", user_stats.voted_outcome); /// println!("Stake: {} stroops", user_stats.stake); /// } -/// +/// /// if !user_stats.has_claimed && market.winning_outcome.is_some() { /// println!("User may be eligible to claim winnings"); /// } @@ -2038,10 +2135,10 @@ pub struct UserStats { /// let env = Env::default(); /// let market_id = Symbol::new(&env, "market_123"); /// let market = MarketStateManager::get_market(&env, &market_id)?; -/// +/// /// let consensus = MarketAnalytics::calculate_community_consensus(&market); /// let oracle_result = String::from_str(&env, "No"); -/// +/// /// // Check consensus strength /// if consensus.percentage > 50 && consensus.total_votes >= 5 { /// println!("Strong community consensus: {} ({}%)", consensus.outcome, consensus.percentage); @@ -2069,7 +2166,7 @@ pub struct CommunityConsensus { /// This struct provides helper functions specifically designed for testing /// market functionality. These functions create test data, simulate market /// operations, and provide utilities for unit tests and integration testing. -/// +/// /// **Note**: These functions are intended for testing environments only. pub struct MarketTestHelpers; @@ -2114,7 +2211,7 @@ impl MarketTestHelpers { /// println!("Test question: {}", test_config.question); /// println!("Duration: {} days", test_config.duration_days); /// println!("Oracle provider: {:?}", test_config.oracle_config.provider); - /// + /// /// // Use config for testing market creation /// // let market_id = MarketCreator::create_market(...); /// ``` @@ -2190,7 +2287,14 @@ impl MarketTestHelpers { pub fn create_test_market(_env: &Env) -> Result { let config = Self::create_test_market_config(_env); - MarketCreator::create_market(_env, config.admin, config.question, config.outcomes, config.duration_days, config.oracle_config) + MarketCreator::create_market( + _env, + config.admin, + config.question, + config.outcomes, + config.duration_days, + config.oracle_config, + ) } /// Adds a test vote to an existing market with comprehensive validation. @@ -2268,7 +2372,6 @@ impl MarketTestHelpers { MarketStateManager::add_vote(&mut market, user, outcome, stake, None); MarketStateManager::update_market(env, market_id, &market); - Ok(()) } @@ -2479,7 +2582,10 @@ impl MarketStateLogic { /// MarketState::Resolved /// ).is_ok()); /// ``` - pub fn check_function_access_for_state(function: &str, state: MarketState) -> Result<(), Error> { + pub fn check_function_access_for_state( + function: &str, + state: MarketState, + ) -> Result<(), Error> { use MarketState::*; let allowed = match function { "vote" => matches!(state, Active), @@ -2535,8 +2641,14 @@ impl MarketStateLogic { /// /// // External systems can now detect this state change /// ``` - pub fn emit_state_change_event(env: &Env, market_id: &Symbol, old_state: MarketState, new_state: MarketState) { - env.events().publish(("market_state_change", market_id), (old_state, new_state)); + pub fn emit_state_change_event( + env: &Env, + market_id: &Symbol, + old_state: MarketState, + new_state: MarketState, + ) { + env.events() + .publish(("market_state_change", market_id), (old_state, new_state)); } /// Validates that a market's state is consistent with its internal data. @@ -2709,12 +2821,16 @@ impl MarketStateLogic { /// &market_id, /// MarketState::Closed /// )?; - /// + /// /// if can_close { /// println!("Market is ready to be closed"); /// } /// ``` - pub fn can_transition_to_state(env: &Env, market_id: &Symbol, target_state: MarketState) -> Result { + pub fn can_transition_to_state( + env: &Env, + market_id: &Symbol, + target_state: MarketState, + ) -> Result { let market = MarketStateManager::get_market(env, market_id)?; Ok(MarketStateLogic::validate_state_transition(market.state, target_state).is_ok()) } diff --git a/contracts/predictify-hybrid/src/oracles.rs b/contracts/predictify-hybrid/src/oracles.rs index a1909700..41db27fa 100644 --- a/contracts/predictify-hybrid/src/oracles.rs +++ b/contracts/predictify-hybrid/src/oracles.rs @@ -47,15 +47,15 @@ use crate::types::*; /// # use predictify_hybrid::types::OracleProvider; /// # let env = Env::default(); /// # let oracle_address = soroban_sdk::Address::generate(&env); -/// +/// /// // Create oracle instance /// let oracle = ReflectorOracle::new(oracle_address); -/// +/// /// // Check oracle health before use /// if oracle.is_healthy(&env).unwrap_or(false) { /// // Get price for BTC/USD feed /// let btc_price = oracle.get_price( -/// &env, +/// &env, /// &String::from_str(&env, "BTC/USD") /// ); /// @@ -134,10 +134,10 @@ pub trait OracleInterface { /// # use predictify_hybrid::oracles::{PythOracle, PythFeedConfig, OracleInterface}; /// # let env = Env::default(); /// # let contract_id = Address::generate(&env); -/// +/// /// // Create Pyth oracle with feed configurations /// let mut oracle = PythOracle::new(contract_id.clone()); -/// +/// /// // Add BTC/USD feed configuration /// oracle.add_feed_config(PythFeedConfig { /// feed_id: String::from_str(&env, "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"), @@ -145,14 +145,14 @@ pub trait OracleInterface { /// decimals: 8, /// is_active: true, /// }); -/// +/// /// // Currently returns error (Pyth not available on Stellar) /// let price_result = oracle.get_price(&env, &String::from_str(&env, "BTC/USD")); /// assert!(price_result.is_err()); -/// +/// /// // Check oracle provider /// assert_eq!(oracle.provider(), OracleProvider::Pyth); -/// +/// /// // Validate feed configurations /// assert_eq!(oracle.get_feed_count(), 1); /// assert!(oracle.is_feed_active(&String::from_str(&env, "BTC/USD"))); @@ -207,16 +207,16 @@ pub struct PythOracle { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::oracles::PythFeedConfig; /// # let env = Env::default(); -/// +/// /// // Configure BTC/USD feed /// let btc_config = PythFeedConfig { -/// feed_id: String::from_str(&env, +/// feed_id: String::from_str(&env, /// "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"), /// asset_symbol: String::from_str(&env, "BTC/USD"), /// decimals: 8, // 8 decimal places for crypto prices /// is_active: true, /// }; -/// +/// /// // Configure ETH/USD feed /// let eth_config = PythFeedConfig { /// feed_id: String::from_str(&env, @@ -225,7 +225,7 @@ pub struct PythOracle { /// decimals: 8, /// is_active: true, /// }; -/// +/// /// // Configure stock feed with different precision /// let aapl_config = PythFeedConfig { /// feed_id: String::from_str(&env, @@ -234,9 +234,9 @@ pub struct PythOracle { /// decimals: 2, // 2 decimal places for stock prices /// is_active: true, /// }; -/// -/// println!("Configured {} with {} decimals", -/// btc_config.asset_symbol.to_string(), +/// +/// println!("Configured {} with {} decimals", +/// btc_config.asset_symbol.to_string(), /// btc_config.decimals); /// ``` /// @@ -541,10 +541,10 @@ impl OracleInterface for PythOracle { /// # use predictify_hybrid::types::ReflectorAsset; /// # let env = Env::default(); /// # let oracle_address = Address::generate(&env); -/// +/// /// // Create Reflector oracle client /// let client = ReflectorOracleClient::new(&env, oracle_address); -/// +/// /// // Check oracle health before use /// if client.is_healthy() { /// // Get latest BTC price @@ -674,19 +674,19 @@ impl<'a> ReflectorOracleClient<'a> { /// # use predictify_hybrid::types::OracleProvider; /// # let env = Env::default(); /// # let oracle_address = Address::generate(&env); -/// +/// /// // Create Reflector oracle instance /// let oracle = ReflectorOracle::new(oracle_address.clone()); -/// +/// /// // Verify oracle provider type /// assert_eq!(oracle.provider(), OracleProvider::Reflector); /// assert_eq!(oracle.contract_id(), oracle_address); -/// +/// /// // Check oracle health before use /// if oracle.is_healthy(&env).unwrap_or(false) { /// // Get BTC price /// let btc_price = oracle.get_price( -/// &env, +/// &env, /// &String::from_str(&env, "BTC/USD") /// ); /// @@ -898,13 +898,13 @@ impl OracleInterface for ReflectorOracle { /// # use predictify_hybrid::types::{OracleProvider, OracleConfig}; /// # let env = Env::default(); /// # let oracle_address = Address::generate(&env); -/// +/// /// // Create Reflector oracle (recommended for Stellar) /// let reflector_oracle = OracleFactory::create_oracle( /// OracleProvider::Reflector, /// oracle_address.clone() /// ); -/// +/// /// match reflector_oracle { /// Ok(OracleInstance::Reflector(oracle)) => { /// println!("Successfully created Reflector oracle"); @@ -912,27 +912,27 @@ impl OracleInterface for ReflectorOracle { /// }, /// Err(e) => println!("Failed to create oracle: {:?}", e), /// } -/// +/// /// // Check provider support before creation /// if OracleFactory::is_provider_supported(&OracleProvider::Reflector) { /// println!("Reflector is supported on Stellar"); /// } -/// +/// /// // Get recommended provider for Stellar /// let recommended = OracleFactory::get_recommended_provider(); /// assert_eq!(recommended, OracleProvider::Reflector); -/// +/// /// // Create from configuration /// let config = OracleConfig { /// provider: OracleProvider::Reflector, /// // ... other config fields /// }; -/// +/// /// let oracle_from_config = OracleFactory::create_from_config( /// &config, /// oracle_address /// ); -/// +/// /// assert!(oracle_from_config.is_ok()); /// ``` /// @@ -1008,7 +1008,7 @@ impl OracleFactory { if !Self::is_provider_supported(&provider) { return Err(Error::InvalidOracleConfig); } - + match provider { OracleProvider::Reflector => { let oracle = ReflectorOracle::new(contract_id); @@ -1183,13 +1183,13 @@ impl OracleFactory { /// # use predictify_hybrid::types::OracleProvider; /// # let env = Env::default(); /// # let oracle_address = Address::generate(&env); -/// +/// /// // Create oracle instance through factory /// let oracle_result = OracleFactory::create_oracle( /// OracleProvider::Reflector, /// oracle_address /// ); -/// +/// /// match oracle_result { /// Ok(oracle_instance) => { /// // Use unified interface regardless of underlying implementation @@ -1234,16 +1234,16 @@ impl OracleFactory { /// # use predictify_hybrid::types::OracleProvider; /// # let env = Env::default(); /// # let oracle_address = Address::generate(&env); -/// +/// /// // Select oracle based on configuration or conditions /// let preferred_provider = if cfg!(feature = "use-reflector") { /// OracleProvider::Reflector /// } else { /// OracleFactory::get_recommended_provider() /// }; -/// +/// /// let oracle = OracleFactory::create_oracle(preferred_provider, oracle_address)?; -/// +/// /// // Use oracle regardless of which provider was selected /// let is_healthy = oracle.is_healthy(&env)?; /// println!("Oracle health: {}", is_healthy); @@ -1341,11 +1341,11 @@ impl OracleInstance { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::oracles::OracleUtils; /// # let env = Env::default(); -/// +/// /// // Compare BTC price against $50k threshold /// let btc_price = 52_000_00; // $52,000 (8 decimal precision) /// let threshold = 50_000_00; // $50,000 -/// +/// /// // Check if BTC is above $50k /// let is_above_threshold = OracleUtils::compare_prices( /// btc_price, @@ -1353,9 +1353,9 @@ impl OracleInstance { /// &String::from_str(&env, "gt"), /// &env /// )?; -/// +/// /// assert!(is_above_threshold); // BTC is above $50k -/// +/// /// // Determine market outcome /// let outcome = OracleUtils::determine_outcome( /// btc_price, @@ -1363,13 +1363,13 @@ impl OracleInstance { /// &String::from_str(&env, "gt"), /// &env /// )?; -/// +/// /// assert_eq!(outcome, String::from_str(&env, "yes")); -/// +/// /// // Validate oracle response /// OracleUtils::validate_oracle_response(btc_price)?; -/// -/// println!("BTC ${} is above ${} threshold: {}", +/// +/// println!("BTC ${} is above ${} threshold: {}", /// btc_price / 100, threshold / 100, is_above_threshold); /// # Ok::<(), predictify_hybrid::errors::Error>(()) /// ``` diff --git a/contracts/predictify-hybrid/src/resolution.rs b/contracts/predictify-hybrid/src/resolution.rs index 222ea5ce..5fccc892 100644 --- a/contracts/predictify-hybrid/src/resolution.rs +++ b/contracts/predictify-hybrid/src/resolution.rs @@ -2,9 +2,7 @@ use soroban_sdk::{contracttype, Address, Env, Map, String, Symbol, Vec}; use crate::errors::Error; -use crate::markets::{ - CommunityConsensus, MarketAnalytics, MarketStateManager, MarketUtils, -}; +use crate::markets::{CommunityConsensus, MarketAnalytics, MarketStateManager, MarketUtils}; use crate::oracles::{OracleFactory, OracleUtils}; use crate::types::*; @@ -47,10 +45,10 @@ use crate::types::*; /// # use predictify_hybrid::markets::Market; /// # let env = Env::default(); /// # let market = Market::default(); // Placeholder -/// +/// /// // Check current resolution state /// let current_state = ResolutionUtils::get_resolution_state(&env, &market); -/// +/// /// match current_state { /// ResolutionState::Active => { /// println!("Market is active, ready for oracle resolution"); @@ -146,14 +144,14 @@ pub enum ResolutionState { /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "btc_50k"); /// # let oracle_contract = Address::generate(&env); -/// +/// /// // Fetch oracle resolution for a market /// let oracle_resolution = OracleResolutionManager::fetch_oracle_result( /// &env, /// &market_id, /// &oracle_contract /// )?; -/// +/// /// // Examine oracle resolution details /// println!("Market: {}", oracle_resolution.market_id); /// println!("Oracle result: {}", oracle_resolution.oracle_result); @@ -162,10 +160,10 @@ pub enum ResolutionState { /// println!("Comparison: {}", oracle_resolution.comparison); /// println!("Provider: {:?}", oracle_resolution.provider); /// println!("Feed: {}", oracle_resolution.feed_id); -/// +/// /// // Validate oracle resolution /// OracleResolutionManager::validate_oracle_resolution(&env, &oracle_resolution)?; -/// +/// /// // Calculate confidence score /// let confidence = OracleResolutionManager::calculate_oracle_confidence(&oracle_resolution); /// println!("Oracle confidence: {}%", confidence); @@ -179,19 +177,19 @@ pub enum ResolutionState { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::oracles::OracleUtils; /// # let env = Env::default(); -/// +/// /// // Example: BTC above $50,000? /// let btc_price = 52_000_00; // $52,000 (8 decimal precision) /// let threshold = 50_000_00; // $50,000 /// let comparison = String::from_str(&env, "gt"); // Greater than -/// +/// /// let outcome = OracleUtils::determine_outcome( /// btc_price, -/// threshold, +/// threshold, /// &comparison, /// &env /// )?; -/// +/// /// assert_eq!(outcome, String::from_str(&env, "yes")); // BTC > $50k = "yes" /// # Ok::<(), predictify_hybrid::errors::Error>(()) /// ``` @@ -268,24 +266,24 @@ pub struct OracleResolution { /// # use predictify_hybrid::resolution::{MarketResolutionManager, MarketResolution, ResolutionMethod}; /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "btc_prediction"); -/// +/// /// // Resolve a market using hybrid method /// let resolution = MarketResolutionManager::resolve_market(&env, &market_id)?; -/// +/// /// // Examine resolution details /// println!("Market: {}", resolution.market_id); /// println!("Final outcome: {}", resolution.final_outcome); /// println!("Oracle result: {}", resolution.oracle_result); -/// println!("Community consensus: {}% ({})", +/// println!("Community consensus: {}% ({})", /// resolution.community_consensus.percentage, /// resolution.community_consensus.outcome /// ); /// println!("Resolution method: {:?}", resolution.resolution_method); /// println!("Confidence: {}%", resolution.confidence_score); -/// +/// /// // Validate the resolution /// MarketResolutionManager::validate_market_resolution(&env, &resolution)?; -/// +/// /// // Check resolution method /// match resolution.resolution_method { /// ResolutionMethod::Hybrid => { @@ -313,7 +311,7 @@ pub struct OracleResolution { /// ```rust /// # use predictify_hybrid::resolution::MarketResolution; /// # let resolution = MarketResolution::default(); // Placeholder -/// +/// /// // Interpret confidence scores /// match resolution.confidence_score { /// 90..=100 => println!("Very high confidence resolution"), @@ -386,7 +384,7 @@ pub struct MarketResolution { /// # use predictify_hybrid::markets::CommunityConsensus; /// # use soroban_sdk::{Env, String}; /// # let env = Env::default(); -/// +/// /// // Example method selection logic /// fn select_resolution_method( /// oracle_available: bool, @@ -415,7 +413,7 @@ pub struct MarketResolution { /// # use predictify_hybrid::resolution::{ResolutionMethod, MarketResolutionAnalytics}; /// # use predictify_hybrid::markets::CommunityConsensus; /// # let env = Env::default(); -/// +/// /// // Determine resolution method based on available data /// let oracle_result = String::from_str(&env, "yes"); /// let community_consensus = CommunityConsensus { @@ -424,12 +422,12 @@ pub struct MarketResolution { /// total_votes: 200, /// percentage: 75, /// }; -/// +/// /// let method = MarketResolutionAnalytics::determine_resolution_method( /// &oracle_result, /// &community_consensus /// ); -/// +/// /// match method { /// ResolutionMethod::Hybrid => { /// println!("Using hybrid resolution - oracle and community agree"); @@ -529,10 +527,10 @@ pub enum ResolutionMethod { /// # use soroban_sdk::{Env, Map, String, Vec}; /// # use predictify_hybrid::resolution::{ResolutionAnalytics, ResolutionAnalyticsManager}; /// # let env = Env::default(); -/// +/// /// // Get current resolution analytics /// let analytics = ResolutionAnalyticsManager::get_resolution_analytics(&env)?; -/// +/// /// // Display system performance metrics /// println!("=== Resolution System Analytics ==="); /// println!("Total resolutions: {}", analytics.total_resolutions); @@ -540,7 +538,7 @@ pub enum ResolutionMethod { /// println!("Community resolutions: {}", analytics.community_resolutions); /// println!("Hybrid resolutions: {}", analytics.hybrid_resolutions); /// println!("Average confidence: {}%", analytics.average_confidence / 100); -/// +/// /// // Calculate method distribution /// let total = analytics.total_resolutions as f64; /// if total > 0.0 { @@ -548,13 +546,13 @@ pub enum ResolutionMethod { /// println!("Community-only: {:.1}%", (analytics.community_resolutions as f64 / total) * 100.0); /// println!("Hybrid: {:.1}%", (analytics.hybrid_resolutions as f64 / total) * 100.0); /// } -/// +/// /// // Analyze resolution times /// if !analytics.resolution_times.is_empty() { /// let avg_time = analytics.resolution_times.iter().sum::() / analytics.resolution_times.len() as u64; /// println!("Average resolution time: {} seconds", avg_time); /// } -/// +/// /// // Display outcome distribution /// for (outcome, count) in analytics.outcome_distribution.iter() { /// println!("Outcome '{}': {} markets", outcome, count); @@ -568,7 +566,7 @@ pub enum ResolutionMethod { /// ```rust /// # use predictify_hybrid::resolution::ResolutionAnalytics; /// # let analytics = ResolutionAnalytics::default(); -/// +/// /// // Monitor system health /// fn assess_system_health(analytics: &ResolutionAnalytics) -> String { /// let confidence_threshold = 80_00; // 80% in basis points @@ -666,10 +664,10 @@ pub struct ResolutionAnalytics { /// # use predictify_hybrid::resolution::{ResolutionValidation, MarketResolutionManager, MarketResolution}; /// # let env = Env::default(); /// # let resolution = MarketResolution::default(); // Placeholder -/// +/// /// // Validate a market resolution /// let validation = MarketResolutionManager::validate_market_resolution(&env, &resolution)?; -/// +/// /// if validation.is_valid { /// println!("✅ Resolution is valid and ready for finalization"); /// @@ -704,7 +702,7 @@ pub struct ResolutionAnalytics { /// # use predictify_hybrid::resolution::{ResolutionValidation, OracleResolution}; /// # use soroban_sdk::{Env, Vec, String}; /// # let env = Env::default(); -/// +/// /// // Example validation workflow /// fn comprehensive_validation_workflow( /// env: &Env, @@ -730,9 +728,9 @@ pub struct ResolutionAnalytics { /// /// Ok(true) /// } -/// +/// /// fn validate_oracle_data( -/// _env: &Env, +/// _env: &Env, /// _oracle_resolution: &OracleResolution /// ) -> Result { /// // Placeholder implementation @@ -816,7 +814,7 @@ pub struct ResolutionValidation { /// /// The typical oracle resolution workflow: /// ```text -/// 1. Validate Market → 2. Fetch Oracle Data → 3. Validate Response → +/// 1. Validate Market → 2. Fetch Oracle Data → 3. Validate Response → /// 4. Calculate Outcome → 5. Score Confidence → 6. Store Resolution /// ``` /// @@ -828,34 +826,34 @@ pub struct ResolutionValidation { /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "btc_50k_market"); /// # let oracle_contract = Address::generate(&env); -/// +/// /// // Fetch oracle resolution for a market /// let oracle_resolution = OracleResolutionManager::fetch_oracle_result( /// &env, /// &market_id, /// &oracle_contract /// )?; -/// +/// /// println!("Oracle Resolution Results:"); /// println!("Market: {}", oracle_resolution.market_id); /// println!("Result: {}", oracle_resolution.oracle_result); /// println!("Price: ${}", oracle_resolution.price / 100); /// println!("Threshold: ${}", oracle_resolution.threshold / 100); /// println!("Provider: {:?}", oracle_resolution.provider); -/// +/// /// // Validate the oracle resolution /// OracleResolutionManager::validate_oracle_resolution(&env, &oracle_resolution)?; -/// +/// /// // Calculate confidence score /// let confidence = OracleResolutionManager::calculate_oracle_confidence(&oracle_resolution); /// println!("Oracle confidence: {}%", confidence); -/// +/// /// // Store resolution for later retrieval /// // (Implementation would store in contract storage) -/// +/// /// // Retrieve stored resolution /// if let Some(stored_resolution) = OracleResolutionManager::get_oracle_resolution( -/// &env, +/// &env, /// &market_id /// )? { /// println!("Successfully retrieved stored oracle resolution"); @@ -872,13 +870,13 @@ pub struct ResolutionValidation { /// # use predictify_hybrid::types::OracleProvider; /// # let env = Env::default(); /// # let oracle_contract = Address::generate(&env); -/// +/// /// // Create oracle instance based on provider /// let oracle = OracleFactory::create_oracle( /// OracleProvider::Reflector, // Primary provider for Stellar /// oracle_contract /// )?; -/// +/// /// // Use oracle for price fetching /// match oracle { /// OracleInstance::Reflector(reflector_oracle) => { @@ -904,10 +902,10 @@ pub struct ResolutionValidation { /// ```rust /// # use predictify_hybrid::resolution::{OracleResolution, OracleResolutionManager}; /// # let oracle_resolution = OracleResolution::default(); // Placeholder -/// +/// /// // Confidence scoring factors /// let confidence = OracleResolutionManager::calculate_oracle_confidence(&oracle_resolution); -/// +/// /// match confidence { /// 90..=100 => println!("Very high confidence - excellent oracle data"), /// 80..=89 => println!("High confidence - reliable oracle data"), @@ -1084,21 +1082,21 @@ impl OracleResolutionManager { /// # let env = Env::default(); /// # let market_id = Symbol::new(&env, "btc_prediction_market"); /// # let admin = Address::generate(&env); -/// +/// /// // Resolve a market using hybrid method (oracle + community) /// let resolution = MarketResolutionManager::resolve_market(&env, &market_id)?; -/// +/// /// println!("Market Resolution Complete:"); /// println!("Market: {}", resolution.market_id); /// println!("Final outcome: {}", resolution.final_outcome); /// println!("Method: {:?}", resolution.resolution_method); /// println!("Confidence: {}%", resolution.confidence_score); -/// +/// /// // Display resolution details /// match resolution.resolution_method { /// ResolutionMethod::Hybrid => { /// println!("Oracle result: {}", resolution.oracle_result); -/// println!("Community consensus: {}% ({})", +/// println!("Community consensus: {}% ({})", /// resolution.community_consensus.percentage, /// resolution.community_consensus.outcome /// ); @@ -1111,10 +1109,10 @@ impl OracleResolutionManager { /// }, /// _ => println!("Other resolution method used"), /// } -/// +/// /// // Validate the resolution /// MarketResolutionManager::validate_market_resolution(&env, &resolution)?; -/// +/// /// // Admin can finalize with override if needed /// if resolution.confidence_score < 70 { /// let admin_resolution = MarketResolutionManager::finalize_market( @@ -1136,7 +1134,7 @@ impl OracleResolutionManager { /// # use predictify_hybrid::resolution::ResolutionMethod; /// # use predictify_hybrid::markets::CommunityConsensus; /// # let env = Env::default(); -/// +/// /// // Example resolution decision logic /// fn determine_final_outcome( /// oracle_result: &String, @@ -1174,7 +1172,7 @@ impl OracleResolutionManager { /// ```rust /// # use predictify_hybrid::resolution::MarketResolution; /// # let resolution = MarketResolution::default(); // Placeholder -/// +/// /// // Interpret confidence levels /// match resolution.confidence_score { /// 95..=100 => println!("Extremely high confidence - virtually certain outcome"), @@ -1398,10 +1396,8 @@ impl MarketResolutionValidator { /// Validate admin permissions pub fn validate_admin_permissions(env: &Env, admin: &Address) -> Result<(), Error> { - let stored_admin: Option
= env - .storage() - .persistent() - .get(&Symbol::new(env, "Admin")); + let stored_admin: Option
= + env.storage().persistent().get(&Symbol::new(env, "Admin")); match stored_admin { Some(stored_admin) => { @@ -1805,11 +1801,10 @@ mod tests { assert!(ResolutionTesting::validate_resolution_structure(&market_resolution).is_ok()); } - #[test] fn test_resolution_method_determination() { let env = Env::default(); - + // Create test data let community_consensus = CommunityConsensus { outcome: String::from_str(&env, "yes"), diff --git a/contracts/predictify-hybrid/src/test.rs b/contracts/predictify-hybrid/src/test.rs index a7cb574b..8e16cac9 100644 --- a/contracts/predictify-hybrid/src/test.rs +++ b/contracts/predictify-hybrid/src/test.rs @@ -18,7 +18,6 @@ use super::*; - use soroban_sdk::{ testutils::{Address as _, Ledger, LedgerInfo}, token::{self, StellarAssetClient}, @@ -142,7 +141,6 @@ fn test_create_market_successful() { String::from_str(&test.env, "no"), ]; - // Create market let market_id = client.create_market( &test.admin, @@ -394,11 +392,11 @@ fn test_fee_calculation() { #[test] fn test_fee_validation() { let _test = PredictifyTest::setup(); - + // Test valid fee amount let valid_fee = 1_0000000; // 1 XLM assert!(valid_fee >= 1_000_000); // MIN_FEE_AMOUNT - + // Test invalid fee amounts would be caught by validation let too_small_fee = 500_000; // 0.5 XLM assert!(too_small_fee < 1_000_000); // Below MIN_FEE_AMOUNT @@ -441,7 +439,7 @@ fn test_question_length_validation() { // Test maximum question length (should not exceed 500 characters) let long_question = "a".repeat(501); let _long_question_str = String::from_str(&test.env, &long_question); - + // This should be handled by validation in the actual implementation // For now, we test that the constant is properly defined assert_eq!(crate::config::MAX_QUESTION_LENGTH, 500); @@ -450,10 +448,10 @@ fn test_question_length_validation() { #[test] fn test_outcome_validation() { let _test = PredictifyTest::setup(); - + // Test outcome length limits assert_eq!(crate::config::MAX_OUTCOME_LENGTH, 100); - + // Test minimum and maximum outcomes assert_eq!(crate::config::MIN_MARKET_OUTCOMES, 2); assert_eq!(crate::config::MAX_MARKET_OUTCOMES, 10); @@ -466,7 +464,7 @@ fn test_outcome_validation() { fn test_percentage_calculations() { // Test percentage denominator assert_eq!(crate::config::PERCENTAGE_DENOMINATOR, 100); - + // Test percentage calculation logic let total = 1000_0000000; // 1000 XLM let percentage = 2; // 2% @@ -477,12 +475,12 @@ fn test_percentage_calculations() { #[test] fn test_time_calculations() { let test = PredictifyTest::setup(); - + // Test duration calculations let current_time = test.env.ledger().timestamp(); let duration_days = 30; let expected_end_time = current_time + (duration_days as u64 * 24 * 60 * 60); - + // Verify the calculation matches what's used in market creation let market_id = test.create_test_market(); let market = test.env.as_contract(&test.contract_id, || { @@ -492,7 +490,7 @@ fn test_time_calculations() { .get::(&market_id) .unwrap() }); - + assert_eq!(market.end_time, expected_end_time); } @@ -503,7 +501,7 @@ fn test_time_calculations() { fn test_market_creation_data() { let test = PredictifyTest::setup(); let market_id = test.create_test_market(); - + let market = test.env.as_contract(&test.contract_id, || { test.env .storage() @@ -511,7 +509,7 @@ fn test_market_creation_data() { .get::(&market_id) .unwrap() }); - + // Verify market creation data is properly stored assert!(!market.question.is_empty()); assert_eq!(market.outcomes.len(), 2); @@ -545,7 +543,7 @@ fn test_voting_data_integrity() { assert!(market.votes.contains_key(test.user.clone())); let user_vote = market.votes.get(test.user.clone()).unwrap(); assert_eq!(user_vote, String::from_str(&test.env, "yes")); - + assert!(market.stakes.contains_key(test.user.clone())); let user_stake = market.stakes.get(test.user.clone()).unwrap(); assert_eq!(user_stake, 1_0000000); @@ -559,7 +557,7 @@ fn test_voting_data_integrity() { fn test_oracle_configuration() { let test = PredictifyTest::setup(); let market_id = test.create_test_market(); - + let market = test.env.as_contract(&test.contract_id, || { test.env .storage() @@ -567,12 +565,18 @@ fn test_oracle_configuration() { .get::(&market_id) .unwrap() }); - + // Verify oracle configuration is properly stored assert_eq!(market.oracle_config.provider, OracleProvider::Reflector); - assert_eq!(market.oracle_config.feed_id, String::from_str(&test.env, "BTC")); + assert_eq!( + market.oracle_config.feed_id, + String::from_str(&test.env, "BTC") + ); assert_eq!(market.oracle_config.threshold, 2500000); - assert_eq!(market.oracle_config.comparison, String::from_str(&test.env, "gt")); + assert_eq!( + market.oracle_config.comparison, + String::from_str(&test.env, "gt") + ); } #[test] @@ -582,8 +586,8 @@ fn test_oracle_provider_types() { let _reflector = OracleProvider::Reflector; let _band = OracleProvider::BandProtocol; let _dia = OracleProvider::DIA; - + // Test oracle provider comparison assert_ne!(OracleProvider::Pyth, OracleProvider::Reflector); assert_eq!(OracleProvider::Pyth, OracleProvider::Pyth); -} \ No newline at end of file +} diff --git a/contracts/predictify-hybrid/src/types.rs b/contracts/predictify-hybrid/src/types.rs index 6ad2f578..9eeb284e 100644 --- a/contracts/predictify-hybrid/src/types.rs +++ b/contracts/predictify-hybrid/src/types.rs @@ -69,7 +69,7 @@ use soroban_sdk::{contracttype, Address, Env, Map, String, Symbol, Vec}; /// # let env = Env::default(); /// # let market = Market::default(); // Placeholder /// # let current_time = env.ledger().timestamp(); -/// +/// /// // Check market state and determine available operations /// match market.state { /// MarketState::Active => { @@ -192,10 +192,10 @@ pub enum MarketState { /// /// ```rust /// # use predictify_hybrid::types::OracleProvider; -/// +/// /// // Check provider support before using /// let provider = OracleProvider::Reflector; -/// +/// /// if provider.is_supported() { /// println!("Using {} oracle provider", provider.name()); /// // Proceed with oracle integration @@ -203,7 +203,7 @@ pub enum MarketState { /// println!("Provider {} not supported on Stellar", provider.name()); /// // Use fallback or error handling /// } -/// +/// /// // Provider selection logic /// let recommended_provider = match std::env::var("ORACLE_PREFERENCE") { /// Ok(pref) if pref == "pyth" => { @@ -216,7 +216,7 @@ pub enum MarketState { /// }, /// _ => OracleProvider::Reflector, // Default to Reflector /// }; -/// +/// /// println!("Selected oracle: {}", recommended_provider.name()); /// ``` /// @@ -229,11 +229,11 @@ pub enum MarketState { /// # use predictify_hybrid::oracles::OracleFactory; /// # let env = Env::default(); /// # let oracle_contract = Address::generate(&env); -/// +/// /// // Create oracle instance based on provider /// let provider = OracleProvider::Reflector; /// let oracle_result = OracleFactory::create_oracle(provider, oracle_contract); -/// +/// /// match oracle_result { /// Ok(oracle_instance) => { /// println!("Successfully created {} oracle", provider.name()); @@ -339,7 +339,7 @@ impl OracleProvider { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::types::{OracleConfig, OracleProvider}; /// # let env = Env::default(); -/// +/// /// // Create oracle config for "Will BTC be above $50,000?" /// let btc_config = OracleConfig::new( /// OracleProvider::Reflector, @@ -347,16 +347,16 @@ impl OracleProvider { /// 50_000_00, // $50,000 in cents /// String::from_str(&env, "gt") // Greater than /// ); -/// +/// /// // Validate the configuration /// btc_config.validate(&env)?; -/// +/// /// println!("Oracle Config:"); /// println!("Provider: {}", btc_config.provider.name()); /// println!("Feed: {}", btc_config.feed_id); /// println!("Threshold: ${}", btc_config.threshold / 100); /// println!("Comparison: {}", btc_config.comparison); -/// +/// /// // Create config for "Will ETH drop below $2,000?" /// let eth_config = OracleConfig::new( /// OracleProvider::Reflector, @@ -364,7 +364,7 @@ impl OracleProvider { /// 2_000_00, // $2,000 in cents /// String::from_str(&env, "lt") // Less than /// ); -/// +/// /// // Create config for "Will XLM equal exactly $0.50?" /// let xlm_config = OracleConfig::new( /// OracleProvider::Reflector, @@ -396,20 +396,20 @@ impl OracleProvider { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::types::{OracleConfig, OracleProvider}; /// # let env = Env::default(); -/// +/// /// let config = OracleConfig::new( /// OracleProvider::Reflector, /// String::from_str(&env, "BTC/USD"), /// 50_000_00, /// String::from_str(&env, "gt") /// ); -/// +/// /// // Validation checks: /// // 1. Threshold must be positive /// // 2. Comparison must be "gt", "lt", or "eq" /// // 3. Provider must be supported on current network /// // 4. Feed ID must not be empty -/// +/// /// match config.validate(&env) { /// Ok(()) => println!("Configuration is valid"), /// Err(e) => println!("Validation failed: {:?}", e), @@ -431,7 +431,7 @@ impl OracleProvider { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::types::{OracleConfig, OracleProvider}; /// # let env = Env::default(); -/// +/// /// // "Will BTC reach $100k by year end?" /// let btc_100k = OracleConfig::new( /// OracleProvider::Reflector, @@ -439,7 +439,7 @@ impl OracleProvider { /// 100_000_00, /// String::from_str(&env, "gt") /// ); -/// +/// /// // "Will ETH stay above $1,500?" /// let eth_support = OracleConfig::new( /// OracleProvider::Reflector, @@ -488,7 +488,6 @@ impl OracleConfig { /// Validate the oracle configuration pub fn validate(&self, env: &Env) -> Result<(), crate::Error> { - // Validate threshold if self.threshold <= 0 { return Err(crate::Error::InvalidThreshold); @@ -555,7 +554,7 @@ impl OracleConfig { /// # use predictify_hybrid::types::{Market, MarketState, OracleConfig, OracleProvider}; /// # let env = Env::default(); /// # let admin = Address::generate(&env); -/// +/// /// // Create a new prediction market /// let market = Market::new( /// &env, @@ -574,10 +573,10 @@ impl OracleConfig { /// ), /// MarketState::Active /// ); -/// +/// /// // Validate the market /// market.validate(&env)?; -/// +/// /// // Check market status /// let current_time = env.ledger().timestamp(); /// if market.is_active(current_time) { @@ -585,13 +584,13 @@ impl OracleConfig { /// } else if market.has_ended(current_time) { /// println!("Market has ended, ready for resolution"); /// } -/// +/// /// // Display market information /// println!("Market Question: {}", market.question); /// println!("Admin: {}", market.admin); /// println!("Total Staked: {} stroops", market.total_staked); /// println!("State: {:?}", market.state); -/// +/// /// // Check if market is resolved /// if market.is_resolved() { /// if let Some(result) = &market.oracle_result { @@ -609,24 +608,24 @@ impl OracleConfig { /// # use predictify_hybrid::types::Market; /// # let mut market = Market::default(); // Placeholder /// # let user = Address::generate(&soroban_sdk::Env::default()); -/// +/// /// // Add user vote and stake (for testing) /// market.add_vote( /// user.clone(), /// String::from_str(&soroban_sdk::Env::default(), "yes"), /// 1_000_000 // 1 XLM in stroops /// ); -/// +/// /// // Check user's vote /// if let Some(user_vote) = market.votes.get(user.clone()) { /// println!("User voted: {}", user_vote); /// } -/// +/// /// // Check user's stake /// if let Some(user_stake) = market.stakes.get(user.clone()) { /// println!("User staked: {} stroops", user_stake); /// } -/// +/// /// // Check if user has claimed payout /// let has_claimed = market.claimed.get(user.clone()).unwrap_or(false); /// println!("User claimed payout: {}", has_claimed); @@ -640,7 +639,7 @@ impl OracleConfig { /// # use predictify_hybrid::types::Market; /// # let env = Env::default(); /// # let market = Market::default(); // Placeholder -/// +/// /// // Validation checks multiple aspects: /// match market.validate(&env) { /// Ok(()) => { @@ -664,14 +663,14 @@ impl OracleConfig { /// ```rust /// # use predictify_hybrid::types::Market; /// # let market = Market::default(); // Placeholder -/// +/// /// // Total market value /// println!("Total staked: {} stroops", market.total_staked); -/// +/// /// // Dispute stakes (for contested resolutions) /// let dispute_total = market.total_dispute_stakes(); /// println!("Total dispute stakes: {} stroops", dispute_total); -/// +/// /// // Calculate potential payouts /// let winner_pool = market.total_staked; // Simplified /// println!("Winner pool: {} stroops", winner_pool); @@ -736,7 +735,6 @@ pub struct Market { /// Extension history pub extension_history: Vec, - } impl Market { @@ -769,7 +767,6 @@ impl Market { total_extension_days: 0, max_extension_days: 30, // Default maximum extension days extension_history: Vec::new(env), - } } @@ -861,13 +858,13 @@ impl Market { /// /// ```rust /// # use predictify_hybrid::types::ReflectorAsset; -/// +/// /// // Asset identification and properties /// let btc = ReflectorAsset::BTC; /// println!("Asset: {}", btc.symbol()); /// println!("Name: {}", btc.name()); /// println!("Decimals: {}", btc.decimals()); -/// +/// /// // Asset validation /// let assets = vec![ReflectorAsset::BTC, ReflectorAsset::ETH, ReflectorAsset::XLM]; /// for asset in assets { @@ -875,11 +872,11 @@ impl Market { /// println!("{} is supported by Reflector", asset.symbol()); /// } /// } -/// +/// /// // Feed ID generation /// let btc_feed = ReflectorAsset::BTC.feed_id(); /// println!("BTC feed ID: {}", btc_feed); // "BTC/USD" -/// +/// /// let eth_feed = ReflectorAsset::ETH.feed_id(); /// println!("ETH feed ID: {}", eth_feed); // "ETH/USD" /// ``` @@ -891,7 +888,7 @@ impl Market { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::types::{ReflectorAsset, OracleConfig, OracleProvider}; /// # let env = Env::default(); -/// +/// /// // Create oracle config for BTC price prediction /// let btc_asset = ReflectorAsset::BTC; /// let oracle_config = OracleConfig::new( @@ -900,7 +897,7 @@ impl Market { /// 50_000_00, // $50,000 threshold /// String::from_str(&env, "gt") /// ); -/// +/// /// // Validate asset support /// if btc_asset.is_supported() { /// println!("BTC oracle config created successfully"); @@ -912,21 +909,21 @@ impl Market { /// Each asset has specific characteristics: /// ```rust /// # use predictify_hybrid::types::ReflectorAsset; -/// +/// /// // Bitcoin properties /// let btc = ReflectorAsset::BTC; /// assert_eq!(btc.symbol(), "BTC"); /// assert_eq!(btc.name(), "Bitcoin"); /// assert_eq!(btc.decimals(), 8); /// assert!(btc.is_supported()); -/// +/// /// // Ethereum properties /// let eth = ReflectorAsset::ETH; /// assert_eq!(eth.symbol(), "ETH"); /// assert_eq!(eth.name(), "Ethereum"); /// assert_eq!(eth.decimals(), 18); /// assert!(eth.is_supported()); -/// +/// /// // Stellar Lumens properties /// let xlm = ReflectorAsset::XLM; /// assert_eq!(xlm.symbol(), "XLM"); @@ -950,7 +947,7 @@ impl Market { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::types::{ReflectorAsset, OracleConfig, OracleProvider}; /// # let env = Env::default(); -/// +/// /// // Create market for "Will BTC reach $100k?" /// let btc_market_config = OracleConfig::new( /// OracleProvider::Reflector, @@ -958,7 +955,7 @@ impl Market { /// 100_000_00, /// String::from_str(&env, "gt") /// ); -/// +/// /// // Create market for "Will ETH drop below $1,000?" /// let eth_market_config = OracleConfig::new( /// OracleProvider::Reflector, @@ -966,7 +963,7 @@ impl Market { /// 1_000_00, /// String::from_str(&env, "lt") /// ); -/// +/// /// // Create market for "Will XLM reach $1?" /// let xlm_market_config = OracleConfig::new( /// OracleProvider::Reflector, @@ -1001,7 +998,6 @@ pub enum ReflectorAsset { Other(Symbol), } - /// Comprehensive price data structure from Reflector Oracle. /// /// This structure contains all price information returned by the Reflector Oracle, @@ -1035,7 +1031,7 @@ pub enum ReflectorAsset { /// # use soroban_sdk::Env; /// # use predictify_hybrid::types::ReflectorPriceData; /// # let env = Env::default(); -/// +/// /// // Create price data from Reflector response /// let btc_price = ReflectorPriceData::new( /// 5_000_000, // $50,000.00 in cents @@ -1045,19 +1041,19 @@ pub enum ReflectorAsset { /// 95, // 95% confidence /// Some(1_000_000_000) // $10M volume /// ); -/// +/// /// // Display price information /// println!("BTC Price: ${:.2}", btc_price.price_in_dollars()); /// println!("Updated: {}", btc_price.timestamp); /// println!("Confidence: {}%", btc_price.confidence); -/// +/// /// // Validate price data quality /// if btc_price.is_valid() { /// println!("Price data is valid and reliable"); /// } else { /// println!("Price data quality concerns detected"); /// } -/// +/// /// // Check data freshness /// let current_time = env.ledger().timestamp(); /// if btc_price.is_fresh(current_time, 300) { // 5 minutes @@ -1073,7 +1069,7 @@ pub enum ReflectorAsset { /// ```rust /// # use predictify_hybrid::types::ReflectorPriceData; /// # let price_data = ReflectorPriceData::default(); // Placeholder -/// +/// /// // Validation checks multiple aspects: /// let validation_result = price_data.validate(); /// match validation_result { @@ -1099,10 +1095,10 @@ pub enum ReflectorAsset { /// # use predictify_hybrid::types::{ReflectorPriceData, OracleConfig}; /// # let price_data = ReflectorPriceData::default(); // Placeholder /// # let oracle_config = OracleConfig::default(); // Placeholder -/// +/// /// // Apply oracle configuration to determine outcome /// let market_outcome = price_data.resolve_outcome(&oracle_config); -/// +/// /// match market_outcome { /// Ok(outcome) => { /// println!("Market resolved to: {}", outcome); @@ -1113,12 +1109,12 @@ pub enum ReflectorAsset { /// // Handle resolution errors /// } /// } -/// +/// /// // Example: BTC > $50,000 check /// let btc_price = 5_500_000; // $55,000 /// let threshold = 5_000_000; // $50,000 /// let comparison = "gt"; // Greater than -/// +/// /// let result = if comparison == "gt" { /// btc_price > threshold /// } else if comparison == "lt" { @@ -1126,7 +1122,7 @@ pub enum ReflectorAsset { /// } else { /// btc_price == threshold /// }; -/// +/// /// println!("Market outcome: {}", if result { "yes" } else { "no" }); /// ``` /// @@ -1136,7 +1132,7 @@ pub enum ReflectorAsset { /// ```rust /// # use predictify_hybrid::types::ReflectorPriceData; /// # let price_data = ReflectorPriceData::default(); // Placeholder -/// +/// /// // Check confidence level /// if price_data.confidence >= 90 { /// println!("High confidence price data"); @@ -1145,7 +1141,7 @@ pub enum ReflectorAsset { /// } else { /// println!("Low confidence - use with caution"); /// } -/// +/// /// // Check trading volume (if available) /// if let Some(volume) = price_data.volume { /// if volume > 1_000_000_00 { // $1M+ @@ -1164,10 +1160,10 @@ pub enum ReflectorAsset { /// # use predictify_hybrid::types::ReflectorPriceData; /// # let env = Env::default(); /// # let price_data = ReflectorPriceData::default(); // Placeholder -/// +/// /// let current_time = env.ledger().timestamp(); /// let max_age = 600; // 10 minutes -/// +/// /// if price_data.is_fresh(current_time, max_age) { /// println!("Price data is current"); /// } else { @@ -1260,7 +1256,7 @@ pub struct ReflectorPriceData { /// # use predictify_hybrid::types::MarketExtension; /// # let env = Env::default(); /// # let requester = Address::generate(&env); -/// +/// /// // Create extension request for low participation /// let extension = MarketExtension::new( /// &env, @@ -1271,16 +1267,16 @@ pub struct ReflectorPriceData { /// 1_000_000, // 1 XLM extension fee /// false // Pending approval /// ); -/// +/// /// // Validate extension request /// extension.validate(&env)?; -/// +/// /// // Display extension information /// println!("Extension requested by: {}", extension.requester); /// println!("Extension duration: {} hours", extension.duration_hours()); /// println!("Extension fee: {} stroops", extension.fee); /// println!("Reason: {}", extension.reason); -/// +/// /// // Check if extension is within limits /// if extension.is_within_limits() { /// println!("Extension request is valid"); @@ -1296,7 +1292,7 @@ pub struct ReflectorPriceData { /// ```rust /// # use predictify_hybrid::types::MarketExtension; /// # let extension = MarketExtension::default(); // Placeholder -/// +/// /// // Validation checks multiple aspects: /// let validation_result = extension.validate(&soroban_sdk::Env::default()); /// match validation_result { @@ -1320,11 +1316,11 @@ pub struct ReflectorPriceData { /// Extension fees vary by type and duration: /// ```rust /// # use predictify_hybrid::types::MarketExtension; -/// +/// /// // Calculate extension fee based on duration /// let base_fee = 1_000_000; // 1 XLM base fee /// let duration_hours = 48; // 48 hour extension -/// +/// /// let total_fee = if duration_hours <= 24 { /// base_fee // Standard 24-hour extension /// } else if duration_hours <= 72 { @@ -1332,7 +1328,7 @@ pub struct ReflectorPriceData { /// } else { /// base_fee * 5 // Long extension (73+ hours) /// }; -/// +/// /// println!("Extension fee for {} hours: {} stroops", duration_hours, total_fee); /// ``` /// @@ -1342,10 +1338,10 @@ pub struct ReflectorPriceData { /// ```rust /// # use predictify_hybrid::types::MarketExtension; /// # let mut extension = MarketExtension::default(); // Placeholder -/// +/// /// // Step 1: Request submitted with fee /// extension.set_status("pending"); -/// +/// /// // Step 2: Admin review /// if extension.meets_criteria() { /// extension.approve(); @@ -1354,7 +1350,7 @@ pub struct ReflectorPriceData { /// extension.reject("Insufficient justification"); /// println!("Extension rejected"); /// } -/// +/// /// // Step 3: Apply extension if approved /// if extension.is_approved() { /// extension.apply_to_market(); @@ -1376,7 +1372,7 @@ pub struct ReflectorPriceData { /// ```rust /// # use predictify_hybrid::types::MarketExtension; /// # let extension = MarketExtension::default(); // Placeholder -/// +/// /// // Extension statistics /// println!("Extension type: {}", extension.extension_type()); /// println!("Participation before: {}%", extension.participation_before()); @@ -1392,7 +1388,7 @@ pub struct ReflectorPriceData { /// # use predictify_hybrid::types::MarketExtension; /// # let env = Env::default(); /// # let system = Address::generate(&env); -/// +/// /// let auto_extension = MarketExtension::new( /// &env, /// system, // System-initiated @@ -1410,7 +1406,7 @@ pub struct ReflectorPriceData { /// # use predictify_hybrid::types::MarketExtension; /// # let env = Env::default(); /// # let community_member = Address::generate(&env); -/// +/// /// let community_extension = MarketExtension::new( /// &env, /// community_member, @@ -1495,10 +1491,10 @@ impl MarketExtension { /// # use soroban_sdk::Env; /// # use predictify_hybrid::types::ExtensionStats; /// # let env = Env::default(); -/// +/// /// // Create extension statistics tracker /// let mut stats = ExtensionStats::new(&env); -/// +/// /// // Record extension request /// stats.record_extension_request( /// "user_requested", @@ -1506,14 +1502,14 @@ impl MarketExtension { /// 2_000_000, // 2 XLM fee /// true // Approved /// ); -/// +/// /// // Record participation impact /// stats.record_participation_change( /// 10, // 10 additional votes /// 5_000_000, // 5 XLM additional stakes /// 25.0 // 25% participation increase /// ); -/// +/// /// // Display statistics /// println!("Total extensions: {}", stats.total_extensions); /// println!("Approval rate: {:.1}%", stats.approval_rate()); @@ -1527,16 +1523,16 @@ impl MarketExtension { /// ```rust /// # use predictify_hybrid::types::ExtensionStats; /// # let stats = ExtensionStats::default(); // Placeholder -/// +/// /// // Calculate effectiveness metrics /// let effectiveness_report = stats.generate_effectiveness_report(); -/// +/// /// println!("Extension Effectiveness Report:"); /// println!("- Auto extensions success rate: {:.1}%", effectiveness_report.auto_success_rate); /// println!("- User extensions success rate: {:.1}%", effectiveness_report.user_success_rate); /// println!("- Average participation boost: {:.1}%", effectiveness_report.avg_participation_boost); /// println!("- ROI on extension fees: {:.2}x", effectiveness_report.fee_roi); -/// +/// /// // Identify optimal extension patterns /// if effectiveness_report.auto_success_rate > effectiveness_report.user_success_rate { /// println!("Recommendation: Favor automatic extensions for low participation"); @@ -1551,12 +1547,12 @@ impl MarketExtension { /// ```rust /// # use predictify_hybrid::types::ExtensionStats; /// # let stats = ExtensionStats::default(); // Placeholder -/// +/// /// // Analyze monthly trends /// let monthly_trends = stats.get_monthly_trends(); -/// +/// /// for (month, trend_data) in monthly_trends { -/// println!("Month {}: {} extensions, {:.1}% approval rate", +/// println!("Month {}: {} extensions, {:.1}% approval rate", /// month, trend_data.count, trend_data.approval_rate); /// /// if trend_data.count > trend_data.previous_month_count { @@ -1565,7 +1561,7 @@ impl MarketExtension { /// println!(" ↘ Extension requests decreasing"); /// } /// } -/// +/// /// // Seasonal patterns /// let seasonal_analysis = stats.analyze_seasonal_patterns(); /// println!("Peak extension period: {}", seasonal_analysis.peak_period); @@ -1578,16 +1574,16 @@ impl MarketExtension { /// ```rust /// # use predictify_hybrid::types::ExtensionStats; /// # let stats = ExtensionStats::default(); // Placeholder -/// +/// /// // Fee effectiveness analysis /// let fee_analysis = stats.analyze_fee_effectiveness(); -/// +/// /// println!("Fee Analysis:"); -/// println!("- Optimal fee range: {} - {} stroops", +/// println!("- Optimal fee range: {} - {} stroops", /// fee_analysis.optimal_min, fee_analysis.optimal_max); /// println!("- Fee elasticity: {:.2}", fee_analysis.elasticity); /// println!("- Revenue maximizing fee: {} stroops", fee_analysis.revenue_max_fee); -/// +/// /// // Fee recommendations /// if fee_analysis.current_fee < fee_analysis.optimal_min { /// println!("Recommendation: Increase extension fees to improve quality"); @@ -1604,15 +1600,15 @@ impl MarketExtension { /// ```rust /// # use predictify_hybrid::types::ExtensionStats; /// # let stats = ExtensionStats::default(); // Placeholder -/// +/// /// // Market quality impact /// let quality_impact = stats.assess_market_quality_impact(); -/// +/// /// println!("Market Quality Impact:"); /// println!("- Resolution accuracy improvement: {:.1}%", quality_impact.accuracy_improvement); /// println!("- Participation diversity increase: {:.1}%", quality_impact.diversity_increase); /// println!("- Stake distribution improvement: {:.1}%", quality_impact.distribution_improvement); -/// +/// /// // Long-term effects /// println!("\nLong-term Effects:"); /// println!("- User retention rate: {:.1}%", quality_impact.retention_rate); @@ -1626,10 +1622,10 @@ impl MarketExtension { /// ```rust /// # use predictify_hybrid::types::ExtensionStats; /// # let stats = ExtensionStats::default(); // Placeholder -/// +/// /// // Benchmark by market category /// let benchmarks = stats.benchmark_by_category(); -/// +/// /// for (category, benchmark) in benchmarks { /// println!("{} Markets:", category); /// println!(" Extension rate: {:.1}%", benchmark.extension_rate); @@ -1637,7 +1633,7 @@ impl MarketExtension { /// println!(" Avg duration: {:.1} hours", benchmark.avg_duration_hours); /// println!(" Participation boost: {:.1}%", benchmark.participation_boost); /// } -/// +/// /// // Identify best practices /// let best_practices = stats.identify_best_practices(); /// println!("\nBest Practices:"); @@ -1662,7 +1658,7 @@ impl MarketExtension { /// ```rust /// # use predictify_hybrid::types::ExtensionStats; /// # let stats = ExtensionStats::default(); // Placeholder -/// +/// /// // Generate monthly report /// let monthly_report = stats.generate_monthly_report(); /// println!("Extension Monthly Report:"); @@ -1670,7 +1666,7 @@ impl MarketExtension { /// println!("Approval Rate: {:.1}%", monthly_report.approval_rate); /// println!("Revenue Generated: {} XLM", monthly_report.revenue_xlm); /// println!("Participation Impact: +{:.1}%", monthly_report.participation_impact); -/// +/// /// // Export data for external analysis /// let csv_data = stats.export_to_csv(); /// println!("CSV export ready: {} records", csv_data.len()); @@ -1735,7 +1731,7 @@ pub struct ExtensionStats { /// # use predictify_hybrid::types::{MarketCreationParams, OracleConfig, OracleProvider}; /// # let env = Env::default(); /// # let admin = Address::generate(&env); -/// +/// /// // Create parameters for a Bitcoin price prediction market /// let btc_market_params = MarketCreationParams::new( /// admin.clone(), @@ -1753,16 +1749,16 @@ pub struct ExtensionStats { /// ), /// 5_000_000 // 5 XLM creation fee /// ); -/// +/// /// // Validate parameters before market creation /// btc_market_params.validate(&env)?; -/// +/// /// // Display market information /// println!("Market Question: {}", btc_market_params.question); /// println!("Duration: {} days", btc_market_params.duration_days); /// println!("Creation Fee: {} stroops", btc_market_params.creation_fee); /// println!("Oracle Provider: {}", btc_market_params.oracle_config.provider.name()); -/// +/// /// // Check if admin has sufficient balance /// if admin_has_sufficient_balance(&admin, btc_market_params.creation_fee) { /// println!("Admin can afford market creation"); @@ -1778,7 +1774,7 @@ pub struct ExtensionStats { /// ```rust /// # use predictify_hybrid::types::MarketCreationParams; /// # let params = MarketCreationParams::default(); // Placeholder -/// +/// /// // Validation checks multiple aspects: /// let validation_result = params.validate(&soroban_sdk::Env::default()); /// match validation_result { @@ -1804,21 +1800,21 @@ pub struct ExtensionStats { /// ```rust /// # use soroban_sdk::{Env, String}; /// # let env = Env::default(); -/// +/// /// // Good question examples: /// let good_questions = vec![ /// "Will Bitcoin reach $100,000 by December 31, 2024?", /// "Will Ethereum's price exceed $5,000 before June 1, 2024?", /// "Will XLM trade above $1.00 within the next 90 days?" /// ]; -/// +/// /// // Question validation criteria: /// // 1. Clear and unambiguous /// // 2. Specific timeframe /// // 3. Measurable outcome /// // 4. Appropriate length (10-200 characters) /// // 5. No offensive or inappropriate content -/// +/// /// for question in good_questions { /// let question_str = String::from_str(&env, question); /// if validate_question(&question_str) { @@ -1833,13 +1829,13 @@ pub struct ExtensionStats { /// ```rust /// # use soroban_sdk::{Env, String, Vec}; /// # let env = Env::default(); -/// +/// /// // Binary outcomes (most common) /// let binary_outcomes = Vec::from_array(&env, [ /// String::from_str(&env, "yes"), /// String::from_str(&env, "no") /// ]); -/// +/// /// // Multiple choice outcomes /// let multiple_outcomes = Vec::from_array(&env, [ /// String::from_str(&env, "under_50k"), @@ -1847,7 +1843,7 @@ pub struct ExtensionStats { /// String::from_str(&env, "75k_to_100k"), /// String::from_str(&env, "over_100k") /// ]); -/// +/// /// // Outcome validation rules: /// // 1. Minimum 2 outcomes /// // 2. Maximum 10 outcomes @@ -1861,7 +1857,7 @@ pub struct ExtensionStats { /// Market duration affects participation and resolution: /// ```rust /// # use predictify_hybrid::types::MarketCreationParams; -/// +/// /// // Duration recommendations by market type: /// let duration_guidelines = vec![ /// ("Short-term price movements", 1..=7), // 1-7 days @@ -1869,12 +1865,12 @@ pub struct ExtensionStats { /// ("Quarterly outcomes", 30..=90), // 1-3 months /// ("Annual predictions", 90..=365), // 3-12 months /// ]; -/// +/// /// for (market_type, duration_range) in duration_guidelines { -/// println!("{}: {} days", market_type, +/// println!("{}: {} days", market_type, /// format!("{}-{}", duration_range.start(), duration_range.end())); /// } -/// +/// /// // Duration validation: /// // - Minimum: 1 day /// // - Maximum: 365 days (1 year) @@ -1886,10 +1882,10 @@ pub struct ExtensionStats { /// Creation fees vary based on market characteristics: /// ```rust /// # use predictify_hybrid::types::MarketCreationParams; -/// +/// /// // Base fee calculation /// let base_fee = 1_000_000; // 1 XLM base fee -/// +/// /// // Fee modifiers based on duration /// let duration_multiplier = |days: u32| -> f64 { /// match days { @@ -1900,7 +1896,7 @@ pub struct ExtensionStats { /// _ => 5.0, // Invalid duration: penalty /// } /// }; -/// +/// /// // Calculate total creation fee /// let duration_days = 30; /// let total_fee = (base_fee as f64 * duration_multiplier(duration_days)) as i128; @@ -1915,7 +1911,7 @@ pub struct ExtensionStats { /// # use predictify_hybrid::types::{MarketCreationParams, OracleConfig, OracleProvider}; /// # let env = Env::default(); /// # let admin = Address::generate(&env); -/// +/// /// // Bitcoin price threshold template /// let btc_template = |threshold: i128, days: u32| -> MarketCreationParams { /// MarketCreationParams::new( @@ -1935,7 +1931,7 @@ pub struct ExtensionStats { /// calculate_creation_fee(days) /// ) /// }; -/// +/// /// // Create BTC $100k market /// let btc_100k_market = btc_template(100_000_00, 90); /// ``` @@ -1997,7 +1993,6 @@ impl MarketCreationParams { } } - // ===== ADDITIONAL TYPES ===== /// Community consensus data structure for tracking collective market resolution. @@ -2043,7 +2038,7 @@ impl MarketCreationParams { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::types::CommunityConsensus; /// # let env = Env::default(); -/// +/// /// // Create community consensus for a market outcome /// let consensus = CommunityConsensus::new( /// String::from_str(&env, "yes"), @@ -2051,13 +2046,13 @@ impl MarketCreationParams { /// 200, // 200 total votes /// 75 // 75% of votes for "yes" /// ); -/// +/// /// // Display consensus information /// println!("Community Consensus:"); /// println!("Outcome: {}", consensus.outcome); /// println!("Votes: {} out of {}", consensus.votes, consensus.total_votes); /// println!("Percentage: {}%", consensus.percentage); -/// +/// /// // Check consensus strength /// if consensus.is_strong_consensus() { /// println!("Strong community consensus achieved"); @@ -2066,7 +2061,7 @@ impl MarketCreationParams { /// } else { /// println!("No clear consensus - may need dispute resolution"); /// } -/// +/// /// // Validate consensus quality /// consensus.validate(&env)?; /// # Ok::<(), predictify_hybrid::errors::Error>(()) @@ -2078,7 +2073,7 @@ impl MarketCreationParams { /// ```rust /// # use predictify_hybrid::types::CommunityConsensus; /// # let consensus = CommunityConsensus::default(); // Placeholder -/// +/// /// // Validation checks multiple aspects: /// let validation_result = consensus.validate(&soroban_sdk::Env::default()); /// match validation_result { @@ -2103,7 +2098,7 @@ impl MarketCreationParams { /// ```rust /// # use predictify_hybrid::types::CommunityConsensus; /// # let consensus = CommunityConsensus::default(); // Placeholder -/// +/// /// // Consensus strength categories /// let strength = match consensus.percentage { /// 90..=100 => "Overwhelming Consensus", @@ -2112,9 +2107,9 @@ impl MarketCreationParams { /// 51..=59 => "Simple Majority", /// _ => "No Consensus" /// }; -/// +/// /// println!("Consensus Strength: {}", strength); -/// +/// /// // Participation analysis /// let participation_rate = consensus.calculate_participation_rate(); /// if participation_rate >= 50 { @@ -2132,15 +2127,15 @@ impl MarketCreationParams { /// ```rust /// # use predictify_hybrid::types::CommunityConsensus; /// # let consensus = CommunityConsensus::default(); // Placeholder -/// +/// /// // Stake-weighted calculation /// let stake_weighted_consensus = consensus.calculate_stake_weighted(); -/// -/// println!("Vote-based consensus: {}% for {}", +/// +/// println!("Vote-based consensus: {}% for {}", /// consensus.percentage, consensus.outcome); -/// println!("Stake-weighted consensus: {:.1}% for {}", +/// println!("Stake-weighted consensus: {:.1}% for {}", /// stake_weighted_consensus.percentage, stake_weighted_consensus.outcome); -/// +/// /// // Compare vote vs stake consensus /// if consensus.outcome == stake_weighted_consensus.outcome { /// println!("Vote and stake consensus align"); @@ -2155,15 +2150,15 @@ impl MarketCreationParams { /// ```rust /// # use predictify_hybrid::types::CommunityConsensus; /// # let consensus = CommunityConsensus::default(); // Placeholder -/// +/// /// // Historical consensus snapshots /// let consensus_history = consensus.get_historical_snapshots(); -/// +/// /// for (timestamp, snapshot) in consensus_history { -/// println!("Time {}: {}% for {}", +/// println!("Time {}: {}% for {}", /// timestamp, snapshot.percentage, snapshot.outcome); /// } -/// +/// /// // Consensus stability analysis /// let stability = consensus.analyze_stability(); /// if stability.is_stable { @@ -2178,26 +2173,26 @@ impl MarketCreationParams { /// Handle markets with multiple possible outcomes: /// ```rust /// # use predictify_hybrid::types::CommunityConsensus; -/// +/// /// // Calculate consensus for all outcomes /// let all_outcomes_consensus = vec![ /// ("outcome_a", 45, 22), // 45% of votes, 22% of stakes /// ("outcome_b", 35, 38), // 35% of votes, 38% of stakes /// ("outcome_c", 20, 40), // 20% of votes, 40% of stakes /// ]; -/// +/// /// // Determine winner by different methods /// let vote_winner = all_outcomes_consensus.iter() /// .max_by_key(|(_, votes, _)| votes) /// .map(|(outcome, _, _)| outcome); -/// +/// /// let stake_winner = all_outcomes_consensus.iter() /// .max_by_key(|(_, _, stakes)| stakes) /// .map(|(outcome, _, _)| outcome); -/// +/// /// println!("Vote winner: {:?}", vote_winner); /// println!("Stake winner: {:?}", stake_winner); -/// +/// /// // Check for conflicts /// if vote_winner != stake_winner { /// println!("Conflict detected - may need hybrid resolution"); @@ -2210,7 +2205,7 @@ impl MarketCreationParams { /// ```rust /// # use predictify_hybrid::types::CommunityConsensus; /// # let consensus = CommunityConsensus::default(); // Placeholder -/// +/// /// // Use consensus for market resolution /// if consensus.is_reliable() { /// let resolution_outcome = consensus.outcome.clone(); @@ -2232,17 +2227,17 @@ impl MarketCreationParams { /// ```rust /// # use predictify_hybrid::types::CommunityConsensus; /// # let consensus = CommunityConsensus::default(); // Placeholder -/// +/// /// // Quality assessment /// let quality_metrics = consensus.assess_quality(); -/// +/// /// println!("Consensus Quality Report:"); /// println!("- Participation Rate: {:.1}%", quality_metrics.participation_rate); /// println!("- Majority Strength: {:.1}%", quality_metrics.majority_strength); /// println!("- Stake Alignment: {:.1}%", quality_metrics.stake_alignment); /// println!("- Time Stability: {:.1}%", quality_metrics.time_stability); /// println!("- Overall Quality: {:.1}/10", quality_metrics.overall_score); -/// +/// /// // Quality-based decision making /// if quality_metrics.overall_score >= 8.0 { /// println!("High quality consensus - safe to use for resolution"); @@ -2282,5 +2277,4 @@ pub struct CommunityConsensus { pub total_votes: u32, /// Percentage of votes for this outcome pub percentage: i128, - } diff --git a/contracts/predictify-hybrid/src/utils.rs b/contracts/predictify-hybrid/src/utils.rs index 7b6a25b6..840c5657 100644 --- a/contracts/predictify-hybrid/src/utils.rs +++ b/contracts/predictify-hybrid/src/utils.rs @@ -4,7 +4,6 @@ use alloc::string::ToString; // Only for primitive types, not soroban_sdk::Strin use soroban_sdk::{Address, Env, Map, String, Symbol, Vec}; - use crate::errors::Error; /// Comprehensive utility function system for Predictify Hybrid contract @@ -50,16 +49,16 @@ use crate::errors::Error; /// # use soroban_sdk::Env; /// # use predictify_hybrid::utils::TimeUtils; /// # let env = Env::default(); -/// +/// /// // Convert market duration to seconds /// let market_duration_days = 30; /// let duration_seconds = TimeUtils::days_to_seconds(market_duration_days); /// println!("Market duration: {} seconds", duration_seconds); -/// +/// /// // Check if market has ended /// let current_time = env.ledger().timestamp(); /// let market_end_time = current_time + TimeUtils::days_to_seconds(7); // 7 days from now -/// +/// /// if TimeUtils::is_deadline_passed(current_time, market_end_time) { /// println!("Market has ended"); /// } else { @@ -67,7 +66,7 @@ use crate::errors::Error; /// let formatted_time = TimeUtils::format_duration(&env, time_remaining); /// println!("Time remaining: {}", formatted_time); /// } -/// +/// /// // Validate market duration /// let proposed_duration = 45; // days /// if TimeUtils::validate_duration(&proposed_duration) { @@ -82,17 +81,17 @@ use crate::errors::Error; /// Convert various time units to seconds for blockchain operations: /// ```rust /// # use predictify_hybrid::utils::TimeUtils; -/// +/// /// // Common time conversions /// let one_day = TimeUtils::days_to_seconds(1); // 86,400 seconds /// let one_hour = TimeUtils::hours_to_seconds(1); // 3,600 seconds /// let one_minute = TimeUtils::minutes_to_seconds(1); // 60 seconds -/// +/// /// // Market duration examples /// let short_market = TimeUtils::days_to_seconds(7); // 1 week /// let medium_market = TimeUtils::days_to_seconds(30); // 1 month /// let long_market = TimeUtils::days_to_seconds(90); // 3 months -/// +/// /// println!("Short market: {} seconds", short_market); /// println!("Medium market: {} seconds", medium_market); /// println!("Long market: {} seconds", long_market); @@ -105,21 +104,21 @@ use crate::errors::Error; /// # use soroban_sdk::Env; /// # use predictify_hybrid::utils::TimeUtils; /// # let env = Env::default(); -/// +/// /// let current_time = env.ledger().timestamp(); /// let future_time = current_time + TimeUtils::days_to_seconds(30); /// let past_time = current_time - TimeUtils::days_to_seconds(30); -/// +/// /// // Timestamp validation /// assert!(TimeUtils::is_future_timestamp(current_time, future_time)); /// assert!(TimeUtils::is_past_timestamp(current_time, past_time)); /// assert!(!TimeUtils::is_deadline_passed(current_time, future_time)); /// assert!(TimeUtils::is_deadline_passed(current_time, past_time)); -/// +/// /// // Calculate time differences /// let diff_future = TimeUtils::time_difference(current_time, future_time); /// let diff_past = TimeUtils::time_difference(current_time, past_time); -/// +/// /// println!("Time to future: {} seconds", diff_future); /// println!("Time from past: {} seconds", diff_past); /// ``` @@ -131,7 +130,7 @@ use crate::errors::Error; /// # use soroban_sdk::Env; /// # use predictify_hybrid::utils::TimeUtils; /// # let env = Env::default(); -/// +/// /// // Format various durations /// let durations = vec![ /// TimeUtils::minutes_to_seconds(45), // "45m" @@ -139,7 +138,7 @@ use crate::errors::Error; /// TimeUtils::days_to_seconds(1), // "1d 0h 0m" /// TimeUtils::days_to_seconds(7) + TimeUtils::hours_to_seconds(12), // "7d 12h 0m" /// ]; -/// +/// /// for duration in durations { /// let formatted = TimeUtils::format_duration(&env, duration); /// println!("Duration: {}", formatted); @@ -153,10 +152,10 @@ use crate::errors::Error; /// # use soroban_sdk::Env; /// # use predictify_hybrid::utils::TimeUtils; /// # let env = Env::default(); -/// +/// /// let current_time = env.ledger().timestamp(); /// let market_end = current_time + TimeUtils::days_to_seconds(7); -/// +/// /// // Check time until deadline /// let time_remaining = TimeUtils::time_until_deadline(current_time, market_end); /// if time_remaining > 0 { @@ -303,24 +302,24 @@ impl TimeUtils { /// # use soroban_sdk::{Env, String, Vec}; /// # use predictify_hybrid::utils::StringUtils; /// # let env = Env::default(); -/// +/// /// // String validation for market questions /// let market_question = String::from_str(&env, "Will Bitcoin reach $100,000?"); -/// +/// /// // Validate question length /// match StringUtils::validate_string_length(&market_question, 10, 200) { /// Ok(()) => println!("Question length is valid"), /// Err(e) => println!("Question too short or too long: {:?}", e), /// } -/// +/// /// // Sanitize user input /// let sanitized_question = StringUtils::sanitize_string(&market_question); /// println!("Sanitized question: {}", sanitized_question); -/// +/// /// // String manipulation /// let trimmed = StringUtils::trim(&market_question); /// let truncated = StringUtils::truncate(&market_question, 50); -/// +/// /// println!("Original: {}", market_question); /// println!("Trimmed: {}", trimmed); /// println!("Truncated: {}", truncated); @@ -333,28 +332,28 @@ impl TimeUtils { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::utils::StringUtils; /// # let env = Env::default(); -/// +/// /// // Market question validation /// let questions = vec![ /// String::from_str(&env, "Will BTC hit $100k?"), // Valid /// String::from_str(&env, "BTC?"), // Too short /// String::from_str(&env, &"x".repeat(300)), // Too long /// ]; -/// +/// /// for question in questions { /// match StringUtils::validate_string_length(&question, 10, 200) { /// Ok(()) => println!("✓ Valid question: {}", question), /// Err(_) => println!("✗ Invalid question length"), /// } /// } -/// +/// /// // Outcome validation /// let outcomes = vec![ /// String::from_str(&env, "yes"), /// String::from_str(&env, "no"), /// String::from_str(&env, "maybe"), /// ]; -/// +/// /// for outcome in outcomes { /// if StringUtils::validate_string_length(&outcome, 1, 50).is_ok() { /// println!("Valid outcome: {}", outcome); @@ -369,21 +368,21 @@ impl TimeUtils { /// # use soroban_sdk::{Env, String, Vec}; /// # use predictify_hybrid::utils::StringUtils; /// # let env = Env::default(); -/// +/// /// let original = String::from_str(&env, " Bitcoin Price Prediction "); -/// +/// /// // Basic transformations /// let uppercase = StringUtils::to_uppercase(&original); /// let lowercase = StringUtils::to_lowercase(&original); /// let trimmed = StringUtils::trim(&original); /// let truncated = StringUtils::truncate(&original, 15); -/// +/// /// println!("Original: '{}'", original); /// println!("Uppercase: '{}'", uppercase); /// println!("Lowercase: '{}'", lowercase); /// println!("Trimmed: '{}'", trimmed); /// println!("Truncated: '{}'", truncated); -/// +/// /// // String replacement /// let replaced = StringUtils::replace(&original, "Bitcoin", "BTC"); /// println!("Replaced: '{}'", replaced); @@ -396,18 +395,18 @@ impl TimeUtils { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::utils::StringUtils; /// # let env = Env::default(); -/// +/// /// let text = String::from_str(&env, "Will Bitcoin reach $100,000 by 2024?"); -/// +/// /// // Content analysis /// let contains_bitcoin = StringUtils::contains(&text, "Bitcoin"); /// let starts_with_will = StringUtils::starts_with(&text, "Will"); /// let ends_with_question = StringUtils::ends_with(&text, "?"); -/// +/// /// println!("Contains 'Bitcoin': {}", contains_bitcoin); /// println!("Starts with 'Will': {}", starts_with_will); /// println!("Ends with '?': {}", ends_with_question); -/// +/// /// // Pattern validation for market questions /// if starts_with_will && ends_with_question { /// println!("Question follows proper format"); @@ -423,22 +422,22 @@ impl TimeUtils { /// # use soroban_sdk::{Env, String, Vec}; /// # use predictify_hybrid::utils::StringUtils; /// # let env = Env::default(); -/// +/// /// // Split comma-separated outcomes /// let outcomes_str = String::from_str(&env, "yes,no,maybe"); /// let outcomes_vec = StringUtils::split(&outcomes_str, ","); -/// +/// /// println!("Split outcomes:"); /// for outcome in outcomes_vec.iter() { /// println!("- {}", outcome); /// } -/// +/// /// // Join outcomes back together /// let mut outcomes = Vec::new(&env); /// outcomes.push_back(String::from_str(&env, "yes")); /// outcomes.push_back(String::from_str(&env, "no")); /// outcomes.push_back(String::from_str(&env, "uncertain")); -/// +/// /// let joined = StringUtils::join(&outcomes, " | "); /// println!("Joined outcomes: {}", joined); /// ``` @@ -450,14 +449,14 @@ impl TimeUtils { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::utils::StringUtils; /// # let env = Env::default(); -/// +/// /// // Sanitize potentially unsafe input /// let unsafe_inputs = vec![ /// String::from_str(&env, "Will BTC reach $100k?"), /// String::from_str(&env, "Question with special chars: @#$%^&*()"), /// String::from_str(&env, "Normal question about Bitcoin price?"), /// ]; -/// +/// /// for input in unsafe_inputs { /// let sanitized = StringUtils::sanitize_string(&input); /// println!("Original: {}", input); @@ -473,14 +472,14 @@ impl TimeUtils { /// # use soroban_sdk::Env; /// # use predictify_hybrid::utils::StringUtils; /// # let env = Env::default(); -/// +/// /// // Generate random strings for testing /// let random_id = StringUtils::generate_random_string(&env, 10); /// let random_token = StringUtils::generate_random_string(&env, 32); -/// +/// /// println!("Random ID: {}", random_id); /// println!("Random token: {}", random_token); -/// +/// /// // Use in market creation for unique identifiers /// let market_id = StringUtils::generate_random_string(&env, 16); /// println!("Generated market ID: {}", market_id); @@ -663,7 +662,7 @@ impl StringUtils { /// # use soroban_sdk::{Env, Vec}; /// # use predictify_hybrid::utils::NumericUtils; /// # let env = Env::default(); -/// +/// /// // Calculate market participation percentage /// let user_stake = 1_000_000; // 1 XLM in stroops /// let total_stakes = 10_000_000; // 10 XLM total @@ -671,29 +670,29 @@ impl StringUtils { /// &user_stake, &100, &total_stakes /// ); /// println!("User participation: {}%", participation_pct); -/// +/// /// // Validate stake amount is within acceptable range /// let min_stake = 100_000; // 0.1 XLM /// let max_stake = 100_000_000; // 100 XLM -/// +/// /// if NumericUtils::is_within_range(&user_stake, &min_stake, &max_stake) { /// println!("Stake amount is valid"); /// } else { /// let clamped_stake = NumericUtils::clamp(&user_stake, &min_stake, &max_stake); /// println!("Stake clamped to: {} stroops", clamped_stake); /// } -/// +/// /// // Calculate weighted consensus /// let mut votes = Vec::new(&env); /// votes.push_back(75); // 75% confidence /// votes.push_back(80); // 80% confidence /// votes.push_back(90); // 90% confidence -/// +/// /// let mut weights = Vec::new(&env); /// weights.push_back(1_000_000); // 1 XLM stake /// weights.push_back(2_000_000); // 2 XLM stake /// weights.push_back(3_000_000); // 3 XLM stake -/// +/// /// let weighted_consensus = NumericUtils::weighted_average(&votes, &weights); /// println!("Weighted consensus: {}%", weighted_consensus); /// ``` @@ -703,7 +702,7 @@ impl StringUtils { /// Calculate percentages for various market operations: /// ```rust /// # use predictify_hybrid::utils::NumericUtils; -/// +/// /// // Market fee calculations /// let transaction_amount = 5_000_000; // 5 XLM /// let fee_rate = 2; // 2% @@ -711,7 +710,7 @@ impl StringUtils { /// &fee_rate, &transaction_amount, &100 /// ); /// println!("Transaction fee: {} stroops", fee_amount); -/// +/// /// // Payout distribution calculations /// let total_pool = 50_000_000; // 50 XLM prize pool /// let winner_percentage = 80; // Winners get 80% @@ -719,7 +718,7 @@ impl StringUtils { /// &winner_percentage, &total_pool, &100 /// ); /// println!("Winner pool: {} stroops", winner_pool); -/// +/// /// // Participation rate calculations /// let active_users = 150; /// let total_users = 200; @@ -734,12 +733,12 @@ impl StringUtils { /// Validate and constrain numeric values: /// ```rust /// # use predictify_hybrid::utils::NumericUtils; -/// +/// /// // Stake validation /// let proposed_stakes = vec![50_000, 1_000_000, 150_000_000, 500_000]; /// let min_stake = 100_000; // 0.1 XLM minimum /// let max_stake = 100_000_000; // 100 XLM maximum -/// +/// /// for stake in proposed_stakes { /// if NumericUtils::is_within_range(&stake, &min_stake, &max_stake) { /// println!("✓ Valid stake: {} stroops", stake); @@ -748,12 +747,12 @@ impl StringUtils { /// println!("✗ Invalid stake {} clamped to {}", stake, clamped); /// } /// } -/// +/// /// // Price threshold validation /// let price_thresholds = vec![0, 50_000_00, 1_000_000_00, -100]; /// let min_price = 1_00; // $0.01 minimum /// let max_price = 10_000_000_00; // $10M maximum -/// +/// /// for price in price_thresholds { /// let valid_price = NumericUtils::clamp(&price, &min_price, &max_price); /// println!("Price {} -> {}", price, valid_price); @@ -767,32 +766,32 @@ impl StringUtils { /// # use soroban_sdk::{Env, Vec}; /// # use predictify_hybrid::utils::NumericUtils; /// # let env = Env::default(); -/// +/// /// // Calculate stake-weighted average confidence /// let mut confidence_scores = Vec::new(&env); /// confidence_scores.push_back(85); // User 1: 85% confidence /// confidence_scores.push_back(92); // User 2: 92% confidence /// confidence_scores.push_back(78); // User 3: 78% confidence -/// +/// /// let mut stake_weights = Vec::new(&env); /// stake_weights.push_back(1_000_000); // User 1: 1 XLM /// stake_weights.push_back(5_000_000); // User 2: 5 XLM (higher weight) /// stake_weights.push_back(2_000_000); // User 3: 2 XLM -/// +/// /// let weighted_confidence = NumericUtils::weighted_average( /// &confidence_scores, &stake_weights /// ); /// println!("Market confidence: {}%", weighted_confidence); -/// +/// /// // Calculate price volatility (using absolute differences) /// let prices = vec![50_000_00, 52_000_00, 48_000_00, 51_000_00]; /// let mut total_volatility = 0; -/// +/// /// for i in 1..prices.len() { /// let diff = NumericUtils::abs_difference(&prices[i], &prices[i-1]); /// total_volatility += diff; /// } -/// +/// /// let avg_volatility = total_volatility / (prices.len() as i128 - 1); /// println!("Average price volatility: {} cents", avg_volatility); /// ``` @@ -802,23 +801,23 @@ impl StringUtils { /// Perform financial computations for market economics: /// ```rust /// # use predictify_hybrid::utils::NumericUtils; -/// +/// /// // Calculate interest on staked amounts /// let principal = 10_000_000; // 10 XLM staked /// let annual_rate = 5; // 5% annual interest /// let periods = 12; // 12 months -/// +/// /// let interest_earned = NumericUtils::simple_interest( /// &principal, &annual_rate, &periods /// ); /// println!("Interest earned: {} stroops", interest_earned); -/// +/// /// // Fee distribution calculations /// let total_fees = 1_000_000; // 1 XLM in fees /// let platform_share = 30; // 30% to platform /// let oracle_share = 20; // 20% to oracle /// let community_share = 50; // 50% to community -/// +/// /// let platform_fee = NumericUtils::calculate_percentage( /// &platform_share, &total_fees, &100 /// ); @@ -828,7 +827,7 @@ impl StringUtils { /// let community_fee = NumericUtils::calculate_percentage( /// &community_share, &total_fees, &100 /// ); -/// +/// /// println!("Platform fee: {} stroops", platform_fee); /// println!("Oracle fee: {} stroops", oracle_fee); /// println!("Community fee: {} stroops", community_fee); @@ -839,25 +838,25 @@ impl StringUtils { /// Handle rounding for display and calculation purposes: /// ```rust /// # use predictify_hybrid::utils::NumericUtils; -/// +/// /// // Round stakes to nearest 0.1 XLM (100,000 stroops) /// let raw_stakes = vec![1_234_567, 2_876_543, 999_999]; /// let rounding_unit = 100_000; // 0.1 XLM -/// +/// /// for stake in raw_stakes { /// let rounded = NumericUtils::round_to_nearest(&stake, &rounding_unit); /// println!("Stake {} rounded to {}", stake, rounded); /// } -/// +/// /// // Calculate square root for standard deviation approximations /// let variance = 1_000_000; // Variance in price movements /// let std_deviation = NumericUtils::sqrt(&variance); /// println!("Standard deviation: {}", std_deviation); -/// +/// /// // Round prices to nearest cent /// let raw_prices = vec![50_123_45, 75_678_90, 100_001_23]; /// let cent_rounding = 1; // Round to nearest cent -/// +/// /// for price in raw_prices { /// let rounded_price = NumericUtils::round_to_nearest(&price, ¢_rounding); /// println!("Price {} rounded to {}", price, rounded_price); @@ -1013,29 +1012,29 @@ impl NumericUtils { /// # use soroban_sdk::{Env, Address, String}; /// # use predictify_hybrid::utils::ValidationUtils; /// # let env = Env::default(); -/// +/// /// // Validate market creation parameters /// let stake_amount = 1_000_000; // 1 XLM /// let min_stake = 100_000; // 0.1 XLM minimum /// let max_stake = 100_000_000; // 100 XLM maximum -/// +/// /// // Validate stake amount /// if ValidationUtils::validate_positive_number(&stake_amount) { /// println!("✓ Stake amount is positive"); /// } -/// +/// /// if ValidationUtils::validate_number_range(&stake_amount, &min_stake, &max_stake) { /// println!("✓ Stake amount is within valid range"); /// } else { /// println!("✗ Stake amount outside valid range"); /// } -/// +/// /// // Validate market end time /// let market_end_time = env.ledger().timestamp() + (30 * 24 * 60 * 60); // 30 days /// if ValidationUtils::validate_future_timestamp(&env, &market_end_time) { /// println!("✓ Market end time is in the future"); /// } -/// +/// /// // Validate admin address /// let admin_address = Address::generate(&env); /// match ValidationUtils::validate_address(&admin_address) { @@ -1049,10 +1048,10 @@ impl NumericUtils { /// Validate numeric inputs for market operations: /// ```rust /// # use predictify_hybrid::utils::ValidationUtils; -/// +/// /// // Validate positive amounts /// let amounts = vec![1_000_000, 0, -500_000, 50_000_000]; -/// +/// /// for amount in amounts { /// if ValidationUtils::validate_positive_number(&amount) { /// println!("✓ Amount {} is positive", amount); @@ -1060,12 +1059,12 @@ impl NumericUtils { /// println!("✗ Amount {} is not positive", amount); /// } /// } -/// +/// /// // Validate fee percentages /// let fee_percentages = vec![0, 1, 5, 10, 50, 101, -5]; /// let min_fee = 0; /// let max_fee = 10; // Maximum 10% fee -/// +/// /// for fee in fee_percentages { /// if ValidationUtils::validate_number_range(&fee, &min_fee, &max_fee) { /// println!("✓ Fee {}% is valid", fee); @@ -1073,12 +1072,12 @@ impl NumericUtils { /// println!("✗ Fee {}% is outside valid range (0-10%)", fee); /// } /// } -/// +/// /// // Validate market duration /// let durations = vec![0, 1, 7, 30, 90, 365, 400]; // days /// let min_duration = 1; /// let max_duration = 365; -/// +/// /// for duration in durations { /// if ValidationUtils::validate_number_range(&duration, &min_duration, &max_duration) { /// println!("✓ Duration {} days is valid", duration); @@ -1095,9 +1094,9 @@ impl NumericUtils { /// # use soroban_sdk::Env; /// # use predictify_hybrid::utils::{ValidationUtils, TimeUtils}; /// # let env = Env::default(); -/// +/// /// let current_time = env.ledger().timestamp(); -/// +/// /// // Test various timestamps /// let timestamps = vec![ /// current_time - 3600, // 1 hour ago (invalid) @@ -1106,7 +1105,7 @@ impl NumericUtils { /// current_time + TimeUtils::days_to_seconds(30), // 30 days (valid) /// current_time + TimeUtils::days_to_seconds(400), // 400 days (may be invalid) /// ]; -/// +/// /// for timestamp in timestamps { /// if ValidationUtils::validate_future_timestamp(&env, ×tamp) { /// let time_diff = timestamp - current_time; @@ -1125,13 +1124,13 @@ impl NumericUtils { /// # use soroban_sdk::{Env, Address, String}; /// # use predictify_hybrid::utils::ValidationUtils; /// # let env = Env::default(); -/// +/// /// // Generate test addresses /// let valid_address = Address::generate(&env); -/// +/// /// // Validate addresses /// let addresses = vec![valid_address]; -/// +/// /// for address in addresses { /// match ValidationUtils::validate_address(&address) { /// Ok(()) => { @@ -1142,16 +1141,16 @@ impl NumericUtils { /// } /// } /// } -/// +/// /// // Address validation in market operations /// let market_admin = Address::generate(&env); /// let market_participant = Address::generate(&env); -/// +/// /// // Validate admin address /// if ValidationUtils::validate_address(&market_admin).is_ok() { /// println!("Market admin address is valid"); /// } -/// +/// /// // Validate participant address /// if ValidationUtils::validate_address(&market_participant).is_ok() { /// println!("Participant address is valid"); @@ -1165,7 +1164,7 @@ impl NumericUtils { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::utils::ValidationUtils; /// # let env = Env::default(); -/// +/// /// // Validate email formats (basic validation) /// let emails = vec![ /// String::from_str(&env, "user@example.com"), @@ -1173,7 +1172,7 @@ impl NumericUtils { /// String::from_str(&env, "test@domain.org"), /// String::from_str(&env, "@invalid.com"), /// ]; -/// +/// /// for email in emails { /// if ValidationUtils::validate_email(&email) { /// println!("✓ Valid email: {}", email); @@ -1181,7 +1180,7 @@ impl NumericUtils { /// println!("✗ Invalid email: {}", email); /// } /// } -/// +/// /// // Validate URL formats (basic validation) /// let urls = vec![ /// String::from_str(&env, "https://example.com"), @@ -1189,7 +1188,7 @@ impl NumericUtils { /// String::from_str(&env, "invalid-url"), /// String::from_str(&env, "ftp://files.com"), /// ]; -/// +/// /// for url in urls { /// if ValidationUtils::validate_url(&url) { /// println!("✓ Valid URL: {}", url); @@ -1206,7 +1205,7 @@ impl NumericUtils { /// # use soroban_sdk::{Env, Address, String}; /// # use predictify_hybrid::utils::ValidationUtils; /// # let env = Env::default(); -/// +/// /// // Validate market parameters /// struct MarketParams { /// admin: Address, @@ -1215,7 +1214,7 @@ impl NumericUtils { /// min_stake: i128, /// max_stake: i128, /// } -/// +/// /// let params = MarketParams { /// admin: Address::generate(&env), /// creation_fee: 5_000_000, // 5 XLM @@ -1223,34 +1222,34 @@ impl NumericUtils { /// min_stake: 100_000, // 0.1 XLM /// max_stake: 100_000_000, // 100 XLM /// }; -/// +/// /// // Comprehensive validation /// let mut validation_errors = Vec::new(); -/// +/// /// // Validate admin address /// if ValidationUtils::validate_address(¶ms.admin).is_err() { /// validation_errors.push("Invalid admin address"); /// } -/// +/// /// // Validate creation fee /// if !ValidationUtils::validate_positive_number(¶ms.creation_fee) { /// validation_errors.push("Creation fee must be positive"); /// } -/// +/// /// // Validate duration /// if !ValidationUtils::validate_number_range(¶ms.duration_days, &1, &365) { /// validation_errors.push("Duration must be 1-365 days"); /// } -/// +/// /// // Validate stake range /// if !ValidationUtils::validate_positive_number(¶ms.min_stake) { /// validation_errors.push("Minimum stake must be positive"); /// } -/// +/// /// if params.min_stake >= params.max_stake { /// validation_errors.push("Minimum stake must be less than maximum stake"); /// } -/// +/// /// if validation_errors.is_empty() { /// println!("✓ All market parameters are valid"); /// } else { @@ -1268,7 +1267,7 @@ impl NumericUtils { /// # use soroban_sdk::{Env, String}; /// # use predictify_hybrid::utils::ValidationUtils; /// # let env = Env::default(); -/// +/// /// // Validate user input for potential security issues /// let user_inputs = vec![ /// String::from_str(&env, "Normal market question?"), @@ -1276,7 +1275,7 @@ impl NumericUtils { /// String::from_str(&env, "'; DROP TABLE markets; --"), /// String::from_str(&env, "Will Bitcoin reach $100,000?"), /// ]; -/// +/// /// for input in user_inputs { /// // Basic security validation (simplified) /// let contains_script = input.to_string().contains("