Skip to main content

streak_api/
instruction.rs

1//! Instruction discriminators + payload types (**Ore**: single `instruction.rs`).
2
3use steel::*;
4
5/// One-time **`Initialize`**: creates **`Treasury`** PDA + treasury USDC ATA. Economics and routing pubkeys are [`crate::consts`] (**`FEE_COLLECTOR`** for team-fee SPL legs). **`payer`** must be [`ADMIN_ADDRESS`](crate::consts::ADMIN_ADDRESS).
6///
7/// **Accounts:** **`payer`**, **`treasury`**, **`mint`**, **`treasury_ata`**, **`system_program`**, **`token_program`**, **`ata_program`**.
8#[repr(C)]
9#[derive(Clone, Copy, Debug, Pod, Zeroable)]
10pub struct Initialize {}
11
12/// Purchase tickets: user pays **`amount`** USDC (**micro base units**); team cut to fee collector; remainder splits into **`Treasury`** pools; **`Ledger`** credits **`unstaked_micros`** and **`tickets`**.
13///
14/// **`previous_*`** resolves **`series_id`** + **`period - 1`** (same **`series_id`**).
15///
16/// **Accounts:** **`user`**, **`treasury`**, **`ledger`**, **`mint`**, **`user_ata`**, **`treasury_ata`**, **`team_ata`**, **`system_program`**, **`token_program`**, **`ata_program`**, **`previous_market`**, **`previous_position`**.
17#[repr(C)]
18#[derive(Clone, Copy, Debug, Pod, Zeroable)]
19pub struct BuyTickets {
20    pub series_id: [u8; 2],
21    pub _pad_ix: [u8; 6],
22    pub period: [u8; 8],
23    pub amount: [u8; 8],
24}
25
26/// Spend **`ticket_units`** from **`Ledger`** into **`Position`** / **`Market`** while **`STATUS_OPEN`** and **`now < close_ts`**:
27///
28/// - **Pre-open commit** (no open oracle yet **or** **`now < open_ts`**): debits tickets; records **[`Position::STATE_COMMITTED_PREOPEN`]**; bumps **`market.committed_{side}`** only (**`total_*`** unchanged).
29/// - **Live window** (**open oracle** + **`open_ts ≤ now < close_ts`**): merges any **[`Position::STATE_COMMITTED_PREOPEN`]** into **`total_*`** first; then debits tickets and adds to **`total_*`** as **[`Position::STATE_PENDING`]** (same as legacy stake).
30/// - **Activate-only:** **`ticket_units == 0`** allowed only in the live window on an existing **COMMITTED** position (merges into **`total_*`** / **PENDING** without debiting).
31///
32/// **`period`** in the ix must match **`market.period`** (**PDA derivation**).
33///
34/// **Accounts:** **`user`**, **`ledger`**, **`treasury`** (**readonly**), **`market`**, **`position`**, **`previous_market`**, **`previous_position`**, **`system_program`**.
35#[repr(C)]
36#[derive(Clone, Copy, Debug, Pod, Zeroable)]
37pub struct PlaceBet {
38    pub series_id: [u8; 2],
39    pub _pad_ix: [u8; 6],
40    pub period: [u8; 8],
41    pub ticket_units: [u8; 8],
42    pub side: u8,
43    pub _pad: [u8; 7],
44}
45
46/// Creates **`Market`** (**executor signer**) **or** records open Pyth snapshot (crank).
47///
48/// **`Create`**: allowed whenever the **`Market`** PDA is **still empty** (executor may calendar **`open_ts`** / **`close_ts`** ahead of **`now`**; live **`PlaceBet`** that writes **`total_*`** remains **`BettingClosed`** until the open oracle anchor exists and **`open_ts ≤ now`**).
49///
50/// **Accounts:** **`authority`**, **`market`**, **`treasury`** (**readonly**), **`system_program`**, **`pyth_price_feed`** (**readonly**).
51#[repr(C)]
52#[derive(Clone, Copy, Debug, Pod, Zeroable)]
53pub struct InitMarket {
54    pub series_id: [u8; 2],
55    pub _pad_ix: [u8; 6],
56    pub period: [u8; 8],
57    pub open_ts: [u8; 8],
58    pub close_ts: [u8; 8],
59    pub pyth_price_feed: [u8; 32],
60    pub rules_hash: [u8; 32],
61}
62
63#[repr(C)]
64#[derive(Clone, Copy, Debug, Pod, Zeroable)]
65pub struct AdminInstantSettlement {
66    pub series_id: [u8; 2],
67    pub _pad_ix: [u8; 6],
68    pub period: [u8; 8],
69}
70
71/// **`EXECUTOR_KIND_DISTRIBUTE_MARKET_REWARD`**: **`pool`** and **`amount`** must be **0** (**`series_id`** must match **`market.series_id`**).
72///
73/// **`EXECUTOR_KIND_JACKPOT_PAY`**: SPL **`transfer_checked`**; **`series_id`** should be **`0`**.
74///
75/// **`EXECUTOR_KIND_MARKET_LINE_RELEASE`**: **`amount`** = **`period`** LE; **`series_id`** in payload.
76///
77/// **`EXECUTOR_KIND_MERGE_COMMITTED_POSITION`**: **`pool`** and **`amount`** must be **0**; **`series_id`** must match **`position.series_id`**; merges one **[`Position::STATE_COMMITTED_PREOPEN`]** into **`Market::total_*`**. Accounts: **`executor`**, **`treasury`** (**readonly**), **`market`**, **`position`**.
78#[repr(C)]
79#[derive(Clone, Copy, Debug, Pod, Zeroable)]
80pub struct ExecutorTreasury {
81    pub kind: u8,
82    pub pool: u8,
83    pub series_id: [u8; 2],
84    pub _pad_ix: [u8; 4],
85    pub amount: [u8; 8],
86}
87
88#[repr(C)]
89#[derive(Clone, Copy, Debug, Pod, Zeroable)]
90pub struct ClaimPositionFee {}
91
92/// Void an open market when BTC moves < 0.01% (or for oracle failure).
93///
94/// **Authority**: `ADMIN_ADDRESS` or `EXECUTOR_ADDRESS` (both accepted).
95///
96/// **Accounts:** `authority` (signer), `treasury` (readonly), `market`.
97///
98/// Preconditions: `market.status == STATUS_OPEN`, `committed_up == 0`, `committed_down == 0`
99/// (all pre-open committed positions must have been merged before voiding).
100/// The instruction may be called before or after `close_ts`.
101///
102/// After voiding, positions are refunded one-by-one via `AdminRefundVoidPosition`.
103#[repr(C)]
104#[derive(Clone, Copy, Debug, Pod, Zeroable)]
105pub struct AdminVoidMarket {
106    pub series_id: [u8; 2],
107    pub _pad_ix: [u8; 6],
108    pub period: [u8; 8],
109}
110
111/// Refund a single `STATE_PENDING` position from a voided market back to the player's `Ledger`.
112///
113/// **Authority**: `EXECUTOR_ADDRESS`.
114///
115/// **Accounts:** `authority` (signer), `market` (readonly), `position`, `ledger`.
116///
117/// Preconditions: `market.status == STATUS_VOIDED`, `position.state == STATE_PENDING`.
118/// Sets `position.state = STATE_COMMIT_REFUNDED`; restores `stake / TICKET_MICROS` tickets and
119/// `stake` `unstaked_micros` to the player's `Ledger`.
120#[repr(C)]
121#[derive(Clone, Copy, Debug, Pod, Zeroable)]
122pub struct AdminRefundVoidPosition {
123    pub series_id: [u8; 2],
124    pub _pad_ix: [u8; 6],
125    pub period: [u8; 8],
126}
127
128/// Advance the global week counter (`Treasury::current_week += 1`).
129///
130/// **Authority**: `EXECUTOR_ADDRESS`.
131///
132/// **Accounts:** `authority` (signer), `treasury`.
133///
134/// Call once at the start of each scoring week. All `Ledger` accounts will lazily zero their
135/// `week_*` counters the next time any instruction touches them (no sweep required).
136/// The new week number is returned in the `current_week` field of `Treasury` after the ix.
137#[repr(C)]
138#[derive(Clone, Copy, Debug, Pod, Zeroable)]
139pub struct AdminInitWeek {}
140
141pub const EXECUTOR_KIND_DISTRIBUTE_MARKET_REWARD: u8 = 0;
142pub const EXECUTOR_KIND_JACKPOT_PAY: u8 = 1;
143pub const EXECUTOR_KIND_MARKET_LINE_RELEASE: u8 = 2;
144pub const EXECUTOR_KIND_MERGE_COMMITTED_POSITION: u8 = 3;
145
146use num_enum::TryFromPrimitive;
147
148#[repr(u8)]
149#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
150pub enum StreakInstruction {
151    Initialize = 0,
152    BuyTickets = 1,
153    PlaceBet = 2,
154    InitMarket = 3,
155    AdminInstantSettlement = 4,
156    ExecutorTreasury = 5,
157    ClaimPositionFee = 6,
158    AdminVoidMarket = 7,
159    AdminRefundVoidPosition = 8,
160    AdminInitWeek = 9,
161}
162
163instruction!(StreakInstruction, Initialize);
164instruction!(StreakInstruction, BuyTickets);
165instruction!(StreakInstruction, PlaceBet);
166instruction!(StreakInstruction, InitMarket);
167instruction!(StreakInstruction, AdminInstantSettlement);
168instruction!(StreakInstruction, ExecutorTreasury);
169instruction!(StreakInstruction, ClaimPositionFee);
170instruction!(StreakInstruction, AdminVoidMarket);
171instruction!(StreakInstruction, AdminRefundVoidPosition);
172instruction!(StreakInstruction, AdminInitWeek);