1use solana_program::{account_info::AccountInfo, program_error::ProgramError};
4
5use crate::error::StreakError;
6use crate::pyth::{load_fresh_price_hermes, outcome_from_open_close, Price};
7use crate::state::{Market, Treasury};
8
9pub fn oracle_outcome_market_vs_pyth(
14 market: &Market,
15 pyth_price_feed_info: &AccountInfo<'_>,
16 unix_timestamp: i64,
17) -> Result<Option<u8>, ProgramError> {
18 if !market.has_open_oracle_snapshot() {
19 return Err(StreakError::MarketNotOracleAnchored.into());
20 }
21
22 let feed_id: [u8; 32] = market.pyth_price_feed.to_bytes();
23 let open_px = Price {
24 price: market.open_ref_price,
25 conf: 0,
26 expo: market.open_ref_expo,
27 publish_time: market.open_ref_publish_time,
28 };
29 let close_px = load_fresh_price_hermes(pyth_price_feed_info, &feed_id, unix_timestamp)?;
30 outcome_from_open_close(open_px, close_px)
31}
32
33pub fn apply_settlement_outcome(
36 market: &mut Market,
37 treasury: &mut Treasury,
38 period: u64,
39 outcome: u8,
40) -> Result<(), ProgramError> {
41 if market.period != period {
42 return Err(StreakError::BadMarketState.into());
43 }
44 if market.status == Market::STATUS_VOIDED {
45 return Err(StreakError::MarketVoided.into());
46 }
47 if market.status != Market::STATUS_OPEN {
48 return Err(StreakError::BadMarketState.into());
49 }
50 if market.committed_up != 0 || market.committed_down != 0 {
51 return Err(StreakError::UnresolvedCommittedStakes.into());
52 }
53
54 let winner_total = if outcome == Market::SIDE_UP {
55 market.total_up
56 } else {
57 market.total_down
58 };
59 let loser_pot = if outcome == Market::SIDE_UP {
60 market.total_down
61 } else {
62 market.total_up
63 };
64
65 let reward_pool = if winner_total == 0 {
66 0u64
67 } else {
68 treasury.daily_jackpot.min(loser_pot)
69 };
70
71 if reward_pool > 0 {
72 treasury.daily_jackpot = treasury
73 .daily_jackpot
74 .checked_sub(reward_pool)
75 .ok_or(StreakError::InsufficientTreasury)?;
76 }
77
78 market.loser_pot = loser_pot;
79 market.reward_pool = reward_pool;
80 market.winning_total = winner_total;
81 market.outcome = outcome;
82 market.status = Market::STATUS_SETTLED;
83
84 if winner_total == 0 {
87 treasury.bump_next_period_floor(market.series_id, period)?;
88 }
89
90 Ok(())
91}