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..7].copy_from_slice(b"percmrk");
72 data[7] = 0x01;
73
74 let header = MarketHeader {
75 authority: ctx.accounts.authority.key(),
76 mint: ctx.accounts.mint.key(),
77 oracle: ctx.accounts.oracle.key(),
78 matcher: ctx.accounts.matcher.key(),
79 pending_authority: Pubkey::default(),
80 bump: ctx.bumps.market,
81 vault_bump: ctx.bumps.vault,
82 _padding: [0; 6],
83 };
84 write_header(&mut data, &header);
85
86 require!(init_oracle_price > 0, PercolatorError::InvalidOraclePriceValue);
87
88 let engine = engine_from_account_data(&mut data);
89 let risk_params = params.to_risk_params();
90 engine.init_in_place(risk_params, init_slot, init_oracle_price);
91
92 emit!(events::MarketInitialized {
93 authority: ctx.accounts.authority.key(),
94 mint: ctx.accounts.mint.key(),
95 oracle: ctx.accounts.oracle.key(),
96 matcher: ctx.accounts.matcher.key(),
97 init_slot,
98 init_oracle_price,
99 });
100
101 Ok(())
102}
103
104#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
106pub struct RiskParamsInput {
107 pub warmup_period_slots: u64,
108 pub maintenance_margin_bps: u64,
109 pub initial_margin_bps: u64,
110 pub trading_fee_bps: u64,
111 pub max_accounts: u64,
112 pub new_account_fee: u64,
113 pub maintenance_fee_per_slot: u64,
114 pub max_crank_staleness_slots: u64,
115 pub liquidation_fee_bps: u64,
116 pub liquidation_fee_cap: u64,
117 pub min_liquidation_abs: u64,
118 pub min_initial_deposit: u64,
119 pub min_nonzero_mm_req: u64,
120 pub min_nonzero_im_req: u64,
121 pub insurance_floor: u64,
122}
123
124impl RiskParamsInput {
125 pub fn to_risk_params(&self) -> RiskParams {
126 use percli_core::U128;
127 RiskParams {
128 warmup_period_slots: self.warmup_period_slots,
129 maintenance_margin_bps: self.maintenance_margin_bps,
130 initial_margin_bps: self.initial_margin_bps,
131 trading_fee_bps: self.trading_fee_bps,
132 max_accounts: self.max_accounts,
133 new_account_fee: U128::new(self.new_account_fee as u128),
134 maintenance_fee_per_slot: U128::new(self.maintenance_fee_per_slot as u128),
135 max_crank_staleness_slots: self.max_crank_staleness_slots,
136 liquidation_fee_bps: self.liquidation_fee_bps,
137 liquidation_fee_cap: U128::new(self.liquidation_fee_cap as u128),
138 min_liquidation_abs: U128::new(self.min_liquidation_abs as u128),
139 min_initial_deposit: U128::new(self.min_initial_deposit as u128),
140 min_nonzero_mm_req: self.min_nonzero_mm_req as u128,
141 min_nonzero_im_req: self.min_nonzero_im_req as u128,
142 insurance_floor: U128::new(self.insurance_floor as u128),
143 }
144 }
145}