gmsol/store/glv/
mod.rs

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
36/// Glv Operations.
37pub trait GlvOps<C> {
38    /// Initialize GLV.
39    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    /// GLV Update Market Config.
47    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    /// GLV toggle market flag.
57    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    /// Update GLV config.
67    fn update_glv_config(
68        &self,
69        store: &Pubkey,
70        glv_token: &Pubkey,
71        params: UpdateGlvParams,
72    ) -> TransactionBuilder<C>;
73
74    /// Insert GLV market.
75    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    /// Remove GLV market.
84    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    /// Create a GLV deposit.
93    fn create_glv_deposit(
94        &self,
95        store: &Pubkey,
96        glv_token: &Pubkey,
97        market_token: &Pubkey,
98    ) -> CreateGlvDepositBuilder<C>;
99
100    /// Close a GLV deposit.
101    fn close_glv_deposit(&self, glv_deposit: &Pubkey) -> CloseGlvDepositBuilder<C>;
102
103    /// Execute the given GLV deposit.
104    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    /// Close a GLV withdrawal.
120    fn close_glv_withdrawal(&self, glv_withdrawal: &Pubkey) -> CloseGlvWithdrawalBuilder<C>;
121
122    /// Execute the given GLV deposit.
123    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    /// Create first GLV deposit.
435    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}