@@ -33,18 +33,11 @@ impl MarketCreator {
3333 let end_time = MarketUtils :: calculate_end_time ( _env, duration_days) ;
3434
3535 // Create market instance
36- let market = Market :: new (
37- _env,
38- admin. clone ( ) ,
39- question,
40- outcomes,
41- end_time,
42- oracle_config,
43- ) ;
44-
45- // Market creation fee is now handled by the fees module
46- // FeeManager::process_creation_fee(_env, &admin)?;
47-
36+ let market = Market :: new ( env, admin. clone ( ) , question, outcomes, end_time, oracle_config, MarketState :: Active ) ;
37+
38+ // Process market creation fee
39+ MarketUtils :: process_creation_fee ( env, & admin) ?;
40+
4841 // Store market
4942 _env. storage ( ) . persistent ( ) . set ( & market_id, & market) ;
5043
@@ -193,25 +186,49 @@ impl MarketStateManager {
193186 }
194187
195188 /// Remove market from storage
196- pub fn remove_market ( _env : & Env , market_id : & Symbol ) {
197- _env. storage ( ) . persistent ( ) . remove ( market_id) ;
189+ pub fn remove_market ( env : & Env , market_id : & Symbol ) {
190+ let mut market = match Self :: get_market ( env, market_id) {
191+ Ok ( m) => m,
192+ Err ( _) => return ,
193+ } ;
194+ if market. state != MarketState :: Closed {
195+ MarketStateLogic :: validate_state_transition ( market. state , MarketState :: Closed ) . unwrap ( ) ;
196+ let old_state = market. state ;
197+ market. state = MarketState :: Closed ;
198+ MarketStateLogic :: emit_state_change_event ( env, market_id, old_state, market. state ) ;
199+ Self :: update_market ( env, market_id, & market) ;
200+ }
201+ env. storage ( ) . persistent ( ) . remove ( market_id) ;
198202 }
199203
200204 /// Add vote to market
201- pub fn add_vote ( market : & mut Market , user : Address , outcome : String , stake : i128 ) {
205+ pub fn add_vote ( market : & mut Market , user : Address , outcome : String , stake : i128 , market_id : Option < & Symbol > ) {
206+ MarketStateLogic :: check_function_access_for_state ( "vote" , market. state ) . unwrap ( ) ;
202207 market. votes . set ( user. clone ( ) , outcome) ;
203208 market. stakes . set ( user. clone ( ) , stake) ;
204209 market. total_staked += stake;
210+ // No state change for voting
205211 }
206212
207213 /// Add dispute stake to market
208- pub fn add_dispute_stake ( market : & mut Market , user : Address , stake : i128 ) {
214+ pub fn add_dispute_stake ( market : & mut Market , user : Address , stake : i128 , market_id : Option < & Symbol > ) {
215+ MarketStateLogic :: check_function_access_for_state ( "dispute" , market. state ) . unwrap ( ) ;
209216 let existing_stake = market. dispute_stakes . get ( user. clone ( ) ) . unwrap_or ( 0 ) ;
210217 market. dispute_stakes . set ( user, existing_stake + stake) ;
218+ // State transition: Ended -> Disputed
219+ if market. state == MarketState :: Ended {
220+ MarketStateLogic :: validate_state_transition ( market. state , MarketState :: Disputed ) . unwrap ( ) ;
221+ let old_state = market. state ;
222+ market. state = MarketState :: Disputed ;
223+ let env = & market. votes . env ( ) ;
224+ let owned_event_id = market_id. cloned ( ) . unwrap_or_else ( || Symbol :: new ( env, "unknown_market_id" ) ) ;
225+ MarketStateLogic :: emit_state_change_event ( env, & owned_event_id, old_state, market. state ) ;
226+ }
211227 }
212228
213229 /// Mark user as claimed
214- pub fn mark_claimed ( market : & mut Market , user : Address ) {
230+ pub fn mark_claimed ( market : & mut Market , user : Address , _market_id : Option < & Symbol > ) {
231+ MarketStateLogic :: check_function_access_for_state ( "claim" , market. state ) . unwrap ( ) ;
215232 market. claimed . set ( user, true ) ;
216233 }
217234
@@ -221,12 +238,32 @@ impl MarketStateManager {
221238 }
222239
223240 /// Set winning outcome
224- pub fn set_winning_outcome ( market : & mut Market , outcome : String ) {
241+ pub fn set_winning_outcome ( market : & mut Market , outcome : String , market_id : Option < & Symbol > ) {
242+ MarketStateLogic :: check_function_access_for_state ( "resolve" , market. state ) . unwrap ( ) ;
243+ let old_state = market. state ;
225244 market. winning_outcome = Some ( outcome) ;
245+ // State transition: Ended/Disputed -> Resolved
246+ if market. state == MarketState :: Ended || market. state == MarketState :: Disputed {
247+ MarketStateLogic :: validate_state_transition ( market. state , MarketState :: Resolved ) . unwrap ( ) ;
248+ market. state = MarketState :: Resolved ;
249+ let env = & market. votes . env ( ) ;
250+ let owned_event_id = market_id. cloned ( ) . unwrap_or_else ( || Symbol :: new ( env, "unknown_market_id" ) ) ;
251+ MarketStateLogic :: emit_state_change_event ( env, & owned_event_id, old_state, market. state ) ;
252+ }
226253 }
227254
228255 /// Mark fees as collected
229- pub fn mark_fees_collected ( market : & mut Market ) {
256+ pub fn mark_fees_collected ( market : & mut Market , market_id : Option < & Symbol > ) {
257+ MarketStateLogic :: check_function_access_for_state ( "close" , market. state ) . unwrap ( ) ;
258+ let old_state = market. state ;
259+ // State transition: Resolved -> Closed
260+ if market. state == MarketState :: Resolved {
261+ MarketStateLogic :: validate_state_transition ( market. state , MarketState :: Closed ) . unwrap ( ) ;
262+ market. state = MarketState :: Closed ;
263+ let env = & market. votes . env ( ) ;
264+ let owned_event_id = market_id. cloned ( ) . unwrap_or_else ( || Symbol :: new ( env, "unknown_market_id" ) ) ;
265+ MarketStateLogic :: emit_state_change_event ( env, & owned_event_id, old_state, market. state ) ;
266+ }
230267 market. fee_collected = true ;
231268 }
232269
@@ -527,8 +564,9 @@ impl MarketTestHelpers {
527564 token_client. transfer ( & user, & _env. current_contract_address ( ) , & stake) ;
528565
529566 // Add vote
530- MarketStateManager :: add_vote ( & mut market, user, outcome, stake) ;
531- MarketStateManager :: update_market ( _env, market_id, & market) ;
567+ MarketStateManager :: add_vote ( & mut market, user, outcome, stake, None ) ;
568+ MarketStateManager :: update_market ( env, market_id, & market) ;
569+
532570
533571 Ok ( ( ) )
534572 }
@@ -554,13 +592,108 @@ impl MarketTestHelpers {
554592 MarketUtils :: determine_final_result ( _env, & oracle_result, & community_consensus) ;
555593
556594 // Set winning outcome
557- MarketStateManager :: set_winning_outcome ( & mut market, final_result. clone ( ) ) ;
558- MarketStateManager :: update_market ( _env , market_id, & market) ;
595+ MarketStateManager :: set_winning_outcome ( & mut market, final_result. clone ( ) , None ) ;
596+ MarketStateManager :: update_market ( env , market_id, & market) ;
559597
560598 Ok ( final_result)
561599 }
562600}
563601
602+ // ===== MARKET STATE LOGIC =====
603+
604+ pub struct MarketStateLogic ;
605+
606+ impl MarketStateLogic {
607+ /// Validate allowed state transitions
608+ pub fn validate_state_transition ( from : MarketState , to : MarketState ) -> Result < ( ) , Error > {
609+ use MarketState :: * ;
610+ let allowed = match from {
611+ Active => matches ! ( to, Ended | Cancelled | Closed | Disputed ) ,
612+ Ended => matches ! ( to, Resolved | Disputed | Closed | Cancelled ) ,
613+ Disputed => matches ! ( to, Resolved | Closed | Cancelled ) ,
614+ Resolved => matches ! ( to, Closed ) ,
615+ Closed => false ,
616+ Cancelled => false ,
617+ } ;
618+ if allowed {
619+ Ok ( ( ) )
620+ } else {
621+ Err ( Error :: InvalidState )
622+ }
623+ }
624+
625+ /// Check if a function is allowed in the given state
626+ pub fn check_function_access_for_state ( function : & str , state : MarketState ) -> Result < ( ) , Error > {
627+ use MarketState :: * ;
628+ let allowed = match function {
629+ "vote" => matches ! ( state, Active ) ,
630+ "dispute" => matches ! ( state, Ended ) ,
631+ "resolve" => matches ! ( state, Ended | Disputed ) ,
632+ "claim" => matches ! ( state, Resolved ) ,
633+ "close" => matches ! ( state, Resolved | Cancelled | Closed ) ,
634+ _ => true , // By default allow
635+ } ;
636+ if allowed {
637+ Ok ( ( ) )
638+ } else {
639+ Err ( Error :: MarketClosed )
640+ }
641+ }
642+
643+ /// Emit a state change event (placeholder: use env.events().publish)
644+ pub fn emit_state_change_event ( env : & Env , market_id : & Symbol , old_state : MarketState , new_state : MarketState ) {
645+ env. events ( ) . publish ( ( "market_state_change" , market_id) , ( old_state, new_state) ) ;
646+ }
647+
648+ /// Validate that the market's state is consistent with its data
649+ pub fn validate_market_state_consistency ( env : & Env , market : & Market ) -> Result < ( ) , Error > {
650+ use MarketState :: * ;
651+ let now = env. ledger ( ) . timestamp ( ) ;
652+ match market. state {
653+ Active => {
654+ if market. end_time <= now {
655+ return Err ( Error :: InvalidState ) ;
656+ }
657+ if market. winning_outcome . is_some ( ) {
658+ return Err ( Error :: InvalidState ) ;
659+ }
660+ }
661+ Ended => {
662+ if market. end_time > now {
663+ return Err ( Error :: InvalidState ) ;
664+ }
665+ if market. winning_outcome . is_some ( ) {
666+ return Err ( Error :: InvalidState ) ;
667+ }
668+ }
669+ Disputed => {
670+ if market. dispute_stakes . is_empty ( ) {
671+ return Err ( Error :: InvalidState ) ;
672+ }
673+ }
674+ Resolved => {
675+ if market. winning_outcome . is_none ( ) {
676+ return Err ( Error :: InvalidState ) ;
677+ }
678+ }
679+ Closed | Cancelled => { }
680+ }
681+ Ok ( ( ) )
682+ }
683+
684+ /// Get the current state of a market
685+ pub fn get_market_state ( env : & Env , market_id : & Symbol ) -> Result < MarketState , Error > {
686+ let market = MarketStateManager :: get_market ( env, market_id) ?;
687+ Ok ( market. state )
688+ }
689+
690+ /// Check if a market can transition to a target state
691+ pub fn can_transition_to_state ( env : & Env , market_id : & Symbol , target_state : MarketState ) -> Result < bool , Error > {
692+ let market = MarketStateManager :: get_market ( env, market_id) ?;
693+ Ok ( MarketStateLogic :: validate_state_transition ( market. state , target_state) . is_ok ( ) )
694+ }
695+ }
696+
564697// ===== MODULE TESTS =====
565698
566699#[ cfg( test) ]
@@ -660,6 +793,7 @@ mod tests {
660793 25_000_00 ,
661794 String :: from_str ( & env, "gt" ) ,
662795 ) ,
796+ MarketState :: Active ,
663797 ) ;
664798
665799 // Test market stats
0 commit comments