Skip to main content

percli_program/instructions/
initialize_market.rs

1use anchor_lang::prelude::*;
2use anchor_spl::token::{Mint, Token, TokenAccount};
3use percli_core::RiskParams;
4
5use crate::error::PercolatorError;
6use crate::state::{engine_from_account_data, write_header, MarketHeader, MARKET_ACCOUNT_SIZE};
7
8#[derive(Accounts)]
9pub struct InitializeMarket<'info> {
10    #[account(mut)]
11    pub authority: Signer<'info>,
12
13    /// CHECK: Manually validated. Initialized as a PDA with the correct size.
14    #[account(
15        init,
16        payer = authority,
17        space = MARKET_ACCOUNT_SIZE,
18        seeds = [b"market", authority.key().as_ref()],
19        bump,
20    )]
21    pub market: UncheckedAccount<'info>,
22
23    /// The SPL token mint for this market's collateral (e.g. USDC).
24    pub mint: Account<'info, Mint>,
25
26    /// Vault token account — holds all deposited collateral for this market.
27    #[account(
28        init,
29        payer = authority,
30        token::mint = mint,
31        token::authority = market,
32        seeds = [b"vault", market.key().as_ref()],
33        bump,
34    )]
35    pub vault: Account<'info, TokenAccount>,
36
37    pub token_program: Program<'info, Token>,
38    pub system_program: Program<'info, System>,
39}
40
41pub fn handler(
42    ctx: Context<InitializeMarket>,
43    init_slot: u64,
44    init_oracle_price: u64,
45    params: RiskParamsInput,
46) -> Result<()> {
47    let market = &ctx.accounts.market;
48    let mut data = market.try_borrow_mut_data()?;
49
50    // Write discriminator (first 8 bytes) — use a fixed marker
51    data[0..8].copy_from_slice(b"percmrkt");
52
53    let header = MarketHeader {
54        authority: ctx.accounts.authority.key(),
55        mint: ctx.accounts.mint.key(),
56        bump: ctx.bumps.market,
57        vault_bump: ctx.bumps.vault,
58        _padding: [0; 6],
59    };
60    write_header(&mut data, &header);
61
62    require!(init_oracle_price > 0, PercolatorError::InvalidOraclePriceValue);
63
64    let engine = engine_from_account_data(&mut data);
65    let risk_params = params.to_risk_params();
66    engine.init_in_place(risk_params, init_slot, init_oracle_price);
67
68    Ok(())
69}
70
71/// Anchor-friendly input for RiskParams (all primitives, no wrapper types).
72#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
73pub struct RiskParamsInput {
74    pub warmup_period_slots: u64,
75    pub maintenance_margin_bps: u64,
76    pub initial_margin_bps: u64,
77    pub trading_fee_bps: u64,
78    pub max_accounts: u64,
79    pub new_account_fee: u64,
80    pub maintenance_fee_per_slot: u64,
81    pub max_crank_staleness_slots: u64,
82    pub liquidation_fee_bps: u64,
83    pub liquidation_fee_cap: u64,
84    pub min_liquidation_abs: u64,
85    pub min_initial_deposit: u64,
86    pub min_nonzero_mm_req: u64,
87    pub min_nonzero_im_req: u64,
88    pub insurance_floor: u64,
89}
90
91impl RiskParamsInput {
92    pub fn to_risk_params(&self) -> RiskParams {
93        use percli_core::U128;
94        RiskParams {
95            warmup_period_slots: self.warmup_period_slots,
96            maintenance_margin_bps: self.maintenance_margin_bps,
97            initial_margin_bps: self.initial_margin_bps,
98            trading_fee_bps: self.trading_fee_bps,
99            max_accounts: self.max_accounts,
100            new_account_fee: U128::new(self.new_account_fee as u128),
101            maintenance_fee_per_slot: U128::new(self.maintenance_fee_per_slot as u128),
102            max_crank_staleness_slots: self.max_crank_staleness_slots,
103            liquidation_fee_bps: self.liquidation_fee_bps,
104            liquidation_fee_cap: U128::new(self.liquidation_fee_cap as u128),
105            min_liquidation_abs: U128::new(self.min_liquidation_abs as u128),
106            min_initial_deposit: U128::new(self.min_initial_deposit as u128),
107            min_nonzero_mm_req: self.min_nonzero_mm_req as u128,
108            min_nonzero_im_req: self.min_nonzero_im_req as u128,
109            insurance_floor: U128::new(self.insurance_floor as u128),
110        }
111    }
112}