Skip to main content

gmsol_store/instructions/
virtual_inventory.rs

1use anchor_lang::prelude::*;
2use gmsol_utils::InitSpace;
3
4use crate::{
5    constants::MARKET_DECIMALS,
6    internal,
7    states::{
8        market::virtual_inventory::{
9            VirtualInventory, VIRTUAL_INVENTORY_FOR_POSITIONS_SEED,
10            VIRTUAL_INVENTORY_FOR_SWAPS_SEED,
11        },
12        Market, Store, TokenMapHeader, TokenMapLoader,
13    },
14    CoreError,
15};
16
17/// The accounts definitions for [`close_virtual_inventory`](crate::gmsol_store::close_virtual_inventory).
18#[derive(Accounts)]
19pub struct CloseVirtualInventory<'info> {
20    /// Authority.
21    #[account(mut)]
22    pub authority: Signer<'info>,
23    /// Store account.
24    pub store: AccountLoader<'info, Store>,
25    /// The store wallet.
26    #[account(mut, seeds = [Store::WALLET_SEED, store.key().as_ref()], bump)]
27    pub store_wallet: SystemAccount<'info>,
28    /// The virtual inventory account to close.
29    #[account(
30        mut,
31        close = store_wallet,
32        has_one = store,
33        constraint = virtual_inventory.load()?.ref_count() == 0,
34    )]
35    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
36}
37
38impl CloseVirtualInventory<'_> {
39    /// Close an unused [`VirtualInventory`] account.
40    ///
41    /// # CHECK
42    /// - Only MARKET_KEEPER is allowed to invoke.
43    pub(crate) fn invoke_unchecked(_ctx: Context<Self>) -> Result<()> {
44        Ok(())
45    }
46}
47
48impl<'info> internal::Authentication<'info> for CloseVirtualInventory<'info> {
49    fn authority(&self) -> &Signer<'info> {
50        &self.authority
51    }
52
53    fn store(&self) -> &AccountLoader<'info, Store> {
54        &self.store
55    }
56}
57
58/// The accounts definitions of [`disable_virtual_inventory`](crate::gmsol_store::disable_virtual_inventory).
59#[derive(Accounts)]
60pub struct DisableVirtualInventory<'info> {
61    /// Authority.
62    pub authority: Signer<'info>,
63    /// Store account.
64    pub store: AccountLoader<'info, Store>,
65    /// The virtual inventory account to close.
66    #[account(
67        mut,
68        has_one = store,
69        constraint = !virtual_inventory.load()?.is_disabled() @ CoreError::PreconditionsAreNotMet,
70    )]
71    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
72}
73
74impl<'info> internal::Authentication<'info> for DisableVirtualInventory<'info> {
75    fn authority(&self) -> &Signer<'info> {
76        &self.authority
77    }
78
79    fn store(&self) -> &AccountLoader<'info, Store> {
80        &self.store
81    }
82}
83
84impl DisableVirtualInventory<'_> {
85    /// Disable the given [`VirtualInventory`] account.
86    ///
87    /// # CHECK
88    /// - Only MARKET_KEEPER is allowed to invoke.
89    pub(crate) fn invoke_unchecked(ctx: Context<Self>) -> Result<()> {
90        ctx.accounts.virtual_inventory.load_mut()?.disable()?;
91        Ok(())
92    }
93}
94
95/// The accounts definitions of [`leave_disabled_virtual_inventory`](crate::gmsol_store::leave_disabled_virtual_inventory).
96#[derive(Accounts)]
97pub struct LeaveDisabledVirtualInventory<'info> {
98    /// Authority.
99    pub authority: Signer<'info>,
100    /// Store account.
101    pub store: AccountLoader<'info, Store>,
102    /// The virtual inventory account to join.
103    #[account(
104        mut,
105        has_one = store,
106        constraint = virtual_inventory.load()?.is_disabled() @ CoreError::PreconditionsAreNotMet,
107    )]
108    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
109    /// The market to be added to the virtual inventory.
110    #[account(mut, has_one = store)]
111    pub market: AccountLoader<'info, Market>,
112}
113
114impl<'info> internal::Authentication<'info> for LeaveDisabledVirtualInventory<'info> {
115    fn authority(&self) -> &Signer<'info> {
116        &self.authority
117    }
118
119    fn store(&self) -> &AccountLoader<'info, Store> {
120        &self.store
121    }
122}
123
124impl LeaveDisabledVirtualInventory<'_> {
125    /// Leave a disabled [`VirtualInventory`] account.
126    ///
127    /// # CHECK
128    /// - Only MARKET_KEEPER is allowed to invoke.
129    pub(crate) fn invoke_unchecked(ctx: Context<Self>) -> Result<()> {
130        let address = ctx.accounts.virtual_inventory.key();
131        let mut market = ctx.accounts.market.load_mut()?;
132        let mut virtual_inventory = ctx.accounts.virtual_inventory.load_mut()?;
133        market.leave_disabled_virtual_inventory_unchecked(&address, &mut virtual_inventory)?;
134        Ok(())
135    }
136}
137
138/// The accounts definitions for [`create_virtual_inventory_for_swaps`](crate::gmsol_store::create_virtual_inventory_for_swaps).
139#[derive(Accounts)]
140#[instruction(index: u32)]
141pub struct CreateVirtualInventoryForSwaps<'info> {
142    /// Authority.
143    #[account(mut)]
144    pub authority: Signer<'info>,
145    /// Store account.
146    pub store: AccountLoader<'info, Store>,
147    /// The virtual inventory account to create.
148    #[account(
149        init,
150        payer = authority,
151        space = 8 + VirtualInventory::INIT_SPACE,
152        seeds = [VIRTUAL_INVENTORY_FOR_SWAPS_SEED, store.key().as_ref(), &index.to_le_bytes()],
153        bump,
154    )]
155    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
156    /// The system program.
157    pub system_program: Program<'info, System>,
158}
159
160impl CreateVirtualInventoryForSwaps<'_> {
161    /// Create a [`VirtualInventory`] account for swaps.
162    ///
163    /// # CHECK
164    /// - Only MARKET_KEEPER is allowed to invoke.
165    pub(crate) fn invoke_unchecked(
166        ctx: Context<Self>,
167        index: u32,
168        long_amount_decimals: u8,
169        short_amount_decimals: u8,
170    ) -> Result<()> {
171        ctx.accounts.virtual_inventory.load_init()?.init(
172            ctx.bumps.virtual_inventory,
173            index,
174            ctx.accounts.store.key(),
175            long_amount_decimals,
176            short_amount_decimals,
177        );
178        Ok(())
179    }
180}
181
182impl<'info> internal::Authentication<'info> for CreateVirtualInventoryForSwaps<'info> {
183    fn authority(&self) -> &Signer<'info> {
184        &self.authority
185    }
186
187    fn store(&self) -> &AccountLoader<'info, Store> {
188        &self.store
189    }
190}
191
192/// The accounts definitions for
193/// [`join_virtual_inventory_for_swaps`](crate::gmsol_store::join_virtual_inventory_for_swaps).
194#[derive(Accounts)]
195pub struct JoinVirtualInventoryForSwaps<'info> {
196    /// Authority.
197    pub authority: Signer<'info>,
198    /// Store account.
199    pub store: AccountLoader<'info, Store>,
200    /// The token map account.
201    #[account(has_one = store)]
202    pub token_map: AccountLoader<'info, TokenMapHeader>,
203    /// The virtual inventory account to join.
204    #[account(
205        mut,
206        seeds = [VIRTUAL_INVENTORY_FOR_SWAPS_SEED, store.key().as_ref(), &virtual_inventory.load()?.index.to_le_bytes()],
207        bump = virtual_inventory.load()?.bump,
208        has_one = store,
209        constraint = !virtual_inventory.load()?.is_disabled() @ CoreError::PreconditionsAreNotMet,
210    )]
211    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
212    /// The market to be added to the virtual inventory.
213    #[account(mut, has_one = store, constraint = !market.load()?.is_pure())]
214    pub market: AccountLoader<'info, Market>,
215}
216
217impl JoinVirtualInventoryForSwaps<'_> {
218    /// Add the market to the given virtual inventory.
219    ///
220    /// # CHECK
221    /// - Only MARKET_KEEPER is allowed to invoke.
222    pub(crate) fn invoke_unchecked(ctx: Context<Self>) -> Result<()> {
223        let address = ctx.accounts.virtual_inventory.key();
224        let mut virtual_inventory = ctx.accounts.virtual_inventory.load_mut()?;
225        let token_map = ctx.accounts.token_map.load_token_map()?;
226        ctx.accounts
227            .market
228            .load_mut()?
229            .join_virtual_inventory_for_swaps_unchecked(
230                &address,
231                &mut virtual_inventory,
232                &token_map,
233            )?;
234        Ok(())
235    }
236}
237
238impl<'info> internal::Authentication<'info> for JoinVirtualInventoryForSwaps<'info> {
239    fn authority(&self) -> &Signer<'info> {
240        &self.authority
241    }
242
243    fn store(&self) -> &AccountLoader<'info, Store> {
244        &self.store
245    }
246}
247
248/// The accounts definitions for
249/// [`leave_virtual_inventory_for_swaps`](crate::gmsol_store::leave_virtual_inventory_for_swaps).
250#[derive(Accounts)]
251pub struct LeaveVirtualInventoryForSwaps<'info> {
252    /// Authority.
253    pub authority: Signer<'info>,
254    /// Store account.
255    pub store: AccountLoader<'info, Store>,
256    /// The virtual inventory account to join.
257    #[account(
258        mut,
259        seeds = [VIRTUAL_INVENTORY_FOR_SWAPS_SEED, store.key().as_ref(), &virtual_inventory.load()?.index.to_le_bytes()],
260        bump = virtual_inventory.load()?.bump,
261        has_one = store,
262        constraint = !virtual_inventory.load()?.is_disabled() @ CoreError::PreconditionsAreNotMet,
263    )]
264    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
265    /// The market to be added to the virtual inventory.
266    #[account(mut, has_one = store, constraint = !market.load()?.is_pure())]
267    pub market: AccountLoader<'info, Market>,
268}
269
270impl LeaveVirtualInventoryForSwaps<'_> {
271    /// Remove the market from the given virtual inventory.
272    ///
273    /// # CHECK
274    /// - Only MARKET_KEEPER is allowed to invoke.
275    pub(crate) fn invoke_unchecked(ctx: Context<Self>) -> Result<()> {
276        let address = ctx.accounts.virtual_inventory.key();
277        let mut market = ctx.accounts.market.load_mut()?;
278        let virtual_inventory_for_swaps = market
279            .virtual_inventory_for_swaps()
280            .ok_or_else(|| error!(CoreError::PreconditionsAreNotMet))?;
281        require_keys_eq!(*virtual_inventory_for_swaps, address);
282        let mut virtual_inventory = ctx.accounts.virtual_inventory.load_mut()?;
283        market.leave_virtual_inventory_for_swaps_unchecked(&mut virtual_inventory)?;
284        Ok(())
285    }
286}
287
288impl<'info> internal::Authentication<'info> for LeaveVirtualInventoryForSwaps<'info> {
289    fn authority(&self) -> &Signer<'info> {
290        &self.authority
291    }
292
293    fn store(&self) -> &AccountLoader<'info, Store> {
294        &self.store
295    }
296}
297
298/// The accounts definitions for [`create_virtual_inventory_for_positions`](crate::gmsol_store::create_virtual_inventory_for_positions).
299#[derive(Accounts)]
300pub struct CreateVirtualInventoryForPositions<'info> {
301    /// Authority.
302    #[account(mut)]
303    pub authority: Signer<'info>,
304    /// Store account.
305    pub store: AccountLoader<'info, Store>,
306    /// Index token address.
307    /// CHECK: only the address of this account is used.
308    pub index_token: UncheckedAccount<'info>,
309    /// The virtual inventory account to create.
310    #[account(
311        init,
312        payer = authority,
313        space = 8 + VirtualInventory::INIT_SPACE,
314        seeds = [
315            VIRTUAL_INVENTORY_FOR_POSITIONS_SEED,
316            store.key().as_ref(),
317            index_token.key.as_ref(),
318        ],
319        bump,
320    )]
321    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
322    /// The system program.
323    pub system_program: Program<'info, System>,
324}
325
326impl CreateVirtualInventoryForPositions<'_> {
327    /// Create a [`VirtualInventory`] account for positions.
328    ///
329    /// # CHECK
330    /// - Only MARKET_KEEPER is allowed to invoke.
331    pub(crate) fn invoke_unchecked(ctx: Context<Self>) -> Result<()> {
332        ctx.accounts.virtual_inventory.load_init()?.init(
333            ctx.bumps.virtual_inventory,
334            0,
335            ctx.accounts.store.key(),
336            MARKET_DECIMALS,
337            MARKET_DECIMALS,
338        );
339        Ok(())
340    }
341}
342
343impl<'info> internal::Authentication<'info> for CreateVirtualInventoryForPositions<'info> {
344    fn authority(&self) -> &Signer<'info> {
345        &self.authority
346    }
347
348    fn store(&self) -> &AccountLoader<'info, Store> {
349        &self.store
350    }
351}
352
353/// The accounts definitions for
354/// [`join_virtual_inventory_for_positions`](crate::gmsol_store::join_virtual_inventory_for_positions)
355/// and [`leave_virtual_inventory_for_positions`](crate::gmsol_store::leave_virtual_inventory_for_positions).
356#[derive(Accounts)]
357pub struct JoinOrLeaveVirtualInventoryForPositions<'info> {
358    /// Authority.
359    pub authority: Signer<'info>,
360    /// Store account.
361    pub store: AccountLoader<'info, Store>,
362    /// The virtual inventory account to join.
363    #[account(
364        mut,
365        seeds = [VIRTUAL_INVENTORY_FOR_POSITIONS_SEED, store.key().as_ref(), market.load()?.meta().index_token_mint.as_ref()],
366        bump = virtual_inventory.load()?.bump,
367        has_one = store,
368        constraint = !virtual_inventory.load()?.is_disabled() @ CoreError::PreconditionsAreNotMet,
369    )]
370    pub virtual_inventory: AccountLoader<'info, VirtualInventory>,
371    /// The market to be added to the virtual inventory.
372    #[account(mut, has_one = store)]
373    pub market: AccountLoader<'info, Market>,
374}
375
376impl JoinOrLeaveVirtualInventoryForPositions<'_> {
377    /// Add the market to the given virtual inventory.
378    ///
379    /// # CHECK
380    /// - Only MARKET_KEEPER is allowed to invoke.
381    pub(crate) fn invoke_join_unchecked(ctx: Context<Self>) -> Result<()> {
382        let address = ctx.accounts.virtual_inventory.key();
383        let mut virtual_inventory = ctx.accounts.virtual_inventory.load_mut()?;
384        ctx.accounts
385            .market
386            .load_mut()?
387            .join_virtual_inventory_for_positions_unchecked(&address, &mut virtual_inventory)?;
388        Ok(())
389    }
390
391    /// Remove the market from the given virtual inventory.
392    ///
393    /// # CHECK
394    /// - Only MARKET_KEEPER is allowed to invoke.
395    pub(crate) fn invoke_leave_unchecked(ctx: Context<Self>) -> Result<()> {
396        let address = ctx.accounts.virtual_inventory.key();
397        let mut market = ctx.accounts.market.load_mut()?;
398        let virtual_inventory_for_positions = market
399            .virtual_inventory_for_positions()
400            .ok_or_else(|| error!(CoreError::PreconditionsAreNotMet))?;
401        require_keys_eq!(*virtual_inventory_for_positions, address);
402        let mut virtual_inventory = ctx.accounts.virtual_inventory.load_mut()?;
403        market.leave_virtual_inventory_for_positions_unchecked(&mut virtual_inventory)?;
404        Ok(())
405    }
406}
407
408impl<'info> internal::Authentication<'info> for JoinOrLeaveVirtualInventoryForPositions<'info> {
409    fn authority(&self) -> &Signer<'info> {
410        &self.authority
411    }
412
413    fn store(&self) -> &AccountLoader<'info, Store> {
414        &self.store
415    }
416}