Skip to main content

percli_chain/
ix.rs

1use borsh::BorshSerialize;
2use sha2::{Digest, Sha256};
3#[allow(deprecated)]
4use solana_sdk::{
5    instruction::{AccountMeta, Instruction},
6    pubkey::Pubkey,
7    system_program,
8};
9
10/// Compute the Anchor instruction discriminator: first 8 bytes of sha256("global:<name>").
11pub fn anchor_discriminator(name: &str) -> [u8; 8] {
12    let hash = Sha256::digest(format!("global:{name}").as_bytes());
13    let mut disc = [0u8; 8];
14    disc.copy_from_slice(&hash[..8]);
15    disc
16}
17
18/// Build an Anchor instruction with discriminator + borsh-serialized args.
19pub fn build_ix(
20    program_id: &Pubkey,
21    name: &str,
22    args: impl BorshSerialize,
23    accounts: Vec<AccountMeta>,
24) -> Instruction {
25    let mut data = anchor_discriminator(name).to_vec();
26    args.serialize(&mut data).expect("borsh serialize");
27    Instruction {
28        program_id: *program_id,
29        accounts,
30        data,
31    }
32}
33
34// --- Arg structs matching the on-chain Anchor ABI ---
35
36#[derive(BorshSerialize)]
37pub struct InitializeMarketArgs {
38    pub init_slot: u64,
39    pub init_oracle_price: u64,
40    pub params: RiskParamsInput,
41}
42
43#[derive(BorshSerialize)]
44pub struct RiskParamsInput {
45    pub warmup_period_slots: u64,
46    pub maintenance_margin_bps: u64,
47    pub initial_margin_bps: u64,
48    pub trading_fee_bps: u64,
49    pub max_accounts: u64,
50    pub new_account_fee: u64,
51    pub maintenance_fee_per_slot: u64,
52    pub max_crank_staleness_slots: u64,
53    pub liquidation_fee_bps: u64,
54    pub liquidation_fee_cap: u64,
55    pub min_liquidation_abs: u64,
56    pub min_initial_deposit: u64,
57    pub min_nonzero_mm_req: u64,
58    pub min_nonzero_im_req: u64,
59    pub insurance_floor: u64,
60}
61
62#[derive(BorshSerialize)]
63pub struct DepositArgs {
64    pub account_idx: u16,
65    pub amount: u64,
66}
67
68#[derive(BorshSerialize)]
69pub struct WithdrawArgs {
70    pub account_idx: u16,
71    pub amount: u64,
72    pub funding_rate: i64,
73}
74
75#[derive(BorshSerialize)]
76pub struct TradeArgs {
77    pub account_a: u16,
78    pub account_b: u16,
79    pub size_q: i128,
80    pub exec_price: u64,
81    pub funding_rate: i64,
82}
83
84#[derive(BorshSerialize)]
85pub struct CrankArgs {
86    pub funding_rate: i64,
87}
88
89#[derive(BorshSerialize)]
90pub struct LiquidateArgs {
91    pub account_idx: u16,
92    pub funding_rate: i64,
93}
94
95#[derive(BorshSerialize)]
96pub struct SettleArgs {
97    pub account_idx: u16,
98    pub funding_rate: i64,
99}
100
101#[derive(BorshSerialize)]
102pub struct CloseAccountArgs {
103    pub account_idx: u16,
104    pub funding_rate: i64,
105}
106
107#[derive(BorshSerialize)]
108pub struct ReclaimAccountArgs {
109    pub account_idx: u16,
110}
111
112#[derive(BorshSerialize)]
113pub struct WithdrawInsuranceArgs {
114    pub amount: u64,
115}
116
117#[derive(BorshSerialize)]
118pub struct TopUpInsuranceArgs {
119    pub amount: u64,
120}
121
122#[derive(BorshSerialize)]
123pub struct DepositFeeCreditsArgs {
124    pub account_idx: u16,
125    pub amount: u64,
126}
127
128#[derive(BorshSerialize)]
129pub struct ConvertReleasedPnlArgs {
130    pub account_idx: u16,
131    pub x_req: u64,
132    pub funding_rate: i64,
133}
134
135#[derive(BorshSerialize)]
136pub struct UpdateMatcherArgs {
137    pub new_matcher: Pubkey,
138}
139
140// --- Instruction builders ---
141
142pub fn initialize_market_ix(
143    program_id: &Pubkey,
144    market: &Pubkey,
145    authority: &Pubkey,
146    mint: &Pubkey,
147    vault: &Pubkey,
148    oracle: &Pubkey,
149    matcher: &Pubkey,
150    token_program: &Pubkey,
151    args: InitializeMarketArgs,
152) -> Instruction {
153    build_ix(
154        program_id,
155        "initialize_market",
156        args,
157        vec![
158            AccountMeta::new(*authority, true),
159            AccountMeta::new(*market, false),
160            AccountMeta::new_readonly(*mint, false),
161            AccountMeta::new(*vault, false),
162            AccountMeta::new_readonly(*oracle, false),
163            AccountMeta::new_readonly(*matcher, false),
164            AccountMeta::new_readonly(*token_program, false),
165            AccountMeta::new_readonly(system_program::id(), false),
166        ],
167    )
168}
169
170pub fn deposit_ix(
171    program_id: &Pubkey,
172    market: &Pubkey,
173    user: &Pubkey,
174    mint: &Pubkey,
175    user_token_account: &Pubkey,
176    vault: &Pubkey,
177    token_program: &Pubkey,
178    args: DepositArgs,
179) -> Instruction {
180    build_ix(
181        program_id,
182        "deposit",
183        args,
184        vec![
185            AccountMeta::new(*user, true),
186            AccountMeta::new(*market, false),
187            AccountMeta::new_readonly(*mint, false),
188            AccountMeta::new(*user_token_account, false),
189            AccountMeta::new(*vault, false),
190            AccountMeta::new_readonly(*token_program, false),
191        ],
192    )
193}
194
195pub fn withdraw_ix(
196    program_id: &Pubkey,
197    market: &Pubkey,
198    user: &Pubkey,
199    mint: &Pubkey,
200    user_token_account: &Pubkey,
201    vault: &Pubkey,
202    token_program: &Pubkey,
203    args: WithdrawArgs,
204) -> Instruction {
205    build_ix(
206        program_id,
207        "withdraw",
208        args,
209        vec![
210            AccountMeta::new(*user, true),
211            AccountMeta::new(*market, false),
212            AccountMeta::new_readonly(*mint, false),
213            AccountMeta::new(*user_token_account, false),
214            AccountMeta::new(*vault, false),
215            AccountMeta::new_readonly(*token_program, false),
216        ],
217    )
218}
219
220pub fn trade_ix(
221    program_id: &Pubkey,
222    market: &Pubkey,
223    authority: &Pubkey,
224    args: TradeArgs,
225) -> Instruction {
226    build_ix(
227        program_id,
228        "trade",
229        args,
230        vec![
231            AccountMeta::new_readonly(*authority, true),
232            AccountMeta::new(*market, false),
233        ],
234    )
235}
236
237pub fn crank_ix(
238    program_id: &Pubkey,
239    market: &Pubkey,
240    cranker: &Pubkey,
241    oracle: &Pubkey,
242    args: CrankArgs,
243) -> Instruction {
244    build_ix(
245        program_id,
246        "crank",
247        args,
248        vec![
249            AccountMeta::new_readonly(*cranker, true),
250            AccountMeta::new(*market, false),
251            AccountMeta::new_readonly(*oracle, false),
252        ],
253    )
254}
255
256pub fn liquidate_ix(
257    program_id: &Pubkey,
258    market: &Pubkey,
259    liquidator: &Pubkey,
260    args: LiquidateArgs,
261) -> Instruction {
262    build_ix(
263        program_id,
264        "liquidate",
265        args,
266        vec![
267            AccountMeta::new_readonly(*liquidator, true),
268            AccountMeta::new(*market, false),
269        ],
270    )
271}
272
273pub fn settle_ix(
274    program_id: &Pubkey,
275    market: &Pubkey,
276    user: &Pubkey,
277    args: SettleArgs,
278) -> Instruction {
279    build_ix(
280        program_id,
281        "settle",
282        args,
283        vec![
284            AccountMeta::new_readonly(*user, true),
285            AccountMeta::new(*market, false),
286        ],
287    )
288}
289
290pub fn close_account_ix(
291    program_id: &Pubkey,
292    market: &Pubkey,
293    user: &Pubkey,
294    args: CloseAccountArgs,
295) -> Instruction {
296    build_ix(
297        program_id,
298        "close_account",
299        args,
300        vec![
301            AccountMeta::new_readonly(*user, true),
302            AccountMeta::new(*market, false),
303        ],
304    )
305}
306
307pub fn reclaim_account_ix(
308    program_id: &Pubkey,
309    market: &Pubkey,
310    reclaimer: &Pubkey,
311    args: ReclaimAccountArgs,
312) -> Instruction {
313    build_ix(
314        program_id,
315        "reclaim_account",
316        args,
317        vec![
318            AccountMeta::new_readonly(*reclaimer, true),
319            AccountMeta::new(*market, false),
320        ],
321    )
322}
323
324pub fn withdraw_insurance_ix(
325    program_id: &Pubkey,
326    market: &Pubkey,
327    authority: &Pubkey,
328    mint: &Pubkey,
329    authority_token_account: &Pubkey,
330    vault: &Pubkey,
331    token_program: &Pubkey,
332    args: WithdrawInsuranceArgs,
333) -> Instruction {
334    build_ix(
335        program_id,
336        "withdraw_insurance",
337        args,
338        vec![
339            AccountMeta::new(*authority, true),
340            AccountMeta::new(*market, false),
341            AccountMeta::new_readonly(*mint, false),
342            AccountMeta::new(*authority_token_account, false),
343            AccountMeta::new(*vault, false),
344            AccountMeta::new_readonly(*token_program, false),
345        ],
346    )
347}
348
349pub fn top_up_insurance_ix(
350    program_id: &Pubkey,
351    market: &Pubkey,
352    depositor: &Pubkey,
353    mint: &Pubkey,
354    depositor_token_account: &Pubkey,
355    vault: &Pubkey,
356    token_program: &Pubkey,
357    args: TopUpInsuranceArgs,
358) -> Instruction {
359    build_ix(
360        program_id,
361        "top_up_insurance",
362        args,
363        vec![
364            AccountMeta::new(*depositor, true),
365            AccountMeta::new(*market, false),
366            AccountMeta::new_readonly(*mint, false),
367            AccountMeta::new(*depositor_token_account, false),
368            AccountMeta::new(*vault, false),
369            AccountMeta::new_readonly(*token_program, false),
370        ],
371    )
372}
373
374pub fn deposit_fee_credits_ix(
375    program_id: &Pubkey,
376    market: &Pubkey,
377    user: &Pubkey,
378    mint: &Pubkey,
379    user_token_account: &Pubkey,
380    vault: &Pubkey,
381    token_program: &Pubkey,
382    args: DepositFeeCreditsArgs,
383) -> Instruction {
384    build_ix(
385        program_id,
386        "deposit_fee_credits",
387        args,
388        vec![
389            AccountMeta::new(*user, true),
390            AccountMeta::new(*market, false),
391            AccountMeta::new_readonly(*mint, false),
392            AccountMeta::new(*user_token_account, false),
393            AccountMeta::new(*vault, false),
394            AccountMeta::new_readonly(*token_program, false),
395        ],
396    )
397}
398
399pub fn convert_released_pnl_ix(
400    program_id: &Pubkey,
401    market: &Pubkey,
402    user: &Pubkey,
403    oracle: &Pubkey,
404    args: ConvertReleasedPnlArgs,
405) -> Instruction {
406    build_ix(
407        program_id,
408        "convert_released_pnl",
409        args,
410        vec![
411            AccountMeta::new_readonly(*user, true),
412            AccountMeta::new(*market, false),
413            AccountMeta::new_readonly(*oracle, false),
414        ],
415    )
416}
417
418pub fn accrue_market_ix(
419    program_id: &Pubkey,
420    market: &Pubkey,
421    signer: &Pubkey,
422    oracle: &Pubkey,
423) -> Instruction {
424    build_ix(
425        program_id,
426        "accrue_market",
427        (),
428        vec![
429            AccountMeta::new_readonly(*signer, true),
430            AccountMeta::new(*market, false),
431            AccountMeta::new_readonly(*oracle, false),
432        ],
433    )
434}
435
436pub fn update_matcher_ix(
437    program_id: &Pubkey,
438    market: &Pubkey,
439    authority: &Pubkey,
440    args: UpdateMatcherArgs,
441) -> Instruction {
442    build_ix(
443        program_id,
444        "update_matcher",
445        args,
446        vec![
447            AccountMeta::new_readonly(*authority, true),
448            AccountMeta::new(*market, false),
449        ],
450    )
451}
452
453pub fn update_oracle_ix(
454    program_id: &Pubkey,
455    market: &Pubkey,
456    authority: &Pubkey,
457    new_oracle: &Pubkey,
458) -> Instruction {
459    build_ix(
460        program_id,
461        "update_oracle",
462        (),
463        vec![
464            AccountMeta::new_readonly(*authority, true),
465            AccountMeta::new(*market, false),
466            AccountMeta::new_readonly(*new_oracle, false),
467        ],
468    )
469}