1use borsh::{BorshDeserialize, BorshSerialize};
2use bytemuck::{Pod, Zeroable};
3use sokoban::node_allocator::ZeroCopy;
4use solana_program::{keccak, program_error::ProgramError, pubkey::Pubkey};
5
6use crate::quantities::{
7 BaseAtomsPerBaseLot, QuoteAtomsPerBaseUnitPerTick, QuoteAtomsPerQuoteLot, WrapperU64,
8};
9
10use super::status::{MarketStatus, SeatApprovalStatus};
11
12pub fn get_discriminant<T>() -> Result<u64, ProgramError> {
20 let type_name = std::any::type_name::<T>();
21 let discriminant = u64::from_le_bytes(
22 keccak::hashv(&[crate::ID.as_ref(), type_name.as_bytes()]).as_ref()[..8]
23 .try_into()
24 .map_err(|_| {
25 phoenix_log!("Failed to convert discriminant hash to u64");
26 ProgramError::InvalidAccountData
27 })?,
28 );
29 phoenix_log!("Discriminant for {} is {}", type_name, discriminant);
30 Ok(discriminant)
31}
32
33#[derive(Default, Debug, Copy, Clone, BorshDeserialize, BorshSerialize, Zeroable, Pod)]
34#[repr(C)]
35pub struct MarketSizeParams {
36 pub bids_size: u64,
37 pub asks_size: u64,
38 pub num_seats: u64,
39}
40impl ZeroCopy for MarketSizeParams {}
41
42#[derive(Debug, Copy, Clone, BorshDeserialize, BorshSerialize, Zeroable, Pod)]
43#[repr(C)]
44pub struct TokenParams {
45 pub decimals: u32,
47
48 pub vault_bump: u32,
50
51 pub mint_key: Pubkey,
53
54 pub vault_key: Pubkey,
56}
57impl ZeroCopy for TokenParams {}
58
59#[derive(Debug, Clone, Copy, Zeroable, Pod)]
60#[repr(C)]
61pub struct MarketHeader {
62 pub discriminant: u64,
63 pub status: u64,
64 pub market_size_params: MarketSizeParams,
65 pub base_params: TokenParams,
66 base_lot_size: BaseAtomsPerBaseLot,
67 pub quote_params: TokenParams,
68 quote_lot_size: QuoteAtomsPerQuoteLot,
69 tick_size_in_quote_atoms_per_base_unit: QuoteAtomsPerBaseUnitPerTick,
70 pub authority: Pubkey,
71 pub fee_recipient: Pubkey,
72 pub market_sequence_number: u64,
73 pub successor: Pubkey,
74 pub raw_base_units_per_base_unit: u32,
75 _padding1: u32,
76 _padding2: [u64; 32],
77}
78impl ZeroCopy for MarketHeader {}
79
80impl MarketHeader {
81 #[allow(clippy::too_many_arguments)]
82 pub fn new(
83 market_size_params: MarketSizeParams,
84 base_params: TokenParams,
85 base_lot_size: BaseAtomsPerBaseLot,
86 quote_params: TokenParams,
87 quote_lot_size: QuoteAtomsPerQuoteLot,
88 tick_size_in_quote_atoms_per_base_unit: QuoteAtomsPerBaseUnitPerTick,
89 authority: Pubkey,
90 successor: Pubkey,
91 fee_recipient: Pubkey,
92 raw_base_units_per_base_unit: u32,
93 ) -> Self {
94 Self {
95 discriminant: get_discriminant::<MarketHeader>().unwrap(),
96 status: MarketStatus::PostOnly as u64,
97 market_size_params,
98 base_params,
99 base_lot_size,
100 quote_params,
101 quote_lot_size,
102 tick_size_in_quote_atoms_per_base_unit,
103 authority,
104 fee_recipient,
105 market_sequence_number: 0,
106 successor,
107 raw_base_units_per_base_unit,
108 _padding1: 0,
109 _padding2: [0; 32],
110 }
111 }
112
113 pub fn price_in_ticks(&self, price: u64) -> u64 {
115 price / self.tick_size_in_quote_atoms_per_base_unit.as_u64()
116 }
117
118 pub fn get_base_lot_size(&self) -> BaseAtomsPerBaseLot {
119 self.base_lot_size
120 }
121
122 pub fn get_quote_lot_size(&self) -> QuoteAtomsPerQuoteLot {
123 self.quote_lot_size
124 }
125
126 pub fn get_tick_size_in_quote_atoms_per_base_unit(&self) -> QuoteAtomsPerBaseUnitPerTick {
127 self.tick_size_in_quote_atoms_per_base_unit
128 }
129
130 pub fn increment_sequence_number(&mut self) {
131 self.market_sequence_number += 1;
132 }
133}
134
135#[derive(Debug, Clone, Copy, BorshDeserialize, BorshSerialize, Zeroable, Pod)]
140#[repr(C)]
141pub struct Seat {
142 pub discriminant: u64,
143 pub market: Pubkey,
144 pub trader: Pubkey,
145 pub approval_status: u64,
146 _padding: [u64; 6],
148}
149
150impl ZeroCopy for Seat {}
151
152impl Seat {
153 pub fn new_init(market: Pubkey, trader: Pubkey) -> Result<Self, ProgramError> {
154 Ok(Self {
155 discriminant: get_discriminant::<Seat>()?,
156 market,
157 trader,
158 approval_status: SeatApprovalStatus::NotApproved as u64,
159 _padding: [0; 6],
160 })
161 }
162}
163
164#[test]
166fn test_valid_discriminants() {
167 assert_eq!(
168 std::any::type_name::<MarketHeader>(),
169 "phoenix::program::accounts::MarketHeader"
170 );
171 assert_eq!(
172 std::any::type_name::<Seat>(),
173 "phoenix::program::accounts::Seat"
174 );
175 assert_eq!(
176 get_discriminant::<MarketHeader>().unwrap(),
177 8167313896524341111
178 );
179 assert_eq!(get_discriminant::<Seat>().unwrap(), 2002603505298356104);
180}