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::{EXECUTOR_ADDRESS, FEE_COLLECTOR, USDC_MAINNET_MINT},
11    instruction::{AdminRouteFees, AdminPayout, Deposit, Initialize},
12    state::Treasury,
13};
14
15#[inline(always)]
16pub fn program_id() -> Pubkey {
17    crate::ID
18}
19
20// ── ATA helpers ──────────────────────────────────────────────────────────────
21
22pub fn treasury_usdc_ata() -> Pubkey {
23    get_associated_token_address(&Treasury::pda().0, &USDC_MAINNET_MINT)
24}
25
26pub fn treasury_usdc_ata_with_token_program(token_program: &Pubkey) -> Pubkey {
27    get_associated_token_address_with_program_id(
28        &Treasury::pda().0,
29        &USDC_MAINNET_MINT,
30        token_program,
31    )
32}
33
34pub fn associated_usdc_ata(owner: Pubkey, token_program: &Pubkey) -> Pubkey {
35    get_associated_token_address_with_program_id(&owner, &USDC_MAINNET_MINT, token_program)
36}
37
38pub fn usdc_ata(owner: Pubkey) -> Pubkey {
39    get_associated_token_address(&owner, &USDC_MAINNET_MINT)
40}
41
42pub fn fee_collector_usdc_ata_with_token_program(token_program: &Pubkey) -> Pubkey {
43    get_associated_token_address_with_program_id(&FEE_COLLECTOR, &USDC_MAINNET_MINT, token_program)
44}
45
46// ── Instruction builders ─────────────────────────────────────────────────────
47
48/// One-time treasury initialization. Creates the Treasury PDA and its USDC ATA.
49/// Genesis price is seeded by the first `AdminInstantSettlement` call.
50/// `payer` must be `ADMIN_ADDRESS`.
51pub fn initialize(payer: Pubkey, token_program: Pubkey) -> Instruction {
52    let treasury_address = Treasury::pda().0;
53    let treasury_ata = treasury_usdc_ata_with_token_program(&token_program);
54    Instruction {
55        program_id: crate::ID,
56        accounts: vec![
57            AccountMeta::new(payer, true),
58            AccountMeta::new(treasury_address, false),
59            AccountMeta::new_readonly(USDC_MAINNET_MINT, false),
60            AccountMeta::new(treasury_ata, false),
61            AccountMeta::new_readonly(system_program::ID, false),
62            AccountMeta::new_readonly(token_program, false),
63            AccountMeta::new_readonly(spl_associated_token_account::ID, false),
64        ],
65        data: Initialize {}.to_bytes(),
66    }
67}
68
69/// User deposits `amount` µUSDC into the treasury.
70/// Server listens for the `Deposited` event to credit the user's off-chain balance.
71pub fn deposit(user: Pubkey, amount: u64, token_program: Pubkey) -> Instruction {
72    let user_ata = associated_usdc_ata(user, &token_program);
73    let treasury_ata = treasury_usdc_ata_with_token_program(&token_program);
74    Instruction {
75        program_id: crate::ID,
76        accounts: vec![
77            AccountMeta::new(user, true),
78            AccountMeta::new(user_ata, false),
79            AccountMeta::new(treasury_ata, false),
80            AccountMeta::new_readonly(USDC_MAINNET_MINT, false),
81            AccountMeta::new_readonly(token_program, false),
82        ],
83        data: Deposit {
84            amount: amount.to_le_bytes(),
85        }
86        .to_bytes(),
87    }
88}
89
90/// Route `amount` µUSDC of claimed DBC / DAMM v2 fees into the treasury.
91///
92/// The executor's ATA must already hold the claimed USDC (obtained off-chain via Meteora SDK).
93/// This instruction transfers it on-chain, updates treasury counters, and forwards the
94/// team share to `FEE_COLLECTOR`.
95pub fn route_fees(executor: Pubkey, amount: u64, token_program: Pubkey) -> Instruction {
96    let treasury_address  = Treasury::pda().0;
97    let treasury_ata      = treasury_usdc_ata_with_token_program(&token_program);
98    let executor_ata      = associated_usdc_ata(executor, &token_program);
99    let fee_collector_ata = fee_collector_usdc_ata_with_token_program(&token_program);
100    Instruction {
101        program_id: crate::ID,
102        accounts: vec![
103            AccountMeta::new(executor, true),
104            AccountMeta::new(executor_ata, false),
105            AccountMeta::new(treasury_address, false),
106            AccountMeta::new(treasury_ata, false),
107            AccountMeta::new(fee_collector_ata, false),
108            AccountMeta::new_readonly(USDC_MAINNET_MINT, false),
109            AccountMeta::new_readonly(token_program, false),
110        ],
111        data: AdminRouteFees { amount: amount.to_le_bytes() }.to_bytes(),
112    }
113}
114
115/// Convenience: `route_fees` signed by the canonical `EXECUTOR_ADDRESS`.
116pub fn route_fees_default_executor(amount: u64, token_program: Pubkey) -> Instruction {
117    route_fees(EXECUTOR_ADDRESS, amount, token_program)
118}
119
120/// Pay `amount` µUSDC from the treasury to `recipient_ata`.
121/// Used for winner payouts, jackpot distributions, and withdrawals.
122pub fn payout(
123    executor: Pubkey,
124    recipient_ata: Pubkey,
125    amount: u64,
126    series_id: u16,
127    period: u64,
128    token_program: Pubkey,
129) -> Instruction {
130    let treasury_address = Treasury::pda().0;
131    let treasury_ata = treasury_usdc_ata_with_token_program(&token_program);
132    Instruction {
133        program_id: crate::ID,
134        accounts: vec![
135            AccountMeta::new(executor, true),
136            AccountMeta::new(treasury_address, false),
137            AccountMeta::new(treasury_ata, false),
138            AccountMeta::new(recipient_ata, false),
139            AccountMeta::new_readonly(USDC_MAINNET_MINT, false),
140            AccountMeta::new_readonly(token_program, false),
141        ],
142        data: AdminPayout {
143            amount: amount.to_le_bytes(),
144            series_id: series_id.to_le_bytes(),
145            _pad_ix: [0; 6],
146            period: period.to_le_bytes(),
147        }
148        .to_bytes(),
149    }
150}
151