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
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
153impl UpdateOrderParams {
154 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 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 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 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 pub fn token(&self) -> Option<Pubkey> {
456 optional_address(&self.token).copied()
457 }
458
459 pub fn account(&self) -> Option<Pubkey> {
461 optional_address(&self.account).copied()
462 }
463
464 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 pub fn side(&self) -> crate::Result<order::OrderSide> {
512 self.side.try_into().map_err(crate::Error::custom)
513 }
514
515 pub fn kind(&self) -> crate::Result<order::OrderKind> {
517 self.kind.try_into().map_err(crate::Error::custom)
518 }
519
520 pub fn position(&self) -> Option<&Pubkey> {
522 optional_address(&self.position)
523 }
524
525 #[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 pub fn kind(&self) -> crate::Result<PositionKind> {
542 self.kind.try_into().map_err(crate::Error::custom)
543 }
544 }
545
546 impl Glv {
547 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 pub fn market_config(&self, market_token: &Pubkey) -> Option<&GlvMarketConfig> {
556 self.markets.get(market_token)
557 }
558
559 pub fn num_markets(&self) -> usize {
561 self.markets.len()
562 }
563
564 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 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 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 pub fn get_flag(&self, flag: TradeFlag) -> bool {
611 let map = TradeFlagContainer::from_value(self.flags);
612 map.get_flag(flag)
613 }
614
615 pub fn is_long(&self) -> bool {
617 self.get_flag(TradeFlag::IsLong)
618 }
619
620 pub fn is_collateral_long(&self) -> bool {
622 self.get_flag(TradeFlag::IsCollateralLong)
623 }
624
625 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 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 pub const ROLE_ENABLED: u8 = u8::MAX;
656
657 pub fn name(&self) -> crate::Result<&str> {
659 bytes_to_fixed_str(&self.name).map_err(crate::Error::custom)
660 }
661
662 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 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 pub fn members(&self) -> impl Iterator<Item = Pubkey> + '_ {
701 self.members
702 .entries()
703 .map(|(key, _)| Pubkey::new_from_array(*key))
704 }
705
706 pub fn roles(&self) -> impl Iterator<Item = crate::Result<&str>> + '_ {
708 self.roles.entries().map(|(_, value)| value.name())
709 }
710 }
711}