1use std::num::NonZeroU64;
2
3use bytemuck::Zeroable;
4
5use crate::gmsol_store::{
6 accounts::{Glv, GtExchange, Market, Position, ReferralCodeV2, Store, VirtualInventory},
7 types::{ActionHeader, EventPositionState, Pool, PositionState},
8};
9
10pub type ReferralCodeBytes = [u8; 8];
12
13impl Default for Market {
14 fn default() -> Self {
15 Zeroable::zeroed()
16 }
17}
18
19impl Default for VirtualInventory {
20 fn default() -> Self {
21 Zeroable::zeroed()
22 }
23}
24
25impl Pool {
26 pub fn is_pure(&self) -> bool {
28 !matches!(self.is_pure, 0)
29 }
30}
31
32impl Default for Glv {
33 fn default() -> Self {
34 Zeroable::zeroed()
35 }
36}
37
38impl Default for Position {
39 fn default() -> Self {
40 Zeroable::zeroed()
41 }
42}
43
44impl AsRef<Position> for Position {
45 fn as_ref(&self) -> &Position {
46 self
47 }
48}
49
50impl Default for ActionHeader {
51 fn default() -> Self {
52 Zeroable::zeroed()
53 }
54}
55
56impl Default for GtExchange {
57 fn default() -> Self {
58 Zeroable::zeroed()
59 }
60}
61
62impl Store {
63 pub fn claimable_time_window(&self) -> crate::Result<NonZeroU64> {
65 NonZeroU64::new(self.amount.claimable_time_window)
66 .ok_or_else(|| crate::Error::custom("claimable time window cannot be zero"))
67 }
68
69 pub fn claimable_time_window_index(&self, timestamp: i64) -> crate::Result<i64> {
71 let window: i64 = self
72 .claimable_time_window()?
73 .get()
74 .try_into()
75 .map_err(crate::Error::custom)?;
76 Ok(timestamp / window)
77 }
78
79 pub fn claimable_time_key(&self, timestamp: i64) -> crate::Result<[u8; 8]> {
81 let index = self.claimable_time_window_index(timestamp)?;
82 Ok(index.to_le_bytes())
83 }
84}
85
86impl ReferralCodeV2 {
87 pub const LEN: usize = std::mem::size_of::<ReferralCodeBytes>();
89
90 pub fn decode(code: &str) -> crate::Result<ReferralCodeBytes> {
92 if code.is_empty() {
93 return Err(crate::Error::custom("empty code is not supported"));
94 }
95 let code = bs58::decode(code)
96 .into_vec()
97 .map_err(crate::Error::custom)?;
98 if code.len() > Self::LEN {
99 return Err(crate::Error::custom("the code is too long"));
100 }
101 let padding = Self::LEN - code.len();
102 let mut code_bytes = ReferralCodeBytes::default();
103 code_bytes[padding..].copy_from_slice(&code);
104
105 Ok(code_bytes)
106 }
107
108 pub fn encode(code: &ReferralCodeBytes, skip_leading_ones: bool) -> String {
110 let code = bs58::encode(code).into_string();
111 if skip_leading_ones {
112 code.trim_start_matches('1').to_owned()
113 } else {
114 code
115 }
116 }
117}
118
119impl From<EventPositionState> for PositionState {
120 fn from(event: EventPositionState) -> Self {
121 let EventPositionState {
122 trade_id,
123 increased_at,
124 updated_at_slot,
125 decreased_at,
126 size_in_tokens,
127 collateral_amount,
128 size_in_usd,
129 borrowing_factor,
130 funding_fee_amount_per_size,
131 long_token_claimable_funding_amount_per_size,
132 short_token_claimable_funding_amount_per_size,
133 reserved,
134 } = event;
135
136 Self {
137 trade_id,
138 increased_at,
139 updated_at_slot,
140 decreased_at,
141 size_in_tokens,
142 collateral_amount,
143 size_in_usd,
144 borrowing_factor,
145 funding_fee_amount_per_size,
146 long_token_claimable_funding_amount_per_size,
147 short_token_claimable_funding_amount_per_size,
148 reserved,
149 }
150 }
151}
152
153#[cfg(feature = "gmsol-model")]
154mod model {
155 use gmsol_model::ClockKind;
156
157 use crate::gmsol_store::types::Clocks;
158
159 impl Clocks {
160 pub fn get(&self, kind: ClockKind) -> Option<i64> {
162 let clock = match kind {
163 ClockKind::PriceImpactDistribution => self.price_impact_distribution,
164 ClockKind::Borrowing => self.borrowing,
165 ClockKind::Funding => self.funding,
166 ClockKind::AdlForLong => self.adl_for_long,
167 ClockKind::AdlForShort => self.adl_for_short,
168 _ => return None,
169 };
170 Some(clock)
171 }
172 }
173}
174
175#[cfg(feature = "gmsol-utils")]
176mod utils {
177 use anchor_lang::prelude::Pubkey;
178 use gmsol_utils::{
179 action::{ActionCallbackKind, ActionFlag, ActionState, MAX_ACTION_FLAGS},
180 fixed_str::bytes_to_fixed_str,
181 glv::{GlvMarketFlag, MAX_GLV_MARKET_FLAGS},
182 impl_fixed_map, impl_flags,
183 market::{
184 self, HasMarketMeta, MarketConfigKey, MarketFlag, VirtualInventoryFlag,
185 MAX_MARKET_FLAGS, MAX_VIRTUAL_INVENTORY_FLAGS,
186 },
187 order::{self, PositionKind, TradeFlag, TradeFlagContainer},
188 pubkey::{self, optional_address},
189 swap::{self, HasSwapParams},
190 token_config::{self, TokensCollector},
191 };
192
193 use crate::gmsol_store::{
194 accounts::{Glv, Market, Position},
195 events::TradeEvent,
196 types::{
197 ActionFlagContainer, ActionHeader, GlvMarketConfig, GlvMarketFlagContainer, GlvMarkets,
198 GlvMarketsEntry, MarketConfig, MarketFlagContainer, MarketMeta, Members, MembersEntry,
199 OrderActionParams, OrderKind, RoleMap, RoleMapEntry, RoleMetadata, RoleStore,
200 SwapActionParams, TokenAndAccount, Tokens, TokensEntry, UpdateTokenConfigParams,
201 VirtualInventoryFlagContainer,
202 },
203 };
204
205 const MAX_TOKENS: usize = 256;
206 const MAX_ALLOWED_NUMBER_OF_MARKETS: usize = 96;
207 const MAX_ROLES: usize = 32;
208 const MAX_MEMBERS: usize = 64;
209
210 impl_fixed_map!(RoleMap, RoleMetadata, MAX_ROLES);
211
212 impl_fixed_map!(Members, Pubkey, pubkey::to_bytes, u32, MAX_MEMBERS);
213
214 impl_fixed_map!(Tokens, Pubkey, pubkey::to_bytes, u8, MAX_TOKENS);
215 impl_fixed_map!(
216 GlvMarkets,
217 Pubkey,
218 pubkey::to_bytes,
219 GlvMarketConfig,
220 MAX_ALLOWED_NUMBER_OF_MARKETS
221 );
222
223 impl_flags!(ActionFlag, MAX_ACTION_FLAGS, u8);
224 impl_flags!(MarketFlag, MAX_MARKET_FLAGS, u8);
225 impl_flags!(GlvMarketFlag, MAX_GLV_MARKET_FLAGS, u8);
226 impl_flags!(VirtualInventoryFlag, MAX_VIRTUAL_INVENTORY_FLAGS, u8);
227
228 impl From<SwapActionParams> for swap::SwapActionParams {
229 fn from(params: SwapActionParams) -> Self {
230 let SwapActionParams {
231 primary_length,
232 secondary_length,
233 num_tokens,
234 padding_0,
235 current_market_token,
236 paths,
237 tokens,
238 } = params;
239 Self {
240 primary_length,
241 secondary_length,
242 num_tokens,
243 padding_0,
244 current_market_token,
245 paths,
246 tokens,
247 }
248 }
249 }
250
251 impl From<MarketMeta> for market::MarketMeta {
252 fn from(meta: MarketMeta) -> Self {
253 let MarketMeta {
254 market_token_mint,
255 index_token_mint,
256 long_token_mint,
257 short_token_mint,
258 } = meta;
259 Self {
260 market_token_mint,
261 index_token_mint,
262 long_token_mint,
263 short_token_mint,
264 }
265 }
266 }
267
268 impl Market {
269 pub fn name(&self) -> crate::Result<&str> {
271 bytes_to_fixed_str(&self.name).map_err(crate::Error::custom)
272 }
273 }
274
275 impl HasMarketMeta for Market {
276 fn market_meta(&self) -> &market::MarketMeta {
277 bytemuck::cast_ref(&self.meta)
278 }
279 }
280
281 impl MarketConfig {
282 pub fn get(&self, key: MarketConfigKey) -> Option<&u128> {
284 let value = match key {
285 MarketConfigKey::SwapImpactExponent => &self.swap_impact_exponent,
286 MarketConfigKey::SwapImpactPositiveFactor => &self.swap_impact_positive_factor,
287 MarketConfigKey::SwapImpactNegativeFactor => &self.swap_impact_negative_factor,
288 MarketConfigKey::SwapFeeReceiverFactor => &self.swap_fee_receiver_factor,
289 MarketConfigKey::SwapFeeFactorForPositiveImpact => {
290 &self.swap_fee_factor_for_positive_impact
291 }
292 MarketConfigKey::SwapFeeFactorForNegativeImpact => {
293 &self.swap_fee_factor_for_negative_impact
294 }
295 MarketConfigKey::MinPositionSizeUsd => &self.min_position_size_usd,
296 MarketConfigKey::MinCollateralValue => &self.min_collateral_value,
297 MarketConfigKey::MinCollateralFactor => &self.min_collateral_factor,
298 MarketConfigKey::MinCollateralFactorForOpenInterestMultiplierForLong => {
299 &self.min_collateral_factor_for_open_interest_multiplier_for_long
300 }
301 MarketConfigKey::MinCollateralFactorForOpenInterestMultiplierForShort => {
302 &self.min_collateral_factor_for_open_interest_multiplier_for_short
303 }
304 MarketConfigKey::MaxPositivePositionImpactFactor => {
305 &self.max_positive_position_impact_factor
306 }
307 MarketConfigKey::MaxNegativePositionImpactFactor => {
308 &self.max_negative_position_impact_factor
309 }
310 MarketConfigKey::MaxPositionImpactFactorForLiquidations => {
311 &self.max_position_impact_factor_for_liquidations
312 }
313 MarketConfigKey::PositionImpactExponent => &self.position_impact_exponent,
314 MarketConfigKey::PositionImpactPositiveFactor => {
315 &self.position_impact_positive_factor
316 }
317 MarketConfigKey::PositionImpactNegativeFactor => {
318 &self.position_impact_negative_factor
319 }
320 MarketConfigKey::OrderFeeReceiverFactor => &self.order_fee_receiver_factor,
321 MarketConfigKey::OrderFeeFactorForPositiveImpact => {
322 &self.order_fee_factor_for_positive_impact
323 }
324 MarketConfigKey::OrderFeeFactorForNegativeImpact => {
325 &self.order_fee_factor_for_negative_impact
326 }
327 MarketConfigKey::LiquidationFeeReceiverFactor => {
328 &self.liquidation_fee_receiver_factor
329 }
330 MarketConfigKey::LiquidationFeeFactor => &self.liquidation_fee_factor,
331 MarketConfigKey::PositionImpactDistributeFactor => {
332 &self.position_impact_distribute_factor
333 }
334 MarketConfigKey::MinPositionImpactPoolAmount => {
335 &self.min_position_impact_pool_amount
336 }
337 MarketConfigKey::BorrowingFeeReceiverFactor => &self.borrowing_fee_receiver_factor,
338 MarketConfigKey::BorrowingFeeFactorForLong => &self.borrowing_fee_factor_for_long,
339 MarketConfigKey::BorrowingFeeFactorForShort => &self.borrowing_fee_factor_for_short,
340 MarketConfigKey::BorrowingFeeExponentForLong => {
341 &self.borrowing_fee_exponent_for_long
342 }
343 MarketConfigKey::BorrowingFeeExponentForShort => {
344 &self.borrowing_fee_exponent_for_short
345 }
346 MarketConfigKey::BorrowingFeeOptimalUsageFactorForLong => {
347 &self.borrowing_fee_optimal_usage_factor_for_long
348 }
349 MarketConfigKey::BorrowingFeeOptimalUsageFactorForShort => {
350 &self.borrowing_fee_optimal_usage_factor_for_short
351 }
352 MarketConfigKey::BorrowingFeeBaseFactorForLong => {
353 &self.borrowing_fee_base_factor_for_long
354 }
355 MarketConfigKey::BorrowingFeeBaseFactorForShort => {
356 &self.borrowing_fee_base_factor_for_short
357 }
358 MarketConfigKey::BorrowingFeeAboveOptimalUsageFactorForLong => {
359 &self.borrowing_fee_above_optimal_usage_factor_for_long
360 }
361 MarketConfigKey::BorrowingFeeAboveOptimalUsageFactorForShort => {
362 &self.borrowing_fee_above_optimal_usage_factor_for_short
363 }
364 MarketConfigKey::FundingFeeExponent => &self.funding_fee_exponent,
365 MarketConfigKey::FundingFeeFactor => &self.funding_fee_factor,
366 MarketConfigKey::FundingFeeMaxFactorPerSecond => {
367 &self.funding_fee_max_factor_per_second
368 }
369 MarketConfigKey::FundingFeeMinFactorPerSecond => {
370 &self.funding_fee_min_factor_per_second
371 }
372 MarketConfigKey::FundingFeeIncreaseFactorPerSecond => {
373 &self.funding_fee_increase_factor_per_second
374 }
375 MarketConfigKey::FundingFeeDecreaseFactorPerSecond => {
376 &self.funding_fee_decrease_factor_per_second
377 }
378 MarketConfigKey::FundingFeeThresholdForStableFunding => {
379 &self.funding_fee_threshold_for_stable_funding
380 }
381 MarketConfigKey::FundingFeeThresholdForDecreaseFunding => {
382 &self.funding_fee_threshold_for_decrease_funding
383 }
384 MarketConfigKey::ReserveFactor => &self.reserve_factor,
385 MarketConfigKey::OpenInterestReserveFactor => &self.open_interest_reserve_factor,
386 MarketConfigKey::MaxPnlFactorForLongDeposit => {
387 &self.max_pnl_factor_for_long_deposit
388 }
389 MarketConfigKey::MaxPnlFactorForShortDeposit => {
390 &self.max_pnl_factor_for_short_deposit
391 }
392 MarketConfigKey::MaxPnlFactorForLongWithdrawal => {
393 &self.max_pnl_factor_for_long_withdrawal
394 }
395 MarketConfigKey::MaxPnlFactorForShortWithdrawal => {
396 &self.max_pnl_factor_for_short_withdrawal
397 }
398 MarketConfigKey::MaxPnlFactorForLongTrader => &self.max_pnl_factor_for_long_trader,
399 MarketConfigKey::MaxPnlFactorForShortTrader => {
400 &self.max_pnl_factor_for_short_trader
401 }
402 MarketConfigKey::MaxPnlFactorForLongAdl => &self.max_pnl_factor_for_long_adl,
403 MarketConfigKey::MaxPnlFactorForShortAdl => &self.max_pnl_factor_for_short_adl,
404 MarketConfigKey::MinPnlFactorAfterLongAdl => &self.min_pnl_factor_after_long_adl,
405 MarketConfigKey::MinPnlFactorAfterShortAdl => &self.min_pnl_factor_after_short_adl,
406 MarketConfigKey::MaxPoolAmountForLongToken => &self.max_pool_amount_for_long_token,
407 MarketConfigKey::MaxPoolAmountForShortToken => {
408 &self.max_pool_amount_for_short_token
409 }
410 MarketConfigKey::MaxPoolValueForDepositForLongToken => {
411 &self.max_pool_value_for_deposit_for_long_token
412 }
413 MarketConfigKey::MaxPoolValueForDepositForShortToken => {
414 &self.max_pool_value_for_deposit_for_short_token
415 }
416 MarketConfigKey::MaxOpenInterestForLong => &self.max_open_interest_for_long,
417 MarketConfigKey::MaxOpenInterestForShort => &self.max_open_interest_for_short,
418 MarketConfigKey::MinTokensForFirstDeposit => &self.min_tokens_for_first_deposit,
419 _ => return None,
420 };
421 Some(value)
422 }
423 }
424
425 impl TokenAndAccount {
426 pub fn token(&self) -> Option<Pubkey> {
428 optional_address(&self.token).copied()
429 }
430
431 pub fn account(&self) -> Option<Pubkey> {
433 optional_address(&self.account).copied()
434 }
435
436 pub fn token_and_account(&self) -> Option<(Pubkey, Pubkey)> {
438 let token = self.token()?;
439 let account = self.account()?;
440 Some((token, account))
441 }
442 }
443
444 impl From<OrderKind> for order::OrderKind {
445 fn from(value: OrderKind) -> Self {
446 match value {
447 OrderKind::Liquidation => Self::Liquidation,
448 OrderKind::AutoDeleveraging => Self::AutoDeleveraging,
449 OrderKind::MarketSwap => Self::MarketSwap,
450 OrderKind::MarketIncrease => Self::MarketIncrease,
451 OrderKind::MarketDecrease => Self::MarketDecrease,
452 OrderKind::LimitSwap => Self::LimitSwap,
453 OrderKind::LimitIncrease => Self::LimitIncrease,
454 OrderKind::LimitDecrease => Self::LimitDecrease,
455 OrderKind::StopLossDecrease => Self::StopLossDecrease,
456 }
457 }
458 }
459
460 impl TryFrom<order::OrderKind> for OrderKind {
461 type Error = crate::Error;
462
463 fn try_from(value: order::OrderKind) -> Result<Self, Self::Error> {
464 match value {
465 order::OrderKind::Liquidation => Ok(Self::Liquidation),
466 order::OrderKind::AutoDeleveraging => Ok(Self::AutoDeleveraging),
467 order::OrderKind::MarketSwap => Ok(Self::MarketSwap),
468 order::OrderKind::MarketIncrease => Ok(Self::MarketIncrease),
469 order::OrderKind::MarketDecrease => Ok(Self::MarketDecrease),
470 order::OrderKind::LimitSwap => Ok(Self::LimitSwap),
471 order::OrderKind::LimitIncrease => Ok(Self::LimitIncrease),
472 order::OrderKind::LimitDecrease => Ok(Self::LimitDecrease),
473 order::OrderKind::StopLossDecrease => Ok(Self::StopLossDecrease),
474 kind => Err(crate::Error::custom(format!(
475 "unsupported order kind: {kind}"
476 ))),
477 }
478 }
479 }
480
481 impl OrderActionParams {
482 pub fn side(&self) -> crate::Result<order::OrderSide> {
484 self.side.try_into().map_err(crate::Error::custom)
485 }
486
487 pub fn kind(&self) -> crate::Result<order::OrderKind> {
489 self.kind.try_into().map_err(crate::Error::custom)
490 }
491
492 pub fn position(&self) -> Option<&Pubkey> {
494 optional_address(&self.position)
495 }
496
497 #[cfg(feature = "model")]
499 pub fn decrease_position_swap_type(
500 &self,
501 ) -> crate::Result<gmsol_model::action::decrease_position::DecreasePositionSwapType>
502 {
503 let ty = self
504 .decrease_position_swap_type
505 .try_into()
506 .map_err(|_| crate::Error::custom("unknown decrease position swap type"))?;
507 Ok(ty)
508 }
509 }
510
511 impl Position {
512 pub fn kind(&self) -> crate::Result<PositionKind> {
514 self.kind.try_into().map_err(crate::Error::custom)
515 }
516 }
517
518 impl Glv {
519 pub fn market_tokens(&self) -> impl Iterator<Item = Pubkey> + '_ {
521 self.markets
522 .entries()
523 .map(|(key, _)| Pubkey::new_from_array(*key))
524 }
525
526 pub fn market_config(&self, market_token: &Pubkey) -> Option<&GlvMarketConfig> {
528 self.markets.get(market_token)
529 }
530
531 pub fn num_markets(&self) -> usize {
533 self.markets.len()
534 }
535
536 pub fn tokens_collector(&self, action: Option<&impl HasSwapParams>) -> TokensCollector {
538 TokensCollector::new(action, self.num_markets())
539 }
540 }
541
542 impl From<token_config::UpdateTokenConfigParams> for UpdateTokenConfigParams {
543 fn from(params: token_config::UpdateTokenConfigParams) -> Self {
544 let token_config::UpdateTokenConfigParams {
545 heartbeat_duration,
546 precision,
547 feeds,
548 timestamp_adjustments,
549 expected_provider,
550 } = params;
551 Self {
552 heartbeat_duration,
553 precision,
554 feeds,
555 timestamp_adjustments,
556 expected_provider,
557 }
558 }
559 }
560
561 impl ActionHeader {
562 pub fn action_state(&self) -> crate::Result<ActionState> {
564 ActionState::try_from(self.action_state)
565 .map_err(|_| crate::Error::custom("unknown action state"))
566 }
567
568 pub fn callback_kind(&self) -> crate::Result<ActionCallbackKind> {
570 ActionCallbackKind::try_from(self.callback_kind)
571 .map_err(|_| crate::Error::custom("unknown callback kind"))
572 }
573 }
574
575 impl TradeEvent {
576 pub fn get_flag(&self, flag: TradeFlag) -> bool {
578 let map = TradeFlagContainer::from_value(self.flags);
579 map.get_flag(flag)
580 }
581
582 pub fn is_long(&self) -> bool {
584 self.get_flag(TradeFlag::IsLong)
585 }
586
587 pub fn is_collateral_long(&self) -> bool {
589 self.get_flag(TradeFlag::IsCollateralLong)
590 }
591
592 pub fn to_position(&self, meta: &impl HasMarketMeta) -> Position {
594 let mut position = Position::default();
595
596 let kind = if self.is_long() {
597 PositionKind::Long
598 } else {
599 PositionKind::Short
600 };
601
602 let collateral_token = if self.is_collateral_long() {
603 meta.market_meta().long_token_mint
604 } else {
605 meta.market_meta().short_token_mint
606 };
607
608 position.kind = kind as u8;
609 position.bump = 0;
611 position.store = self.store;
612 position.owner = self.user;
613 position.market_token = self.market_token;
614 position.collateral_token = collateral_token;
615 position.state = self.after.into();
616 position
617 }
618 }
619
620 impl RoleMetadata {
621 pub const ROLE_ENABLED: u8 = u8::MAX;
623
624 pub fn name(&self) -> crate::Result<&str> {
626 bytes_to_fixed_str(&self.name).map_err(crate::Error::custom)
627 }
628
629 pub fn is_enabled(&self) -> bool {
631 self.enabled == Self::ROLE_ENABLED
632 }
633 }
634
635 impl RoleStore {
636 fn enabled_role_index(&self, role: &str) -> crate::Result<Option<u8>> {
637 if let Some(metadata) = self.roles.get(role) {
638 if metadata.name()? != role {
639 return Err(crate::Error::custom("invalid role store"));
640 }
641 if !metadata.is_enabled() {
642 return Err(crate::Error::custom("the given role is disabled"));
643 }
644 Ok(Some(metadata.index))
645 } else {
646 Ok(None)
647 }
648 }
649
650 pub fn has_role(&self, authority: &Pubkey, role: &str) -> crate::Result<bool> {
652 use gmsol_utils::bitmaps::Bitmap;
653 type RoleBitmap = Bitmap<MAX_ROLES>;
654
655 let value = self
656 .members
657 .get(authority)
658 .ok_or_else(|| crate::Error::custom("not a member"))?;
659 let index = self
660 .enabled_role_index(role)?
661 .ok_or_else(|| crate::Error::custom("no such role"))?;
662 let bitmap = RoleBitmap::from_value(*value);
663 Ok(bitmap.get(index as usize))
664 }
665
666 pub fn members(&self) -> impl Iterator<Item = Pubkey> + '_ {
668 self.members
669 .entries()
670 .map(|(key, _)| Pubkey::new_from_array(*key))
671 }
672
673 pub fn roles(&self) -> impl Iterator<Item = crate::Result<&str>> + '_ {
675 self.roles.entries().map(|(_, value)| value.name())
676 }
677 }
678}