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},
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
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        /// Get clock value by [`ClockKind`].
161        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        /// Get name.
270        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        /// Get config by [`MarketConfigKey`].
283        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        /// Get token.
427        pub fn token(&self) -> Option<Pubkey> {
428            optional_address(&self.token).copied()
429        }
430
431        /// Get account.
432        pub fn account(&self) -> Option<Pubkey> {
433            optional_address(&self.account).copied()
434        }
435
436        /// Get token and account.
437        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        /// Get order side.
483        pub fn side(&self) -> crate::Result<order::OrderSide> {
484            self.side.try_into().map_err(crate::Error::custom)
485        }
486
487        /// Get order kind.
488        pub fn kind(&self) -> crate::Result<order::OrderKind> {
489            self.kind.try_into().map_err(crate::Error::custom)
490        }
491
492        /// Get position.
493        pub fn position(&self) -> Option<&Pubkey> {
494            optional_address(&self.position)
495        }
496
497        /// Get decrease position swap type.
498        #[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        /// Get position kind.
513        pub fn kind(&self) -> crate::Result<PositionKind> {
514            self.kind.try_into().map_err(crate::Error::custom)
515        }
516    }
517
518    impl Glv {
519        /// Get all market tokens.
520        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        /// Get [`GlvMarketConfig`] for the given market.
527        pub fn market_config(&self, market_token: &Pubkey) -> Option<&GlvMarketConfig> {
528            self.markets.get(market_token)
529        }
530
531        /// Get the total number of markets.
532        pub fn num_markets(&self) -> usize {
533            self.markets.len()
534        }
535
536        /// Create a new [`TokensCollector`].
537        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        /// Get action state.
563        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        /// Get callback kind.
569        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        /// Get trade data flag.
577        pub fn get_flag(&self, flag: TradeFlag) -> bool {
578            let map = TradeFlagContainer::from_value(self.flags);
579            map.get_flag(flag)
580        }
581
582        /// Return whether the position side is long.
583        pub fn is_long(&self) -> bool {
584            self.get_flag(TradeFlag::IsLong)
585        }
586
587        /// Return whether the collateral side is long.
588        pub fn is_collateral_long(&self) -> bool {
589            self.get_flag(TradeFlag::IsCollateralLong)
590        }
591
592        /// Create position from this event.
593        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            // Note: there's no need to provide a correct bump here for now.
610            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        /// A `u8` value indicates that this role is enabled.
622        pub const ROLE_ENABLED: u8 = u8::MAX;
623
624        /// Get the name of this role.
625        pub fn name(&self) -> crate::Result<&str> {
626            bytes_to_fixed_str(&self.name).map_err(crate::Error::custom)
627        }
628
629        /// Is enabled.
630        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        /// Returns whether the address has the give role.
651        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        /// Returns all members.
667        pub fn members(&self) -> impl Iterator<Item = Pubkey> + '_ {
668            self.members
669                .entries()
670                .map(|(key, _)| Pubkey::new_from_array(*key))
671        }
672
673        /// Returns all roles.
674        pub fn roles(&self) -> impl Iterator<Item = crate::Result<&str>> + '_ {
675            self.roles.entries().map(|(_, value)| value.name())
676        }
677    }
678}