1use anchor_lang::prelude::*;
2use borsh::{BorshDeserialize, BorshSerialize};
3use bytemuck::Pod;
4use num_traits::{One, Zero};
5use solana_program::msg;
6use std::{cell::{Ref, RefMut}, marker::PhantomData, panic::Location};
7use crate::errors::ReflectErrorCodes;
8
9pub trait Size {
10 const SIZE: usize;
11}
12
13pub trait SpotBalance {
14 fn market_index(&self) -> u16;
15 fn balance(&self) -> u128;
16 fn balance_type(&self) -> &SpotBalanceType;
17}
18
19impl SpotBalance for PoolBalance {
20 fn market_index(&self) -> u16 {
21 self.market_index
22 }
23
24 fn balance_type(&self) -> &SpotBalanceType {
25 &SpotBalanceType::Deposit
26 }
27
28 fn balance(&self) -> u128 {
29 self.scaled_balance
30 }
31}
32
33#[zero_copy(unsafe)]
34#[derive(Default, Eq, PartialEq, Debug, AnchorSerialize, AnchorDeserialize,)]
35#[repr(C)]
36pub struct InsuranceFund {
37 pub vault: Pubkey,
38 pub total_shares: u128,
39 pub user_shares: u128,
40 pub shares_base: u128, pub unstaking_period: i64, pub last_revenue_settle_ts: i64,
43 pub revenue_settle_period: i64,
44 pub total_factor: u32, pub user_factor: u32, }
47
48#[zero_copy(unsafe)]
49#[derive(Default, Eq, PartialEq, Debug, AnchorDeserialize, AnchorSerialize)]
50#[repr(C)]
51pub struct SpotPosition {
52 pub scaled_balance: u64,
56 pub open_bids: i64,
59 pub open_asks: i64,
62 pub cumulative_deposits: i64,
65 pub market_index: u16,
67 pub balance_type: SpotBalanceType,
69 pub open_orders: u8,
71 pub padding: [u8; 4],
72}
73
74#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Default)]
75pub enum SpotBalanceType {
76 #[default]
77 Deposit,
78 Borrow,
79}
80
81#[derive(Default, Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq, PartialOrd, Ord)]
82pub enum AssetTier {
83 Collateral,
85 Protected,
87 Cross,
89 Isolated,
91 #[default]
93 Unlisted,
94}
95
96#[derive(Default, AnchorSerialize, AnchorDeserialize, Clone, Copy, Eq, PartialEq, Debug)]
97#[repr(C)]
98pub struct HistoricalIndexData {
99 pub last_index_bid_price: u64,
101 pub last_index_ask_price: u64,
103 pub last_index_price_twap: u64,
105 pub last_index_price_twap_5min: u64,
107 pub last_index_price_twap_ts: i64,
109}
110
111pub const SPOT_MARKET_ACCOUNT_DISCM: [u8; 8] = [100, 177, 8, 107, 168, 65, 65, 39];
112
113#[zero_copy(unsafe)]
114#[derive(PartialEq, Eq, Debug)]
115#[repr(C)]
116pub struct SpotMarket {
117 pub pubkey: Pubkey,
119 pub oracle: Pubkey,
121 pub mint: Pubkey,
123 pub vault: Pubkey,
126 pub name: [u8; 32],
128 pub historical_oracle_data: HistoricalOracleData,
129 pub historical_index_data: HistoricalIndexData,
130 pub revenue_pool: PoolBalance, pub spot_fee_pool: PoolBalance,
136 pub insurance_fund: InsuranceFund,
139 pub total_spot_fee: u128,
142 pub deposit_balance: u128,
146 pub borrow_balance: u128,
150 pub cumulative_deposit_interest: u128,
154 pub cumulative_borrow_interest: u128,
158 pub total_social_loss: u128,
161 pub total_quote_social_loss: u128,
164 pub withdraw_guard_threshold: u64,
167 pub max_token_deposits: u64,
171 pub deposit_token_twap: u64,
174 pub borrow_token_twap: u64,
177 pub utilization_twap: u64,
181 pub last_interest_ts: u64,
183 pub last_twap_ts: u64,
185 pub expiry_ts: i64,
187 pub order_step_size: u64,
190 pub order_tick_size: u64,
193 pub min_order_size: u64,
196 pub max_position_size: u64,
200 pub next_fill_record_id: u64,
202 pub next_deposit_record_id: u64,
204 pub initial_asset_weight: u32,
208 pub maintenance_asset_weight: u32,
212 pub initial_liability_weight: u32,
216 pub maintenance_liability_weight: u32,
220 pub imf_factor: u32,
223 pub liquidator_fee: u32,
226 pub if_liquidation_fee: u32,
229 pub optimal_utilization: u32,
233 pub optimal_borrow_rate: u32,
236 pub max_borrow_rate: u32,
239 pub decimals: u32,
241 pub market_index: u16,
242 pub orders_enabled: bool,
244 pub oracle_source: OracleSource,
245 pub status: MarketStatus,
246 pub asset_tier: AssetTier,
248 pub paused_operations: u8,
249 pub if_paused_operations: u8,
250 pub fee_adjustment: i16,
251 pub max_token_borrows_fraction: u16,
255 pub flash_loan_amount: u64,
258 pub flash_loan_initial_token_amount: u64,
262 pub total_swap_fee: u64,
265 pub scale_initial_asset_weight_start: u64,
269 pub min_borrow_rate: u8,
273 pub fuel_boost_deposits: u8,
276 pub fuel_boost_borrows: u8,
279 pub fuel_boost_taker: u8,
282 pub fuel_boost_maker: u8,
285 pub fuel_boost_insurance: u8,
288 pub token_program_flag: u8,
289 pub pool_id: u8,
290 pub padding: [u8; 40],
291}
292
293impl SpotMarket {
294 pub fn deserialize(buf: &mut &[u8]) -> Result<Self> {
295 use std::io::Read;
296
297 let mut spot_market = SpotMarket::default();
298
299 let mut pubkey_bytes = [0u8; 32];
301 buf.read_exact(&mut pubkey_bytes)?;
302 spot_market.pubkey = Pubkey::from(pubkey_bytes);
303
304 let mut oracle_bytes = [0u8; 32];
305 buf.read_exact(&mut oracle_bytes)?;
306 spot_market.oracle = Pubkey::from(oracle_bytes);
307
308 let mut mint_bytes = [0u8; 32];
309 buf.read_exact(&mut mint_bytes)?;
310 spot_market.mint = Pubkey::from(mint_bytes);
311
312 let mut vault_bytes = [0u8; 32];
313 buf.read_exact(&mut vault_bytes)?;
314 spot_market.vault = Pubkey::from(vault_bytes);
315
316 buf.read_exact(&mut spot_market.name)?;
317
318
319 spot_market.historical_oracle_data = HistoricalOracleData::deserialize(buf)?;
320 spot_market.historical_index_data = HistoricalIndexData::deserialize(buf)?;
321 spot_market.revenue_pool = PoolBalance::deserialize(buf)?;
322 spot_market.spot_fee_pool = PoolBalance::deserialize(buf)?;
323 spot_market.insurance_fund = InsuranceFund::deserialize(buf)?;
324
325 spot_market.total_spot_fee = u128::deserialize(buf)?;
326 spot_market.deposit_balance = u128::deserialize(buf)?;
327 spot_market.borrow_balance = u128::deserialize(buf)?;
328 spot_market.cumulative_deposit_interest = u128::deserialize(buf)?;
329 spot_market.cumulative_borrow_interest = u128::deserialize(buf)?;
330 spot_market.total_social_loss = u128::deserialize(buf)?;
331 spot_market.total_quote_social_loss = u128::deserialize(buf)?;
332
333 spot_market.withdraw_guard_threshold = u64::deserialize(buf)?;
334 spot_market.max_token_deposits = u64::deserialize(buf)?;
335 spot_market.deposit_token_twap = u64::deserialize(buf)?;
336 spot_market.borrow_token_twap = u64::deserialize(buf)?;
337 spot_market.utilization_twap = u64::deserialize(buf)?;
338 spot_market.last_interest_ts = u64::deserialize(buf)?;
339 spot_market.last_twap_ts = u64::deserialize(buf)?;
340 spot_market.expiry_ts = i64::deserialize(buf)?;
341 spot_market.order_step_size = u64::deserialize(buf)?;
342 spot_market.order_tick_size = u64::deserialize(buf)?;
343 spot_market.min_order_size = u64::deserialize(buf)?;
344 spot_market.max_position_size = u64::deserialize(buf)?;
345 spot_market.next_fill_record_id = u64::deserialize(buf)?;
346 spot_market.next_deposit_record_id = u64::deserialize(buf)?;
347
348 spot_market.initial_asset_weight = u32::deserialize(buf)?;
349 spot_market.maintenance_asset_weight = u32::deserialize(buf)?;
350 spot_market.initial_liability_weight = u32::deserialize(buf)?;
351 spot_market.maintenance_liability_weight = u32::deserialize(buf)?;
352 spot_market.imf_factor = u32::deserialize(buf)?;
353 spot_market.liquidator_fee = u32::deserialize(buf)?;
354 spot_market.if_liquidation_fee = u32::deserialize(buf)?;
355 spot_market.optimal_utilization = u32::deserialize(buf)?;
356 spot_market.optimal_borrow_rate = u32::deserialize(buf)?;
357 spot_market.max_borrow_rate = u32::deserialize(buf)?;
358 spot_market.decimals = u32::deserialize(buf)?;
359
360 spot_market.market_index = u16::deserialize(buf)?;
361 spot_market.orders_enabled = bool::deserialize(buf)?;
362 spot_market.oracle_source = OracleSource::deserialize(buf)?;
363 spot_market.status = MarketStatus::deserialize(buf)?;
364 spot_market.asset_tier = AssetTier::deserialize(buf)?;
365 spot_market.paused_operations = u8::deserialize(buf)?;
366 spot_market.if_paused_operations = u8::deserialize(buf)?;
367 spot_market.fee_adjustment = i16::deserialize(buf)?;
368 spot_market.max_token_borrows_fraction = u16::deserialize(buf)?;
369
370 spot_market.flash_loan_amount = u64::deserialize(buf)?;
371 spot_market.flash_loan_initial_token_amount = u64::deserialize(buf)?;
372 spot_market.total_swap_fee = u64::deserialize(buf)?;
373 spot_market.scale_initial_asset_weight_start = u64::deserialize(buf)?;
374
375 spot_market.min_borrow_rate = u8::deserialize(buf)?;
376 spot_market.fuel_boost_deposits = u8::deserialize(buf)?;
377 spot_market.fuel_boost_borrows = u8::deserialize(buf)?;
378 spot_market.fuel_boost_taker = u8::deserialize(buf)?;
379 spot_market.fuel_boost_maker = u8::deserialize(buf)?;
380 spot_market.fuel_boost_insurance = u8::deserialize(buf)?;
381 spot_market.token_program_flag = u8::deserialize(buf)?;
382 spot_market.pool_id = u8::deserialize(buf)?;
383
384 buf.read_exact(&mut spot_market.padding)?;
385
386 Ok(spot_market)
387 }
388}
389
390impl Size for SpotMarket {
391 const SIZE: usize = 776;
392}
393
394impl Default for SpotMarket {
395 fn default() -> Self {
396 SpotMarket {
397 pubkey: Pubkey::default(),
398 oracle: Pubkey::default(),
399 mint: Pubkey::default(),
400 vault: Pubkey::default(),
401 name: [0; 32],
402 historical_oracle_data: HistoricalOracleData::default(),
403 historical_index_data: HistoricalIndexData::default(),
404 revenue_pool: PoolBalance::default(),
405 spot_fee_pool: PoolBalance::default(),
406 insurance_fund: InsuranceFund::default(),
407 total_spot_fee: 0,
408 deposit_balance: 0,
409 borrow_balance: 0,
410 cumulative_deposit_interest: 0,
411 cumulative_borrow_interest: 0,
412 total_social_loss: 0,
413 total_quote_social_loss: 0,
414 withdraw_guard_threshold: 0,
415 max_token_deposits: 0,
416 deposit_token_twap: 0,
417 borrow_token_twap: 0,
418 utilization_twap: 0,
419 last_interest_ts: 0,
420 last_twap_ts: 0,
421 expiry_ts: 0,
422 order_step_size: 1,
423 order_tick_size: 0,
424 min_order_size: 0,
425 max_position_size: 0,
426 next_fill_record_id: 0,
427 next_deposit_record_id: 0,
428 initial_asset_weight: 0,
429 maintenance_asset_weight: 0,
430 initial_liability_weight: 0,
431 maintenance_liability_weight: 0,
432 imf_factor: 0,
433 liquidator_fee: 0,
434 if_liquidation_fee: 0,
435 optimal_utilization: 0,
436 optimal_borrow_rate: 0,
437 max_borrow_rate: 0,
438 decimals: 0,
439 market_index: 0,
440 orders_enabled: false,
441 oracle_source: OracleSource::default(),
442 status: MarketStatus::default(),
443 asset_tier: AssetTier::default(),
444 paused_operations: 0,
445 if_paused_operations: 0,
446 fee_adjustment: 0,
447 max_token_borrows_fraction: 0,
448 flash_loan_amount: 0,
449 flash_loan_initial_token_amount: 0,
450 total_swap_fee: 0,
451 scale_initial_asset_weight_start: 0,
452 min_borrow_rate: 0,
453 fuel_boost_deposits: 0,
454 fuel_boost_borrows: 0,
455 fuel_boost_taker: 0,
456 fuel_boost_maker: 0,
457 fuel_boost_insurance: 0,
458 token_program_flag: 0,
459 pool_id: 0,
460 padding: [0; 40],
461 }
462 }
463}
464
465#[derive(Default, Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq)]
466pub enum MarketStatus {
467 #[default]
469 Initialized,
470 Active,
472 FundingPaused,
474 AmmPaused,
476 FillPaused,
478 WithdrawPaused,
480 ReduceOnly,
482 Settlement,
484 Delisted,
486}
487
488#[zero_copy(unsafe)]
489#[derive(Default, Eq, PartialEq, Debug, AnchorSerialize, AnchorDeserialize,)]
490#[repr(C)]
491pub struct PoolBalance {
492 pub scaled_balance: u128,
496 pub market_index: u16,
498 pub padding: [u8; 6],
499}
500
501#[zero_copy(unsafe)]
503#[derive(Debug, PartialEq, Eq)]
504#[repr(C)]
505pub struct AMM {
506 pub oracle: Pubkey,
508 pub historical_oracle_data: HistoricalOracleData,
510 pub base_asset_amount_per_lp: i128,
513 pub quote_asset_amount_per_lp: i128,
516 pub fee_pool: PoolBalance,
518 pub base_asset_reserve: u128,
521 pub quote_asset_reserve: u128,
524 pub concentration_coef: u128,
528 pub min_base_asset_reserve: u128,
531 pub max_base_asset_reserve: u128,
534 pub sqrt_k: u128,
537 pub peg_multiplier: u128,
540 pub terminal_quote_asset_reserve: u128,
543 pub base_asset_amount_long: i128,
546 pub base_asset_amount_short: i128,
549 pub base_asset_amount_with_amm: i128,
552 pub base_asset_amount_with_unsettled_lp: i128,
555 pub max_open_interest: u128,
558 pub quote_asset_amount: i128,
561 pub quote_entry_amount_long: i128,
564 pub quote_entry_amount_short: i128,
567 pub quote_break_even_amount_long: i128,
570 pub quote_break_even_amount_short: i128,
573 pub user_lp_shares: u128,
576 pub last_funding_rate: i64,
579 pub last_funding_rate_long: i64,
582 pub last_funding_rate_short: i64,
585 pub last_24h_avg_funding_rate: i64,
588 pub total_fee: i128,
591 pub total_mm_fee: i128,
594 pub total_exchange_fee: u128,
597 pub total_fee_minus_distributions: i128,
600 pub total_fee_withdrawn: u128,
603 pub total_liquidation_fee: u128,
606 pub cumulative_funding_rate_long: i128,
608 pub cumulative_funding_rate_short: i128,
610 pub total_social_loss: u128,
612 pub ask_base_asset_reserve: u128,
615 pub ask_quote_asset_reserve: u128,
618 pub bid_base_asset_reserve: u128,
621 pub bid_quote_asset_reserve: u128,
624 pub last_oracle_normalised_price: i64,
627 pub last_oracle_reserve_price_spread_pct: i64,
629 pub last_bid_price_twap: u64,
632 pub last_ask_price_twap: u64,
635 pub last_mark_price_twap: u64,
638 pub last_mark_price_twap_5min: u64,
640 pub last_update_slot: u64,
642 pub last_oracle_conf_pct: u64,
645 pub net_revenue_since_last_funding: i64,
648 pub last_funding_rate_ts: i64,
650 pub funding_period: i64,
652 pub order_step_size: u64,
655 pub order_tick_size: u64,
658 pub min_order_size: u64,
661 pub max_position_size: u64,
664 pub volume_24h: u64,
667 pub long_intensity_volume: u64,
669 pub short_intensity_volume: u64,
671 pub last_trade_ts: i64,
673 pub mark_std: u64,
676 pub oracle_std: u64,
679 pub last_mark_price_twap_ts: i64,
681 pub base_spread: u32,
683 pub max_spread: u32,
685 pub long_spread: u32,
687 pub short_spread: u32,
689 pub long_intensity_count: u32,
691 pub short_intensity_count: u32,
693 pub max_fill_reserve_fraction: u16,
695 pub max_slippage_ratio: u16,
697 pub curve_update_intensity: u8,
699 pub amm_jit_intensity: u8,
702 pub oracle_source: OracleSource,
704 pub last_oracle_valid: bool,
706 pub target_base_asset_amount_per_lp: i32,
709 pub per_lp_base: i8,
711 pub padding1: u8,
712 pub padding2: u16,
713 pub total_fee_earned_per_lp: u64,
714 pub net_unsettled_funding_pnl: i64,
715 pub quote_asset_amount_with_unsettled_lp: i64,
716 pub reference_price_offset: i32,
717 pub padding: [u8; 12],
718}
719
720#[derive(Default, AnchorSerialize, AnchorDeserialize, Clone, Copy, Eq, PartialEq, Debug)]
721#[repr(C)]
722pub struct HistoricalOracleData {
723 pub last_oracle_price: i64,
725 pub last_oracle_conf: u64,
727 pub last_oracle_delay: i64,
729 pub last_oracle_price_twap: i64,
731 pub last_oracle_price_twap_5min: i64,
733 pub last_oracle_price_twap_ts: i64,
735}
736
737impl Default for AMM {
738 fn default() -> Self {
739 AMM {
740 oracle: Pubkey::default(),
741 historical_oracle_data: HistoricalOracleData::default(),
742 base_asset_amount_per_lp: 0,
743 quote_asset_amount_per_lp: 0,
744 fee_pool: PoolBalance::default(),
745 base_asset_reserve: 0,
746 quote_asset_reserve: 0,
747 concentration_coef: 0,
748 min_base_asset_reserve: 0,
749 max_base_asset_reserve: 0,
750 sqrt_k: 0,
751 peg_multiplier: 0,
752 terminal_quote_asset_reserve: 0,
753 base_asset_amount_long: 0,
754 base_asset_amount_short: 0,
755 base_asset_amount_with_amm: 0,
756 base_asset_amount_with_unsettled_lp: 0,
757 max_open_interest: 0,
758 quote_asset_amount: 0,
759 quote_entry_amount_long: 0,
760 quote_entry_amount_short: 0,
761 quote_break_even_amount_long: 0,
762 quote_break_even_amount_short: 0,
763 user_lp_shares: 0,
764 last_funding_rate: 0,
765 last_funding_rate_long: 0,
766 last_funding_rate_short: 0,
767 last_24h_avg_funding_rate: 0,
768 total_fee: 0,
769 total_mm_fee: 0,
770 total_exchange_fee: 0,
771 total_fee_minus_distributions: 0,
772 total_fee_withdrawn: 0,
773 total_liquidation_fee: 0,
774 cumulative_funding_rate_long: 0,
775 cumulative_funding_rate_short: 0,
776 total_social_loss: 0,
777 ask_base_asset_reserve: 0,
778 ask_quote_asset_reserve: 0,
779 bid_base_asset_reserve: 0,
780 bid_quote_asset_reserve: 0,
781 last_oracle_normalised_price: 0,
782 last_oracle_reserve_price_spread_pct: 0,
783 last_bid_price_twap: 0,
784 last_ask_price_twap: 0,
785 last_mark_price_twap: 0,
786 last_mark_price_twap_5min: 0,
787 last_update_slot: 0,
788 last_oracle_conf_pct: 0,
789 net_revenue_since_last_funding: 0,
790 last_funding_rate_ts: 0,
791 funding_period: 0,
792 order_step_size: 0,
793 order_tick_size: 0,
794 min_order_size: 1,
795 max_position_size: 0,
796 volume_24h: 0,
797 long_intensity_volume: 0,
798 short_intensity_volume: 0,
799 last_trade_ts: 0,
800 mark_std: 0,
801 oracle_std: 0,
802 last_mark_price_twap_ts: 0,
803 base_spread: 0,
804 max_spread: 0,
805 long_spread: 0,
806 short_spread: 0,
807 long_intensity_count: 0,
808 short_intensity_count: 0,
809 max_fill_reserve_fraction: 0,
810 max_slippage_ratio: 0,
811 curve_update_intensity: 0,
812 amm_jit_intensity: 0,
813 oracle_source: OracleSource::default(),
814 last_oracle_valid: false,
815 target_base_asset_amount_per_lp: 0,
816 per_lp_base: 0,
817 padding1: 0,
818 padding2: 0,
819 total_fee_earned_per_lp: 0,
820 net_unsettled_funding_pnl: 0,
821 quote_asset_amount_with_unsettled_lp: 0,
822 reference_price_offset: 0,
823 padding: [0; 12],
824 }
825 }
826}
827
828pub trait SafeUnwrap {
829 type Item;
830 fn safe_unwrap(self) -> std::result::Result<Self::Item, ReflectErrorCodes>;
831}
832
833impl<T> SafeUnwrap for Option<T> {
834 type Item = T;
835
836 #[track_caller]
837 #[inline(always)]
838 fn safe_unwrap(self) -> std::result::Result<T, ReflectErrorCodes> {
839 match self {
840 Some(v) => Ok(v),
841 None => {
842 let caller = Location::caller();
843 msg!("Unwrap error thrown at {}:{}", caller.file(), caller.line());
844 Err(ReflectErrorCodes::FailedUnwrap) }
846 }
847 }
848}
849
850impl<T> SafeUnwrap for std::result::Result<T, ReflectErrorCodes> {
851 type Item = T;
852
853 #[track_caller]
854 #[inline(always)]
855 fn safe_unwrap(self) -> std::result::Result<T, ReflectErrorCodes> {
856 match self {
857 Ok(v) => Ok(v),
858 Err(_) => {
859 let caller = Location::caller();
860 msg!("Unwrap error thrown at {}:{}", caller.file(), caller.line());
861 Err(ReflectErrorCodes::FailedUnwrap) }
863 }
864 }
865}
866
867#[macro_export]
868macro_rules! validate {
869 ($assert:expr, $err:expr) => {
870 {
871 if ($assert) {
872 Ok(())
873 } else {
874 let error_code: ReflectErrorCodes = $err;
875 msg!("Error {} thrown at {}:{}", error_code, file!(), line!());
876 Err(error_code)
877 }
878 }
879 };
880 (
881 $assert:expr,
882 $err:expr,
883 $($arg:tt)+
884 ) => {
885 {
886 if ($assert) {
887 Ok(())
888 } else {
889 let error_code: ReflectErrorCodes = $err;
890 msg!("Error {} thrown at {}:{}", error_code, file!(), line!());
891 msg!($($arg)*);
892 Err(error_code)
893 }
894 }
895 };
896}
897
898
899#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, Eq, PartialEq, Debug, Default, Ord, PartialOrd,)]
900pub enum OracleSource {
901 #[default]
902 Pyth,
903 Switchboard,
904 QuoteAsset,
905 Pyth1K,
906 Pyth1M,
907 PythStableCoin,
908 Prelaunch,
909 PythPull,
910 Pyth1KPull,
911 Pyth1MPull,
912 PythStableCoinPull,
913 SwitchboardOnDemand,
914 PythLazer,
915 PythLazer1K,
916 PythLazer1M,
917 PythLazerStableCoin,
918}
919
920pub trait SafeMath: Sized {
921 fn safe_add(self, rhs: Self) -> Result<Self>;
922 fn safe_sub(self, rhs: Self) -> Result<Self>;
923 fn safe_mul(self, rhs: Self) -> Result<Self>;
924 fn safe_div(self, rhs: Self) -> Result<Self>;
925 fn safe_div_ceil(self, rhs: Self) -> Result<Self>;
926}
927
928impl From<Error> for ReflectErrorCodes {
929 fn from(err: Error) -> Self {
930 ReflectErrorCodes::MathError }
932}
933
934pub trait Cast: Sized {
935 #[track_caller]
936 #[inline(always)]
937 fn cast<T: std::convert::TryFrom<Self>>(self) -> Result<T> {
938 match self.try_into() {
939 Ok(result) => Ok(result),
940 Err(_) => {
941 let caller = Location::caller();
942 msg!(
943 "Casting error thrown at {}:{}",
944 caller.file(),
945 caller.line()
946 );
947 Err(ReflectErrorCodes::ConversionFailed.into())
948 }
949 }
950 }
951}
952
953impl Cast for u128 {}
954impl Cast for u64 {}
955impl Cast for u32 {}
956impl Cast for u16 {}
957impl Cast for u8 {}
958impl Cast for i128 {}
959impl Cast for i64 {}
960impl Cast for i32 {}
961impl Cast for i16 {}
962impl Cast for i8 {}
963impl Cast for bool {}
964
965macro_rules! checked_impl {
966 ($t:ty) => {
967 impl SafeMath for $t {
968 #[track_caller]
969 #[inline(always)]
970 fn safe_add(self, v: $t) -> Result<$t> {
971 match self.checked_add(v) {
972 Some(result) => Ok(result),
973 None => {
974 let caller = Location::caller();
975 msg!("Math error thrown at {}:{}", caller.file(), caller.line());
976 Err(ReflectErrorCodes::MathError.into())
977 }
978 }
979 }
980
981 #[track_caller]
982 #[inline(always)]
983 fn safe_sub(self, v: $t) -> Result<$t> {
984 match self.checked_sub(v) {
985 Some(result) => Ok(result),
986 None => {
987 let caller = Location::caller();
988 msg!("Math error thrown at {}:{}", caller.file(), caller.line());
989 Err(ReflectErrorCodes::MathError.into())
990 }
991 }
992 }
993
994 #[track_caller]
995 #[inline(always)]
996 fn safe_mul(self, v: $t) -> Result<$t> {
997 match self.checked_mul(v) {
998 Some(result) => Ok(result),
999 None => {
1000 let caller = Location::caller();
1001 msg!("Math error thrown at {}:{}", caller.file(), caller.line());
1002 Err(ReflectErrorCodes::MathError.into())
1003 }
1004 }
1005 }
1006
1007 #[track_caller]
1008 #[inline(always)]
1009 fn safe_div(self, v: $t) -> Result<$t> {
1010 match self.checked_div(v) {
1011 Some(result) => Ok(result),
1012 None => {
1013 let caller = Location::caller();
1014 msg!("Math error thrown at {}:{}", caller.file(), caller.line());
1015 Err(ReflectErrorCodes::MathError.into())
1016 }
1017 }
1018 }
1019
1020 #[track_caller]
1021 #[inline(always)]
1022 fn safe_div_ceil(self, v: $t) -> Result<$t> {
1023 match self.checked_ceil_div(v) {
1024 Some(result) => Ok(result),
1025 None => {
1026 let caller = Location::caller();
1027 msg!("Math error thrown at {}:{}", caller.file(), caller.line());
1028 Err(ReflectErrorCodes::MathError.into())
1029 }
1030 }
1031 }
1032 }
1033 };
1034}
1035checked_impl!(u128);
1036checked_impl!(u64);
1037checked_impl!(u32);
1038checked_impl!(u16);
1039checked_impl!(u8);
1040checked_impl!(i128);
1041checked_impl!(i64);
1042checked_impl!(i32);
1043checked_impl!(i16);
1044checked_impl!(i8);
1045
1046pub trait SafeDivFloor: Sized {
1047 fn safe_div_floor(self, rhs: Self) -> Result<Self>;
1049}
1050
1051pub trait CheckedFloorDiv: Sized {
1052 fn checked_floor_div(&self, rhs: Self) -> Option<Self>;
1054}
1055
1056macro_rules! checked_impl {
1057 ($t:ty) => {
1058 impl CheckedFloorDiv for $t {
1059 #[track_caller]
1060 #[inline]
1061 fn checked_floor_div(&self, rhs: $t) -> Option<$t> {
1062 let quotient = self.checked_div(rhs)?;
1063
1064 let remainder = self.checked_rem(rhs)?;
1065
1066 if remainder != <$t>::zero() {
1067 quotient.checked_sub(<$t>::one())
1068 } else {
1069 Some(quotient)
1070 }
1071 }
1072 }
1073 };
1074}
1075
1076macro_rules! div_floor_impl {
1077 ($t:ty) => {
1078 impl SafeDivFloor for $t {
1079 #[track_caller]
1080 #[inline(always)]
1081 fn safe_div_floor(self, v: $t) -> Result<$t> {
1082 match self.checked_floor_div(v) {
1083 Some(result) => Ok(result),
1084 None => {
1085 let caller = Location::caller();
1086 msg!("Math error thrown at {}:{}", caller.file(), caller.line());
1087 Err(ReflectErrorCodes::MathError.into())
1088 }
1089 }
1090 }
1091 }
1092 };
1093}
1094
1095div_floor_impl!(i128);
1096div_floor_impl!(i64);
1097div_floor_impl!(i32);
1098div_floor_impl!(i16);
1099div_floor_impl!(i8);
1100
1101checked_impl!(u128);
1102checked_impl!(u64);
1103checked_impl!(u32);
1104checked_impl!(u16);
1105checked_impl!(u8);
1106checked_impl!(i128);
1107checked_impl!(i64);
1108checked_impl!(i32);
1109checked_impl!(i16);
1110checked_impl!(i8);
1111
1112pub trait CheckedCeilDiv: Sized {
1113 fn checked_ceil_div(&self, rhs: Self) -> Option<Self>;
1115}
1116
1117macro_rules! checked_impl {
1118 ($t:ty) => {
1119 impl CheckedCeilDiv for $t {
1120 #[track_caller]
1121 #[inline]
1122 fn checked_ceil_div(&self, rhs: $t) -> Option<$t> {
1123 let quotient = self.checked_div(rhs)?;
1124
1125 let remainder = self.checked_rem(rhs)?;
1126
1127 if remainder > <$t>::zero() {
1128 quotient.checked_add(<$t>::one())
1129 } else {
1130 Some(quotient)
1131 }
1132 }
1133 }
1134 };
1135}
1136
1137checked_impl!(u128);
1138checked_impl!(u64);
1139checked_impl!(u32);
1140checked_impl!(u16);
1141checked_impl!(u8);
1142checked_impl!(i128);
1143checked_impl!(i64);
1144checked_impl!(i32);
1145checked_impl!(i16);
1146checked_impl!(i8);
1147
1148
1149#[derive(Clone, PartialEq, Debug, Eq, Default)]
1150pub struct UserSpots {
1151 pub spot_positions: [SpotPosition; SPOT_POSITION_COUNT],
1152}
1153
1154#[inline(never)]
1155pub fn get_token_amount(
1156 balance: u128,
1157 spot_market: &SpotMarket,
1158 balance_type: &SpotBalanceType,
1159) -> Result<u128> {
1160
1161 let precision_decrease = (10_u128).pow((19_u32).safe_sub(6)?);
1163
1164 let cumulative_interest = match balance_type {
1165 SpotBalanceType::Deposit => spot_market.cumulative_deposit_interest,
1166 SpotBalanceType::Borrow => spot_market.cumulative_borrow_interest,
1167 };
1168
1169 let token_amount = match balance_type {
1170 SpotBalanceType::Deposit => balance
1171 .safe_mul(cumulative_interest)?
1172 .safe_div(precision_decrease)?,
1173 SpotBalanceType::Borrow => balance
1174 .safe_mul(cumulative_interest)?
1175 .safe_div_ceil(precision_decrease)?,
1176 };
1177
1178 Ok(token_amount)
1179}
1180
1181pub const PUBKEY_LENGTH: usize = 32;
1182pub const NAME_LENGTH: usize = 32;
1183pub const SPOT_POSITION_COUNT: usize = 8;
1184pub const ANCHOR_DISCRIMINATOR_SIZE: usize = 8;
1185
1186pub const SPOT_POSITIONS_OFFSET: usize =
1188 PUBKEY_LENGTH + PUBKEY_LENGTH + NAME_LENGTH;
1191
1192pub const PERP_POSITIONS_OFFSET: usize = ANCHOR_DISCRIMINATOR_SIZE +
1194 PUBKEY_LENGTH * 2 + NAME_LENGTH +
1196 std::mem::size_of::<SpotPosition>() * SPOT_POSITION_COUNT;
1197
1198
1199pub struct ForeignAccountLoader<'info, T: Pod> {
1200 acc_info: &'info AccountInfo<'info>,
1201 phantom: PhantomData<T>,
1202}
1203
1204impl<'info, T: Pod> ForeignAccountLoader<'info, T> {
1205 pub fn try_from_unchecked(
1206 acc_info: &'info AccountInfo<'info>
1207 ) -> std::result::Result<Self, Error> {
1208 Ok(Self {
1209 acc_info,
1210 phantom: PhantomData,
1211 })
1212 }
1213
1214 pub fn load(&self) -> std::result::Result<Ref<T>, Error> {
1215 let data = self.acc_info.try_borrow_data()?;
1216 Ok(Ref::map(data, |data| bytemuck::from_bytes(&data[8..])))
1217 }
1218
1219 pub fn load_mut(&self) -> std::result::Result<RefMut<T>, Error> {
1220 let data = self.acc_info.try_borrow_mut_data()?;
1221 Ok(RefMut::map(data, |data| bytemuck::from_bytes_mut(&mut data[8..])))
1222 }
1223}