@@ -5,7 +5,7 @@ use std::sync::Arc;
55use async_graphql:: { EmptySubscription , Object , Schema } ;
66use linera_sdk:: {
77 graphql:: GraphQLMutationRoot ,
8- linera_base_types:: { Amount , WithServiceAbi } ,
8+ linera_base_types:: { Amount , Timestamp , WithServiceAbi } ,
99 views:: View ,
1010 Service , ServiceRuntime ,
1111} ;
@@ -22,6 +22,7 @@ use predictive_manager::Operation;
2222// 3. No concurrent access to state during query execution
2323struct StateWrapper {
2424 state : * const PredictionMarketState ,
25+ runtime : Arc < ServiceRuntime < PredictiveManagerService > > ,
2526}
2627
2728unsafe impl Send for StateWrapper { }
@@ -36,6 +37,11 @@ impl StateWrapper {
3637 unsafe fn state ( & self ) -> & PredictionMarketState {
3738 & * self . state
3839 }
40+
41+ #[ inline]
42+ fn runtime ( & self ) -> & Arc < ServiceRuntime < PredictiveManagerService > > {
43+ & self . runtime
44+ }
3945}
4046
4147pub struct PredictiveManagerService {
@@ -63,10 +69,11 @@ impl Service for PredictiveManagerService {
6369 }
6470
6571 async fn handle_query ( & self , query : Self :: Query ) -> Self :: QueryResponse {
66- // Pass state access through GraphQL data context using a raw pointer
72+ // Pass state and runtime access through GraphQL data context using raw pointers
6773 // Safe because query execution completes within this method
6874 let state_wrapper = StateWrapper {
6975 state : & self . state as * const PredictionMarketState ,
76+ runtime : self . runtime . clone ( ) ,
7077 } ;
7178
7279 Schema :: build (
@@ -211,8 +218,145 @@ impl QueryRoot {
211218 . map ( |m| m. clone ( ) )
212219 . ok_or_else ( || async_graphql:: Error :: new ( "Market not found" ) )
213220 }
221+
222+ /// Get the result of a player's daily prediction (mirrors contract's get_daily_outcome)
223+ /// Returns whether the player's prediction was correct
224+ async fn get_daily_outcome (
225+ & self ,
226+ ctx : & async_graphql:: Context < ' _ > ,
227+ player_id : PlayerId ,
228+ ) -> async_graphql:: Result < bool > {
229+ let state_wrapper = ctx. data_unchecked :: < StateWrapper > ( ) ;
230+ let state = unsafe { state_wrapper. state ( ) } ;
231+
232+ // Verify player exists
233+ state
234+ . players
235+ . get ( & player_id)
236+ . await ?
237+ . ok_or_else ( || async_graphql:: Error :: new ( "Player not found" ) ) ?;
238+
239+ // Get current time from runtime
240+ let runtime = state_wrapper. runtime ( ) ;
241+ let current_time = runtime. system_time ( ) ;
242+ let period_start = get_daily_period_start ( current_time) ;
243+ let prediction_key = format ! (
244+ "{:?}_{:?}_{}" ,
245+ player_id,
246+ PredictionPeriod :: Daily ,
247+ period_start. micros( )
248+ ) ;
249+
250+ // Get the prediction
251+ if let Some ( prediction) = state. predictions . get ( & prediction_key) . await ? {
252+ if let Some ( correct) = prediction. correct {
253+ return Ok ( correct) ;
254+ }
255+ }
256+
257+ Ok ( false ) // Prediction not found or not resolved
258+ }
259+
260+ /// Get the result of a player's weekly prediction (mirrors contract's get_weekly_outcome)
261+ /// Returns whether the player's prediction was correct
262+ async fn get_weekly_outcome (
263+ & self ,
264+ ctx : & async_graphql:: Context < ' _ > ,
265+ player_id : PlayerId ,
266+ ) -> async_graphql:: Result < bool > {
267+ let state_wrapper = ctx. data_unchecked :: < StateWrapper > ( ) ;
268+ let state = unsafe { state_wrapper. state ( ) } ;
269+
270+ // Verify player exists
271+ state
272+ . players
273+ . get ( & player_id)
274+ . await ?
275+ . ok_or_else ( || async_graphql:: Error :: new ( "Player not found" ) ) ?;
276+
277+ // Get current time from runtime
278+ let runtime = state_wrapper. runtime ( ) ;
279+ let current_time = runtime. system_time ( ) ;
280+ let period_start = get_weekly_period_start ( current_time) ;
281+ let prediction_key = format ! (
282+ "{:?}_{:?}_{}" ,
283+ player_id,
284+ PredictionPeriod :: Weekly ,
285+ period_start. micros( )
286+ ) ;
287+
288+ // Get the prediction
289+ if let Some ( prediction) = state. predictions . get ( & prediction_key) . await ? {
290+ if let Some ( correct) = prediction. correct {
291+ return Ok ( correct) ;
292+ }
293+ }
294+
295+ Ok ( false ) // Prediction not found or not resolved
296+ }
297+
298+ /// Get the result of a player's monthly prediction (mirrors contract's get_monthly_outcome)
299+ /// Returns whether the player's prediction was correct
300+ async fn get_monthly_outcome (
301+ & self ,
302+ ctx : & async_graphql:: Context < ' _ > ,
303+ player_id : PlayerId ,
304+ ) -> async_graphql:: Result < bool > {
305+ let state_wrapper = ctx. data_unchecked :: < StateWrapper > ( ) ;
306+ let state = unsafe { state_wrapper. state ( ) } ;
307+
308+ // Verify player exists
309+ state
310+ . players
311+ . get ( & player_id)
312+ . await ?
313+ . ok_or_else ( || async_graphql:: Error :: new ( "Player not found" ) ) ?;
314+
315+ // Get current time from runtime
316+ let runtime = state_wrapper. runtime ( ) ;
317+ let current_time = runtime. system_time ( ) ;
318+ let period_start = get_monthly_period_start ( current_time) ;
319+ let prediction_key = format ! (
320+ "{:?}_{:?}_{}" ,
321+ player_id,
322+ PredictionPeriod :: Monthly ,
323+ period_start. micros( )
324+ ) ;
325+
326+ // Get the prediction
327+ if let Some ( prediction) = state. predictions . get ( & prediction_key) . await ? {
328+ if let Some ( correct) = prediction. correct {
329+ return Ok ( correct) ;
330+ }
331+ }
332+
333+ Ok ( false ) // Prediction not found or not resolved
334+ }
335+ }
336+
337+ // Helper functions for period calculations
338+ fn get_daily_period_start ( timestamp : Timestamp ) -> Timestamp {
339+
340+ let one_day_micros = 24 * 60 * 60 * 1_000_000 ;
341+ let day_start = ( timestamp. micros ( ) / one_day_micros) * one_day_micros;
342+ Timestamp :: from ( day_start)
343+ }
344+
345+ fn get_weekly_period_start ( timestamp : Timestamp ) -> Timestamp {
346+
347+ let one_week_micros = 7 * 24 * 60 * 60 * 1_000_000 ;
348+ let week_start = ( timestamp. micros ( ) / one_week_micros) * one_week_micros;
349+ Timestamp :: from ( week_start)
214350}
215351
352+ fn get_monthly_period_start ( timestamp : Timestamp ) -> Timestamp {
353+
354+ let one_month_micros = 30 * 24 * 60 * 60 * 1_000_000 ; // Approximate
355+ let month_start = ( timestamp. micros ( ) / one_month_micros) * one_month_micros;
356+ Timestamp :: from ( month_start)
357+ }
358+
359+
216360#[ cfg( test) ]
217361mod tests {
218362 use std:: sync:: Arc ;
0 commit comments