@@ -24,16 +24,22 @@ use soroban_sdk::{contracttype, symbol_short, Address, Env, Map, String, Symbol}
2424use crate :: errors:: Error ;
2525use crate :: events:: EventEmitter ;
2626use crate :: markets:: { MarketStateManager , MarketUtils , MarketValidator } ;
27- use crate :: types:: { Bet , BetStats , BetStatus , Market , MarketState } ;
27+ use crate :: types:: { Bet , BetLimits , BetStats , BetStatus , Market , MarketState } ;
28+ use crate :: validation;
2829
2930// ===== CONSTANTS =====
3031
31- /// Minimum bet amount (0.1 XLM = 1,000,000 stroops)
32+ /// Minimum bet amount (0.1 XLM = 1,000,000 stroops). Absolute floor for any configured limit.
3233pub const MIN_BET_AMOUNT : i128 = 1_000_000 ;
3334
34- /// Maximum bet amount (10,000 XLM = 100,000,000,000 stroops)
35+ /// Maximum bet amount (10,000 XLM = 100,000,000,000 stroops). Absolute ceiling for any configured limit.
3536pub const MAX_BET_AMOUNT : i128 = 100_000_000_000 ;
3637
38+ /// Storage key for global bet limits.
39+ const GLOBAL_BET_LIMITS_KEY : & str = "bet_limits_global" ;
40+ /// Storage key for per-event bet limits map (Symbol -> BetLimits).
41+ const PER_EVENT_BET_LIMITS_KEY : & str = "bet_limits_evt" ;
42+
3743// ===== STORAGE KEY TYPES =====
3844
3945/// Storage key for user bets on a specific market
@@ -59,6 +65,65 @@ pub struct BetRegistryKey {
5965 pub market_id : Symbol ,
6066}
6167
68+ // ===== BET LIMITS STORAGE =====
69+
70+ /// Get effective bet limits for a market: per-event if set, else global, else default constants.
71+ pub fn get_effective_bet_limits ( env : & Env , market_id : & Symbol ) -> BetLimits {
72+ let key_evt = Symbol :: new ( env, PER_EVENT_BET_LIMITS_KEY ) ;
73+ let per_event: soroban_sdk:: Map < Symbol , BetLimits > = env
74+ . storage ( )
75+ . persistent ( )
76+ . get ( & key_evt)
77+ . unwrap_or ( soroban_sdk:: Map :: new ( env) ) ;
78+ if let Some ( limits) = per_event. get ( market_id. clone ( ) ) {
79+ return limits;
80+ }
81+ let key_global = Symbol :: new ( env, GLOBAL_BET_LIMITS_KEY ) ;
82+ env. storage ( )
83+ . persistent ( )
84+ . get :: < Symbol , BetLimits > ( & key_global)
85+ . unwrap_or ( BetLimits {
86+ min_bet : MIN_BET_AMOUNT ,
87+ max_bet : MAX_BET_AMOUNT ,
88+ } )
89+ }
90+
91+ /// Set global bet limits (admin only; validation of bounds done by caller).
92+ pub fn set_global_bet_limits ( env : & Env , limits : & BetLimits ) -> Result < ( ) , Error > {
93+ validate_limits_bounds ( limits) ?;
94+ let key = Symbol :: new ( env, GLOBAL_BET_LIMITS_KEY ) ;
95+ env. storage ( ) . persistent ( ) . set ( & key, limits) ;
96+ Ok ( ( ) )
97+ }
98+
99+ /// Set per-event bet limits (admin only; validation of bounds done by caller).
100+ pub fn set_event_bet_limits ( env : & Env , market_id : & Symbol , limits : & BetLimits ) -> Result < ( ) , Error > {
101+ validate_limits_bounds ( limits) ?;
102+ let key = Symbol :: new ( env, PER_EVENT_BET_LIMITS_KEY ) ;
103+ let mut per_event: soroban_sdk:: Map < Symbol , BetLimits > = env
104+ . storage ( )
105+ . persistent ( )
106+ . get ( & key)
107+ . unwrap_or ( soroban_sdk:: Map :: new ( env) ) ;
108+ per_event. set ( market_id. clone ( ) , limits. clone ( ) ) ;
109+ env. storage ( ) . persistent ( ) . set ( & key, & per_event) ;
110+ Ok ( ( ) )
111+ }
112+
113+ /// Validate that min <= max and both are within absolute bounds.
114+ fn validate_limits_bounds ( limits : & BetLimits ) -> Result < ( ) , Error > {
115+ if limits. min_bet > limits. max_bet {
116+ return Err ( Error :: InvalidInput ) ;
117+ }
118+ if limits. min_bet < MIN_BET_AMOUNT {
119+ return Err ( Error :: InsufficientStake ) ;
120+ }
121+ if limits. max_bet > MAX_BET_AMOUNT {
122+ return Err ( Error :: InvalidInput ) ;
123+ }
124+ Ok ( ( ) )
125+ }
126+
62127// ===== BET MANAGER =====
63128
64129/// Comprehensive bet manager for prediction market betting operations.
@@ -181,8 +246,8 @@ impl BetManager {
181246 let mut market = MarketStateManager :: get_market ( env, & market_id) ?;
182247 BetValidator :: validate_market_for_betting ( env, & market) ?;
183248
184- // Validate bet parameters
185- BetValidator :: validate_bet_parameters ( env, & outcome, & market. outcomes , amount) ?;
249+ // Validate bet parameters (uses configurable min/max limits per event or global)
250+ BetValidator :: validate_bet_parameters ( env, & market_id , & outcome, & market. outcomes , amount) ?;
186251
187252 // Check if user has already bet on this market
188253 if Self :: has_user_bet ( env, & market_id, & user) {
@@ -599,59 +664,37 @@ impl BetValidator {
599664
600665 /// Validate bet parameters.
601666 ///
602- /// # Validation Rules
603- ///
604- /// - Outcome must be one of the valid market outcomes
605- /// - Amount must be within allowed range
606- ///
607- /// # Parameters
608- ///
609- /// - `env` - The Soroban environment
610- /// - `outcome` - The selected outcome
611- /// - `valid_outcomes` - List of valid outcomes for the market
612- /// - `amount` - The bet amount
613- ///
614- /// # Returns
615- ///
616- /// Returns `Ok(())` if parameters are valid, `Err(Error)` otherwise.
667+ /// Uses effective bet limits (per-event if set, else global, else default min/max).
668+ /// Rejects bets below min with InsufficientStake, above max with InvalidInput.
617669 pub fn validate_bet_parameters (
618670 env : & Env ,
671+ market_id : & Symbol ,
619672 outcome : & String ,
620673 valid_outcomes : & soroban_sdk:: Vec < String > ,
621674 amount : i128 ,
622675 ) -> Result < ( ) , Error > {
623- // Validate outcome
624676 MarketValidator :: validate_outcome ( env, outcome, valid_outcomes) ?;
677+ Self :: validate_bet_amount_against_limits ( env, market_id, amount)
678+ }
625679
626- // Validate amount
627- Self :: validate_bet_amount ( amount) ?;
628-
629- Ok ( ( ) )
680+ /// Validate bet amount against effective limits (per-event or global or defaults).
681+ pub fn validate_bet_amount_against_limits (
682+ env : & Env ,
683+ market_id : & Symbol ,
684+ amount : i128 ,
685+ ) -> Result < ( ) , Error > {
686+ let limits = get_effective_bet_limits ( env, market_id) ;
687+ validation:: validate_bet_amount_against_limits ( amount, & limits)
630688 }
631689
632- /// Validate bet amount.
633- ///
634- /// # Validation Rules
635- ///
636- /// - Amount must be greater than or equal to MIN_BET_AMOUNT
637- /// - Amount must be less than or equal to MAX_BET_AMOUNT
638- ///
639- /// # Parameters
640- ///
641- /// - `amount` - The bet amount to validate
642- ///
643- /// # Returns
644- ///
645- /// Returns `Ok(())` if amount is valid, `Err(Error)` otherwise.
690+ /// Validate bet amount using default constants (for tests / backward compatibility).
646691 pub fn validate_bet_amount ( amount : i128 ) -> Result < ( ) , Error > {
647692 if amount < MIN_BET_AMOUNT {
648693 return Err ( Error :: InsufficientStake ) ;
649694 }
650-
651695 if amount > MAX_BET_AMOUNT {
652696 return Err ( Error :: InvalidInput ) ;
653697 }
654-
655698 Ok ( ( ) )
656699 }
657700}
0 commit comments