Skip to main content

streak_api/
sdk.rs

1//! Off-chain instruction builders. Fixed account order mirrors on-chain `process_*` comments.
2
3use solana_program::pubkey::Pubkey;
4use spl_associated_token_account::{
5    get_associated_token_address, get_associated_token_address_with_program_id,
6};
7use steel::*;
8
9use crate::{
10    consts::{
11        EXECUTOR_ADDRESS, FEE_COLLECTOR, PYTH_BTC_USD_PRICE_FEED_ACCOUNT, USDC_MAINNET_MINT,
12    },
13    instruction::{AdminInstantSettlement, AdminPayout, Deposit, Initialize},
14    state::Treasury,
15};
16
17#[inline(always)]
18pub fn program_id() -> Pubkey {
19    crate::ID
20}
21
22// ── ATA helpers ──────────────────────────────────────────────────────────────
23
24pub fn treasury_usdc_ata() -> Pubkey {
25    get_associated_token_address(&Treasury::pda().0, &USDC_MAINNET_MINT)
26}
27
28pub fn treasury_usdc_ata_with_token_program(token_program: &Pubkey) -> Pubkey {
29    get_associated_token_address_with_program_id(
30        &Treasury::pda().0,
31        &USDC_MAINNET_MINT,
32        token_program,
33    )
34}
35
36pub fn associated_usdc_ata(owner: Pubkey, token_program: &Pubkey) -> Pubkey {
37    get_associated_token_address_with_program_id(&owner, &USDC_MAINNET_MINT, token_program)
38}
39
40pub fn usdc_ata(owner: Pubkey) -> Pubkey {
41    get_associated_token_address(&owner, &USDC_MAINNET_MINT)
42}
43
44pub fn fee_collector_usdc_ata_with_token_program(token_program: &Pubkey) -> Pubkey {
45    get_associated_token_address_with_program_id(&FEE_COLLECTOR, &USDC_MAINNET_MINT, token_program)
46}
47
48// ── Instruction builders ─────────────────────────────────────────────────────
49
50/// One-time treasury initialization. Seeds genesis BTC price from Pyth push feed.
51/// `payer` must be `ADMIN_ADDRESS`.
52pub fn initialize(payer: Pubkey, token_program: Pubkey) -> Instruction {
53    let treasury_address = Treasury::pda().0;
54    let treasury_ata = treasury_usdc_ata_with_token_program(&token_program);
55    Instruction {
56        program_id: crate::ID,
57        accounts: vec![
58            AccountMeta::new(payer, true),
59            AccountMeta::new(treasury_address, false),
60            AccountMeta::new_readonly(USDC_MAINNET_MINT, false),
61            AccountMeta::new(treasury_ata, false),
62            AccountMeta::new_readonly(system_program::ID, false),
63            AccountMeta::new_readonly(token_program, false),
64            AccountMeta::new_readonly(spl_associated_token_account::ID, false),
65            AccountMeta::new_readonly(PYTH_BTC_USD_PRICE_FEED_ACCOUNT, false),
66        ],
67        data: Initialize {}.to_bytes(),
68    }
69}
70
71/// User deposits `amount` µUSDC into the treasury.
72/// Server listens for the `Deposited` event to credit the user's off-chain balance.
73pub fn deposit(user: Pubkey, amount: u64, token_program: Pubkey) -> Instruction {
74    let user_ata = associated_usdc_ata(user, &token_program);
75    let treasury_ata = treasury_usdc_ata_with_token_program(&token_program);
76    Instruction {
77        program_id: crate::ID,
78        accounts: vec![
79            AccountMeta::new(user, true),
80            AccountMeta::new(user_ata, false),
81            AccountMeta::new(treasury_ata, false),
82            AccountMeta::new_readonly(USDC_MAINNET_MINT, false),
83            AccountMeta::new_readonly(token_program, false),
84        ],
85        data: Deposit {
86            amount: amount.to_le_bytes(),
87        }
88        .to_bytes(),
89    }
90}
91
92/// Advance the price chain and emit `PeriodSettled`. Reads close price from the Pyth push feed.
93pub fn settle_period(authority: Pubkey, series_id: u16, period: u64) -> Instruction {
94    Instruction {
95        program_id: crate::ID,
96        accounts: vec![
97            AccountMeta::new(authority, true),
98            AccountMeta::new(Treasury::pda().0, false),
99            AccountMeta::new_readonly(PYTH_BTC_USD_PRICE_FEED_ACCOUNT, false),
100        ],
101        data: AdminInstantSettlement {
102            series_id: series_id.to_le_bytes(),
103            _pad_ix: [0; 6],
104            period: period.to_le_bytes(),
105        }
106        .to_bytes(),
107    }
108}
109
110/// Pay `amount` µUSDC from the treasury to `recipient_ata`.
111/// Used for winner payouts, jackpot distributions, and withdrawals.
112pub fn payout(
113    executor: Pubkey,
114    recipient_ata: Pubkey,
115    amount: u64,
116    series_id: u16,
117    period: u64,
118    token_program: Pubkey,
119) -> Instruction {
120    let treasury_address = Treasury::pda().0;
121    let treasury_ata = treasury_usdc_ata_with_token_program(&token_program);
122    Instruction {
123        program_id: crate::ID,
124        accounts: vec![
125            AccountMeta::new(executor, true),
126            AccountMeta::new(treasury_address, false),
127            AccountMeta::new(treasury_ata, false),
128            AccountMeta::new(recipient_ata, false),
129            AccountMeta::new_readonly(USDC_MAINNET_MINT, false),
130            AccountMeta::new_readonly(token_program, false),
131        ],
132        data: AdminPayout {
133            amount: amount.to_le_bytes(),
134            series_id: series_id.to_le_bytes(),
135            _pad_ix: [0; 6],
136            period: period.to_le_bytes(),
137        }
138        .to_bytes(),
139    }
140}
141
142/// Convenience: `settle_period` signed by the canonical `EXECUTOR_ADDRESS`.
143pub fn settle_period_default_executor(series_id: u16, period: u64) -> Instruction {
144    settle_period(EXECUTOR_ADDRESS, series_id, period)
145}