gmsol_programs/utils/
store.rs

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, UpdateOrderParams},
8};
9
10/// Referral Code Bytes.
11pub 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    /// Returns whether the pool is a pure pool.
27    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    /// Get claimable time window size.
64    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    /// Get claimable time window index for the given timestamp.
70    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    /// Get claimable time key for the given timestamp.
80    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    /// The length of referral code.
88    pub const LEN: usize = std::mem::size_of::<ReferralCodeBytes>();
89
90    /// Decode the given code string to code bytes.
91    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    /// Encode the given code to code string.
109    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
153impl UpdateOrderParams {
154    /// Is empty.
155    pub fn is_empty(&self) -> bool {
156        self.size_delta_value.is_none()
157            && self.acceptable_price.is_none()
158            && self.trigger_price.is_none()
159            && self.min_output.is_none()
160            && self.valid_from_ts.is_none()
161    }
162}
163
164#[cfg(feature = "gmsol-model")]
165mod model {
166    use gmsol_model::ClockKind;
167
168    use crate::gmsol_store::types::Clocks;
169
170    impl Clocks {
171        /// Get clock value by [`ClockKind`].
172        pub fn get(&self, kind: ClockKind) -> Option<i64> {
173            let clock = match kind {
174                ClockKind::PriceImpactDistribution => self.price_impact_distribution,
175                ClockKind::Borrowing => self.borrowing,
176                ClockKind::Funding => self.funding,
177                ClockKind::AdlForLong => self.adl_for_long,
178                ClockKind::AdlForShort => self.adl_for_short,
179                _ => return None,
180            };
181            Some(clock)
182        }
183    }
184}
185
186#[cfg(feature = "gmsol-utils")]
187mod utils {
188    use anchor_lang::prelude::Pubkey;
189    use gmsol_utils::{
190        action::{ActionCallbackKind, ActionFlag, ActionState, MAX_ACTION_FLAGS},
191        fixed_str::bytes_to_fixed_str,
192        glv::{GlvMarketFlag, MAX_GLV_MARKET_FLAGS},
193        impl_fixed_map, impl_flags,
194        market::{
195            self, HasMarketMeta, MarketConfigFactor, MarketConfigFlag, MarketConfigKey, MarketFlag,
196            VirtualInventoryFlag, MAX_MARKET_CONFIG_FACTORS, MAX_MARKET_CONFIG_FLAGS,
197            MAX_MARKET_FLAGS, MAX_VIRTUAL_INVENTORY_FLAGS,
198        },
199        order::{self, OrderFlag, PositionKind, TradeFlag, TradeFlagContainer, MAX_ORDER_FLAGS},
200        pubkey::{self, optional_address},
201        swap::{self, HasSwapParams},
202        token_config::{self, TokensCollector},
203    };
204
205    use crate::gmsol_store::{
206        accounts::{Glv, Market, Position},
207        events::TradeEvent,
208        types::{
209            ActionFlagContainer, ActionHeader, GlvMarketConfig, GlvMarketFlagContainer, GlvMarkets,
210            GlvMarketsEntry, MarketConfig, MarketConfigFactorContainer, MarketConfigFlagContainer,
211            MarketFlagContainer, MarketMeta, Members, MembersEntry, OrderActionParams,
212            OrderFlagContainer, OrderKind, RoleMap, RoleMapEntry, RoleMetadata, RoleStore,
213            SwapActionParams, TokenAndAccount, Tokens, TokensEntry, UpdateTokenConfigParams,
214            VirtualInventoryFlagContainer,
215        },
216    };
217
218    const MAX_TOKENS: usize = 256;
219    const MAX_ALLOWED_NUMBER_OF_MARKETS: usize = 96;
220    const MAX_ROLES: usize = 32;
221    const MAX_MEMBERS: usize = 64;
222
223    impl_fixed_map!(RoleMap, RoleMetadata, MAX_ROLES);
224
225    impl_fixed_map!(Members, Pubkey, pubkey::to_bytes, u32, MAX_MEMBERS);
226
227    impl_fixed_map!(Tokens, Pubkey, pubkey::to_bytes, u8, MAX_TOKENS);
228    impl_fixed_map!(
229        GlvMarkets,
230        Pubkey,
231        pubkey::to_bytes,
232        GlvMarketConfig,
233        MAX_ALLOWED_NUMBER_OF_MARKETS
234    );
235
236    impl_flags!(ActionFlag, MAX_ACTION_FLAGS, u8);
237    impl_flags!(MarketFlag, MAX_MARKET_FLAGS, u8);
238    impl_flags!(GlvMarketFlag, MAX_GLV_MARKET_FLAGS, u8);
239    impl_flags!(VirtualInventoryFlag, MAX_VIRTUAL_INVENTORY_FLAGS, u8);
240    impl_flags!(MarketConfigFlag, MAX_MARKET_CONFIG_FLAGS, u128);
241    impl_flags!(MarketConfigFactor, MAX_MARKET_CONFIG_FACTORS, u128);
242    impl_flags!(OrderFlag, MAX_ORDER_FLAGS, u8);
243
244    impl From<SwapActionParams> for swap::SwapActionParams {
245        fn from(params: SwapActionParams) -> Self {
246            let SwapActionParams {
247                primary_length,
248                secondary_length,
249                num_tokens,
250                padding_0,
251                current_market_token,
252                paths,
253                tokens,
254            } = params;
255            Self {
256                primary_length,
257                secondary_length,
258                num_tokens,
259                padding_0,
260                current_market_token,
261                paths,
262                tokens,
263            }
264        }
265    }
266
267    impl From<MarketMeta> for market::MarketMeta {
268        fn from(meta: MarketMeta) -> Self {
269            let MarketMeta {
270                market_token_mint,
271                index_token_mint,
272                long_token_mint,
273                short_token_mint,
274            } = meta;
275            Self {
276                market_token_mint,
277                index_token_mint,
278                long_token_mint,
279                short_token_mint,
280            }
281        }
282    }
283
284    impl Market {
285        /// Get name.
286        pub fn name(&self) -> crate::Result<&str> {
287            bytes_to_fixed_str(&self.name).map_err(crate::Error::custom)
288        }
289    }
290
291    impl HasMarketMeta for Market {
292        fn market_meta(&self) -> &market::MarketMeta {
293            bytemuck::cast_ref(&self.meta)
294        }
295    }
296
297    impl MarketConfig {
298        /// Get config by [`MarketConfigKey`].
299        pub fn get(&self, key: MarketConfigKey) -> Option<&u128> {
300            let value = match key {
301                MarketConfigKey::SwapImpactExponent => &self.swap_impact_exponent,
302                MarketConfigKey::SwapImpactPositiveFactor => &self.swap_impact_positive_factor,
303                MarketConfigKey::SwapImpactNegativeFactor => &self.swap_impact_negative_factor,
304                MarketConfigKey::SwapFeeReceiverFactor => &self.swap_fee_receiver_factor,
305                MarketConfigKey::SwapFeeFactorForPositiveImpact => {
306                    &self.swap_fee_factor_for_positive_impact
307                }
308                MarketConfigKey::SwapFeeFactorForNegativeImpact => {
309                    &self.swap_fee_factor_for_negative_impact
310                }
311                MarketConfigKey::MinPositionSizeUsd => &self.min_position_size_usd,
312                MarketConfigKey::MinCollateralValue => &self.min_collateral_value,
313                MarketConfigKey::MinCollateralFactor => &self.min_collateral_factor,
314                MarketConfigKey::MinCollateralFactorForOpenInterestMultiplierForLong => {
315                    &self.min_collateral_factor_for_open_interest_multiplier_for_long
316                }
317                MarketConfigKey::MinCollateralFactorForOpenInterestMultiplierForShort => {
318                    &self.min_collateral_factor_for_open_interest_multiplier_for_short
319                }
320                MarketConfigKey::MaxPositivePositionImpactFactor => {
321                    &self.max_positive_position_impact_factor
322                }
323                MarketConfigKey::MaxNegativePositionImpactFactor => {
324                    &self.max_negative_position_impact_factor
325                }
326                MarketConfigKey::MaxPositionImpactFactorForLiquidations => {
327                    &self.max_position_impact_factor_for_liquidations
328                }
329                MarketConfigKey::PositionImpactExponent => &self.position_impact_exponent,
330                MarketConfigKey::PositionImpactPositiveFactor => {
331                    &self.position_impact_positive_factor
332                }
333                MarketConfigKey::PositionImpactNegativeFactor => {
334                    &self.position_impact_negative_factor
335                }
336                MarketConfigKey::OrderFeeReceiverFactor => &self.order_fee_receiver_factor,
337                MarketConfigKey::OrderFeeFactorForPositiveImpact => {
338                    &self.order_fee_factor_for_positive_impact
339                }
340                MarketConfigKey::OrderFeeFactorForNegativeImpact => {
341                    &self.order_fee_factor_for_negative_impact
342                }
343                MarketConfigKey::LiquidationFeeReceiverFactor => {
344                    &self.liquidation_fee_receiver_factor
345                }
346                MarketConfigKey::LiquidationFeeFactor => &self.liquidation_fee_factor,
347                MarketConfigKey::PositionImpactDistributeFactor => {
348                    &self.position_impact_distribute_factor
349                }
350                MarketConfigKey::MinPositionImpactPoolAmount => {
351                    &self.min_position_impact_pool_amount
352                }
353                MarketConfigKey::BorrowingFeeReceiverFactor => &self.borrowing_fee_receiver_factor,
354                MarketConfigKey::BorrowingFeeFactorForLong => &self.borrowing_fee_factor_for_long,
355                MarketConfigKey::BorrowingFeeFactorForShort => &self.borrowing_fee_factor_for_short,
356                MarketConfigKey::BorrowingFeeExponentForLong => {
357                    &self.borrowing_fee_exponent_for_long
358                }
359                MarketConfigKey::BorrowingFeeExponentForShort => {
360                    &self.borrowing_fee_exponent_for_short
361                }
362                MarketConfigKey::BorrowingFeeOptimalUsageFactorForLong => {
363                    &self.borrowing_fee_optimal_usage_factor_for_long
364                }
365                MarketConfigKey::BorrowingFeeOptimalUsageFactorForShort => {
366                    &self.borrowing_fee_optimal_usage_factor_for_short
367                }
368                MarketConfigKey::BorrowingFeeBaseFactorForLong => {
369                    &self.borrowing_fee_base_factor_for_long
370                }
371                MarketConfigKey::BorrowingFeeBaseFactorForShort => {
372                    &self.borrowing_fee_base_factor_for_short
373                }
374                MarketConfigKey::BorrowingFeeAboveOptimalUsageFactorForLong => {
375                    &self.borrowing_fee_above_optimal_usage_factor_for_long
376                }
377                MarketConfigKey::BorrowingFeeAboveOptimalUsageFactorForShort => {
378                    &self.borrowing_fee_above_optimal_usage_factor_for_short
379                }
380                MarketConfigKey::FundingFeeExponent => &self.funding_fee_exponent,
381                MarketConfigKey::FundingFeeFactor => &self.funding_fee_factor,
382                MarketConfigKey::FundingFeeMaxFactorPerSecond => {
383                    &self.funding_fee_max_factor_per_second
384                }
385                MarketConfigKey::FundingFeeMinFactorPerSecond => {
386                    &self.funding_fee_min_factor_per_second
387                }
388                MarketConfigKey::FundingFeeIncreaseFactorPerSecond => {
389                    &self.funding_fee_increase_factor_per_second
390                }
391                MarketConfigKey::FundingFeeDecreaseFactorPerSecond => {
392                    &self.funding_fee_decrease_factor_per_second
393                }
394                MarketConfigKey::FundingFeeThresholdForStableFunding => {
395                    &self.funding_fee_threshold_for_stable_funding
396                }
397                MarketConfigKey::FundingFeeThresholdForDecreaseFunding => {
398                    &self.funding_fee_threshold_for_decrease_funding
399                }
400                MarketConfigKey::ReserveFactor => &self.reserve_factor,
401                MarketConfigKey::OpenInterestReserveFactor => &self.open_interest_reserve_factor,
402                MarketConfigKey::MaxPnlFactorForLongDeposit => {
403                    &self.max_pnl_factor_for_long_deposit
404                }
405                MarketConfigKey::MaxPnlFactorForShortDeposit => {
406                    &self.max_pnl_factor_for_short_deposit
407                }
408                MarketConfigKey::MaxPnlFactorForLongWithdrawal => {
409                    &self.max_pnl_factor_for_long_withdrawal
410                }
411                MarketConfigKey::MaxPnlFactorForShortWithdrawal => {
412                    &self.max_pnl_factor_for_short_withdrawal
413                }
414                MarketConfigKey::MaxPnlFactorForLongTrader => &self.max_pnl_factor_for_long_trader,
415                MarketConfigKey::MaxPnlFactorForShortTrader => {
416                    &self.max_pnl_factor_for_short_trader
417                }
418                MarketConfigKey::MaxPnlFactorForLongAdl => &self.max_pnl_factor_for_long_adl,
419                MarketConfigKey::MaxPnlFactorForShortAdl => &self.max_pnl_factor_for_short_adl,
420                MarketConfigKey::MinPnlFactorAfterLongAdl => &self.min_pnl_factor_after_long_adl,
421                MarketConfigKey::MinPnlFactorAfterShortAdl => &self.min_pnl_factor_after_short_adl,
422                MarketConfigKey::MaxPoolAmountForLongToken => &self.max_pool_amount_for_long_token,
423                MarketConfigKey::MaxPoolAmountForShortToken => {
424                    &self.max_pool_amount_for_short_token
425                }
426                MarketConfigKey::MaxPoolValueForDepositForLongToken => {
427                    &self.max_pool_value_for_deposit_for_long_token
428                }
429                MarketConfigKey::MaxPoolValueForDepositForShortToken => {
430                    &self.max_pool_value_for_deposit_for_short_token
431                }
432                MarketConfigKey::MaxOpenInterestForLong => &self.max_open_interest_for_long,
433                MarketConfigKey::MaxOpenInterestForShort => &self.max_open_interest_for_short,
434                MarketConfigKey::MinTokensForFirstDeposit => &self.min_tokens_for_first_deposit,
435                MarketConfigKey::MinCollateralFactorForLiquidation => {
436                    &self.min_collateral_factor_for_liquidation
437                }
438                MarketConfigKey::MarketClosedMinCollateralFactorForLiquidation => {
439                    &self.market_closed_min_collateral_factor_for_liquidation
440                }
441                MarketConfigKey::MarketClosedBorrowingFeeBaseFactor => {
442                    &self.market_closed_borrowing_fee_base_factor
443                }
444                MarketConfigKey::MarketClosedBorrowingFeeAboveOptimalUsageFactor => {
445                    &self.market_closed_borrowing_fee_above_optimal_usage_factor
446                }
447                _ => return None,
448            };
449            Some(value)
450        }
451    }
452
453    impl TokenAndAccount {
454        /// Get token.
455        pub fn token(&self) -> Option<Pubkey> {
456            optional_address(&self.token).copied()
457        }
458
459        /// Get account.
460        pub fn account(&self) -> Option<Pubkey> {
461            optional_address(&self.account).copied()
462        }
463
464        /// Get token and account.
465        pub fn token_and_account(&self) -> Option<(Pubkey, Pubkey)> {
466            let token = self.token()?;
467            let account = self.account()?;
468            Some((token, account))
469        }
470    }
471
472    impl From<OrderKind> for order::OrderKind {
473        fn from(value: OrderKind) -> Self {
474            match value {
475                OrderKind::Liquidation => Self::Liquidation,
476                OrderKind::AutoDeleveraging => Self::AutoDeleveraging,
477                OrderKind::MarketSwap => Self::MarketSwap,
478                OrderKind::MarketIncrease => Self::MarketIncrease,
479                OrderKind::MarketDecrease => Self::MarketDecrease,
480                OrderKind::LimitSwap => Self::LimitSwap,
481                OrderKind::LimitIncrease => Self::LimitIncrease,
482                OrderKind::LimitDecrease => Self::LimitDecrease,
483                OrderKind::StopLossDecrease => Self::StopLossDecrease,
484            }
485        }
486    }
487
488    impl TryFrom<order::OrderKind> for OrderKind {
489        type Error = crate::Error;
490
491        fn try_from(value: order::OrderKind) -> Result<Self, Self::Error> {
492            match value {
493                order::OrderKind::Liquidation => Ok(Self::Liquidation),
494                order::OrderKind::AutoDeleveraging => Ok(Self::AutoDeleveraging),
495                order::OrderKind::MarketSwap => Ok(Self::MarketSwap),
496                order::OrderKind::MarketIncrease => Ok(Self::MarketIncrease),
497                order::OrderKind::MarketDecrease => Ok(Self::MarketDecrease),
498                order::OrderKind::LimitSwap => Ok(Self::LimitSwap),
499                order::OrderKind::LimitIncrease => Ok(Self::LimitIncrease),
500                order::OrderKind::LimitDecrease => Ok(Self::LimitDecrease),
501                order::OrderKind::StopLossDecrease => Ok(Self::StopLossDecrease),
502                kind => Err(crate::Error::custom(format!(
503                    "unsupported order kind: {kind}"
504                ))),
505            }
506        }
507    }
508
509    impl OrderActionParams {
510        /// Get order side.
511        pub fn side(&self) -> crate::Result<order::OrderSide> {
512            self.side.try_into().map_err(crate::Error::custom)
513        }
514
515        /// Get order kind.
516        pub fn kind(&self) -> crate::Result<order::OrderKind> {
517            self.kind.try_into().map_err(crate::Error::custom)
518        }
519
520        /// Get position.
521        pub fn position(&self) -> Option<&Pubkey> {
522            optional_address(&self.position)
523        }
524
525        /// Get decrease position swap type.
526        #[cfg(feature = "model")]
527        pub fn decrease_position_swap_type(
528            &self,
529        ) -> crate::Result<gmsol_model::action::decrease_position::DecreasePositionSwapType>
530        {
531            let ty = self
532                .decrease_position_swap_type
533                .try_into()
534                .map_err(|_| crate::Error::custom("unknown decrease position swap type"))?;
535            Ok(ty)
536        }
537    }
538
539    impl Position {
540        /// Get position kind.
541        pub fn kind(&self) -> crate::Result<PositionKind> {
542            self.kind.try_into().map_err(crate::Error::custom)
543        }
544    }
545
546    impl Glv {
547        /// Get all market tokens.
548        pub fn market_tokens(&self) -> impl Iterator<Item = Pubkey> + '_ {
549            self.markets
550                .entries()
551                .map(|(key, _)| Pubkey::new_from_array(*key))
552        }
553
554        /// Get [`GlvMarketConfig`] for the given market.
555        pub fn market_config(&self, market_token: &Pubkey) -> Option<&GlvMarketConfig> {
556            self.markets.get(market_token)
557        }
558
559        /// Get the total number of markets.
560        pub fn num_markets(&self) -> usize {
561            self.markets.len()
562        }
563
564        /// Create a new [`TokensCollector`].
565        pub fn tokens_collector(&self, action: Option<&impl HasSwapParams>) -> TokensCollector {
566            let mut collector = TokensCollector::new(action, self.num_markets());
567            if action.is_none() {
568                collector.insert_token(&self.long_token);
569                collector.insert_token(&self.short_token);
570            }
571            collector
572        }
573    }
574
575    impl From<token_config::UpdateTokenConfigParams> for UpdateTokenConfigParams {
576        fn from(params: token_config::UpdateTokenConfigParams) -> Self {
577            let token_config::UpdateTokenConfigParams {
578                heartbeat_duration,
579                precision,
580                feeds,
581                timestamp_adjustments,
582                expected_provider,
583            } = params;
584            Self {
585                heartbeat_duration,
586                precision,
587                feeds,
588                timestamp_adjustments,
589                expected_provider,
590            }
591        }
592    }
593
594    impl ActionHeader {
595        /// Get action state.
596        pub fn action_state(&self) -> crate::Result<ActionState> {
597            ActionState::try_from(self.action_state)
598                .map_err(|_| crate::Error::custom("unknown action state"))
599        }
600
601        /// Get callback kind.
602        pub fn callback_kind(&self) -> crate::Result<ActionCallbackKind> {
603            ActionCallbackKind::try_from(self.callback_kind)
604                .map_err(|_| crate::Error::custom("unknown callback kind"))
605        }
606    }
607
608    impl TradeEvent {
609        /// Get trade data flag.
610        pub fn get_flag(&self, flag: TradeFlag) -> bool {
611            let map = TradeFlagContainer::from_value(self.flags);
612            map.get_flag(flag)
613        }
614
615        /// Return whether the position side is long.
616        pub fn is_long(&self) -> bool {
617            self.get_flag(TradeFlag::IsLong)
618        }
619
620        /// Return whether the collateral side is long.
621        pub fn is_collateral_long(&self) -> bool {
622            self.get_flag(TradeFlag::IsCollateralLong)
623        }
624
625        /// Create position from this event.
626        pub fn to_position(&self, meta: &impl HasMarketMeta) -> Position {
627            let mut position = Position::default();
628
629            let kind = if self.is_long() {
630                PositionKind::Long
631            } else {
632                PositionKind::Short
633            };
634
635            let collateral_token = if self.is_collateral_long() {
636                meta.market_meta().long_token_mint
637            } else {
638                meta.market_meta().short_token_mint
639            };
640
641            position.kind = kind as u8;
642            // Note: there's no need to provide a correct bump here for now.
643            position.bump = 0;
644            position.store = self.store;
645            position.owner = self.user;
646            position.market_token = self.market_token;
647            position.collateral_token = collateral_token;
648            position.state = self.after.into();
649            position
650        }
651    }
652
653    impl RoleMetadata {
654        /// A `u8` value indicates that this role is enabled.
655        pub const ROLE_ENABLED: u8 = u8::MAX;
656
657        /// Get the name of this role.
658        pub fn name(&self) -> crate::Result<&str> {
659            bytes_to_fixed_str(&self.name).map_err(crate::Error::custom)
660        }
661
662        /// Is enabled.
663        pub fn is_enabled(&self) -> bool {
664            self.enabled == Self::ROLE_ENABLED
665        }
666    }
667
668    impl RoleStore {
669        fn enabled_role_index(&self, role: &str) -> crate::Result<Option<u8>> {
670            if let Some(metadata) = self.roles.get(role) {
671                if metadata.name()? != role {
672                    return Err(crate::Error::custom("invalid role store"));
673                }
674                if !metadata.is_enabled() {
675                    return Err(crate::Error::custom("the given role is disabled"));
676                }
677                Ok(Some(metadata.index))
678            } else {
679                Ok(None)
680            }
681        }
682
683        /// Returns whether the address has the give role.
684        pub fn has_role(&self, authority: &Pubkey, role: &str) -> crate::Result<bool> {
685            use gmsol_utils::bitmaps::Bitmap;
686            type RoleBitmap = Bitmap<MAX_ROLES>;
687
688            let value = self
689                .members
690                .get(authority)
691                .ok_or_else(|| crate::Error::custom("not a member"))?;
692            let index = self
693                .enabled_role_index(role)?
694                .ok_or_else(|| crate::Error::custom("no such role"))?;
695            let bitmap = RoleBitmap::from_value(*value);
696            Ok(bitmap.get(index as usize))
697        }
698
699        /// Returns all members.
700        pub fn members(&self) -> impl Iterator<Item = Pubkey> + '_ {
701            self.members
702                .entries()
703                .map(|(key, _)| Pubkey::new_from_array(*key))
704        }
705
706        /// Returns all roles.
707        pub fn roles(&self) -> impl Iterator<Item = crate::Result<&str>> + '_ {
708            self.roles.entries().map(|(_, value)| value.name())
709        }
710    }
711}