1use crate::{
2 events::EventEmitter,
3 ops::market::MarketTransferOutOperation,
4 states::{
5 market::{
6 revertible::{Revertible, RevertibleMarket},
7 status::MarketStatus,
8 utils::ValidateMarketBalances,
9 },
10 Factor, HasMarketMeta,
11 },
12 ModelError,
13};
14
15use anchor_lang::prelude::*;
16use anchor_spl::token::{Mint, Token, TokenAccount};
17use gmsol_model::{
18 num::Unsigned, price::Prices, BalanceExt, Bank, BaseMarketMut, LiquidityMarketExt,
19 PnlFactorKind, PoolExt,
20};
21use gmsol_utils::InitSpace;
22
23use crate::{
24 constants,
25 states::{
26 market::config::{EntryArgs, MarketConfigBuffer},
27 Market, Seed, Store, TokenMapAccess, TokenMapHeader, TokenMapLoader,
28 },
29 utils::internal,
30 CoreError,
31};
32
33#[derive(Accounts)]
37#[instruction(index_token_mint: Pubkey)]
38pub struct InitializeMarket<'info> {
39 #[account(mut)]
41 pub authority: Signer<'info>,
42 #[account(has_one = token_map)]
44 pub store: AccountLoader<'info, Store>,
45 #[account(
47 init,
48 payer = authority,
49 mint::decimals = constants::MARKET_TOKEN_DECIMALS,
50 mint::authority = store.key(),
52 seeds = [
53 constants::MAREKT_TOKEN_MINT_SEED,
54 store.key().as_ref(),
55 index_token_mint.as_ref(),
56 long_token_mint.key().as_ref(),
57 short_token_mint.key().as_ref(),
58 ],
59 bump,
60 )]
61 pub market_token_mint: Account<'info, Mint>,
62 pub long_token_mint: Account<'info, Mint>,
64 pub short_token_mint: Account<'info, Mint>,
66 #[account(
68 init,
69 payer = authority,
70 space = 8 + Market::INIT_SPACE,
71 seeds = [
72 Market::SEED,
73 store.key().as_ref(),
74 market_token_mint.key().as_ref(),
75 ],
76 bump,
77 )]
78 pub market: AccountLoader<'info, Market>,
79 #[account(has_one = store)]
81 pub token_map: AccountLoader<'info, TokenMapHeader>,
82 #[account(
84 token::mint = long_token_mint,
85 token::authority = store,
87 seeds = [
88 constants::MARKET_VAULT_SEED,
89 store.key().as_ref(),
90 long_token_mint.key().as_ref(),
91 ],
92 bump,
93 )]
94 pub long_token_vault: Account<'info, TokenAccount>,
95 #[account(
97 token::mint = short_token_mint,
98 token::authority = store,
100 seeds = [
101 constants::MARKET_VAULT_SEED,
102 store.key().as_ref(),
103 short_token_mint.key().as_ref(),
104 ],
105 bump,
106 )]
107 pub short_token_vault: Account<'info, TokenAccount>,
108 pub system_program: Program<'info, System>,
110 pub token_program: Program<'info, Token>,
111}
112
113pub(crate) fn unchecked_initialize_market(
118 ctx: Context<InitializeMarket>,
119 index_token_mint: Pubkey,
120 name: &str,
121 enable: bool,
122) -> Result<()> {
123 {
124 let token_map = ctx.accounts.token_map.load_token_map()?;
125 require!(
126 token_map
127 .get(&index_token_mint)
128 .ok_or_else(|| error!(CoreError::NotFound))?
129 .is_enabled(),
130 CoreError::InvalidArgument
131 );
132
133 let long_token = &ctx.accounts.long_token_mint;
134 let long_token_config = token_map
135 .get(&long_token.key())
136 .ok_or_else(|| error!(CoreError::NotFound))?;
137 require!(
138 long_token_config.is_enabled(),
139 CoreError::TokenConfigDisabled
140 );
141 require!(
142 long_token_config.is_valid_pool_token_config(),
143 CoreError::InvalidArgument
144 );
145 require_eq!(
148 long_token_config.token_decimals(),
149 long_token.decimals,
150 CoreError::TokenDecimalsMismatched
151 );
152
153 let short_token = &ctx.accounts.short_token_mint;
154 let short_token_config = token_map
155 .get(&short_token.key())
156 .ok_or_else(|| error!(CoreError::NotFound))?;
157 require!(
158 short_token_config.is_enabled(),
159 CoreError::TokenConfigDisabled
160 );
161 require!(
162 short_token_config.is_valid_pool_token_config(),
163 CoreError::InvalidArgument
164 );
165 require_eq!(
168 short_token_config.token_decimals(),
169 short_token.decimals,
170 CoreError::TokenDecimalsMismatched
171 );
172 }
173 let market = &ctx.accounts.market;
174 market.load_init()?.init(
175 ctx.bumps.market,
176 ctx.accounts.store.key(),
177 name,
178 ctx.accounts.market_token_mint.key(),
179 index_token_mint,
180 ctx.accounts.long_token_mint.key(),
181 ctx.accounts.short_token_mint.key(),
182 enable,
183 )?;
184 Ok(())
185}
186
187impl<'info> internal::Authentication<'info> for InitializeMarket<'info> {
188 fn authority(&self) -> &Signer<'info> {
189 &self.authority
190 }
191
192 fn store(&self) -> &AccountLoader<'info, Store> {
193 &self.store
194 }
195}
196
197#[derive(Accounts)]
201pub struct ToggleMarket<'info> {
202 pub authority: Signer<'info>,
204 pub store: AccountLoader<'info, Store>,
206 #[account(mut, has_one = store)]
208 pub market: AccountLoader<'info, Market>,
209}
210
211pub(crate) fn unchecked_toggle_market(ctx: Context<ToggleMarket>, enable: bool) -> Result<()> {
216 ctx.accounts.market.load_mut()?.set_enabled(enable);
217 Ok(())
218}
219
220impl<'info> internal::Authentication<'info> for ToggleMarket<'info> {
221 fn authority(&self) -> &Signer<'info> {
222 &self.authority
223 }
224
225 fn store(&self) -> &AccountLoader<'info, Store> {
226 &self.store
227 }
228}
229
230#[event_cpi]
232#[derive(Accounts)]
233pub struct MarketTransferIn<'info> {
234 pub authority: Signer<'info>,
236 pub store: AccountLoader<'info, Store>,
238 pub from_authority: Signer<'info>,
240 #[account(mut, has_one = store)]
242 pub market: AccountLoader<'info, Market>,
243 #[account(mut, token::mint = vault.mint, constraint = from.key() != vault.key())]
245 pub from: Account<'info, TokenAccount>,
246 #[account(
248 mut,
249 token::authority = store,
250 seeds = [
251 constants::MARKET_VAULT_SEED,
252 store.key().as_ref(),
253 vault.mint.as_ref(),
254 ],
255 bump,
256 )]
257 pub vault: Account<'info, TokenAccount>,
258 pub token_program: Program<'info, Token>,
260}
261
262pub(crate) fn unchecked_market_transfer_in(
267 ctx: Context<MarketTransferIn>,
268 amount: u64,
269) -> Result<()> {
270 use anchor_spl::token;
271
272 {
273 let is_collateral_token = ctx
274 .accounts
275 .market
276 .load()?
277 .validated_meta(&ctx.accounts.store.key())?
278 .is_collateral_token(&ctx.accounts.from.mint);
279 require!(is_collateral_token, CoreError::InvalidArgument);
280 }
281
282 if amount != 0 {
283 token::transfer(
284 CpiContext::new(
285 ctx.accounts.token_program.to_account_info(),
286 token::Transfer {
287 from: ctx.accounts.from.to_account_info(),
288 to: ctx.accounts.vault.to_account_info(),
289 authority: ctx.accounts.from_authority.to_account_info(),
290 },
291 ),
292 amount,
293 )?;
294 let event_emitter =
295 EventEmitter::new(&ctx.accounts.event_authority, ctx.bumps.event_authority);
296 let token = &ctx.accounts.vault.mint;
297 let mut market = RevertibleMarket::new(&ctx.accounts.market, event_emitter)?;
298 market
299 .record_transferred_in_by_token(token, &amount)
300 .map_err(ModelError::from)?;
301 market.commit();
302 }
303
304 Ok(())
305}
306
307impl<'info> internal::Authentication<'info> for MarketTransferIn<'info> {
308 fn authority(&self) -> &Signer<'info> {
309 &self.authority
310 }
311
312 fn store(&self) -> &AccountLoader<'info, Store> {
313 &self.store
314 }
315}
316
317#[derive(Accounts)]
320pub struct UpdateMarketConfig<'info> {
321 pub authority: Signer<'info>,
323 pub store: AccountLoader<'info, Store>,
325 #[account(mut, has_one = store)]
327 pub market: AccountLoader<'info, Market>,
328}
329
330impl<'info> internal::Authentication<'info> for UpdateMarketConfig<'info> {
331 fn authority(&self) -> &Signer<'info> {
332 &self.authority
333 }
334
335 fn store(&self) -> &AccountLoader<'info, Store> {
336 &self.store
337 }
338}
339
340pub(crate) fn unchecked_update_market_config(
345 ctx: Context<UpdateMarketConfig>,
346 key: &str,
347 value: Factor,
348) -> Result<()> {
349 *ctx.accounts.market.load_mut()?.get_config_mut(key)? = value;
350 msg!(
351 "{}: set {} = {}",
352 ctx.accounts.market.load()?.meta.market_token_mint,
353 key,
354 value
355 );
356 Ok(())
357}
358
359pub(crate) fn unchecked_update_market_config_flag(
364 ctx: Context<UpdateMarketConfig>,
365 key: &str,
366 value: bool,
367) -> Result<()> {
368 let previous = ctx
369 .accounts
370 .market
371 .load_mut()?
372 .set_config_flag(key, value)?;
373 msg!(
374 "{}: set {} = {}, previous = {}",
375 ctx.accounts.market.load()?.meta.market_token_mint,
376 key,
377 value,
378 previous,
379 );
380 Ok(())
381}
382
383#[derive(Accounts)]
387pub struct UpdateMarketConfigWithBuffer<'info> {
388 pub authority: Signer<'info>,
390 pub store: AccountLoader<'info, Store>,
392 #[account(mut, has_one = store)]
394 pub market: AccountLoader<'info, Market>,
395 #[account(mut, has_one = store, has_one = authority @ CoreError::PermissionDenied)]
397 pub buffer: Account<'info, MarketConfigBuffer>,
398}
399
400pub(crate) fn unchecked_update_market_config_with_buffer(
405 ctx: Context<UpdateMarketConfigWithBuffer>,
406) -> Result<()> {
407 let buffer = &ctx.accounts.buffer;
408 require_gt!(
409 buffer.expiry,
410 Clock::get()?.unix_timestamp,
411 CoreError::InvalidArgument
412 );
413 ctx.accounts
414 .market
415 .load_mut()?
416 .update_config_with_buffer(buffer)?;
417 msg!(
418 "{} updated with buffer {}",
419 ctx.accounts.market.load()?.description()?,
420 buffer.key()
421 );
422 Ok(())
423}
424
425impl<'info> internal::Authentication<'info> for UpdateMarketConfigWithBuffer<'info> {
426 fn authority(&self) -> &Signer<'info> {
427 &self.authority
428 }
429
430 fn store(&self) -> &AccountLoader<'info, Store> {
431 &self.store
432 }
433}
434
435#[derive(Accounts)]
437pub struct ReadMarket<'info> {
438 pub market: AccountLoader<'info, Market>,
440}
441
442pub(crate) fn get_market_status(
444 ctx: Context<ReadMarket>,
445 prices: &Prices<u128>,
446 maximize_pnl: bool,
447 maximize_pool_value: bool,
448) -> Result<MarketStatus> {
449 let market = ctx.accounts.market.load()?;
450 let status = MarketStatus::from_market(&market, prices, maximize_pnl, maximize_pool_value)
451 .map_err(ModelError::from)?;
452 Ok(status)
453}
454
455#[derive(Accounts)]
457pub struct ReadMarketWithToken<'info> {
458 #[account(
460 constraint = market.load()?.meta.market_token_mint == market_token.key() @ CoreError::InvalidArgument,
461 )]
462 pub market: AccountLoader<'info, Market>,
463 pub market_token: Account<'info, Mint>,
465}
466
467pub(crate) fn get_market_token_price(
469 ctx: Context<ReadMarketWithToken>,
470 prices: &Prices<u128>,
471 pnl_factor: PnlFactorKind,
472 maximize: bool,
473) -> Result<u128> {
474 let market = ctx.accounts.market.load()?;
475 let liquidity_market = market.as_liquidity_market(&ctx.accounts.market_token);
476 let price = liquidity_market
477 .market_token_price(prices, pnl_factor, maximize)
478 .map_err(ModelError::from)?;
479 Ok(price)
480}
481
482#[derive(Accounts)]
486pub struct InitializeMarketConfigBuffer<'info> {
487 #[account(mut)]
489 pub authority: Signer<'info>,
490 pub store: AccountLoader<'info, Store>,
492 #[account(init, payer = authority, space = 8 + MarketConfigBuffer::init_space(0))]
494 pub buffer: Account<'info, MarketConfigBuffer>,
495 pub system_program: Program<'info, System>,
497}
498
499pub(crate) fn initialize_market_config_buffer(
501 ctx: Context<InitializeMarketConfigBuffer>,
502 expire_after_secs: u32,
503) -> Result<()> {
504 let buffer = &mut ctx.accounts.buffer;
505 buffer.authority = ctx.accounts.authority.key();
506 buffer.store = ctx.accounts.store.key();
507 buffer.expiry = Clock::get()?
508 .unix_timestamp
509 .saturating_add_unsigned(expire_after_secs as u64);
510 Ok(())
511}
512
513#[derive(Accounts)]
517pub struct SetMarketConfigBufferAuthority<'info> {
518 #[account(mut)]
520 pub authority: Signer<'info>,
521 #[account(mut, has_one = authority @ CoreError::PermissionDenied)]
523 pub buffer: Account<'info, MarketConfigBuffer>,
524}
525
526pub(crate) fn set_market_config_buffer_authority(
528 ctx: Context<SetMarketConfigBufferAuthority>,
529 new_authority: Pubkey,
530) -> Result<()> {
531 ctx.accounts.buffer.authority = new_authority;
532 Ok(())
533}
534
535#[derive(Accounts)]
539pub struct CloseMarketConfigBuffer<'info> {
540 #[account(mut)]
542 pub authority: Signer<'info>,
543 #[account(mut, close = receiver, has_one = authority @ CoreError::PermissionDenied)]
545 pub buffer: Account<'info, MarketConfigBuffer>,
546 #[account(mut)]
549 pub receiver: UncheckedAccount<'info>,
550}
551
552pub(crate) fn close_market_config_buffer(_ctx: Context<CloseMarketConfigBuffer>) -> Result<()> {
554 Ok(())
555}
556
557#[derive(Accounts)]
561#[instruction(new_configs: Vec<(String, Factor)>)]
562pub struct PushToMarketConfigBuffer<'info> {
563 #[account(mut)]
565 pub authority: Signer<'info>,
566 #[account(
568 mut,
569 has_one = authority @ CoreError::PermissionDenied,
570 realloc = 8 + buffer.space_after_push(new_configs.len()),
571 realloc::payer = authority,
572 realloc::zero = false,
573 )]
574 pub buffer: Account<'info, MarketConfigBuffer>,
575 system_program: Program<'info, System>,
576}
577
578pub(crate) fn push_to_market_config_buffer(
580 ctx: Context<PushToMarketConfigBuffer>,
581 new_configs: Vec<EntryArgs>,
582) -> Result<()> {
583 let buffer = &mut ctx.accounts.buffer;
584 for entry in new_configs {
585 buffer.push(entry.try_into()?);
586 }
587 Ok(())
588}
589
590#[derive(Accounts)]
594pub struct ToggleGTMinting<'info> {
595 pub authority: Signer<'info>,
597 pub store: AccountLoader<'info, Store>,
599 #[account(mut, has_one = store)]
601 pub market: AccountLoader<'info, Market>,
602}
603
604pub(crate) fn unchecked_toggle_gt_minting(
609 ctx: Context<ToggleGTMinting>,
610 enable: bool,
611) -> Result<()> {
612 ctx.accounts
613 .market
614 .load_mut()?
615 .set_is_gt_minting_enbaled(enable);
616 Ok(())
617}
618
619impl<'info> internal::Authentication<'info> for ToggleGTMinting<'info> {
620 fn authority(&self) -> &Signer<'info> {
621 &self.authority
622 }
623
624 fn store(&self) -> &AccountLoader<'info, Store> {
625 &self.store
626 }
627}
628
629#[event_cpi]
633#[derive(Accounts)]
634pub struct ClaimFeesFromMarket<'info> {
635 pub authority: Signer<'info>,
636 pub store: AccountLoader<'info, Store>,
637 #[account(mut, has_one = store)]
638 pub market: AccountLoader<'info, Market>,
639 pub token_mint: InterfaceAccount<'info, anchor_spl::token_interface::Mint>,
640 #[account(
641 mut,
642 token::mint = token_mint,
643 token::authority = store,
644 token::token_program = token_program,
645 seeds = [
646 constants::MARKET_VAULT_SEED,
647 store.key().as_ref(),
648 token_mint.key().as_ref(),
649 ],
650 bump,
651 )]
652 pub vault: InterfaceAccount<'info, anchor_spl::token_interface::TokenAccount>,
653 #[account(
654 mut,
655 token::mint = token_mint,
656 )]
657 pub target: InterfaceAccount<'info, anchor_spl::token_interface::TokenAccount>,
658 pub token_program: Interface<'info, anchor_spl::token_interface::TokenInterface>,
659}
660
661pub(crate) fn claim_fees_from_market(ctx: Context<ClaimFeesFromMarket>) -> Result<u64> {
666 ctx.accounts
668 .store
669 .load()?
670 .validate_not_restarted()?
671 .validate_claim_fees_address(ctx.accounts.authority.key)?;
672
673 let event_emitter = EventEmitter::new(&ctx.accounts.event_authority, ctx.bumps.event_authority);
674
675 let amount = {
676 let token = ctx.accounts.token_mint.key();
677
678 let mut market = RevertibleMarket::new(&ctx.accounts.market, event_emitter)?;
679 let is_long_token = market
680 .market_meta()
681 .to_token_side(&token)
682 .map_err(CoreError::from)?;
683 let the_opposite_side = !is_long_token;
684 let is_pure = market.market_meta().is_pure();
685 let pool = market.claimable_fee_pool_mut().map_err(ModelError::from)?;
686
687 let mut deltas = (0, 0);
688
689 let mut amount: u64 = pool
691 .amount(is_long_token)
692 .map_err(ModelError::from)?
693 .min(u128::from(u64::MAX))
694 .try_into()
695 .expect("must success");
696
697 deltas.0 = (u128::from(amount))
698 .to_opposite_signed()
699 .map_err(ModelError::from)?;
700
701 if is_pure {
702 let the_opposite_side_amount: u64 = pool
703 .amount(the_opposite_side)
704 .map_err(ModelError::from)?
705 .min(u128::from(u64::MAX))
706 .try_into()
707 .expect("must success");
708 deltas.1 = (u128::from(the_opposite_side_amount))
709 .to_opposite_signed()
710 .map_err(ModelError::from)?;
711 amount = amount
712 .checked_add(the_opposite_side_amount)
713 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
714 }
715
716 if deltas.0 != 0 {
717 pool.apply_delta_amount(is_long_token, &deltas.0)
718 .map_err(ModelError::from)?;
719 }
720
721 if deltas.1 != 0 {
722 pool.apply_delta_amount(the_opposite_side, &deltas.1)
723 .map_err(ModelError::from)?;
724 }
725
726 market
727 .validate_market_balance_for_the_given_token(&token, amount)
728 .map_err(ModelError::from)?;
729 market.commit();
730
731 amount
732 };
733
734 let token = &ctx.accounts.token_mint;
736 MarketTransferOutOperation::builder()
737 .store(&ctx.accounts.store)
738 .market(&ctx.accounts.market)
739 .amount(amount)
740 .decimals(token.decimals)
741 .to(ctx.accounts.target.to_account_info())
742 .token_mint(token.to_account_info())
743 .vault(ctx.accounts.vault.to_account_info())
744 .token_program(ctx.accounts.token_program.to_account_info())
745 .event_emitter(event_emitter)
746 .build()
747 .execute()?;
748
749 msg!(
750 "Claimed `{}` {} from the {} market",
751 amount,
752 token.key(),
753 ctx.accounts.market.load()?.meta.market_token_mint
754 );
755 Ok(amount)
756}