Skip to content
Merged
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
9 changes: 6 additions & 3 deletions contract/contracts/predifi-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ pub const MIN_WITHDRAWAL_AMOUNT: i128 = 1;
#[contracterror]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PredifiError {
AlreadyInitializedOrConfigNotSet = 2,
Unauthorized = 10,
PoolNotFound = 20,
PoolNotResolved = 22,
Expand Down Expand Up @@ -1666,8 +1667,11 @@ impl PredifiContract {
min_pool_duration: u64,
max_predictions_per_user: u32,
) {
if !env.storage().instance().has(&DataKey::Config) {
// Enforce the same 30-day cap on resolution_delay that
if env.storage().instance().has(&DataKey::Config) {
soroban_sdk::panic_with_error!(&env, PredifiError::AlreadyInitializedOrConfigNotSet);
}

// Enforce the same 30-day cap on resolution_delay that
// set_resolution_delay enforces, so the contract cannot be
// initialised with an unbounded delay.
if resolution_delay > MAX_RESOLUTION_DELAY {
Expand Down Expand Up @@ -1706,7 +1710,6 @@ impl PredifiContract {
max_predictions_per_user,
}
.publish(&env);
}
}

/// Pause the contract. Only callable by Admin (role 0).
Expand Down
61 changes: 61 additions & 0 deletions contract/contracts/predifi-contract/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12713,3 +12713,64 @@ fn test_close_staking_fails_for_resolved_pool() {
"close_staking should fail for an already-resolved pool"
);
}

#[should_panic(expected = "Error(Contract, #2)")]
#[test]
fn test_already_initialized_panics() {
let env = Env::default();
env.mock_all_auths();
let ac_id = env.register(dummy_access_control::DummyAccessControl, ());
let contract_id = env.register(PredifiContract, ());
let client = PredifiContractClient::new(&env, &contract_id);
let treasury = Address::generate(&env);

// First initialization should succeed
client.init(&ac_id, &treasury, &0u32, &0u64, &3600u64, &0u32);

// Second initialization should panic with AlreadyInitializedOrConfigNotSet
client.init(&ac_id, &treasury, &0u32, &0u64, &3600u64, &0u32);
}

#[should_panic(expected = "Error(Contract, #91)")]
#[test]
fn test_delisted_token_prevents_prediction() {
let env = Env::default();
env.mock_all_auths();

let (ac_client, client, token_address, token, token_admin_client, treasury, operator, creator) = setup(&env);

let admin = Address::generate(&env);
let user = Address::generate(&env);
ac_client.grant_role(&admin, &ROLE_ADMIN);

token_admin_client.mint(&creator, &10000);
token_admin_client.mint(&user, &10000);

// Create pool
let end_time = env.ledger().timestamp() + 3600;
let config = crate::PoolConfig {
start_time: 0,
description: soroban_sdk::String::from_str(&env, "Test Pool"),
metadata_url: soroban_sdk::String::from_str(&env, "ipfs://test"),
min_stake: 1i128,
max_stake: 0i128,
max_total_stake: 100000i128,
min_total_stake: 1,
initial_liquidity: 0i128,
required_resolutions: 1u32,
private: false,
whitelist_key: None,
outcome_descriptions: soroban_sdk::vec![
&env,
soroban_sdk::String::from_str(&env, "Outcome 0"),
soroban_sdk::String::from_str(&env, "Outcome 1"),
],
};
let pool_id = client.create_pool(&creator, &end_time, &token_address, &2u32, &soroban_sdk::Symbol::new(&env, "Sports"), &config);

// Remove token from whitelist
client.remove_token_from_whitelist(&admin, &token_address);

// User places prediction - should panic with TokenNotWhitelisted (Error 48)
client.place_prediction(&user, &pool_id, &100i128, &0u32, &None, &None);
}