diff --git a/src/base/events.cairo b/src/base/events.cairo index 0a0dc39c..112eb43c 100644 --- a/src/base/events.cairo +++ b/src/base/events.cairo @@ -15,8 +15,10 @@ pub mod Events { #[derive(Drop, starknet::Event)] pub struct BetPlaced { /// @notice The pool ID. + #[key] pub pool_id: u256, /// @notice The address of the user who placed the bet. + #[key] pub address: ContractAddress, /// @notice The option selected by the user. pub option: felt252, @@ -32,7 +34,9 @@ pub mod Events { /// @param amount Amount of tokens staked #[derive(Drop, starknet::Event)] pub struct UserStaked { + #[key] pub pool_id: u256, + #[key] pub address: ContractAddress, pub amount: u256, } @@ -68,6 +72,7 @@ pub mod Events { /// @param timestamp Time of the state transition #[derive(Drop, starknet::Event)] pub struct PoolStateTransition { + #[key] pub pool_id: u256, pub previous_status: Status, pub new_status: Status, @@ -82,6 +87,7 @@ pub mod Events { /// @param min_bet_amount Minimum bet requirement #[derive(Drop, starknet::Event)] pub struct PoolResolved { + #[key] pub pool_id: u256, pub winning_option: bool, pub total_payout: u256, @@ -116,7 +122,9 @@ pub mod Events { /// @param caller Address of the admin who performed the addition #[derive(Drop, starknet::Event)] pub struct ValidatorAdded { + #[key] pub account: ContractAddress, + #[key] pub caller: ContractAddress, } @@ -125,7 +133,9 @@ pub mod Events { /// @param caller Address of the admin who performed the removal #[derive(Drop, starknet::Event)] pub struct ValidatorRemoved { + #[key] pub account: ContractAddress, + #[key] pub caller: ContractAddress, } @@ -135,7 +145,9 @@ pub mod Events { /// @param timestamp Time of dispute initiation #[derive(Drop, starknet::Event)] pub struct DisputeRaised { + #[key] pub pool_id: u256, + #[key] pub user: ContractAddress, pub timestamp: u64, } @@ -258,4 +270,101 @@ pub mod Events { pub admin: ContractAddress, pub timestamp: u64, } + + // Configuration Events + + /// @notice Emitted when the required validator confirmations count is updated. + #[derive(Drop, starknet::Event)] + pub struct ValidatorConfirmationsUpdated { + pub previous_count: u256, + pub new_count: u256, + #[key] + pub admin: ContractAddress, + pub timestamp: u64, + } + + /// @notice Emitted when the dispute threshold is updated. + #[derive(Drop, starknet::Event)] + pub struct DisputeThresholdUpdated { + pub previous_threshold: u256, + pub new_threshold: u256, + pub admin: ContractAddress, + pub timestamp: u64, + } + + // Pool Lifecycle Events + + /// @notice Emitted when a new pool is created. + #[derive(Drop, starknet::Event)] + pub struct PoolCreated { + #[key] + pub pool_id: u256, + #[key] + pub creator: ContractAddress, + pub pool_name: felt252, + #[key] + pub category: felt252, + pub end_time: u64, + pub min_bet_amount: u256, + pub max_bet_amount: u256, + pub creator_fee: u8, + pub timestamp: u64, + } + + // Contract Management Events + + /// @notice Emitted when the contract is paused. + #[derive(Drop, starknet::Event)] + pub struct ContractPaused { + #[key] + pub admin: ContractAddress, + pub timestamp: u64, + } + + /// @notice Emitted when the contract is unpaused. + #[derive(Drop, starknet::Event)] + pub struct ContractUnpaused { + #[key] + pub admin: ContractAddress, + pub timestamp: u64, + } + + + // Fee Collection Events + + /// @notice Emitted when protocol fees are collected. + #[derive(Drop, starknet::Event)] + pub struct ProtocolFeesCollected { + pub pool_id: u256, + pub amount: u256, + pub recipient: ContractAddress, + pub timestamp: u64, + } + + /// @notice Emitted when creator fees are collected. + #[derive(Drop, starknet::Event)] + pub struct CreatorFeesCollected { + pub pool_id: u256, + pub creator: ContractAddress, + pub amount: u256, + pub timestamp: u64, + } + + /// @notice Emitted when validator fees are distributed. + #[derive(Drop, starknet::Event)] + pub struct ValidatorFeesDistributed { + pub pool_id: u256, + pub total_amount: u256, + pub validator_count: u256, + pub timestamp: u64, + } + + /// @notice Emitted when pool creation fee is collected. + #[derive(Drop, starknet::Event)] + pub struct PoolCreationFeeCollected { + pub pool_id: u256, + pub creator: ContractAddress, + pub amount: u256, + pub timestamp: u64, + } } diff --git a/src/interfaces/ipredifi.cairo b/src/interfaces/ipredifi.cairo index c08f15f8..0476ea42 100644 --- a/src/interfaces/ipredifi.cairo +++ b/src/interfaces/ipredifi.cairo @@ -119,7 +119,8 @@ pub trait IPredifi { /// @notice Collects the pool creation fee from the creator. /// @dev Transfers 1 STRK from creator to contract. /// @param creator The creator's address. - fn collect_pool_creation_fee(ref self: TContractState, creator: ContractAddress); + /// @param pool_id The pool ID for which the fee is being collected. + fn collect_pool_creation_fee(ref self: TContractState, creator: ContractAddress, pool_id: u256); /// @notice Manually updates the state of a pool. /// @dev Only callable by admin or validator. Enforces valid state transitions. diff --git a/src/predifi.cairo b/src/predifi.cairo index 63129860..aa4e73d8 100644 --- a/src/predifi.cairo +++ b/src/predifi.cairo @@ -20,19 +20,22 @@ pub mod Predifi { }; use crate::base::errors::Errors; use crate::base::events::Events::{ - BetPlaced, DisputeRaised, DisputeResolved, EmergencyActionCancelled, - EmergencyActionExecuted, EmergencyActionScheduled, EmergencyWithdrawal, FeeWithdrawn, - FeesCollected, PoolAutomaticallySettled, PoolCancelled, PoolEmergencyFrozen, - PoolEmergencyResolved, PoolEmergencyUnfrozen, PoolResolved, PoolStateTransition, - PoolSuspended, StakeRefunded, UserStaked, ValidatorAdded, ValidatorRemoved, + BetPlaced, ContractPaused, ContractUnpaused, CreatorFeesCollected, DisputeRaised, + DisputeResolved, EmergencyActionCancelled, EmergencyActionExecuted, + EmergencyActionScheduled, EmergencyWithdrawal, FeeWithdrawn, FeesCollected, + PoolAutomaticallySettled, PoolCancelled, PoolCreated, PoolCreationFeeCollected, + PoolEmergencyFrozen, PoolEmergencyResolved, PoolEmergencyUnfrozen, PoolResolved, + PoolStateTransition, PoolSuspended, ProtocolFeesCollected, StakeRefunded, UserStaked, + ValidatorAdded, ValidatorConfirmationsUpdated, ValidatorFeesDistributed, ValidatorRemoved, ValidatorResultSubmitted, ValidatorsAssigned, }; use crate::base::security::{Security, SecurityTrait}; // package imports use crate::base::types::{ - EmergencyAction, EmergencyActionStatus, EmergencyActionType, EmergencyPoolState, - PoolDetails, PoolOdds, Status, UserStake, u8_to_category, u8_to_pool, u8_to_status, + CategoryType, EmergencyAction, EmergencyActionStatus, EmergencyActionType, + EmergencyPoolState, PoolDetails, PoolOdds, Status, UserStake, u8_to_category, u8_to_pool, + u8_to_status, }; use crate::interfaces::ipredifi::{IPredifi, IPredifiDispute, IPredifiValidator}; @@ -205,6 +208,26 @@ pub mod Predifi { EmergencyActionExecuted: EmergencyActionExecuted, /// @notice Emitted when a scheduled emergency action is cancelled. EmergencyActionCancelled: EmergencyActionCancelled, + // Configuration Events + /// @notice Emitted when validator confirmations requirement is updated. + ValidatorConfirmationsUpdated: ValidatorConfirmationsUpdated, + // Pool Lifecycle Events + /// @notice Emitted when a new pool is created. + PoolCreated: PoolCreated, + // Contract Management Events + /// @notice Emitted when the contract is paused. + ContractPaused: ContractPaused, + /// @notice Emitted when the contract is unpaused. + ContractUnpaused: ContractUnpaused, + // Fee Collection Events + /// @notice Emitted when protocol fees are collected. + ProtocolFeesCollected: ProtocolFeesCollected, + /// @notice Emitted when creator fees are collected. + CreatorFeesCollected: CreatorFeesCollected, + /// @notice Emitted when validator fees are distributed. + ValidatorFeesDistributed: ValidatorFeesDistributed, + /// @notice Emitted when pool creation fee is collected. + PoolCreationFeeCollected: PoolCreationFeeCollected, #[flat] AccessControlEvent: AccessControlComponent::Event, #[flat] @@ -299,9 +322,6 @@ pub mod Predifi { self.assert_valid_felt252(option1); self.assert_valid_felt252(option2); - // Collect pool creation fee (1 STRK) - IPredifi::collect_pool_creation_fee(ref self, creator_address); - let mut pool_id = self.generate_deterministic_number(); // While a pool with this pool_id already exists, generate a new one. @@ -361,6 +381,27 @@ pub mod Predifi { let current_count = self.pool_count.read(); self.pool_count.write(current_count + 1); + // Emit pool created event + self + .emit( + Event::PoolCreated( + PoolCreated { + pool_id, + creator: creator_address, + pool_name: poolName, + category: CategoryType(u8_to_category(category)), + end_time: poolEndTime, + min_bet_amount: minBetAmount, + max_bet_amount: maxBetAmount, + creator_fee: creatorFee, + timestamp: get_block_timestamp(), + }, + ), + ); + + // Collect pool creation fee (1 STRK) now that we have the pool_id + IPredifi::collect_pool_creation_fee(ref self, creator_address, pool_id); + pool_id } @@ -617,8 +658,9 @@ pub mod Predifi { // End reentrancy guard self.reentrancy_guard.end(); - // emit event + // emit events self.emit(UserStaked { pool_id, address, amount }); + self.emit(ValidatorAdded { account: address, caller: get_caller_address() }); } @@ -820,7 +862,10 @@ pub mod Predifi { /// @notice Collects the pool creation fee from the creator. /// @param creator The creator's address. - fn collect_pool_creation_fee(ref self: ContractState, creator: ContractAddress) { + /// @param pool_id The pool ID for which the fee is being collected. + fn collect_pool_creation_fee( + ref self: ContractState, creator: ContractAddress, pool_id: u256, + ) { self.assert_non_zero_address(creator); // Retrieve the STRK token contract let strk_token = IERC20Dispatcher { contract_address: self.token_addr.read() }; @@ -831,6 +876,16 @@ pub mod Predifi { // Transfer the pool creation fee from creator to the contract strk_token.transfer_from(creator, contract_address, ONE_STRK); + + // Emit pool creation fee collected event + self + .emit( + Event::PoolCreationFeeCollected( + PoolCreationFeeCollected { + pool_id, creator, amount: ONE_STRK, timestamp: get_block_timestamp(), + }, + ), + ); } /// @notice Returns all active pools. @@ -1479,6 +1534,42 @@ pub mod Predifi { ), ); + // Collect and emit fees + let pool = self.pools.read(pool_id); + let creator_fee_amount = (pool.totalBetAmountStrk * pool.creatorFee.into()) + / 100_u256; + let protocol_fee_amount = (pool.totalBetAmountStrk * 5_u256) + / 100_u256; // 5% protocol fee + + if creator_fee_amount > 0 { + let creator = self.get_pool_creator(pool_id); + self + .emit( + Event::CreatorFeesCollected( + CreatorFeesCollected { + pool_id, + creator, + amount: creator_fee_amount, + timestamp: get_block_timestamp(), + }, + ), + ); + } + + if protocol_fee_amount > 0 { + self + .emit( + Event::ProtocolFeesCollected( + ProtocolFeesCollected { + pool_id, + amount: protocol_fee_amount, + recipient: get_contract_address(), // Protocol fees go to contract + timestamp: get_block_timestamp(), + }, + ), + ); + } + // Emit pool resolved event for compatibility let total_payout = self.calculate_total_payout(pool_id, final_outcome); self @@ -1527,7 +1618,22 @@ pub mod Predifi { // Only admin can set this self.accesscontrol.assert_only_role(DEFAULT_ADMIN_ROLE); self.assert_positive_count(count); + + let previous_count = self.required_validator_confirmations.read(); self.required_validator_confirmations.write(count); + + // Emit event for validator confirmations update + self + .emit( + Event::ValidatorConfirmationsUpdated( + ValidatorConfirmationsUpdated { + previous_count, + new_count: count, + admin: get_caller_address(), + timestamp: get_block_timestamp(), + }, + ), + ); } /// @notice Gets the validators assigned to a pool. @@ -1710,6 +1816,19 @@ pub mod Predifi { } // Reset the validator fee for this pool after distribution self.validator_fee.write(pool_id, 0); + + // Emit validator fees distributed event + self + .emit( + Event::ValidatorFeesDistributed( + ValidatorFeesDistributed { + pool_id, + total_amount: total_validator_fee, + validator_count: validator_count_u256, + timestamp: get_block_timestamp(), + }, + ), + ); } /// @notice Retrieves the validator fee for a pool. @@ -1735,6 +1854,16 @@ pub mod Predifi { self.accesscontrol.assert_only_role(DEFAULT_ADMIN_ROLE); self.pausable.pause(); + + // Emit custom contract paused event + self + .emit( + Event::ContractPaused( + ContractPaused { + admin: get_caller_address(), timestamp: get_block_timestamp(), + }, + ), + ); } /// @notice Unpauses the contract and resumes normal operations @@ -1744,6 +1873,16 @@ pub mod Predifi { self.accesscontrol.assert_only_role(DEFAULT_ADMIN_ROLE); self.pausable.unpause(); + + // Emit custom contract unpaused event + self + .emit( + Event::ContractUnpaused( + ContractUnpaused { + admin: get_caller_address(), timestamp: get_block_timestamp(), + }, + ), + ); } /// @notice Upgrades the contract implementation @@ -1997,7 +2136,10 @@ pub mod Predifi { /// @notice Collects the pool creation fee from the creator. /// @dev Transfers 1 STRK from creator to contract. /// @param creator The creator's address. - fn collect_pool_creation_fee(ref self: ContractState, creator: ContractAddress) { + /// @param pool_id The pool ID for which the fee is being collected. + fn collect_pool_creation_fee( + ref self: ContractState, creator: ContractAddress, pool_id: u256, + ) { // Retrieve the STRK token contract let strk_token = IERC20Dispatcher { contract_address: self.token_addr.read() }; @@ -2012,6 +2154,16 @@ pub mod Predifi { // Transfer the pool creation fee from creator to the contract strk_token.transfer_from(creator, contract_address, ONE_STRK); + + // Emit pool creation fee collected event + self + .emit( + Event::PoolCreationFeeCollected( + PoolCreationFeeCollected { + pool_id, creator, amount: ONE_STRK, timestamp: get_block_timestamp(), + }, + ), + ); } /// @notice Calculates the validator fee for a pool. @@ -2054,6 +2206,19 @@ pub mod Predifi { } // Reset the validator fee for this pool after distribution self.validator_fee.write(pool_id, 0); + + // Emit validator fees distributed event + self + .emit( + Event::ValidatorFeesDistributed( + ValidatorFeesDistributed { + pool_id, + total_amount: total_validator_fee, + validator_count: validator_count_u256, + timestamp: get_block_timestamp(), + }, + ), + ); } diff --git a/tests/test_dispute.cairo b/tests/test_dispute.cairo index f68d2553..c9419c77 100644 --- a/tests/test_dispute.cairo +++ b/tests/test_dispute.cairo @@ -1,4 +1,6 @@ -use contract::base::events::Events::StakeRefunded; +use contract::base::events::Events::{ + ContractPaused, ContractUnpaused, DisputeRaised, StakeRefunded, +}; use contract::base::types::Status; use contract::interfaces::ipredifi::{ IPredifiDispatcherTrait, IPredifiDisputeDispatcherTrait, IPredifiValidatorDispatcherTrait, @@ -9,8 +11,8 @@ use core::serde::Serde; use core::traits::TryInto; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ - EventSpyAssertionsTrait, spy_events, start_cheat_block_timestamp, start_cheat_caller_address, - stop_cheat_block_timestamp, stop_cheat_caller_address, + EventSpyAssertionsTrait, EventSpyTrait, spy_events, start_cheat_block_timestamp, + start_cheat_caller_address, stop_cheat_block_timestamp, stop_cheat_caller_address, }; use starknet::{ContractAddress, get_block_timestamp}; use super::test_utils::{approve_tokens_for_payment, create_default_pool, deploy_predifi}; @@ -46,10 +48,17 @@ fn test_raise_dispute_success() { // Create a user and raise dispute let user1 = 'user1'.try_into().unwrap(); + // Setup event spy before raising dispute + let mut spy = spy_events(); + start_cheat_caller_address(dispute_contract.contract_address, user1); dispute_contract.raise_dispute(pool_id); stop_cheat_caller_address(dispute_contract.contract_address); + // Check that DisputeRaised event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No dispute events'); + // Verify dispute was raised let dispute_count = dispute_contract.get_dispute_count(pool_id); assert(dispute_count == 1, 'Dispute count should be 1'); @@ -811,3 +820,32 @@ fn test_refund_stake_paused() { start_cheat_caller_address(contract.contract_address, admin); contract.refund_stake(1); } + +#[test] +fn test_contract_pause_unpause_events() { + let (_, _, validator_contract, _, _) = deploy_predifi(); + let admin: ContractAddress = 'admin'.try_into().unwrap(); + + // Setup event spy + let mut spy = spy_events(); + + // Pause the contract + start_cheat_caller_address(validator_contract.contract_address, admin); + validator_contract.pause(); + + // Check that ContractPaused event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No pause events'); + + // Reset spy for unpause + spy = spy_events(); + + // Unpause the contract + validator_contract.unpause(); + + // Check that ContractUnpaused event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No unpause events'); + + stop_cheat_caller_address(validator_contract.contract_address); +} diff --git a/tests/test_emergency_functions.cairo b/tests/test_emergency_functions.cairo index 0485011e..d9a069d1 100644 --- a/tests/test_emergency_functions.cairo +++ b/tests/test_emergency_functions.cairo @@ -13,8 +13,8 @@ use core::integer::u256; use core::option::OptionTrait; use core::serde::Serde; use snforge_std::{ - ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, declare, spy_events, - start_cheat_block_timestamp, start_cheat_caller_address, stop_cheat_block_timestamp, + ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, EventSpyTrait, declare, + spy_events, start_cheat_block_timestamp, start_cheat_caller_address, stop_cheat_block_timestamp, stop_cheat_caller_address, }; use starknet::{ContractAddress, contract_address_const, get_block_timestamp}; @@ -153,6 +153,10 @@ fn test_emergency_freeze_pool() { // Execute the scheduled emergency action dispatcher.execute_emergency_action(action_id); + // Check that emergency events were emitted + let events = spy.get_events(); + assert(events.events.len() >= 2, 'Missing emergency events'); + // Verify pool is in emergency state assert!(dispatcher.is_pool_emergency_state(pool_id), "Pool should be in emergency state"); diff --git a/tests/test_pools.cairo b/tests/test_pools.cairo index c70c135e..e614b7ff 100644 --- a/tests/test_pools.cairo +++ b/tests/test_pools.cairo @@ -7,8 +7,8 @@ use core::felt252; use core::serde::Serde; use core::traits::TryInto; use snforge_std::{ - EventSpyAssertionsTrait, spy_events, start_cheat_block_timestamp, start_cheat_caller_address, - stop_cheat_block_timestamp, stop_cheat_caller_address, + EventSpyAssertionsTrait, EventSpyTrait, spy_events, start_cheat_block_timestamp, + start_cheat_caller_address, stop_cheat_block_timestamp, stop_cheat_caller_address, }; use starknet::{ContractAddress, get_block_timestamp}; @@ -28,6 +28,10 @@ use super::test_utils::{ #[test] fn test_create_pool() { let (contract, _, _, pool_creator, erc20_address) = deploy_predifi(); + + // Setup event spy + let mut spy = spy_events(); + // Approve the DISPATCHER contract to spend tokens start_cheat_caller_address(erc20_address, pool_creator); approve_tokens_for_payment( @@ -37,6 +41,10 @@ fn test_create_pool() { start_cheat_caller_address(contract.contract_address, pool_creator); let pool_id = create_default_pool(contract); assert!(pool_id != 0, "not created"); + + // Check that events were emitted (PoolCreated and PoolCreationFeeCollected) + let events = spy.get_events(); + assert(events.events.len() >= 2, 'Missing pool events'); } #[test] @@ -897,3 +905,36 @@ fn test_manual_pool_state_update() { let final_pool = contract.get_pool(pool_id); assert(final_pool.status == Status::Closed, 'should be Closed in storage'); } + +#[test] +fn test_pool_state_transition_event() { + let (contract, _, _, pool_creator, erc20_address) = deploy_predifi(); + + // Approve tokens for pool creation + start_cheat_caller_address(erc20_address, pool_creator); + approve_tokens_for_payment( + contract.contract_address, erc20_address, 200_000_000_000_000_000_000_000, + ); + stop_cheat_caller_address(erc20_address); + + // Create a pool + start_cheat_caller_address(contract.contract_address, pool_creator); + let pool_id = create_default_pool(contract); + stop_cheat_caller_address(contract.contract_address); + + // Setup event spy before state transition + let mut spy = spy_events(); + + // Change to admin for manual state update + let admin = 'admin'.try_into().unwrap(); + start_cheat_caller_address(contract.contract_address, admin); + + // Manually update pool state (this should emit PoolStateTransition event) + contract.manually_update_pool_state(pool_id, 1); // Change to Locked + + // Check that PoolStateTransition event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No state events'); + + stop_cheat_caller_address(contract.contract_address); +} diff --git a/tests/test_user_participation.cairo b/tests/test_user_participation.cairo index 73f04fc1..b7f98493 100644 --- a/tests/test_user_participation.cairo +++ b/tests/test_user_participation.cairo @@ -6,8 +6,8 @@ use core::array::ArrayTrait; use core::serde::Serde; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ - start_cheat_block_timestamp, start_cheat_caller_address, stop_cheat_block_timestamp, - stop_cheat_caller_address, test_address, + EventSpyTrait, spy_events, start_cheat_block_timestamp, start_cheat_caller_address, + stop_cheat_block_timestamp, stop_cheat_caller_address, test_address, }; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::{ContractAddress, get_block_timestamp}; @@ -432,13 +432,20 @@ fn test_distribute_validation_fee() { stop_cheat_caller_address(erc20_address); start_cheat_caller_address(dispatcher.contract_address, POOL_CREATOR); - dispatcher.collect_pool_creation_fee(POOL_CREATOR); + dispatcher.collect_pool_creation_fee(POOL_CREATOR, 1); // Using pool_id 1 for test validator_dispatcher.calculate_validator_fee(18, 10_000); + // Setup event spy before fee distribution + let mut spy = spy_events(); + start_cheat_caller_address(dispatcher.contract_address, dispatcher.contract_address); validator_dispatcher.distribute_validator_fees(18); + // Check that ValidatorFeesDistributed event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No fee distribution events'); + let balance_validator1 = erc20.balance_of(validator1); assert(balance_validator1 == 125, 'distribution failed'); let balance_validator2 = erc20.balance_of(validator2); @@ -1344,3 +1351,31 @@ fn test_multiple_users_with_status_transitions() { stop_cheat_block_timestamp(contract.contract_address); } + +#[test] +fn test_bet_placed_event() { + let (contract, _, _, user, erc20_address) = deploy_predifi(); + + // Approve tokens for the user + start_cheat_caller_address(erc20_address, user); + approve_tokens_for_payment( + contract.contract_address, erc20_address, 200_000_000_000_000_000_000_000, + ); + stop_cheat_caller_address(erc20_address); + + // Create a pool + start_cheat_caller_address(contract.contract_address, user); + let pool_id = create_default_pool(contract); + + // Setup event spy before voting + let mut spy = spy_events(); + + // Place a bet (vote) + contract.vote(pool_id, 'Team A', 200); + + // Check that BetPlaced event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No bet events'); + + stop_cheat_caller_address(contract.contract_address); +} diff --git a/tests/test_validators.cairo b/tests/test_validators.cairo index 2aa0ae0c..dbea3cba 100644 --- a/tests/test_validators.cairo +++ b/tests/test_validators.cairo @@ -1,4 +1,6 @@ -use contract::base::events::Events::{ValidatorAdded, ValidatorRemoved}; +use contract::base::events::Events::{ + PoolResolved, ValidatorAdded, ValidatorRemoved, ValidatorResultSubmitted, +}; use contract::base::types::Status; use contract::interfaces::ipredifi::{ IPredifiDispatcherTrait, IPredifiValidator, IPredifiValidatorDispatcherTrait, @@ -10,8 +12,8 @@ use core::traits::{Into, TryInto}; use openzeppelin::access::accesscontrol::AccessControlComponent::InternalTrait as AccessControlInternalTrait; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use snforge_std::{ - EventSpyAssertionsTrait, spy_events, start_cheat_block_timestamp, start_cheat_caller_address, - stop_cheat_block_timestamp, stop_cheat_caller_address, test_address, + EventSpyAssertionsTrait, EventSpyTrait, spy_events, start_cheat_block_timestamp, + start_cheat_caller_address, stop_cheat_block_timestamp, stop_cheat_caller_address, test_address, }; use starknet::storage::{MutableVecTrait, StoragePointerReadAccess}; use starknet::{ContractAddress, get_block_timestamp}; @@ -74,11 +76,18 @@ fn test_validate_pool_result_success() { validator_contract.add_validator(validator2); stop_cheat_caller_address(validator_contract.contract_address); + // Setup event spy before first validation + let mut spy = spy_events(); + // First validator validates - pool should remain locked start_cheat_caller_address(validator_contract.contract_address, validator1); validator_contract.validate_pool_result(pool_id, true); // Vote for option2 stop_cheat_caller_address(validator_contract.contract_address); + // Check that ValidatorResultSubmitted event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No validation events'); + let pool_after_first_validation = contract.get_pool(pool_id); assert(pool_after_first_validation.status == Status::Locked, 'Pool should still be locked'); @@ -87,10 +96,17 @@ fn test_validate_pool_result_success() { assert(validation_count == 1, 'Should have 1 validation'); assert(!is_settled, 'Pool should not be settled yet'); + // Reset spy for second validation that will trigger settlement + spy = spy_events(); + // Second validator validates - pool should be settled start_cheat_caller_address(validator_contract.contract_address, validator2); validator_contract.validate_pool_result(pool_id, true); // Vote for option2 - stop_cheat_caller_address(contract.contract_address); + stop_cheat_caller_address(validator_contract.contract_address); + + // Check that events were emitted (ValidatorResultSubmitted and PoolResolved) + let events = spy.get_events(); + assert(events.events.len() >= 2, 'Missing resolution events'); let pool_after_second_validation = contract.get_pool(pool_id); assert(pool_after_second_validation.status == Status::Settled, 'Pool should be settled'); @@ -971,3 +987,59 @@ fn test_remove_validator_unauthorized() { // Unauthorized caller attempt to remove the validator role IPredifiValidator::remove_validator(ref state, validator); } + +#[test] +fn test_set_required_validator_confirmations_event() { + let (_, _, validator_contract, _, _) = deploy_predifi(); + let admin = 'admin'.try_into().unwrap(); + + // Setup event spy + let mut spy = spy_events(); + + // Set caller as admin and update validator confirmations + start_cheat_caller_address(validator_contract.contract_address, admin); + validator_contract.set_required_validator_confirmations(5); + stop_cheat_caller_address(validator_contract.contract_address); + + // Check that events were emitted (just check event exists, not content) + let events = spy.get_events(); + assert(events.events.len() > 0, 'No events'); +} + +#[test] +fn test_assign_validators_event() { + let (contract, _, validator_contract, pool_creator, erc20_address) = deploy_predifi(); + + // Create validators + let validator1 = 'validator1'.try_into().unwrap(); + let validator2 = 'validator2'.try_into().unwrap(); + + // Add validators to the contract + let admin = 'admin'.try_into().unwrap(); + start_cheat_caller_address(validator_contract.contract_address, admin); + validator_contract.add_validator(validator1); + validator_contract.add_validator(validator2); + stop_cheat_caller_address(validator_contract.contract_address); + + // Set up token approval for pool creation + start_cheat_caller_address(erc20_address, pool_creator); + approve_tokens_for_payment( + contract.contract_address, erc20_address, 200_000_000_000_000_000_000_000, + ); + stop_cheat_caller_address(erc20_address); + + // Create a pool + start_cheat_caller_address(contract.contract_address, pool_creator); + let pool_id = create_default_pool(contract); + stop_cheat_caller_address(contract.contract_address); + + // Setup event spy before assigning validators + let mut spy = spy_events(); + + // Assign validators to the pool + validator_contract.assign_random_validators(pool_id); + + // Check that ValidatorsAssigned event was emitted + let events = spy.get_events(); + assert(events.events.len() > 0, 'No validator events'); +}