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