diff --git a/contract/contracts/predifi-contract/src/lib.rs b/contract/contracts/predifi-contract/src/lib.rs index 17a93bf3..9519556d 100644 --- a/contract/contracts/predifi-contract/src/lib.rs +++ b/contract/contracts/predifi-contract/src/lib.rs @@ -1,7 +1,8 @@ #![no_std] -use soroban_sdk::IntoVal; -use soroban_sdk::{contract, contractimpl, contracttype, token, Address, Env, Symbol, Vec}; +use soroban_sdk::{ + contract, contractimpl, contracttype, token, Address, Env, IntoVal, Symbol, Vec, +}; #[contracttype] #[derive(Clone)] @@ -43,6 +44,7 @@ pub enum DataKey { UserPredictionCount(Address), UserPredictionIndex(Address, u32), Config, + Paused, } #[contracttype] @@ -57,8 +59,6 @@ pub struct PredifiContract; #[contractimpl] impl PredifiContract { - /// Cross-contract call to access control using u32 role, - /// matching the dummy and real contract's external ABI. fn has_role(env: &Env, contract: &Address, user: &Address, role: u32) -> bool { env.invoke_contract( contract, @@ -85,6 +85,19 @@ impl PredifiContract { .expect("Config not set") } + fn is_paused(env: &Env) -> bool { + env.storage() + .instance() + .get(&DataKey::Paused) + .unwrap_or(false) + } + + fn require_not_paused(env: &Env) { + if Self::is_paused(env) { + panic!("Contract is paused"); + } + } + /// Initialize the contract. Idempotent — safe to call multiple times. pub fn init(env: Env, access_control: Address, treasury: Address, fee_bps: u32) { if !env.storage().instance().has(&DataKey::Config) { @@ -98,8 +111,23 @@ impl PredifiContract { } } + /// Pause the contract. Only callable by Admin (role 0). + pub fn pause(env: Env, admin: Address) { + admin.require_auth(); + Self::require_role(&env, &admin, 0); + env.storage().instance().set(&DataKey::Paused, &true); + } + + /// Unpause the contract. Only callable by Admin (role 0). + pub fn unpause(env: Env, admin: Address) { + admin.require_auth(); + Self::require_role(&env, &admin, 0); + env.storage().instance().set(&DataKey::Paused, &false); + } + /// Set fee in basis points. Caller must have Admin role (0). pub fn set_fee_bps(env: Env, admin: Address, fee_bps: u32) { + Self::require_not_paused(&env); admin.require_auth(); Self::require_role(&env, &admin, 0); let mut config = Self::get_config(&env); @@ -109,6 +137,7 @@ impl PredifiContract { /// Set treasury address. Caller must have Admin role (0). pub fn set_treasury(env: Env, admin: Address, treasury: Address) { + Self::require_not_paused(&env); admin.require_auth(); Self::require_role(&env, &admin, 0); let mut config = Self::get_config(&env); @@ -118,6 +147,8 @@ impl PredifiContract { /// Create a new prediction pool. Returns the new pool ID. pub fn create_pool(env: Env, end_time: u64, token: Address) -> u64 { + Self::require_not_paused(&env); + let pool_id: u64 = env .storage() .instance() @@ -142,6 +173,7 @@ impl PredifiContract { /// Resolve a pool with a winning outcome. Caller must have Operator role (1). pub fn resolve_pool(env: Env, operator: Address, pool_id: u64, outcome: u32) { + Self::require_not_paused(&env); operator.require_auth(); Self::require_role(&env, &operator, 1); @@ -161,6 +193,7 @@ impl PredifiContract { /// Place a prediction on a pool. pub fn place_prediction(env: Env, user: Address, pool_id: u64, amount: i128, outcome: u32) { + Self::require_not_paused(&env); user.require_auth(); let mut pool: Pool = env @@ -171,28 +204,23 @@ impl PredifiContract { assert!(!pool.resolved, "Pool already resolved"); - // Transfer stake into the contract let token_client = token::Client::new(&env, &pool.token); token_client.transfer(&user, env.current_contract_address(), &amount); - // Record prediction env.storage().instance().set( &DataKey::Prediction(user.clone(), pool_id), &Prediction { amount, outcome }, ); - // Update total pool stake pool.total_stake = pool.total_stake.checked_add(amount).expect("overflow"); env.storage().instance().set(&DataKey::Pool(pool_id), &pool); - // Update per-outcome stake let outcome_key = DataKey::OutcomeStake(pool_id, outcome); let current_stake: i128 = env.storage().instance().get(&outcome_key).unwrap_or(0); env.storage() .instance() .set(&outcome_key, &(current_stake + amount)); - // Index prediction for pagination let count: u32 = env .storage() .instance() @@ -208,6 +236,7 @@ impl PredifiContract { /// Claim winnings from a resolved pool. Returns the amount paid out (0 for losers). pub fn claim_winnings(env: Env, user: Address, pool_id: u64) -> i128 { + Self::require_not_paused(&env); user.require_auth(); let pool: Pool = env @@ -229,7 +258,6 @@ impl PredifiContract { .instance() .set(&DataKey::HasClaimed(user.clone(), pool_id), &true); - // Return 0 for users with no prediction or wrong outcome let prediction: Option = env .storage() .instance() @@ -244,7 +272,6 @@ impl PredifiContract { return 0; } - // Share = (user_stake / winning_stake) * total_pool let winning_stake: i128 = env .storage() .instance() @@ -287,7 +314,6 @@ impl PredifiContract { return results; } - // core::cmp::min — NOT std::cmp::min (this crate is no_std) let end = core::cmp::min(offset.saturating_add(limit), count); for i in offset..end { @@ -324,5 +350,3 @@ impl PredifiContract { } mod test; -mod test_utils; -mod integration_test; diff --git a/contract/contracts/predifi-contract/src/test.rs b/contract/contracts/predifi-contract/src/test.rs index 0301cda4..e5bc376b 100644 --- a/contract/contracts/predifi-contract/src/test.rs +++ b/contract/contracts/predifi-contract/src/test.rs @@ -27,15 +27,14 @@ mod dummy_access_control { const ROLE_ADMIN: u32 = 0; const ROLE_OPERATOR: u32 = 1; -/// Registers all standard contracts and returns commonly needed handles. fn setup( env: &Env, ) -> ( - dummy_access_control::DummyAccessControlClient<'_>, - PredifiContractClient<'_>, + dummy_access_control::DummyAccessControlClient, + PredifiContractClient, Address, // token_address - token::Client<'_>, - token::StellarAssetClient<'_>, + token::Client, + token::StellarAssetClient, Address, // treasury Address, // operator ) { @@ -68,14 +67,14 @@ fn setup( ) } +// ── Core prediction tests ───────────────────────────────────────────────────── + #[test] fn test_claim_winnings() { let env = Env::default(); env.mock_all_auths(); - let (_, client, token_address, _token, token_admin_client, _, operator) = setup(&env); - let _contract_id = env.register(PredifiContract, ()); // get contract address for balance check - // Re-derive contract address from client + let (_, client, token_address, token, token_admin_client, _, operator) = setup(&env); let contract_addr = client.address.clone(); let user1 = Address::generate(&env); @@ -84,7 +83,6 @@ fn test_claim_winnings() { token_admin_client.mint(&user2, &1000); let pool_id = client.create_pool(&100u64, &token_address); - client.place_prediction(&user1, &pool_id, &100, &1); client.place_prediction(&user2, &pool_id, &100, &2); @@ -119,7 +117,7 @@ fn test_double_claim() { client.resolve_pool(&operator, &pool_id, &1u32); client.claim_winnings(&user1, &pool_id); - client.claim_winnings(&user1, &pool_id); // Should panic: "Already claimed" + client.claim_winnings(&user1, &pool_id); // Should panic } #[test] @@ -136,10 +134,39 @@ fn test_claim_unresolved() { let pool_id = client.create_pool(&100u64, &token_address); client.place_prediction(&user1, &pool_id, &100, &1); - // Do NOT resolve — should panic - client.claim_winnings(&user1, &pool_id); + client.claim_winnings(&user1, &pool_id); // Should panic } +#[test] +fn test_multiple_pools_independent() { + let env = Env::default(); + env.mock_all_auths(); + + let (_, client, token_address, _, token_admin_client, _, operator) = setup(&env); + + let user1 = Address::generate(&env); + let user2 = Address::generate(&env); + token_admin_client.mint(&user1, &1000); + token_admin_client.mint(&user2, &1000); + + let pool_a = client.create_pool(&100u64, &token_address); + let pool_b = client.create_pool(&200u64, &token_address); + + client.place_prediction(&user1, &pool_a, &100, &1); + client.place_prediction(&user2, &pool_b, &100, &1); + + client.resolve_pool(&operator, &pool_a, &1u32); + client.resolve_pool(&operator, &pool_b, &2u32); // user2 loses + + let w1 = client.claim_winnings(&user1, &pool_a); + assert_eq!(w1, 100); // sole winner, gets own stake back + + let w2 = client.claim_winnings(&user2, &pool_b); + assert_eq!(w2, 0); // lost +} + +// ── Access control tests ────────────────────────────────────────────────────── + #[test] #[should_panic(expected = "Unauthorized: missing required role")] fn test_unauthorized_set_fee_bps() { @@ -147,9 +174,8 @@ fn test_unauthorized_set_fee_bps() { env.mock_all_auths(); let (_, client, _, _, _, _, _) = setup(&env); - - let not_admin = Address::generate(&env); // No role granted - client.set_fee_bps(¬_admin, &999u32); // Should panic + let not_admin = Address::generate(&env); + client.set_fee_bps(¬_admin, &999u32); } #[test] @@ -159,10 +185,9 @@ fn test_unauthorized_set_treasury() { env.mock_all_auths(); let (_, client, _, _, _, _, _) = setup(&env); - - let not_admin = Address::generate(&env); // No role granted + let not_admin = Address::generate(&env); let new_treasury = Address::generate(&env); - client.set_treasury(¬_admin, &new_treasury); // Should panic + client.set_treasury(¬_admin, &new_treasury); } #[test] @@ -172,54 +197,109 @@ fn test_unauthorized_resolve_pool() { env.mock_all_auths(); let (_, client, token_address, _, _, _, _) = setup(&env); - let pool_id = client.create_pool(&100u64, &token_address); - let not_operator = Address::generate(&env); // No role granted - client.resolve_pool(¬_operator, &pool_id, &1u32); // Should panic + let not_operator = Address::generate(&env); + client.resolve_pool(¬_operator, &pool_id, &1u32); } #[test] -fn test_get_user_predictions() { +fn test_admin_can_set_fee_bps() { let env = Env::default(); env.mock_all_auths(); - let (_, client, token_address, _, token_admin_client, _, _) = setup(&env); + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); - let user = Address::generate(&env); - token_admin_client.mint(&user, &1000); + let admin = Address::generate(&env); + let treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); - let pool0 = client.create_pool(&100u64, &token_address); - let pool1 = client.create_pool(&200u64, &token_address); - let pool2 = client.create_pool(&300u64, &token_address); + client.set_fee_bps(&admin, &500u32); // 5% — should not panic +} - client.place_prediction(&user, &pool0, &10, &1); - client.place_prediction(&user, &pool1, &20, &2); - client.place_prediction(&user, &pool2, &30, &1); +#[test] +fn test_admin_can_set_treasury() { + let env = Env::default(); + env.mock_all_auths(); - // Offset 0, Limit 2 - let first_two = client.get_user_predictions(&user, &0, &2); - assert_eq!(first_two.len(), 2); - assert_eq!(first_two.get(0).unwrap().pool_id, pool0); - assert_eq!(first_two.get(1).unwrap().pool_id, pool1); + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); - // Offset 1, Limit 2 - let last_two = client.get_user_predictions(&user, &1, &2); - assert_eq!(last_two.len(), 2); - assert_eq!(last_two.get(0).unwrap().pool_id, pool1); - assert_eq!(last_two.get(1).unwrap().pool_id, pool2); + let admin = Address::generate(&env); + let treasury = Address::generate(&env); + let new_treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); - // Offset 2, Limit 1 - let last_one = client.get_user_predictions(&user, &2, &1); - assert_eq!(last_one.len(), 1); - assert_eq!(last_one.get(0).unwrap().pool_id, pool2); + client.set_treasury(&admin, &new_treasury); // Should not panic +} - // Out of bounds - let empty = client.get_user_predictions(&user, &3, &1); - assert_eq!(empty.len(), 0); +// ── Pause tests ─────────────────────────────────────────────────────────────── + +#[test] +fn test_admin_can_pause_and_unpause() { + let env = Env::default(); + env.mock_all_auths(); + + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); + + client.pause(&admin); + client.unpause(&admin); } #[test] -fn test_admin_can_set_fee_bps() { +#[should_panic(expected = "Unauthorized: missing required role")] +fn test_non_admin_cannot_pause() { + 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 not_admin = Address::generate(&env); + let treasury = Address::generate(&env); + client.init(&ac_id, &treasury, &0u32); + + client.pause(¬_admin); +} + +#[test] +#[should_panic(expected = "Contract is paused")] +fn test_paused_blocks_set_fee_bps() { + let env = Env::default(); + env.mock_all_auths(); + + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); + + client.pause(&admin); + client.set_fee_bps(&admin, &100u32); // Should panic +} + +#[test] +#[should_panic(expected = "Contract is paused")] +fn test_paused_blocks_set_treasury() { let env = Env::default(); env.mock_all_auths(); @@ -230,15 +310,37 @@ fn test_admin_can_set_fee_bps() { let admin = Address::generate(&env); let treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); + + client.pause(&admin); + client.set_treasury(&admin, &Address::generate(&env)); // Should panic +} + +#[test] +#[should_panic(expected = "Contract is paused")] +fn test_paused_blocks_create_pool() { + let env = Env::default(); + env.mock_all_auths(); + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let treasury = Address::generate(&env); + let token = Address::generate(&env); ac_client.grant_role(&admin, &ROLE_ADMIN); client.init(&ac_id, &treasury, &0u32); - client.set_fee_bps(&admin, &500u32); // 5% — should not panic + client.pause(&admin); + client.create_pool(&100u64, &token); // Should panic } #[test] -fn test_admin_can_set_treasury() { +#[should_panic(expected = "Contract is paused")] +fn test_paused_blocks_place_prediction() { let env = Env::default(); env.mock_all_auths(); @@ -248,39 +350,125 @@ fn test_admin_can_set_treasury() { let client = PredifiContractClient::new(&env, &contract_id); let admin = Address::generate(&env); + let user = Address::generate(&env); let treasury = Address::generate(&env); - let new_treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); + + client.pause(&admin); + client.place_prediction(&user, &0u64, &10, &1); // Should panic +} +#[test] +#[should_panic(expected = "Contract is paused")] +fn test_paused_blocks_resolve_pool() { + let env = Env::default(); + env.mock_all_auths(); + + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let operator = Address::generate(&env); + let treasury = Address::generate(&env); ac_client.grant_role(&admin, &ROLE_ADMIN); + ac_client.grant_role(&operator, &ROLE_OPERATOR); client.init(&ac_id, &treasury, &0u32); - client.set_treasury(&admin, &new_treasury); // Should not panic + client.pause(&admin); + client.resolve_pool(&operator, &0u64, &1u32); // Should panic } #[test] -fn test_multiple_pools_independent() { +#[should_panic(expected = "Contract is paused")] +fn test_paused_blocks_claim_winnings() { let env = Env::default(); env.mock_all_auths(); - let (_, client, token_address, _token, token_admin_client, _, operator) = setup(&env); + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); - let user1 = Address::generate(&env); - let user2 = Address::generate(&env); - token_admin_client.mint(&user1, &1000); - token_admin_client.mint(&user2, &1000); + let admin = Address::generate(&env); + let user = Address::generate(&env); + let treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); - let pool_a = client.create_pool(&100u64, &token_address); - let pool_b = client.create_pool(&200u64, &token_address); + client.pause(&admin); + client.claim_winnings(&user, &0u64); // Should panic +} - client.place_prediction(&user1, &pool_a, &100, &1); - client.place_prediction(&user2, &pool_b, &100, &1); +#[test] +fn test_unpause_restores_functionality() { + let env = Env::default(); + env.mock_all_auths(); - client.resolve_pool(&operator, &pool_a, &1u32); - client.resolve_pool(&operator, &pool_b, &2u32); // user2 loses + let ac_id = env.register(dummy_access_control::DummyAccessControl, ()); + let ac_client = dummy_access_control::DummyAccessControlClient::new(&env, &ac_id); + let contract_id = env.register(PredifiContract, ()); + let client = PredifiContractClient::new(&env, &contract_id); - let w1 = client.claim_winnings(&user1, &pool_a); - assert_eq!(w1, 100); // only winner in pool_a, gets back their own stake + let token_admin = Address::generate(&env); + let token_contract = env.register_stellar_asset_contract(token_admin.clone()); + let token_admin_client = token::StellarAssetClient::new(&env, &token_contract); - let w2 = client.claim_winnings(&user2, &pool_b); - assert_eq!(w2, 0); // lost in pool_b + let admin = Address::generate(&env); + let user = Address::generate(&env); + let treasury = Address::generate(&env); + ac_client.grant_role(&admin, &ROLE_ADMIN); + client.init(&ac_id, &treasury, &0u32); + token_admin_client.mint(&user, &1000); + + client.pause(&admin); + client.unpause(&admin); + + // After unpause these should work fine + let pool_id = client.create_pool(&100u64, &token_contract); + client.place_prediction(&user, &pool_id, &10, &1); +} + +// ── Pagination tests ────────────────────────────────────────────────────────── + +#[test] +fn test_get_user_predictions() { + let env = Env::default(); + env.mock_all_auths(); + + let (_, client, token_address, _, token_admin_client, _, _) = setup(&env); + + let user = Address::generate(&env); + token_admin_client.mint(&user, &1000); + + let pool0 = client.create_pool(&100u64, &token_address); + let pool1 = client.create_pool(&200u64, &token_address); + let pool2 = client.create_pool(&300u64, &token_address); + + client.place_prediction(&user, &pool0, &10, &1); + client.place_prediction(&user, &pool1, &20, &2); + client.place_prediction(&user, &pool2, &30, &1); + + // Offset 0, Limit 2 + let first_two = client.get_user_predictions(&user, &0, &2); + assert_eq!(first_two.len(), 2); + assert_eq!(first_two.get(0).unwrap().pool_id, pool0); + assert_eq!(first_two.get(1).unwrap().pool_id, pool1); + + // Offset 1, Limit 2 + let last_two = client.get_user_predictions(&user, &1, &2); + assert_eq!(last_two.len(), 2); + assert_eq!(last_two.get(0).unwrap().pool_id, pool1); + assert_eq!(last_two.get(1).unwrap().pool_id, pool2); + + // Offset 2, Limit 1 + let last_one = client.get_user_predictions(&user, &2, &1); + assert_eq!(last_one.len(), 1); + assert_eq!(last_one.get(0).unwrap().pool_id, pool2); + + // Out of bounds + let empty = client.get_user_predictions(&user, &3, &1); + assert_eq!(empty.len(), 0); } diff --git a/contract/contracts/predifi-contract/test_snapshots/test/test_claim_winnings.1.json b/contract/contracts/predifi-contract/test_snapshots/test/test_claim_winnings.1.json index 51e3380b..e8ced886 100644 --- a/contract/contracts/predifi-contract/test_snapshots/test/test_claim_winnings.1.json +++ b/contract/contracts/predifi-contract/test_snapshots/test/test_claim_winnings.1.json @@ -1,6 +1,6 @@ { "generators": { - "address": 9, + "address": 8, "nonce": 0, "mux_id": 0 }, @@ -28,7 +28,6 @@ ], [], [], - [], [ [ "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", @@ -39,7 +38,7 @@ "function_name": "mint", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "i128": "1000" @@ -61,7 +60,7 @@ "function_name": "mint", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" }, { "i128": "1000" @@ -76,7 +75,7 @@ [], [ [ - "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", { "function": { "contract_fn": { @@ -84,7 +83,7 @@ "function_name": "place_prediction", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "u64": "0" @@ -106,7 +105,7 @@ "function_name": "transfer", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" @@ -125,7 +124,7 @@ ], [ [ - "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", { "function": { "contract_fn": { @@ -133,7 +132,7 @@ "function_name": "place_prediction", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" }, { "u64": "0" @@ -155,7 +154,7 @@ "function_name": "transfer", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" }, { "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" @@ -200,7 +199,7 @@ ], [ [ - "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", { "function": { "contract_fn": { @@ -208,7 +207,7 @@ "function_name": "claim_winnings", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "u64": "0" @@ -223,7 +222,7 @@ [], [ [ - "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", { "function": { "contract_fn": { @@ -231,7 +230,7 @@ "function_name": "claim_winnings", "args": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" }, { "u64": "0" @@ -434,7 +433,7 @@ "symbol": "HasClaimed" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "u64": "0" @@ -452,7 +451,7 @@ "symbol": "HasClaimed" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" }, { "u64": "0" @@ -574,7 +573,7 @@ "symbol": "Prediction" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "u64": "0" @@ -609,7 +608,7 @@ "symbol": "Prediction" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" }, { "u64": "0" @@ -644,7 +643,7 @@ "symbol": "UserPredictionCount" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" } ] }, @@ -659,7 +658,7 @@ "symbol": "UserPredictionCount" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" } ] }, @@ -674,7 +673,7 @@ "symbol": "UserPredictionIndex" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "u32": 0 @@ -692,7 +691,7 @@ "symbol": "UserPredictionIndex" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" }, { "u32": 0 @@ -816,38 +815,6 @@ { "contract_data": { "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", - "key": "ledger_key_contract_instance", - "durability": "persistent" - } - }, - [ - { - "last_modified_ledger_seq": 0, - "data": { - "contract_data": { - "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", - "key": "ledger_key_contract_instance", - "durability": "persistent", - "val": { - "contract_instance": { - "executable": { - "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - }, - "storage": null - } - } - } - }, - "ext": "v0" - }, - 4095 - ] - ], - [ - { - "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", "key": { "ledger_key_nonce": { "nonce": "4837995959683129791" @@ -862,7 +829,7 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", "key": { "ledger_key_nonce": { "nonce": "4837995959683129791" @@ -880,7 +847,7 @@ [ { "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", "key": { "ledger_key_nonce": { "nonce": "8370022561469687789" @@ -895,7 +862,7 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", "key": { "ledger_key_nonce": { "nonce": "8370022561469687789" @@ -913,7 +880,7 @@ [ { "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", "key": { "ledger_key_nonce": { "nonce": "2032731177588607455" @@ -928,7 +895,7 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", "key": { "ledger_key_nonce": { "nonce": "2032731177588607455" @@ -946,7 +913,7 @@ [ { "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", "key": { "ledger_key_nonce": { "nonce": "6277191135259896685" @@ -961,7 +928,7 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5", "key": { "ledger_key_nonce": { "nonce": "6277191135259896685" @@ -1056,7 +1023,7 @@ "symbol": "Balance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" } ] }, @@ -1076,7 +1043,7 @@ "symbol": "Balance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" } ] }, @@ -1126,7 +1093,7 @@ "symbol": "Balance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" } ] }, @@ -1146,7 +1113,7 @@ "symbol": "Balance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5" } ] },