phoenix/program/processor/
initialize.rs1use crate::{
2 program::{
3 dispatch_market::load_with_dispatch_init,
4 error::{assert_with_msg, PhoenixError},
5 loaders::{get_vault_address, InitializeMarketContext},
6 system_utils::create_account,
7 MarketHeader, MarketSizeParams, PhoenixMarketContext, TokenParams,
8 },
9 quantities::{
10 BaseAtomsPerBaseUnit, BaseLotsPerBaseUnit, QuoteAtomsPerQuoteUnit,
11 QuoteLotsPerBaseUnitPerTick, QuoteLotsPerQuoteUnit, WrapperU64,
12 },
13};
14use borsh::{BorshDeserialize, BorshSerialize};
15use solana_program::{
16 account_info::AccountInfo, entrypoint::ProgramResult, program::invoke,
17 program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, rent::Rent, sysvar::Sysvar,
18};
19use std::{mem::size_of, ops::DerefMut};
20
21#[derive(BorshDeserialize, BorshSerialize)]
22pub struct InitializeParams {
23 pub market_size_params: MarketSizeParams,
26
27 pub num_quote_lots_per_quote_unit: u64,
36
37 pub tick_size_in_quote_lots_per_base_unit: u64,
43
44 pub num_base_lots_per_base_unit: u64,
52
53 pub taker_fee_bps: u16,
55
56 pub fee_collector: Pubkey,
58
59 pub raw_base_units_per_base_unit: Option<u32>,
66}
67
68pub(crate) fn process_initialize_market<'a, 'info>(
69 _program_id: &Pubkey,
70 market_context: &PhoenixMarketContext<'a, 'info>,
71 accounts: &'a [AccountInfo<'info>],
72 data: &[u8],
73) -> ProgramResult {
74 let PhoenixMarketContext {
75 market_info,
76 signer: market_creator,
77 } = market_context;
78 let InitializeMarketContext {
79 base_mint,
80 quote_mint,
81 base_vault,
82 quote_vault,
83 system_program,
84 token_program,
85 ..
86 } = InitializeMarketContext::load(accounts)?;
87
88 let InitializeParams {
89 market_size_params,
90 tick_size_in_quote_lots_per_base_unit,
91 num_quote_lots_per_quote_unit,
92 num_base_lots_per_base_unit,
93 taker_fee_bps,
94 fee_collector,
95 raw_base_units_per_base_unit,
96 } = InitializeParams::try_from_slice(data)?;
97
98 let tick_size_in_quote_lots_per_base_unit =
99 QuoteLotsPerBaseUnitPerTick::new(tick_size_in_quote_lots_per_base_unit);
100 let num_quote_lots_per_quote_unit = QuoteLotsPerQuoteUnit::new(num_quote_lots_per_quote_unit);
101 let num_base_lots_per_base_unit = BaseLotsPerBaseUnit::new(num_base_lots_per_base_unit);
102 assert_with_msg(
103 taker_fee_bps <= 10000,
104 ProgramError::InvalidInstructionData,
105 "Taker fee must be less than or equal to 10000 basis points (100%)",
106 )?;
107
108 let base_atoms_per_base_unit = BaseAtomsPerBaseUnit::new(
109 10u64.pow(base_mint.decimals as u32) * raw_base_units_per_base_unit.unwrap_or(1) as u64,
110 );
111 let quote_atoms_per_quote_unit =
112 QuoteAtomsPerQuoteUnit::new(10u64.pow(quote_mint.decimals as u32));
113
114 assert_with_msg(
115 base_atoms_per_base_unit % num_base_lots_per_base_unit == 0,
116 PhoenixError::InvalidLotSize,
117 &format!(
118 "Base lots per base unit ({}) must be a factor of base atoms per base unit ({})",
119 num_base_lots_per_base_unit, base_atoms_per_base_unit
120 ),
121 )?;
122 assert_with_msg(
123 quote_atoms_per_quote_unit % num_quote_lots_per_quote_unit == 0,
124 PhoenixError::InvalidLotSize,
125 &format!(
126 "Quote lots per quote unit ({}) must be a factor of quote atoms per quote unit ({})",
127 num_quote_lots_per_quote_unit, quote_atoms_per_quote_unit
128 ),
129 )?;
130
131 let quote_lot_size = quote_atoms_per_quote_unit / num_quote_lots_per_quote_unit;
132 let tick_size_in_quote_atoms_per_base_unit =
133 quote_lot_size * tick_size_in_quote_lots_per_base_unit;
134
135 phoenix_log!(
136 "Market parameters:
137 num_quote_lots_per_quote_unit: {},
138 tick_size_in_quote_lots_per_base_unit: {},
139 num_base_lots_per_base_unit: {},
140 tick_size_in_quote_atoms_per_base_unit: {},",
141 num_quote_lots_per_quote_unit,
142 tick_size_in_quote_lots_per_base_unit,
143 num_base_lots_per_base_unit,
144 tick_size_in_quote_atoms_per_base_unit,
145 );
146 assert_with_msg(
152 tick_size_in_quote_lots_per_base_unit % num_base_lots_per_base_unit == 0,
153 ProgramError::InvalidInstructionData,
154 "The number of quote lots per tick be a multiple of the number of base lots per base unit",
155 )?;
156
157 let rent = Rent::get()?;
159 let mut bumps = vec![];
160 for (token_account, mint) in [
161 (base_vault.as_ref(), base_mint.as_ref()),
162 (quote_vault.as_ref(), quote_mint.as_ref()),
163 ] {
164 let (vault_key, bump) = get_vault_address(market_info.key, mint.key);
165 assert_with_msg(
166 vault_key == *token_account.key,
167 PhoenixError::InvalidMarketSigner,
168 &format!(
169 "Supplied vault ({}) does not match computed key ({})",
170 token_account.key, vault_key
171 ),
172 )?;
173 let space = spl_token::state::Account::LEN;
174 let seeds = vec![
175 b"vault".to_vec(),
176 market_info.key.as_ref().to_vec(),
177 mint.key.as_ref().to_vec(),
178 vec![bump],
179 ];
180 create_account(
181 market_creator.as_ref(),
182 token_account,
183 system_program.as_ref(),
184 &spl_token::id(),
185 &rent,
186 space as u64,
187 seeds,
188 )?;
189 invoke(
190 &spl_token::instruction::initialize_account3(
191 &spl_token::id(),
192 token_account.key,
193 mint.key,
194 token_account.key,
195 )?,
196 &[
197 market_creator.as_ref().clone(),
198 token_account.clone(),
199 mint.clone(),
200 token_program.as_ref().clone(),
201 ],
202 )?;
203 bumps.push(bump);
204 }
205
206 {
208 let market_bytes = &mut market_info.try_borrow_mut_data()?[size_of::<MarketHeader>()..];
209 let market = load_with_dispatch_init(&market_size_params, market_bytes)?.inner;
210 assert_with_msg(
211 market.get_sequence_number() == 0,
212 PhoenixError::MarketAlreadyInitialized,
213 "Market must have a sequence number of 0",
214 )?;
215
216 market.initialize_with_params(
217 tick_size_in_quote_lots_per_base_unit,
218 num_base_lots_per_base_unit,
219 );
220 market.set_fee(taker_fee_bps as u64);
221 }
222
223 let mut header = market_info.get_header_mut()?;
225 *header.deref_mut() = MarketHeader::new(
227 market_size_params,
228 TokenParams {
229 vault_bump: bumps[0] as u32,
230 decimals: base_mint.decimals as u32,
231 mint_key: *base_mint.as_ref().key,
232 vault_key: *base_vault.key,
233 },
234 base_atoms_per_base_unit / num_base_lots_per_base_unit,
235 TokenParams {
236 vault_bump: bumps[1] as u32,
237 decimals: quote_mint.decimals as u32,
238 mint_key: *quote_mint.as_ref().key,
239 vault_key: *quote_vault.key,
240 },
241 quote_lot_size,
242 tick_size_in_quote_atoms_per_base_unit,
243 *market_creator.key,
244 *market_creator.key,
245 fee_collector,
246 raw_base_units_per_base_unit.unwrap_or(1),
247 );
248
249 drop(header);
250 Ok(())
251}