Skip to main content

streak_api/
settlement.rs

1//! Price-chain settlement helper for `AdminInstantSettlement`.
2//!
3//! Reads the open reference from `Treasury::last_close_*`, reads the Pyth close price,
4//! resolves UP/DOWN, emits `PeriodSettled`, and writes the close price back to Treasury.
5//! All market pool math and USDC distribution happen off-chain.
6
7use steel::*;
8
9use crate::consts::PYTH_BTC_USD_FEED_ID;
10use crate::event::PeriodSettled;
11use crate::pyth::{load_fresh_price, outcome_from_open_close, Price};
12use crate::state::Treasury;
13
14pub struct SettlementResult {
15    pub outcome: u8,
16    pub close_px: Price,
17    pub open_px: Price,
18}
19
20/// Read open from Treasury, read close from Pyth, return outcome + both prices.
21///
22/// On the very first call (`last_close_price == 0`, genesis not yet set), the
23/// Pyth close price is used as both open and close — outcome is UP (0) and the
24/// price chain is seeded for subsequent periods.
25pub fn resolve_settlement(
26    treasury: &Treasury,
27    pyth_price_feed_info: &AccountInfo<'_>,
28    unix_timestamp: i64,
29) -> Result<SettlementResult, ProgramError> {
30    let close_px = load_fresh_price(pyth_price_feed_info, &PYTH_BTC_USD_FEED_ID, unix_timestamp)?;
31
32    if treasury.last_close_price == 0 {
33        // Genesis: seed the price chain; emit outcome UP as a neutral sentinel.
34        return Ok(SettlementResult {
35            outcome: crate::settlement::SIDE_UP,
36            open_px: close_px,
37            close_px,
38        });
39    }
40
41    let open_px = Price {
42        price: treasury.last_close_price,
43        conf: 0,
44        expo: treasury.last_close_expo,
45        publish_time: treasury.last_close_publish_time,
46    };
47
48    let outcome = outcome_from_open_close(open_px, close_px)?;
49
50    Ok(SettlementResult { outcome, close_px, open_px })
51}
52
53/// Write close price back to Treasury and emit the `PeriodSettled` event.
54pub fn commit_settlement(
55    treasury: &mut Treasury,
56    result: SettlementResult,
57    series_id: u16,
58    period: u64,
59) {
60    PeriodSettled {
61        series_id,
62        outcome: result.outcome,
63        _pad: [0; 5],
64        period,
65        close_price: result.close_px.price,
66        close_expo: result.close_px.expo,
67        _pad2: [0; 4],
68        close_publish_time: result.close_px.publish_time,
69        open_price: result.open_px.price,
70        open_expo: result.open_px.expo,
71        _pad3: [0; 4],
72    }
73    .log();
74
75    treasury.last_close_price = result.close_px.price;
76    treasury.last_close_expo = result.close_px.expo;
77    treasury.last_close_publish_time = result.close_px.publish_time;
78}
79
80/// UP outcome constant (close > open).
81pub const SIDE_UP: u8 = 0;
82/// DOWN outcome constant (close <= open, including ties).
83pub const SIDE_DOWN: u8 = 1;