streak-api 0.3.15

API for interacting with the STREAK directional markets protocol on Solana
Documentation
//! Protocol USDC custody (`treasury` PDA).
//!
//! The treasury serves two roles:
//!
//! 1. **USDC custodian** — the treasury ATA holds all deposited USDC. Deposits come in via
//!    `Deposit`; payouts go out via `AdminPayout` (executor-signed).
//!
//! 2. **Price chain** — `last_close_*` carries the Pyth close price from the most recently
//!    settled period. `AdminInstantSettlement` reads it as the open reference and writes the
//!    new close price back, forming a self-perpetuating chain. Zero until `Initialize` seeds it.

use steel::*;

use super::{treasury_pda, StreakAccount};

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Treasury {
    /// Daily jackpot pool — credited on `Deposit`; debited via `AdminPayout`.
    pub daily_jackpot: u64,
    /// Weekly jackpot pool — credited on `Deposit`; debited via `AdminPayout`.
    pub weekly_jackpot: u64,
    /// Buyback reserve (10% of ticket sales).
    pub buyback: u64,
    /// Close price from the most recently settled period — used as the open reference
    /// for the next settlement. Zero until `Initialize` seeds it.
    pub last_close_price: i64,
    /// Pyth exponent matching `last_close_price`.
    pub last_close_expo: i32,
    pub _pad_close: [u8; 4],
    /// `publish_time` from the Pyth feed at last settlement.
    pub last_close_publish_time: i64,
    /// Stability reserve — excess weekly accrual + 60% of routed trading fees (brief v2).
    pub stability_reserve: u64,
}

impl Treasury {
    pub fn pda() -> (Pubkey, u8) {
        treasury_pda()
    }

    /// Body length before `stability_reserve` (deployed accounts pre-0.3.12).
    pub const LEGACY_BODY_LEN: usize = 48;

    /// Decode raw treasury account data (8-byte Steel discriminator + body).
    ///
    /// Legacy accounts are shorter; `stability_reserve` reads as 0 until the first
    /// `AdminSetTreasuryPools` realloc + write.
    pub fn decode_account_data(data: &[u8]) -> Result<Self, &'static str> {
        const DISC: usize = 8;
        let body = data.get(DISC..).ok_or("treasury account empty")?;
        let full = std::mem::size_of::<Self>();
        if body.len() >= full {
            return Ok(*bytemuck::from_bytes(&body[..full]));
        }
        if body.len() >= Self::LEGACY_BODY_LEN {
            #[repr(C)]
            #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
            struct Legacy {
                daily_jackpot: u64,
                weekly_jackpot: u64,
                buyback: u64,
                last_close_price: i64,
                last_close_expo: i32,
                _pad_close: [u8; 4],
                last_close_publish_time: i64,
            }
            let leg: &Legacy = bytemuck::from_bytes(&body[..Self::LEGACY_BODY_LEN]);
            return Ok(Self {
                daily_jackpot: leg.daily_jackpot,
                weekly_jackpot: leg.weekly_jackpot,
                buyback: leg.buyback,
                last_close_price: leg.last_close_price,
                last_close_expo: leg.last_close_expo,
                _pad_close: leg._pad_close,
                last_close_publish_time: leg.last_close_publish_time,
                stability_reserve: 0,
            });
        }
        Err("treasury account too short")
    }
}

account!(StreakAccount, Treasury);