1use std::num::NonZeroU64;
2
3use bytemuck::Zeroable;
4
5use crate::gmsol_store::{
6 accounts::{Glv, GtExchange, Market, Position, ReferralCodeV2, Store},
7 types::ActionHeader,
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 Glv {
20 fn default() -> Self {
21 Zeroable::zeroed()
22 }
23}
24
25impl Default for Position {
26 fn default() -> Self {
27 Zeroable::zeroed()
28 }
29}
30
31impl Default for ActionHeader {
32 fn default() -> Self {
33 Zeroable::zeroed()
34 }
35}
36
37impl Default for GtExchange {
38 fn default() -> Self {
39 Zeroable::zeroed()
40 }
41}
42
43impl Store {
44 pub fn claimable_time_window(&self) -> crate::Result<NonZeroU64> {
46 NonZeroU64::new(self.amount.claimable_time_window)
47 .ok_or_else(|| crate::Error::custom("claimable time window cannot be zero"))
48 }
49
50 pub fn claimable_time_window_index(&self, timestamp: i64) -> crate::Result<i64> {
52 let window: i64 = self
53 .claimable_time_window()?
54 .get()
55 .try_into()
56 .map_err(crate::Error::custom)?;
57 Ok(timestamp / window)
58 }
59
60 pub fn claimable_time_key(&self, timestamp: i64) -> crate::Result<[u8; 8]> {
62 let index = self.claimable_time_window_index(timestamp)?;
63 Ok(index.to_le_bytes())
64 }
65}
66
67impl ReferralCodeV2 {
68 pub const LEN: usize = std::mem::size_of::<ReferralCodeBytes>();
70
71 pub fn decode(code: &str) -> crate::Result<ReferralCodeBytes> {
73 if code.is_empty() {
74 return Err(crate::Error::custom("empty code is not supported"));
75 }
76 let code = bs58::decode(code)
77 .into_vec()
78 .map_err(crate::Error::custom)?;
79 if code.len() > Self::LEN {
80 return Err(crate::Error::custom("the code is too long"));
81 }
82 let padding = Self::LEN - code.len();
83 let mut code_bytes = ReferralCodeBytes::default();
84 code_bytes[padding..].copy_from_slice(&code);
85
86 Ok(code_bytes)
87 }
88
89 pub fn encode(code: &ReferralCodeBytes, skip_leading_ones: bool) -> String {
91 let code = bs58::encode(code).into_string();
92 if skip_leading_ones {
93 code.trim_start_matches('1').to_owned()
94 } else {
95 code
96 }
97 }
98}
99
100#[cfg(feature = "gmsol-utils")]
101mod utils {
102 use anchor_lang::prelude::Pubkey;
103 use gmsol_utils::{
104 action::{ActionFlag, MAX_ACTION_FLAGS},
105 impl_fixed_map, impl_flags, market,
106 order::{self, PositionKind},
107 pubkey::{self, optional_address},
108 swap::{self, HasSwapParams},
109 token_config::{self, TokensCollector},
110 };
111
112 use crate::gmsol_store::{
113 accounts::{Glv, Position},
114 types::{
115 ActionFlagContainer, GlvMarketConfig, GlvMarkets, GlvMarketsEntry, MarketMeta,
116 OrderActionParams, OrderKind, SwapActionParams, TokenAndAccount, Tokens, TokensEntry,
117 UpdateTokenConfigParams,
118 },
119 };
120
121 const MAX_TOKENS: usize = 256;
122 const MAX_ALLOWED_NUMBER_OF_MARKETS: usize = 96;
123
124 impl_fixed_map!(Tokens, Pubkey, pubkey::to_bytes, u8, MAX_TOKENS);
125 impl_fixed_map!(
126 GlvMarkets,
127 Pubkey,
128 pubkey::to_bytes,
129 GlvMarketConfig,
130 MAX_ALLOWED_NUMBER_OF_MARKETS
131 );
132
133 impl_flags!(ActionFlag, MAX_ACTION_FLAGS, u8);
134
135 impl From<SwapActionParams> for swap::SwapActionParams {
136 fn from(params: SwapActionParams) -> Self {
137 let SwapActionParams {
138 primary_length,
139 secondary_length,
140 num_tokens,
141 padding_0,
142 current_market_token,
143 paths,
144 tokens,
145 } = params;
146 Self {
147 primary_length,
148 secondary_length,
149 num_tokens,
150 padding_0,
151 current_market_token,
152 paths,
153 tokens,
154 }
155 }
156 }
157
158 impl From<MarketMeta> for market::MarketMeta {
159 fn from(meta: MarketMeta) -> Self {
160 let MarketMeta {
161 market_token_mint,
162 index_token_mint,
163 long_token_mint,
164 short_token_mint,
165 } = meta;
166 Self {
167 market_token_mint,
168 index_token_mint,
169 long_token_mint,
170 short_token_mint,
171 }
172 }
173 }
174
175 impl TokenAndAccount {
176 pub fn token(&self) -> Option<Pubkey> {
178 optional_address(&self.token).copied()
179 }
180
181 pub fn account(&self) -> Option<Pubkey> {
183 optional_address(&self.account).copied()
184 }
185
186 pub fn token_and_account(&self) -> Option<(Pubkey, Pubkey)> {
188 let token = self.token()?;
189 let account = self.account()?;
190 Some((token, account))
191 }
192 }
193
194 impl From<OrderKind> for order::OrderKind {
195 fn from(value: OrderKind) -> Self {
196 match value {
197 OrderKind::Liquidation => Self::Liquidation,
198 OrderKind::AutoDeleveraging => Self::AutoDeleveraging,
199 OrderKind::MarketSwap => Self::MarketSwap,
200 OrderKind::MarketIncrease => Self::MarketIncrease,
201 OrderKind::MarketDecrease => Self::MarketDecrease,
202 OrderKind::LimitSwap => Self::LimitSwap,
203 OrderKind::LimitIncrease => Self::LimitIncrease,
204 OrderKind::LimitDecrease => Self::LimitDecrease,
205 OrderKind::StopLossDecrease => Self::StopLossDecrease,
206 }
207 }
208 }
209
210 impl TryFrom<order::OrderKind> for OrderKind {
211 type Error = crate::Error;
212
213 fn try_from(value: order::OrderKind) -> Result<Self, Self::Error> {
214 match value {
215 order::OrderKind::Liquidation => Ok(Self::Liquidation),
216 order::OrderKind::AutoDeleveraging => Ok(Self::AutoDeleveraging),
217 order::OrderKind::MarketSwap => Ok(Self::MarketSwap),
218 order::OrderKind::MarketIncrease => Ok(Self::MarketIncrease),
219 order::OrderKind::MarketDecrease => Ok(Self::MarketDecrease),
220 order::OrderKind::LimitSwap => Ok(Self::LimitSwap),
221 order::OrderKind::LimitIncrease => Ok(Self::LimitIncrease),
222 order::OrderKind::LimitDecrease => Ok(Self::LimitDecrease),
223 order::OrderKind::StopLossDecrease => Ok(Self::StopLossDecrease),
224 kind => Err(crate::Error::custom(format!(
225 "unsupported order kind: {kind}"
226 ))),
227 }
228 }
229 }
230
231 impl OrderActionParams {
232 pub fn side(&self) -> crate::Result<order::OrderSide> {
234 self.side.try_into().map_err(crate::Error::custom)
235 }
236
237 pub fn kind(&self) -> crate::Result<order::OrderKind> {
239 self.kind.try_into().map_err(crate::Error::custom)
240 }
241
242 pub fn position(&self) -> Option<&Pubkey> {
244 optional_address(&self.position)
245 }
246 }
247
248 impl Position {
249 pub fn kind(&self) -> crate::Result<PositionKind> {
251 self.kind.try_into().map_err(crate::Error::custom)
252 }
253 }
254
255 impl Glv {
256 pub fn market_tokens(&self) -> impl Iterator<Item = Pubkey> + '_ {
258 self.markets
259 .entries()
260 .map(|(key, _)| Pubkey::new_from_array(*key))
261 }
262
263 pub fn market_config(&self, market_token: &Pubkey) -> Option<&GlvMarketConfig> {
265 self.markets.get(market_token)
266 }
267
268 pub fn num_markets(&self) -> usize {
270 self.markets.len()
271 }
272
273 pub fn tokens_collector(&self, action: Option<&impl HasSwapParams>) -> TokensCollector {
275 TokensCollector::new(action, self.num_markets())
276 }
277 }
278
279 impl From<token_config::UpdateTokenConfigParams> for UpdateTokenConfigParams {
280 fn from(params: token_config::UpdateTokenConfigParams) -> Self {
281 let token_config::UpdateTokenConfigParams {
282 heartbeat_duration,
283 precision,
284 feeds,
285 timestamp_adjustments,
286 expected_provider,
287 } = params;
288 Self {
289 heartbeat_duration,
290 precision,
291 feeds,
292 timestamp_adjustments,
293 expected_provider,
294 }
295 }
296 }
297}