1use std::{collections::BTreeSet, ops::Deref};
2
3use anchor_client::{
4 anchor_lang::system_program,
5 solana_sdk::{instruction::AccountMeta, pubkey::Pubkey, signer::Signer},
6};
7use anchor_spl::associated_token::get_associated_token_address_with_program_id;
8use gmsol_solana_utils::transaction_builder::TransactionBuilder;
9use gmsol_store::{
10 accounts, instruction,
11 states::{
12 glv::{GlvMarketFlag, UpdateGlvParams},
13 Market,
14 },
15};
16
17mod deposit;
18mod shift;
19mod withdrawal;
20
21pub use self::{
22 deposit::{
23 CloseGlvDepositBuilder, CloseGlvDepositHint, CreateGlvDepositBuilder, CreateGlvDepositHint,
24 ExecuteGlvDepositBuilder, ExecuteGlvDepositHint,
25 },
26 shift::{
27 CloseGlvShiftBuilder, CloseGlvShiftHint, CreateGlvShiftBuilder, ExecuteGlvShiftBuilder,
28 ExecuteGlvShiftHint,
29 },
30 withdrawal::{
31 CloseGlvWithdrawalBuilder, CloseGlvWithdrawalHint, CreateGlvWithdrawalBuilder,
32 CreateGlvWithdrawalHint, ExecuteGlvWithdrawalBuilder, ExecuteGlvWithdrawalHint,
33 },
34};
35
36pub trait GlvOps<C> {
38 fn initialize_glv(
40 &self,
41 store: &Pubkey,
42 index: u16,
43 market_tokens: impl IntoIterator<Item = Pubkey>,
44 ) -> crate::Result<(TransactionBuilder<C>, Pubkey)>;
45
46 fn update_glv_market_config(
48 &self,
49 store: &Pubkey,
50 glv_token: &Pubkey,
51 market_token: &Pubkey,
52 max_amount: Option<u64>,
53 max_value: Option<u128>,
54 ) -> TransactionBuilder<C>;
55
56 fn toggle_glv_market_flag(
58 &self,
59 store: &Pubkey,
60 glv_token: &Pubkey,
61 market_token: &Pubkey,
62 flag: GlvMarketFlag,
63 enable: bool,
64 ) -> TransactionBuilder<C>;
65
66 fn update_glv_config(
68 &self,
69 store: &Pubkey,
70 glv_token: &Pubkey,
71 params: UpdateGlvParams,
72 ) -> TransactionBuilder<C>;
73
74 fn insert_glv_market(
76 &self,
77 store: &Pubkey,
78 glv_token: &Pubkey,
79 market_token: &Pubkey,
80 token_program_id: Option<&Pubkey>,
81 ) -> TransactionBuilder<C>;
82
83 fn remove_glv_market(
85 &self,
86 store: &Pubkey,
87 glv_token: &Pubkey,
88 market_token: &Pubkey,
89 token_program_id: Option<&Pubkey>,
90 ) -> TransactionBuilder<C>;
91
92 fn create_glv_deposit(
94 &self,
95 store: &Pubkey,
96 glv_token: &Pubkey,
97 market_token: &Pubkey,
98 ) -> CreateGlvDepositBuilder<C>;
99
100 fn close_glv_deposit(&self, glv_deposit: &Pubkey) -> CloseGlvDepositBuilder<C>;
102
103 fn execute_glv_deposit(
105 &self,
106 oracle: &Pubkey,
107 glv_deposit: &Pubkey,
108 cancel_on_execution_error: bool,
109 ) -> ExecuteGlvDepositBuilder<C>;
110
111 fn create_glv_withdrawal(
112 &self,
113 store: &Pubkey,
114 glv_token: &Pubkey,
115 market_token: &Pubkey,
116 amount: u64,
117 ) -> CreateGlvWithdrawalBuilder<C>;
118
119 fn close_glv_withdrawal(&self, glv_withdrawal: &Pubkey) -> CloseGlvWithdrawalBuilder<C>;
121
122 fn execute_glv_withdrawal(
124 &self,
125 oracle: &Pubkey,
126 glv_withdrawal: &Pubkey,
127 cancel_on_execution_error: bool,
128 ) -> ExecuteGlvWithdrawalBuilder<C>;
129
130 fn create_glv_shift(
131 &self,
132 store: &Pubkey,
133 glv_token: &Pubkey,
134 from_market_token: &Pubkey,
135 to_market_token: &Pubkey,
136 amount: u64,
137 ) -> CreateGlvShiftBuilder<C>;
138
139 fn close_glv_shift(&self, glv_shift: &Pubkey) -> CloseGlvShiftBuilder<C>;
140
141 fn execute_glv_shift(
142 &self,
143 oracle: &Pubkey,
144 glv_shift: &Pubkey,
145 cancel_on_execution_error: bool,
146 ) -> ExecuteGlvShiftBuilder<C>;
147}
148
149impl<C: Deref<Target = impl Signer> + Clone> GlvOps<C> for crate::Client<C> {
150 fn initialize_glv(
151 &self,
152 store: &Pubkey,
153 index: u16,
154 market_tokens: impl IntoIterator<Item = Pubkey>,
155 ) -> crate::Result<(TransactionBuilder<C>, Pubkey)> {
156 let authority = self.payer();
157 let glv_token = self.find_glv_token_address(store, index);
158 let glv = self.find_glv_address(&glv_token);
159 let market_token_program_id = anchor_spl::token::ID;
160
161 let (accounts, length) = split_to_accounts(
162 market_tokens,
163 &glv,
164 store,
165 self.store_program_id(),
166 &market_token_program_id,
167 true,
168 );
169
170 let rpc = self
171 .store_transaction()
172 .anchor_accounts(accounts::InitializeGlv {
173 authority,
174 store: *store,
175 glv_token,
176 glv,
177 system_program: system_program::ID,
178 token_program: anchor_spl::token_2022::ID,
179 market_token_program: market_token_program_id,
180 associated_token_program: anchor_spl::associated_token::ID,
181 })
182 .anchor_args(instruction::InitializeGlv {
183 index,
184 length: length
185 .try_into()
186 .map_err(|_| crate::Error::invalid_argument("too many markets"))?,
187 })
188 .accounts(accounts);
189 Ok((rpc, glv_token))
190 }
191
192 fn update_glv_market_config(
193 &self,
194 store: &Pubkey,
195 glv_token: &Pubkey,
196 market_token: &Pubkey,
197 max_amount: Option<u64>,
198 max_value: Option<u128>,
199 ) -> TransactionBuilder<C> {
200 let glv = self.find_glv_address(glv_token);
201 self.store_transaction()
202 .anchor_accounts(accounts::UpdateGlvMarketConfig {
203 authority: self.payer(),
204 store: *store,
205 glv,
206 market_token: *market_token,
207 })
208 .anchor_args(instruction::UpdateGlvMarketConfig {
209 max_amount,
210 max_value,
211 })
212 }
213
214 fn toggle_glv_market_flag(
215 &self,
216 store: &Pubkey,
217 glv_token: &Pubkey,
218 market_token: &Pubkey,
219 flag: GlvMarketFlag,
220 enable: bool,
221 ) -> TransactionBuilder<C> {
222 let glv = self.find_glv_address(glv_token);
223 self.store_transaction()
224 .anchor_accounts(accounts::UpdateGlvMarketConfig {
225 authority: self.payer(),
226 store: *store,
227 glv,
228 market_token: *market_token,
229 })
230 .anchor_args(instruction::ToggleGlvMarketFlag {
231 flag: flag.to_string(),
232 enable,
233 })
234 }
235
236 fn update_glv_config(
237 &self,
238 store: &Pubkey,
239 glv_token: &Pubkey,
240 params: UpdateGlvParams,
241 ) -> TransactionBuilder<C> {
242 let glv = self.find_glv_address(glv_token);
243 self.store_transaction()
244 .anchor_accounts(accounts::UpdateGlvConfig {
245 authority: self.payer(),
246 store: *store,
247 glv,
248 })
249 .anchor_args(instruction::UpdateGlvConfig { params })
250 }
251
252 fn insert_glv_market(
253 &self,
254 store: &Pubkey,
255 glv_token: &Pubkey,
256 market_token: &Pubkey,
257 token_program_id: Option<&Pubkey>,
258 ) -> TransactionBuilder<C> {
259 let token_program_id = token_program_id.unwrap_or(&anchor_spl::token::ID);
260 let glv = self.find_glv_address(glv_token);
261 let market = self.find_market_address(store, market_token);
262 let vault =
263 get_associated_token_address_with_program_id(&glv, market_token, token_program_id);
264 self.store_transaction()
265 .anchor_accounts(accounts::InsertGlvMarket {
266 authority: self.payer(),
267 store: *store,
268 glv,
269 market_token: *market_token,
270 market,
271 vault,
272 system_program: system_program::ID,
273 token_program: *token_program_id,
274 associated_token_program: anchor_spl::associated_token::ID,
275 })
276 .anchor_args(instruction::InsertGlvMarket {})
277 }
278
279 fn remove_glv_market(
280 &self,
281 store: &Pubkey,
282 glv_token: &Pubkey,
283 market_token: &Pubkey,
284 token_program_id: Option<&Pubkey>,
285 ) -> TransactionBuilder<C> {
286 let token_program_id = token_program_id.unwrap_or(&anchor_spl::token::ID);
287 let glv = self.find_glv_address(glv_token);
288 let vault =
289 get_associated_token_address_with_program_id(&glv, market_token, token_program_id);
290 let store_wallet = self.find_store_wallet_address(store);
291 let store_wallet_ata = get_associated_token_address_with_program_id(
292 &store_wallet,
293 market_token,
294 token_program_id,
295 );
296 self.store_transaction()
297 .anchor_accounts(accounts::RemoveGlvMarket {
298 authority: self.payer(),
299 store: *store,
300 store_wallet,
301 glv,
302 market_token: *market_token,
303 vault,
304 store_wallet_ata,
305 token_program: *token_program_id,
306 associated_token_program: anchor_spl::associated_token::ID,
307 system_program: system_program::ID,
308 })
309 .anchor_args(instruction::RemoveGlvMarket {})
310 }
311
312 fn create_glv_deposit(
313 &self,
314 store: &Pubkey,
315 glv_token: &Pubkey,
316 market_token: &Pubkey,
317 ) -> CreateGlvDepositBuilder<C> {
318 CreateGlvDepositBuilder::new(self, *store, *glv_token, *market_token)
319 }
320
321 fn close_glv_deposit(&self, glv_deposit: &Pubkey) -> CloseGlvDepositBuilder<C> {
322 CloseGlvDepositBuilder::new(self, *glv_deposit)
323 }
324
325 fn execute_glv_deposit(
326 &self,
327 oracle: &Pubkey,
328 glv_deposit: &Pubkey,
329 cancel_on_execution_error: bool,
330 ) -> ExecuteGlvDepositBuilder<C> {
331 ExecuteGlvDepositBuilder::new(self, *oracle, *glv_deposit, cancel_on_execution_error)
332 }
333
334 fn create_glv_withdrawal(
335 &self,
336 store: &Pubkey,
337 glv_token: &Pubkey,
338 market_token: &Pubkey,
339 amount: u64,
340 ) -> CreateGlvWithdrawalBuilder<C> {
341 CreateGlvWithdrawalBuilder::new(self, *store, *glv_token, *market_token, amount)
342 }
343
344 fn close_glv_withdrawal(&self, glv_withdrawal: &Pubkey) -> CloseGlvWithdrawalBuilder<C> {
345 CloseGlvWithdrawalBuilder::new(self, *glv_withdrawal)
346 }
347
348 fn execute_glv_withdrawal(
349 &self,
350 oracle: &Pubkey,
351 glv_withdrawal: &Pubkey,
352 cancel_on_execution_error: bool,
353 ) -> ExecuteGlvWithdrawalBuilder<C> {
354 ExecuteGlvWithdrawalBuilder::new(self, *oracle, *glv_withdrawal, cancel_on_execution_error)
355 }
356
357 fn create_glv_shift(
358 &self,
359 store: &Pubkey,
360 glv_token: &Pubkey,
361 from_market_token: &Pubkey,
362 to_market_token: &Pubkey,
363 amount: u64,
364 ) -> CreateGlvShiftBuilder<C> {
365 CreateGlvShiftBuilder::new(
366 self,
367 store,
368 glv_token,
369 from_market_token,
370 to_market_token,
371 amount,
372 )
373 }
374
375 fn close_glv_shift(&self, glv_shift: &Pubkey) -> CloseGlvShiftBuilder<C> {
376 CloseGlvShiftBuilder::new(self, glv_shift)
377 }
378
379 fn execute_glv_shift(
380 &self,
381 oracle: &Pubkey,
382 glv_shift: &Pubkey,
383 cancel_on_execution_error: bool,
384 ) -> ExecuteGlvShiftBuilder<C> {
385 let mut builder = ExecuteGlvShiftBuilder::new(self, oracle, glv_shift);
386 builder.cancel_on_execution_error(cancel_on_execution_error);
387 builder
388 }
389}
390
391fn split_to_accounts(
392 market_tokens: impl IntoIterator<Item = Pubkey>,
393 glv: &Pubkey,
394 store: &Pubkey,
395 store_program_id: &Pubkey,
396 token_program_id: &Pubkey,
397 with_vaults: bool,
398) -> (Vec<AccountMeta>, usize) {
399 let market_token_addresses = market_tokens.into_iter().collect::<BTreeSet<_>>();
400
401 let markets = market_token_addresses.iter().map(|token| {
402 AccountMeta::new_readonly(
403 Market::find_market_address(store, token, store_program_id).0,
404 false,
405 )
406 });
407
408 let market_tokens = market_token_addresses
409 .iter()
410 .map(|token| AccountMeta::new_readonly(*token, false));
411
412 let length = market_token_addresses.len();
413
414 let accounts = if with_vaults {
415 let market_token_vaults = market_token_addresses.iter().map(|token| {
416 let market_token_vault =
417 get_associated_token_address_with_program_id(glv, token, token_program_id);
418
419 AccountMeta::new(market_token_vault, false)
420 });
421
422 markets
423 .chain(market_tokens)
424 .chain(market_token_vaults)
425 .collect::<Vec<_>>()
426 } else {
427 markets.chain(market_tokens).collect::<Vec<_>>()
428 };
429
430 (accounts, length)
431}
432
433impl<C: Deref<Target = impl Signer> + Clone> crate::Client<C> {
434 pub fn create_first_glv_deposit(
436 &self,
437 store: &Pubkey,
438 glv_token: &Pubkey,
439 market_token: &Pubkey,
440 ) -> CreateGlvDepositBuilder<C> {
441 let mut builder = self.create_glv_deposit(store, glv_token, market_token);
442 builder.receiver(Some(self.find_first_deposit_owner_address()));
443 builder
444 }
445}