1use solana_sdk::pubkey::Pubkey;
7
8use crate::program::constants::{
9 EXCHANGE_DISCRIMINATOR, EXCHANGE_SIZE, MARKET_DISCRIMINATOR, MARKET_SIZE,
10 ORDER_STATUS_DISCRIMINATOR, ORDER_STATUS_SIZE, POSITION_DISCRIMINATOR, POSITION_SIZE,
11 USER_NONCE_DISCRIMINATOR, USER_NONCE_SIZE,
12};
13use crate::program::error::{SdkError, SdkResult};
14use crate::program::types::MarketStatus;
15
16#[inline]
18fn read_bytes<const N: usize>(data: &[u8], offset: usize) -> [u8; N] {
19 let mut arr = [0u8; N];
20 arr.copy_from_slice(&data[offset..offset + N]);
21 arr
22}
23
24#[inline]
26fn read_pubkey(data: &[u8], offset: usize) -> Pubkey {
27 Pubkey::new_from_array(read_bytes::<32>(data, offset))
28}
29
30#[inline]
32fn read_u64(data: &[u8], offset: usize) -> u64 {
33 u64::from_le_bytes(read_bytes::<8>(data, offset))
34}
35
36#[derive(Debug, Clone)]
51pub struct Exchange {
52 pub discriminator: [u8; 8],
54 pub authority: Pubkey,
56 pub operator: Pubkey,
58 pub market_count: u64,
60 pub paused: bool,
62 pub bump: u8,
64}
65
66impl Exchange {
67 pub const LEN: usize = EXCHANGE_SIZE;
69
70 pub fn deserialize(data: &[u8]) -> SdkResult<Self> {
72 if data.len() < Self::LEN {
73 return Err(SdkError::InvalidDataLength {
74 expected: Self::LEN,
75 actual: data.len(),
76 });
77 }
78
79 let discriminator = read_bytes::<8>(data, 0);
80 if discriminator != EXCHANGE_DISCRIMINATOR {
81 return Err(SdkError::InvalidDiscriminator {
82 expected: String::from_utf8_lossy(&EXCHANGE_DISCRIMINATOR).to_string(),
83 actual: String::from_utf8_lossy(&discriminator).to_string(),
84 });
85 }
86
87 Ok(Self {
88 discriminator,
89 authority: read_pubkey(data, 8),
90 operator: read_pubkey(data, 40),
91 market_count: read_u64(data, 72),
92 paused: data[80] != 0,
93 bump: data[81],
94 })
95 }
96
97 pub fn is_exchange_account(data: &[u8]) -> bool {
99 data.len() >= 8 && data[0..8] == EXCHANGE_DISCRIMINATOR
100 }
101}
102
103#[derive(Debug, Clone)]
122pub struct Market {
123 pub discriminator: [u8; 8],
125 pub market_id: u64,
127 pub num_outcomes: u8,
129 pub status: MarketStatus,
131 pub winning_outcome: u8,
133 pub has_winning_outcome: bool,
135 pub bump: u8,
137 pub oracle: Pubkey,
139 pub question_id: [u8; 32],
141 pub condition_id: [u8; 32],
143}
144
145impl Market {
146 pub const LEN: usize = MARKET_SIZE;
148
149 pub fn deserialize(data: &[u8]) -> SdkResult<Self> {
151 if data.len() < Self::LEN {
152 return Err(SdkError::InvalidDataLength {
153 expected: Self::LEN,
154 actual: data.len(),
155 });
156 }
157
158 let discriminator = read_bytes::<8>(data, 0);
159 if discriminator != MARKET_DISCRIMINATOR {
160 return Err(SdkError::InvalidDiscriminator {
161 expected: String::from_utf8_lossy(&MARKET_DISCRIMINATOR).to_string(),
162 actual: String::from_utf8_lossy(&discriminator).to_string(),
163 });
164 }
165
166 Ok(Self {
167 discriminator,
168 market_id: read_u64(data, 8),
169 num_outcomes: data[16],
170 status: MarketStatus::try_from(data[17])?,
171 winning_outcome: data[18],
172 has_winning_outcome: data[19] != 0,
173 bump: data[20],
174 oracle: read_pubkey(data, 24),
175 question_id: read_bytes::<32>(data, 56),
176 condition_id: read_bytes::<32>(data, 88),
177 })
178 }
179
180 pub fn is_market_account(data: &[u8]) -> bool {
182 data.len() >= 8 && data[0..8] == MARKET_DISCRIMINATOR
183 }
184}
185
186#[derive(Debug, Clone)]
199pub struct Position {
200 pub discriminator: [u8; 8],
202 pub owner: Pubkey,
204 pub market: Pubkey,
206 pub bump: u8,
208}
209
210impl Position {
211 pub const LEN: usize = POSITION_SIZE;
213
214 pub fn deserialize(data: &[u8]) -> SdkResult<Self> {
216 if data.len() < Self::LEN {
217 return Err(SdkError::InvalidDataLength {
218 expected: Self::LEN,
219 actual: data.len(),
220 });
221 }
222
223 let discriminator = read_bytes::<8>(data, 0);
224 if discriminator != POSITION_DISCRIMINATOR {
225 return Err(SdkError::InvalidDiscriminator {
226 expected: String::from_utf8_lossy(&POSITION_DISCRIMINATOR).to_string(),
227 actual: String::from_utf8_lossy(&discriminator).to_string(),
228 });
229 }
230
231 Ok(Self {
232 discriminator,
233 owner: read_pubkey(data, 8),
234 market: read_pubkey(data, 40),
235 bump: data[72],
236 })
237 }
238
239 pub fn is_position_account(data: &[u8]) -> bool {
241 data.len() >= 8 && data[0..8] == POSITION_DISCRIMINATOR
242 }
243}
244
245#[derive(Debug, Clone)]
257pub struct OrderStatus {
258 pub discriminator: [u8; 8],
260 pub remaining: u64,
262 pub is_cancelled: bool,
264}
265
266impl OrderStatus {
267 pub const LEN: usize = ORDER_STATUS_SIZE;
269
270 pub fn deserialize(data: &[u8]) -> SdkResult<Self> {
272 if data.len() < Self::LEN {
273 return Err(SdkError::InvalidDataLength {
274 expected: Self::LEN,
275 actual: data.len(),
276 });
277 }
278
279 let discriminator = read_bytes::<8>(data, 0);
280 if discriminator != ORDER_STATUS_DISCRIMINATOR {
281 return Err(SdkError::InvalidDiscriminator {
282 expected: String::from_utf8_lossy(&ORDER_STATUS_DISCRIMINATOR).to_string(),
283 actual: String::from_utf8_lossy(&discriminator).to_string(),
284 });
285 }
286
287 Ok(Self {
288 discriminator,
289 remaining: read_u64(data, 8),
290 is_cancelled: data[16] != 0,
291 })
292 }
293
294 pub fn is_order_status_account(data: &[u8]) -> bool {
296 data.len() >= 8 && data[0..8] == ORDER_STATUS_DISCRIMINATOR
297 }
298}
299
300#[derive(Debug, Clone)]
310pub struct UserNonce {
311 pub discriminator: [u8; 8],
313 pub nonce: u64,
315}
316
317impl UserNonce {
318 pub const LEN: usize = USER_NONCE_SIZE;
320
321 pub fn deserialize(data: &[u8]) -> SdkResult<Self> {
323 if data.len() < Self::LEN {
324 return Err(SdkError::InvalidDataLength {
325 expected: Self::LEN,
326 actual: data.len(),
327 });
328 }
329
330 let discriminator = read_bytes::<8>(data, 0);
331 if discriminator != USER_NONCE_DISCRIMINATOR {
332 return Err(SdkError::InvalidDiscriminator {
333 expected: String::from_utf8_lossy(&USER_NONCE_DISCRIMINATOR).to_string(),
334 actual: String::from_utf8_lossy(&discriminator).to_string(),
335 });
336 }
337
338 Ok(Self {
339 discriminator,
340 nonce: read_u64(data, 8),
341 })
342 }
343
344 pub fn is_user_nonce_account(data: &[u8]) -> bool {
346 data.len() >= 8 && data[0..8] == USER_NONCE_DISCRIMINATOR
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353
354 #[test]
355 fn test_exchange_deserialization() {
356 let mut data = vec![0u8; EXCHANGE_SIZE];
357 data[0..8].copy_from_slice(&EXCHANGE_DISCRIMINATOR);
358 data[8..40].copy_from_slice(&[1u8; 32]);
360 data[40..72].copy_from_slice(&[2u8; 32]);
362 data[72..80].copy_from_slice(&5u64.to_le_bytes());
364 data[80] = 0;
366 data[81] = 255;
368
369 let exchange = Exchange::deserialize(&data).unwrap();
370 assert_eq!(exchange.market_count, 5);
371 assert!(!exchange.paused);
372 assert_eq!(exchange.bump, 255);
373 }
374
375 #[test]
376 fn test_market_deserialization() {
377 let mut data = vec![0u8; MARKET_SIZE];
378 data[0..8].copy_from_slice(&MARKET_DISCRIMINATOR);
379 data[8..16].copy_from_slice(&42u64.to_le_bytes());
381 data[16] = 3;
383 data[17] = 1; data[18] = 255;
387 data[19] = 0;
389 data[20] = 254;
391
392 let market = Market::deserialize(&data).unwrap();
393 assert_eq!(market.market_id, 42);
394 assert_eq!(market.num_outcomes, 3);
395 assert_eq!(market.status, MarketStatus::Active);
396 assert_eq!(market.winning_outcome, 255);
397 assert!(!market.has_winning_outcome);
398 }
399
400 #[test]
401 fn test_position_deserialization() {
402 let mut data = vec![0u8; POSITION_SIZE];
403 data[0..8].copy_from_slice(&POSITION_DISCRIMINATOR);
404 data[8..40].copy_from_slice(&[1u8; 32]);
406 data[40..72].copy_from_slice(&[2u8; 32]);
408 data[72] = 253;
410
411 let position = Position::deserialize(&data).unwrap();
412 assert_eq!(position.bump, 253);
413 }
414
415 #[test]
416 fn test_order_status_deserialization() {
417 let mut data = vec![0u8; ORDER_STATUS_SIZE];
418 data[0..8].copy_from_slice(&ORDER_STATUS_DISCRIMINATOR);
419 data[8..16].copy_from_slice(&1000u64.to_le_bytes());
421 data[16] = 0;
423
424 let order_status = OrderStatus::deserialize(&data).unwrap();
425 assert_eq!(order_status.remaining, 1000);
426 assert!(!order_status.is_cancelled);
427 }
428
429 #[test]
430 fn test_user_nonce_deserialization() {
431 let mut data = vec![0u8; USER_NONCE_SIZE];
432 data[0..8].copy_from_slice(&USER_NONCE_DISCRIMINATOR);
433 data[8..16].copy_from_slice(&99u64.to_le_bytes());
435
436 let user_nonce = UserNonce::deserialize(&data).unwrap();
437 assert_eq!(user_nonce.nonce, 99);
438 }
439}