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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 7 additions & 33 deletions contracts/predictify-hybrid/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,12 +465,7 @@ impl AdminAccessControl {
admin: &Address,
permission: &AdminPermission,
) -> Result<(), Error> {
// Check original admin for backward compatibility first
if AdminManager::is_original_admin(env, admin) {
return Ok(());
}

// Try new multi-admin system if migrated
// Try new multi-admin system first if migrated
if AdminSystemIntegration::is_migrated(env) {
return AdminManager::validate_admin_permission(env, admin, *permission);
}
Expand Down Expand Up @@ -1258,16 +1253,6 @@ impl AdminManager {
.persistent()
.set(&count_key, &(current_count + 1));

// Maintain a list of admin addresses for iteration
let list_key = Symbol::new(env, "AdminList");
let mut admin_list: Vec<Address> = env
.storage()
.persistent()
.get(&list_key)
.unwrap_or_else(|| Vec::new(env));
admin_list.push_back(new_admin.clone());
env.storage().persistent().set(&list_key, &admin_list);

// Emit event using existing system
Self::emit_admin_change_event(env, new_admin, AdminActionType::Added);

Expand Down Expand Up @@ -1310,18 +1295,6 @@ impl AdminManager {
.set(&count_key, &(current_count - 1));
}

// Remove from admin list
let list_key = Symbol::new(env, "AdminList");
if let Some(admin_list) = env.storage().persistent().get::<_, Vec<Address>>(&list_key) {
let mut new_list: Vec<Address> = Vec::new(env);
for addr in admin_list.iter() {
if &addr != admin_to_remove {
new_list.push_back(addr.clone());
}
}
env.storage().persistent().set(&list_key, &new_list);
}

Self::emit_admin_change_event(env, admin_to_remove, AdminActionType::Removed);
Ok(())
}
Expand Down Expand Up @@ -1441,6 +1414,7 @@ impl AdminManager {
None
}


/// Emits admin change events using existing AdminActionType
pub fn emit_admin_change_event(env: &Env, admin: &Address, action: AdminActionType) {
let action_str = match action {
Expand All @@ -1462,14 +1436,14 @@ impl AdminManager {
// ===== Helper Methods =====

/// Generate a proper admin storage key using the correct environment
fn get_admin_key(env: &Env, admin: &Address) -> (Symbol, Address) {
// Use a tuple key for per-admin storage
// This avoids Symbol character limitations by using Address directly
(Symbol::new(env, "MultiAdmin"), admin.clone())
fn get_admin_key(env: &Env, admin: &Address) -> Symbol {
// Create a unique key based on admin address
let key_str = format!("MultiAdmin_{:?}", admin.to_string());
Symbol::new(env, &key_str)
}

/// Check if an address is the original admin from single-admin system
fn is_original_admin(env: &Env, admin: &Address) -> bool {
pub fn is_original_admin(env: &Env, admin: &Address) -> bool {
if let Some(original_admin) = Self::get_original_admin(env) {
return admin == &original_admin;
}
Expand Down
29 changes: 16 additions & 13 deletions contracts/predictify-hybrid/src/batch_operations_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#[cfg(test)]
#[allow(unused_assignments)]
#[allow(unused_variables)]
#[allow(dead_code)]
mod batch_operations_tests {
use crate::admin::AdminRoleManager;
use crate::batch_operations::*;
Expand Down Expand Up @@ -366,15 +369,15 @@ mod batch_operations_tests {
};

// Invalid vote data - zero stake
let invalid_vote = VoteData {
let _invalid_vote = VoteData {
market_id: market_id.clone(),
voter: <soroban_sdk::Address as Address>::generate(&env),
outcome: String::from_str(&env, "Yes"),
stake_amount: 0,
};

// Invalid vote data - empty outcome
let invalid_vote2 = VoteData {
let _invalid_vote2 = VoteData {
market_id: market_id.clone(),
voter: <soroban_sdk::Address as Address>::generate(&env),
outcome: String::from_str(&env, ""),
Expand All @@ -383,22 +386,22 @@ mod batch_operations_tests {

// Test claim data validation
// Valid claim data
let valid_claim = ClaimData {
let _valid_claim = ClaimData {
market_id: market_id.clone(),
claimant: <soroban_sdk::Address as Address>::generate(&env),
expected_amount: 2_000_000_000,
};

// Invalid claim data - zero amount
let invalid_claim = ClaimData {
let _invalid_claim = ClaimData {
market_id: market_id.clone(),
claimant: <soroban_sdk::Address as Address>::generate(&env),
expected_amount: 0,
};

// Test market data validation
// Valid market data
let valid_market = MarketData {
let _valid_market = MarketData {
question: String::from_str(&env, "Test question?"),
outcomes: vec![
&env,
Expand All @@ -415,7 +418,7 @@ mod batch_operations_tests {
};

// Invalid market data - empty question
let invalid_market = MarketData {
let _invalid_market = MarketData {
question: String::from_str(&env, ""),
outcomes: vec![
&env,
Expand All @@ -432,7 +435,7 @@ mod batch_operations_tests {
};

// Invalid market data - insufficient outcomes
let invalid_market2 = MarketData {
let _invalid_market2 = MarketData {
question: String::from_str(&env, "Test question?"),
outcomes: vec![&env, String::from_str(&env, "Yes")],
duration_days: 30,
Expand All @@ -445,7 +448,7 @@ mod batch_operations_tests {
};

// Invalid market data - zero duration
let invalid_market3 = MarketData {
let _invalid_market3 = MarketData {
question: String::from_str(&env, "Test question?"),
outcomes: vec![
&env,
Expand All @@ -463,7 +466,7 @@ mod batch_operations_tests {

// Test oracle feed data validation
// Valid oracle feed data
let valid_feed = OracleFeed {
let _valid_feed = OracleFeed {
market_id: market_id.clone(),
feed_id: String::from_str(&env, "BTC/USD"),
provider: OracleProvider::Reflector,
Expand All @@ -472,7 +475,7 @@ mod batch_operations_tests {
};

// Invalid oracle feed data - empty feed ID
let invalid_feed = OracleFeed {
let _invalid_feed = OracleFeed {
market_id: market_id.clone(),
feed_id: String::from_str(&env, ""),
provider: OracleProvider::Reflector,
Expand All @@ -481,7 +484,7 @@ mod batch_operations_tests {
};

// Invalid oracle feed data - zero threshold
let invalid_feed2 = OracleFeed {
let _invalid_feed2 = OracleFeed {
market_id: market_id.clone(),
feed_id: String::from_str(&env, "BTC/USD"),
provider: OracleProvider::Reflector,
Expand All @@ -499,7 +502,7 @@ mod batch_operations_tests {
BatchProcessor::initialize(&env).unwrap();

// Create test batch result
let test_result = BatchResult {
let _test_result = BatchResult {
successful_operations: 8,
failed_operations: 2,
total_operations: 10,
Expand Down Expand Up @@ -587,7 +590,7 @@ mod batch_operations_tests {

let claims = vec![&env, BatchTesting::create_test_claim_data(&env, &market_id)];

let markets = vec![&env, BatchTesting::create_test_market_data(&env)];
let _markets = vec![&env, BatchTesting::create_test_market_data(&env)];

let feeds = vec![
&env,
Expand Down
69 changes: 59 additions & 10 deletions contracts/predictify-hybrid/src/bet_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,33 +395,82 @@ fn test_place_bet_double_betting_prevented() {
}

#[test]
#[should_panic(expected = "Error(Contract, #102)")] // MarketClosed = 102
fn test_place_bet_on_ended_market() {
// Placing bet after market ended would return MarketClosed (#102).
assert_eq!(crate::errors::Error::MarketClosed as i128, 102);
let setup = BetTestSetup::new();
let client = setup.client();

// Advance time past market end
setup.advance_past_market_end();

// Try to place bet after market ended
client.place_bet(
&setup.user,
&setup.market_id,
&String::from_str(&setup.env, "yes"),
&10_0000000,
);
}

#[test]
#[should_panic(expected = "Error(Contract, #108)")] // InvalidOutcome = 108
fn test_place_bet_invalid_outcome() {
// Betting on invalid outcome would return InvalidOutcome (#108).
assert_eq!(crate::errors::Error::InvalidOutcome as i128, 108);
let setup = BetTestSetup::new();
let client = setup.client();

// Try to bet on invalid outcome
client.place_bet(
&setup.user,
&setup.market_id,
&String::from_str(&setup.env, "maybe"), // Not a valid outcome
&10_0000000,
);
}

#[test]
#[should_panic(expected = "Error(Contract, #107)")] // InsufficientStake = 107
fn test_place_bet_below_minimum() {
// Betting below minimum would return InsufficientStake (#107).
assert_eq!(crate::errors::Error::InsufficientStake as i128, 107);
let setup = BetTestSetup::new();
let client = setup.client();

// Try to place bet below minimum
client.place_bet(
&setup.user,
&setup.market_id,
&String::from_str(&setup.env, "yes"),
&(MIN_BET_AMOUNT - 1), // Below minimum
);
}

#[test]
#[should_panic(expected = "Error(Contract, #401)")] // InvalidInput = 401
fn test_place_bet_above_maximum() {
// Betting above maximum would return InvalidInput (#401).
assert_eq!(crate::errors::Error::InvalidInput as i128, 401);
let setup = BetTestSetup::new();
let client = setup.client();

// Try to place bet above maximum
client.place_bet(
&setup.user,
&setup.market_id,
&String::from_str(&setup.env, "yes"),
&(MAX_BET_AMOUNT + 1), // Above maximum
);
}

#[test]
#[should_panic(expected = "Error(Contract, #101)")] // MarketNotFound = 101
fn test_place_bet_nonexistent_market() {
// Betting on non-existent market would return MarketNotFound (#101).
assert_eq!(crate::errors::Error::MarketNotFound as i128, 101);
let setup = BetTestSetup::new();
let client = setup.client();

// Try to bet on non-existent market
let fake_market_id = Symbol::new(&setup.env, "fake_market");
client.place_bet(
&setup.user,
&fake_market_id,
&String::from_str(&setup.env, "yes"),
&10_0000000,
);
}

// ===== BET STATUS TESTS =====
Expand Down
3 changes: 3 additions & 0 deletions contracts/predictify-hybrid/src/circuit_breaker_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#[cfg(test)]
#[allow(unused_assignments)]
#[allow(unused_variables)]
#[allow(dead_code)]
mod circuit_breaker_tests {
use crate::admin::AdminRoleManager;
use crate::circuit_breaker::*;
Expand Down
10 changes: 5 additions & 5 deletions contracts/predictify-hybrid/src/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,17 +577,17 @@ impl ExtensionValidator {
// Get market and validate state
let market = MarketStateManager::get_market(env, market_id)?;

// Check if market is already resolved
if market.state == MarketState::Resolved {
return Err(Error::MarketAlreadyResolved);
}

// Check if market is still active
let current_time = env.ledger().timestamp();
if current_time >= market.end_time {
return Err(Error::MarketClosed);
}

// Check if market is already resolved
if market.oracle_result.is_some() {
return Err(Error::MarketAlreadyResolved);
}

Ok(())
}

Expand Down
Loading
Loading