//! Period directional market (`market` PDA). Users fund **`BuyTickets`**, stake with **`PlaceBet`**. **`open_*`** is frozen by **`InitMarket`** (**create inside the anchor window**, or **`InitMarket` crank**) during **`[open_ts, min(open_ts + MARKET_OPEN_ANCHOR_GRACE_SECS, close_ts))`**. Betting uses **`Clock::unix_timestamp` ∈ `[open_ts, close_ts)`**. **`InitMarket`** stores **`open_ts`**, **`close_ts`**, **`pyth_price_feed`**; **`AdminInstantSettlement`** settles after **`close_ts`** vs fresh Pyth; **`ExecutorTreasury`** (**distribute**) pays winners.
use steel::*;
use super::{market_pda, StreakAccount};
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Market {
pub period: u64,
/// Inclusive Unix second when betting may begin (**after** open anchor within grace window).
pub open_ts: i64,
/// Exclusive Unix second end of betting window (**betting**: **`open_ts ≤ now < close_ts`**).
pub close_ts: i64,
/// Commitment to resolution rules (e.g. hash of IPFS CID); UX / audits — does not affect oracle math.
pub rules_hash: [u8; 32],
pub total_up: u64,
pub total_down: u64,
/// Losing-side stake totals at settle (for pro-rata pot).
pub loser_pot: u64,
/// Subsidy from [`Treasury`](super::Treasury)::`daily_jackpot` at settle (`USDC` base units).
pub reward_pool: u64,
/// Winning-side stake total at settle.
pub winning_total: u64,
/// Legacy Pyth **price account** pubkey (passed at **`InitMarket`**).
pub pyth_price_feed: Pubkey,
/// Pyth aggregate (**`price`**) frozen by **`InitMarket`** near **`open_ts`**.
pub open_ref_price: i64,
/// Matching Pyth **`publish_time`** (Unix seconds).
pub open_ref_publish_time: i64,
/// Pyth aggregate exponent for **`open_ref_price`**.
pub open_ref_expo: i32,
pub _pad_expo: [u8; 4],
pub outcome: u8,
pub status: u8,
pub _pad_tail: [u8; 6],
}
impl Market {
/// Same encoding as [`Position::side`](super::Position::side): resolved **UP** won.
pub const SIDE_UP: u8 = 0;
/// Resolved **DOWN** won (includes oracle tie at normalized precision — strict-up wins only).
pub const SIDE_DOWN: u8 = 1;
/// Before **`Market`** (**`ADMIN_INSTANT`** / **`FINALIZE`**); replaced at settle by [`Self::SIDE_UP`] or [`Self::SIDE_DOWN`].
pub const OUTCOME_UNSET: u8 = 255;
/// Accepting stakes via **`PlaceBet`** while **`STATUS_OPEN`**.
pub const STATUS_OPEN: u8 = 0;
/// Resolved; executor **`ExecutorTreasury`** (**distribute**) for winners, or loss rollover on the next **`BuyTickets`** / **`PlaceBet`**.
pub const STATUS_SETTLED: u8 = 1;
pub fn pda(period: u64) -> (Pubkey, u8) {
market_pda(period)
}
pub fn init_open(
period: u64,
open_ts: i64,
close_ts: i64,
pyth_price_feed: Pubkey,
rules_hash: [u8; 32],
) -> Self {
Self {
period,
open_ts,
close_ts,
rules_hash,
total_up: 0,
total_down: 0,
loser_pot: 0,
reward_pool: 0,
winning_total: 0,
pyth_price_feed,
open_ref_price: 0,
open_ref_publish_time: 0,
open_ref_expo: 0,
_pad_expo: [0; 4],
outcome: Self::OUTCOME_UNSET,
status: Self::STATUS_OPEN,
_pad_tail: [0; 6],
}
}
#[inline(always)]
pub fn has_open_oracle_snapshot(&self) -> bool {
self.open_ref_publish_time != 0
}
}
account!(StreakAccount, Market);