percli_program/instructions/
initialize_market.rs1use anchor_lang::prelude::*;
2use anchor_spl::token::{Mint, Token, TokenAccount};
3use percli_core::RiskParams;
4
5use crate::error::PercolatorError;
6use crate::instructions::events;
7#[allow(unused_imports)]
8use crate::state::{engine_from_account_data, write_header, MarketHeader, MARKET_ACCOUNT_SIZE};
9
10#[derive(Accounts)]
11pub struct InitializeMarket<'info> {
12 #[account(mut)]
13 pub authority: Signer<'info>,
14
15 #[account(
20 mut,
21 owner = crate::ID @ PercolatorError::AccountNotFound,
22 constraint = market.data_len() >= MARKET_ACCOUNT_SIZE @ PercolatorError::AccountNotFound,
23 seeds = [b"market", authority.key().as_ref()],
24 bump,
25 )]
26 pub market: UncheckedAccount<'info>,
27
28 pub mint: Account<'info, Mint>,
30
31 #[account(
33 init,
34 payer = authority,
35 token::mint = mint,
36 token::authority = market,
37 seeds = [b"vault", market.key().as_ref()],
38 bump,
39 )]
40 pub vault: Account<'info, TokenAccount>,
41
42 pub oracle: UncheckedAccount<'info>,
44
45 pub matcher: UncheckedAccount<'info>,
48
49 pub token_program: Program<'info, Token>,
50 pub system_program: Program<'info, System>,
51}
52
53pub fn handler(
54 ctx: Context<InitializeMarket>,
55 init_slot: u64,
56 init_oracle_price: u64,
57 params: RiskParamsInput,
58) -> Result<()> {
59 let market = &ctx.accounts.market;
60 let mut data = market.try_borrow_mut_data()?;
61
62 require!(
64 data[0..8] == [0u8; 8],
65 PercolatorError::AccountNotFound
66 );
67
68 data[0..8].copy_from_slice(b"percmrkt");
70
71 let header = MarketHeader {
72 authority: ctx.accounts.authority.key(),
73 mint: ctx.accounts.mint.key(),
74 oracle: ctx.accounts.oracle.key(),
75 matcher: ctx.accounts.matcher.key(),
76 bump: ctx.bumps.market,
77 vault_bump: ctx.bumps.vault,
78 _padding: [0; 6],
79 };
80 write_header(&mut data, &header);
81
82 require!(init_oracle_price > 0, PercolatorError::InvalidOraclePriceValue);
83
84 let engine = engine_from_account_data(&mut data);
85 let risk_params = params.to_risk_params();
86 engine.init_in_place(risk_params, init_slot, init_oracle_price);
87
88 emit!(events::MarketInitialized {
89 authority: ctx.accounts.authority.key(),
90 mint: ctx.accounts.mint.key(),
91 oracle: ctx.accounts.oracle.key(),
92 matcher: ctx.accounts.matcher.key(),
93 init_slot,
94 init_oracle_price,
95 });
96
97 Ok(())
98}
99
100#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
102pub struct RiskParamsInput {
103 pub warmup_period_slots: u64,
104 pub maintenance_margin_bps: u64,
105 pub initial_margin_bps: u64,
106 pub trading_fee_bps: u64,
107 pub max_accounts: u64,
108 pub new_account_fee: u64,
109 pub maintenance_fee_per_slot: u64,
110 pub max_crank_staleness_slots: u64,
111 pub liquidation_fee_bps: u64,
112 pub liquidation_fee_cap: u64,
113 pub min_liquidation_abs: u64,
114 pub min_initial_deposit: u64,
115 pub min_nonzero_mm_req: u64,
116 pub min_nonzero_im_req: u64,
117 pub insurance_floor: u64,
118}
119
120impl RiskParamsInput {
121 pub fn to_risk_params(&self) -> RiskParams {
122 use percli_core::U128;
123 RiskParams {
124 warmup_period_slots: self.warmup_period_slots,
125 maintenance_margin_bps: self.maintenance_margin_bps,
126 initial_margin_bps: self.initial_margin_bps,
127 trading_fee_bps: self.trading_fee_bps,
128 max_accounts: self.max_accounts,
129 new_account_fee: U128::new(self.new_account_fee as u128),
130 maintenance_fee_per_slot: U128::new(self.maintenance_fee_per_slot as u128),
131 max_crank_staleness_slots: self.max_crank_staleness_slots,
132 liquidation_fee_bps: self.liquidation_fee_bps,
133 liquidation_fee_cap: U128::new(self.liquidation_fee_cap as u128),
134 min_liquidation_abs: U128::new(self.min_liquidation_abs as u128),
135 min_initial_deposit: U128::new(self.min_initial_deposit as u128),
136 min_nonzero_mm_req: self.min_nonzero_mm_req as u128,
137 min_nonzero_im_req: self.min_nonzero_im_req as u128,
138 insurance_floor: U128::new(self.insurance_floor as u128),
139 }
140 }
141}