1use std::borrow::Borrow;
2
3use anchor_lang::prelude::*;
4use anchor_spl::{
5 token::{transfer_checked, Mint, TokenAccount, TransferChecked},
6 token_interface,
7};
8use gmsol_model::{price::Prices, BaseMarketExt, PnlFactorKind};
9use typed_builder::TypedBuilder;
10
11use crate::{
12 constants,
13 events::{EventEmitter, GlvPricing, GlvPricingKind},
14 states::{
15 common::{
16 action::{Action, ActionExt, ActionParams, ActionSigner},
17 swap::SwapActionParamsExt,
18 },
19 glv::{GlvShift, GlvWithdrawal},
20 market::revertible::Revertible,
21 withdrawal::WithdrawalActionParams,
22 Glv, GlvDeposit, HasMarketMeta, Market, NonceBytes, Oracle, Shift, Store,
23 ValidateOracleTime,
24 },
25 utils::internal::TransferUtils,
26 CoreError, CoreResult, ModelError,
27};
28
29use super::market::{Execute, RevertibleLiquidityMarketOperation};
30
31#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
33pub struct CreateGlvDepositParams {
34 pub execution_lamports: u64,
36 pub long_token_swap_length: u8,
38 pub short_token_swap_length: u8,
40 pub initial_long_token_amount: u64,
42 pub initial_short_token_amount: u64,
44 pub market_token_amount: u64,
46 pub min_market_token_amount: u64,
48 pub min_glv_token_amount: u64,
50 pub should_unwrap_native_token: bool,
52}
53
54impl ActionParams for CreateGlvDepositParams {
55 fn execution_lamports(&self) -> u64 {
56 self.execution_lamports
57 }
58}
59
60#[derive(TypedBuilder)]
62pub(crate) struct CreateGlvDepositOperation<'a, 'info> {
63 glv_deposit: AccountLoader<'info, GlvDeposit>,
64 market: AccountLoader<'info, Market>,
65 store: AccountLoader<'info, Store>,
66 owner: &'a AccountInfo<'info>,
67 receiver: &'a AccountInfo<'info>,
68 nonce: &'a NonceBytes,
69 bump: u8,
70 initial_long_token: Option<&'a Account<'info, TokenAccount>>,
71 initial_short_token: Option<&'a Account<'info, TokenAccount>>,
72 market_token: &'a Account<'info, TokenAccount>,
73 glv_token: &'a InterfaceAccount<'info, token_interface::TokenAccount>,
74 params: &'a CreateGlvDepositParams,
75 swap_paths: &'info [AccountInfo<'info>],
76}
77
78impl CreateGlvDepositOperation<'_, '_> {
79 pub(crate) fn unchecked_execute(self) -> Result<()> {
102 let (long_token, short_token) = self.validate_market_and_get_tokens()?;
103
104 self.validate_params_excluding_swap()?;
105
106 let id = self
107 .market
108 .load_mut()?
109 .indexer_mut()
110 .next_glv_deposit_id()?;
111
112 let mut glv_deposit = self.glv_deposit.load_init()?;
113
114 glv_deposit.header.init(
115 id,
116 self.store.key(),
117 self.market.key(),
118 self.owner.key(),
119 self.receiver.key(),
120 *self.nonce,
121 self.bump,
122 self.params.execution_lamports,
123 self.params.should_unwrap_native_token,
124 )?;
125
126 let primary_token_in = if let Some(account) = self.initial_long_token {
128 glv_deposit.tokens.initial_long_token.init(account);
129 account.mint
130 } else {
131 long_token
132 };
133
134 let secondary_token_in = if let Some(account) = self.initial_short_token {
135 glv_deposit.tokens.initial_short_token.init(account);
136 account.mint
137 } else {
138 short_token
139 };
140 glv_deposit.tokens.market_token.init(self.market_token);
141 glv_deposit
142 .tokens
143 .glv_token
144 .init_with_interface(self.glv_token);
145
146 glv_deposit.params.deposit.initial_long_token_amount =
148 self.params.initial_long_token_amount;
149 glv_deposit.params.deposit.initial_short_token_amount =
150 self.params.initial_short_token_amount;
151 glv_deposit.params.deposit.min_market_token_amount = self.params.min_market_token_amount;
152
153 glv_deposit.params.market_token_amount = self.params.market_token_amount;
154 glv_deposit.params.min_glv_token_amount = self.params.min_glv_token_amount;
155
156 glv_deposit.swap.validate_and_init(
158 &*self.market.load()?,
159 self.params.long_token_swap_length,
160 self.params.short_token_swap_length,
161 self.swap_paths,
162 &self.store.key(),
163 (&primary_token_in, &secondary_token_in),
164 (&long_token, &short_token),
165 )?;
166
167 Ok(())
168 }
169
170 fn validate_market_and_get_tokens(&self) -> Result<(Pubkey, Pubkey)> {
171 let market = self.market.load()?;
172 let meta = market.validated_meta(&self.store.key())?;
173 Ok((meta.long_token_mint, meta.short_token_mint))
174 }
175
176 fn validate_params_excluding_swap(&self) -> Result<()> {
177 let params = self.params;
178 require!(
179 params.initial_long_token_amount != 0
180 || params.initial_short_token_amount != 0
181 || params.market_token_amount != 0,
182 CoreError::EmptyDeposit
183 );
184
185 if params.initial_long_token_amount != 0 {
186 let Some(account) = self.initial_long_token.as_ref() else {
187 return err!(CoreError::TokenAccountNotProvided);
188 };
189 require_gte!(
190 account.amount,
191 params.initial_long_token_amount,
192 CoreError::NotEnoughTokenAmount
193 );
194 }
195
196 if params.initial_short_token_amount != 0 {
197 let Some(account) = self.initial_short_token.as_ref() else {
198 return err!(CoreError::TokenAccountNotProvided);
199 };
200 require_gte!(
201 account.amount,
202 params.initial_short_token_amount,
203 CoreError::NotEnoughTokenAmount
204 );
205 }
206
207 let same_initial_token_amount = self.initial_long_token.as_ref().and_then(|long| {
209 self.initial_short_token
210 .as_ref()
211 .and_then(|short| (long.key() == short.key()).then(|| long.amount))
212 });
213 if let Some(amount) = same_initial_token_amount {
214 let total_amount = params
215 .initial_long_token_amount
216 .checked_add(params.initial_short_token_amount)
217 .ok_or_else(|| error!(CoreError::TokenAmountExceedsLimit))?;
218 require_gte!(amount, total_amount, CoreError::NotEnoughTokenAmount);
219 }
220
221 ActionExt::validate_balance(&self.glv_deposit, params.execution_lamports)?;
222
223 Ok(())
224 }
225}
226
227#[derive(TypedBuilder)]
229pub(crate) struct ExecuteGlvDepositOperation<'a, 'info> {
230 glv_deposit: AccountLoader<'info, GlvDeposit>,
231 token_program: AccountInfo<'info>,
232 glv_token_program: AccountInfo<'info>,
233 throw_on_execution_error: bool,
234 store: AccountLoader<'info, Store>,
235 glv: AccountLoader<'info, Glv>,
236 glv_token_mint: &'a mut InterfaceAccount<'info, token_interface::Mint>,
237 glv_token_receiver: AccountInfo<'info>,
238 market: AccountLoader<'info, Market>,
239 market_token_mint: &'a mut Account<'info, Mint>,
240 market_token_source: &'a Account<'info, TokenAccount>,
241 market_token_vault: AccountInfo<'info>,
242 markets: &'info [AccountInfo<'info>],
243 market_tokens: &'info [AccountInfo<'info>],
244 oracle: &'a Oracle,
245 remaining_accounts: &'info [AccountInfo<'info>],
246 #[builder(setter(into))]
247 event_emitter: EventEmitter<'a, 'info>,
248}
249
250impl ExecuteGlvDepositOperation<'_, '_> {
251 pub(crate) fn unchecked_execute(mut self) -> Result<bool> {
266 let throw_on_execution_error = self.throw_on_execution_error;
267 match self.validate_oracle() {
268 Ok(()) => {}
269 Err(CoreError::OracleTimestampsAreLargerThanRequired) if !throw_on_execution_error => {
270 msg!(
271 "GLV Deposit expired at {}",
272 self.oracle_updated_before()
273 .ok()
274 .flatten()
275 .expect("must have an expiration time"),
276 );
277 return Ok(false);
278 }
279 Err(err) => {
280 return Err(error!(err));
281 }
282 }
283 let executed = match self.perform_glv_deposit() {
284 Ok(()) => true,
285 Err(err) if !throw_on_execution_error => {
286 msg!("Execute GLV deposit error: {}", err);
287 false
288 }
289 Err(err) => return Err(err),
290 };
291
292 self.validate_after_execution()?;
293
294 Ok(executed)
295 }
296
297 fn validate_oracle(&self) -> CoreResult<()> {
298 self.oracle.validate_time(self)
299 }
300
301 fn validate_before_execution(&self) -> Result<()> {
302 let market = self.market.load()?;
303 market.validate(&self.store.key())?;
304
305 let glv = self.glv.load()?;
306 let glv_deposit = self.glv_deposit.load()?;
307
308 glv_deposit.unchecked_validate_for_execution(self.glv_token_mint, &glv)?;
309
310 require_gte!(
311 self.market_token_source.amount,
312 glv_deposit.params.market_token_amount,
313 CoreError::NotEnoughTokenAmount,
314 );
315
316 Ok(())
317 }
318
319 fn validate_after_execution(&self) -> Result<()> {
320 use anchor_spl::token::accessor::amount;
321
322 let glv = self.glv.load()?;
323 let market_token = self.market_token_mint.key();
324 let vault_balance = amount(&self.market_token_vault)?;
325
326 require_gte!(
327 vault_balance,
328 glv.market_config(&market_token)
329 .ok_or_else(|| error!(CoreError::NotFound))?
330 .balance(),
331 CoreError::Internal
332 );
333
334 Ok(())
335 }
336
337 #[inline(never)]
338 fn perform_glv_deposit(&mut self) -> Result<()> {
339 use gmsol_model::utils::usd_to_market_token_amount;
340
341 self.validate_before_execution()?;
342
343 let glv_token_amount = {
344 let deposit = self.glv_deposit.load()?;
345 let mut market_token_amount = deposit.params.market_token_amount;
346
347 let mut market = RevertibleLiquidityMarketOperation::new(
348 &self.store,
349 self.oracle,
350 &self.market,
351 self.market_token_mint,
352 self.token_program.clone(),
353 Some(&deposit.swap),
354 self.remaining_accounts,
355 self.event_emitter,
356 )?;
357
358 let mut op = market.op()?;
359
360 if market_token_amount != 0 {
361 let prices = self.oracle.market_prices(op.market())?;
365 op.market()
366 .validate_max_pnl(
367 &prices,
368 PnlFactorKind::MaxAfterWithdrawal,
369 PnlFactorKind::MaxAfterWithdrawal,
370 )
371 .map_err(ModelError::from)?;
372 }
373
374 if deposit.is_market_deposit_required() {
375 let executed = op.unchecked_deposit(
376 &deposit.header().receiver(),
377 &self.market_token_vault,
378 &deposit.params.deposit,
379 (
380 deposit.tokens.initial_long_token.token(),
381 deposit.tokens.initial_short_token.token(),
382 ),
383 None,
384 )?;
385
386 market_token_amount = market_token_amount
387 .checked_add(executed.output)
388 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
389
390 op = executed.with_output(());
391 }
392
393 let market_token_mint = op.market().market_meta().market_token_mint;
394 let next_market_token_balance = self
395 .glv
396 .load()?
397 .market_config(&market_token_mint)
398 .ok_or_else(|| error!(CoreError::NotFound))?
399 .balance()
400 .checked_add(market_token_amount)
401 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
402
403 let glv_amount = {
405 let maximize_value = true;
406 let glv_supply = self.glv_token_mint.supply;
407 let glv_value = unchecked_get_glv_value(
408 &*self.glv.load()?,
409 self.oracle,
410 &op,
411 self.markets,
412 self.market_tokens,
413 maximize_value,
414 )?;
415
416 let (received_value, market_pool_value, market_token_supply) = {
417 let mut prices = self.oracle.market_prices(op.market())?;
418 let balance = u128::from(market_token_amount);
419 let received_value = get_glv_value_for_market_with_new_index_price(
420 self.oracle,
421 &mut prices,
422 op.market(),
423 balance,
424 false,
425 )?
426 .0;
427
428 let (_, market_pool_value, market_token_supply) =
429 get_glv_value_for_market(&prices, op.market(), balance, true)?;
430
431 (received_value, market_pool_value, market_token_supply)
432 };
433
434 self.glv.load()?.validate_market_token_balance(
436 &market_token_mint,
437 next_market_token_balance,
438 &market_pool_value,
439 &market_token_supply,
440 )?;
441
442 msg!(
443 "[GLV] Calculating GLV amount with glv_supply={}, glv_value={}, received_value={}",
444 glv_supply,
445 glv_value,
446 received_value,
447 );
448
449 let glv_amount = usd_to_market_token_amount(
450 received_value,
451 glv_value,
452 u128::from(glv_supply),
453 constants::MARKET_USD_TO_AMOUNT_DIVISOR,
454 )
455 .ok_or_else(|| error!(CoreError::FailedToCalculateGlvAmountToMint))?;
456 let output_amount = u64::try_from(glv_amount)
457 .map_err(|_| error!(CoreError::TokenAmountOverflow))?;
458
459 self.event_emitter.emit_cpi(&GlvPricing {
460 glv_token: self.glv_token_mint.key(),
461 market_token: market_token_mint.key(),
462 supply: glv_supply,
463 value_maximized: maximize_value,
464 value: glv_value,
465 input_amount: market_token_amount,
466 input_value: received_value,
467 output_amount,
468 kind: GlvPricingKind::Deposit,
469 })?;
470
471 output_amount
472 };
473
474 deposit.validate_output_amount(glv_amount)?;
475
476 self.glv
478 .load_mut()?
479 .update_market_token_balance(&market_token_mint, next_market_token_balance)?;
480
481 op.commit();
482
483 glv_amount
484 };
485
486 {
488 self.transfer_market_tokens_in();
490
491 self.mint_glv_tokens(glv_token_amount);
493 }
494
495 Ok(())
496 }
497
498 fn mint_glv_tokens(&self, glv_token_amount: u64) {
503 if glv_token_amount != 0 {
504 TransferUtils::new(
505 self.glv_token_program.clone(),
506 &self.store,
507 self.glv_token_mint.to_account_info(),
508 )
509 .mint_to(&self.glv_token_receiver, glv_token_amount)
510 .expect("failed to mint glv tokens");
511 }
512 }
513
514 fn transfer_market_tokens_in(&self) {
519 use anchor_spl::token_interface::{transfer_checked, TransferChecked};
520
521 let deposit = self.glv_deposit.load().expect("must have been checked");
522 let signer = deposit.signer();
523
524 let amount = deposit.params.market_token_amount;
525 if amount != 0 {
526 let token = &*self.market_token_mint;
527 let from = &self.market_token_source;
528 let to = &self.market_token_vault;
529 let ctx = CpiContext::new(
530 self.token_program.to_account_info(),
531 TransferChecked {
532 from: from.to_account_info(),
533 mint: token.to_account_info(),
534 to: to.to_account_info(),
535 authority: self.glv_deposit.to_account_info(),
536 },
537 );
538 transfer_checked(
539 ctx.with_signer(&[&signer.as_seeds()]),
540 amount,
541 token.decimals,
542 )
543 .expect("failed to transfer market tokens");
544 }
545 }
546}
547
548impl ValidateOracleTime for ExecuteGlvDepositOperation<'_, '_> {
549 fn oracle_updated_after(&self) -> CoreResult<Option<i64>> {
550 Ok(Some(
551 self.glv_deposit
552 .load()
553 .map_err(|_| CoreError::LoadAccountError)?
554 .header
555 .updated_at,
556 ))
557 }
558
559 fn oracle_updated_before(&self) -> CoreResult<Option<i64>> {
560 let ts = self
561 .store
562 .load()
563 .map_err(|_| CoreError::LoadAccountError)?
564 .request_expiration_at(
565 self.glv_deposit
566 .load()
567 .map_err(|_| CoreError::LoadAccountError)?
568 .header
569 .updated_at,
570 )?;
571 Ok(Some(ts))
572 }
573
574 fn oracle_updated_after_slot(&self) -> CoreResult<Option<u64>> {
575 Ok(Some(
576 self.glv_deposit
577 .load()
578 .map_err(|_| CoreError::LoadAccountError)?
579 .header
580 .updated_at_slot,
581 ))
582 }
583}
584
585#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
587pub struct CreateGlvWithdrawalParams {
588 pub execution_lamports: u64,
590 pub long_token_swap_length: u8,
592 pub short_token_swap_length: u8,
594 pub glv_token_amount: u64,
596 pub min_final_long_token_amount: u64,
598 pub min_final_short_token_amount: u64,
600 pub should_unwrap_native_token: bool,
602}
603
604impl ActionParams for CreateGlvWithdrawalParams {
605 fn execution_lamports(&self) -> u64 {
606 self.execution_lamports
607 }
608}
609
610#[derive(TypedBuilder)]
612pub(crate) struct CreateGlvWithdrawalOperation<'a, 'info> {
613 glv_withdrawal: AccountLoader<'info, GlvWithdrawal>,
614 market: AccountLoader<'info, Market>,
615 store: AccountLoader<'info, Store>,
616 owner: &'a AccountInfo<'info>,
617 receiver: &'a AccountInfo<'info>,
618 nonce: &'a NonceBytes,
619 bump: u8,
620 final_long_token: &'a Account<'info, TokenAccount>,
621 final_short_token: &'a Account<'info, TokenAccount>,
622 market_token: &'a Account<'info, TokenAccount>,
623 glv_token: &'a InterfaceAccount<'info, token_interface::TokenAccount>,
624 params: &'a CreateGlvWithdrawalParams,
625 swap_paths: &'info [AccountInfo<'info>],
626}
627
628impl CreateGlvWithdrawalOperation<'_, '_> {
629 pub(crate) fn unchecked_execute(self) -> Result<()> {
636 let (long_token, short_token) = self.validate_market_and_get_tokens()?;
637
638 self.validate_params_excluding_swap()?;
639
640 let id = self
641 .market
642 .load_mut()?
643 .indexer_mut()
644 .next_glv_withdrawal_id()?;
645
646 let mut glv_withdrawal = self.glv_withdrawal.load_init()?;
647
648 glv_withdrawal.header.init(
650 id,
651 self.store.key(),
652 self.market.key(),
653 self.owner.key(),
654 self.receiver.key(),
655 *self.nonce,
656 self.bump,
657 self.params.execution_lamports,
658 self.params.should_unwrap_native_token,
659 )?;
660
661 let tokens = &mut glv_withdrawal.tokens;
663 tokens.glv_token.init_with_interface(self.glv_token);
664 tokens.market_token.init(self.market_token);
665 tokens.final_long_token.init(self.final_long_token);
666 tokens.final_short_token.init(self.final_short_token);
667
668 let params = &mut glv_withdrawal.params;
670 params.glv_token_amount = self.params.glv_token_amount;
671 params.min_final_long_token_amount = self.params.min_final_long_token_amount;
672 params.min_final_short_token_amount = self.params.min_final_short_token_amount;
673
674 glv_withdrawal.swap.validate_and_init(
676 &*self.market.load()?,
677 self.params.long_token_swap_length,
678 self.params.short_token_swap_length,
679 self.swap_paths,
680 &self.store.key(),
681 (&long_token, &short_token),
682 (&self.final_long_token.mint, &self.final_short_token.mint),
683 )?;
684
685 Ok(())
686 }
687
688 fn validate_market_and_get_tokens(&self) -> Result<(Pubkey, Pubkey)> {
689 let market = self.market.load()?;
690 let meta = market.validated_meta(&self.store.key())?;
691 Ok((meta.long_token_mint, meta.short_token_mint))
692 }
693
694 fn validate_params_excluding_swap(&self) -> Result<()> {
695 let params = self.params;
696 let amount = params.glv_token_amount;
697 require!(amount != 0, CoreError::EmptyGlvWithdrawal);
698 require_gte!(
699 self.glv_token.amount,
700 amount,
701 CoreError::NotEnoughTokenAmount
702 );
703
704 ActionExt::validate_balance(&self.glv_withdrawal, params.execution_lamports)?;
705
706 Ok(())
707 }
708}
709
710#[derive(TypedBuilder)]
712pub(crate) struct ExecuteGlvWithdrawalOperation<'a, 'info> {
713 glv_withdrawal: AccountLoader<'info, GlvWithdrawal>,
714 token_program: AccountInfo<'info>,
715 glv_token_program: AccountInfo<'info>,
716 throw_on_execution_error: bool,
717 store: AccountLoader<'info, Store>,
718 glv: &'a AccountLoader<'info, Glv>,
719 glv_token_mint: &'a mut InterfaceAccount<'info, token_interface::Mint>,
720 glv_token_account: AccountInfo<'info>,
721 market: AccountLoader<'info, Market>,
722 market_token_mint: &'a mut Account<'info, Mint>,
723 market_token_glv_vault: &'a Account<'info, TokenAccount>,
724 market_token_withdrawal_vault: AccountInfo<'info>,
725 markets: &'info [AccountInfo<'info>],
726 market_tokens: &'info [AccountInfo<'info>],
727 oracle: &'a Oracle,
728 remaining_accounts: &'info [AccountInfo<'info>],
729 #[builder(setter(into))]
730 event_emitter: EventEmitter<'a, 'info>,
731}
732
733impl ExecuteGlvWithdrawalOperation<'_, '_> {
734 pub(crate) fn unchecked_execute(mut self) -> Result<Option<(u64, u64)>> {
741 let throw_on_execution_error = self.throw_on_execution_error;
742 match self.validate_oracle() {
743 Ok(()) => {}
744 Err(CoreError::OracleTimestampsAreLargerThanRequired) if !throw_on_execution_error => {
745 msg!(
746 "GLV Withdrawal expired at {}",
747 self.oracle_updated_before()
748 .ok()
749 .flatten()
750 .expect("must have an expiration time"),
751 );
752 return Ok(None);
753 }
754 Err(err) => {
755 return Err(error!(err));
756 }
757 }
758
759 let executed = match self.perform_glv_withdrawal() {
760 Ok(amounts) => Some(amounts),
761 Err(err) if !throw_on_execution_error => {
762 msg!("Execute GLV withdrawal error: {}", err);
763 None
764 }
765 Err(err) => return Err(err),
766 };
767
768 self.validate_after_execution()?;
769
770 Ok(executed)
771 }
772
773 fn validate_oracle(&self) -> CoreResult<()> {
774 self.oracle.validate_time(self)
775 }
776
777 fn validate_market(&self) -> Result<()> {
778 self.market.load()?.validate(&self.store.key())?;
779 Ok(())
780 }
781
782 fn validate_after_execution(&self) -> Result<()> {
783 use anchor_spl::token::accessor::amount;
784
785 let glv = self.glv.load()?;
786 let market_token = self.market_token_mint.key();
787 let vault_balance = amount(&self.market_token_glv_vault.to_account_info())?;
788
789 require_gte!(
790 vault_balance,
791 glv.market_config(&market_token)
792 .ok_or_else(|| error!(CoreError::NotFound))?
793 .balance(),
794 CoreError::Internal
795 );
796
797 Ok(())
798 }
799
800 #[inline(never)]
801 fn perform_glv_withdrawal(&mut self) -> Result<(u64, u64)> {
802 use gmsol_model::utils::market_token_amount_to_usd;
803
804 self.validate_market()?;
805
806 let withdrawal_signer = self.glv_withdrawal.load()?.signer();
807
808 let (glv_token_amount, amounts) = {
809 let withdrawal = self.glv_withdrawal.load()?;
810 let glv_token_amount = withdrawal.params.glv_token_amount;
811 let market_token_mint = self.market_token_mint.to_account_info();
812 let market_token_decimals = self.market_token_mint.decimals;
813
814 let mut market = RevertibleLiquidityMarketOperation::new(
815 &self.store,
816 self.oracle,
817 &self.market,
818 self.market_token_mint,
819 self.token_program.clone(),
820 Some(&withdrawal.swap),
821 self.remaining_accounts,
822 self.event_emitter,
823 )?;
824
825 let op = market.op()?;
826
827 let market_token_amount = {
829 let maximize_value = false;
830 let glv_supply = self.glv_token_mint.supply;
831 let glv_value = unchecked_get_glv_value(
832 &*self.glv.load()?,
833 self.oracle,
834 &op,
835 self.markets,
836 self.market_tokens,
837 maximize_value,
838 )?;
839
840 let market_token_value = market_token_amount_to_usd(
841 &(u128::from(glv_token_amount)),
842 &glv_value,
843 &(u128::from(glv_supply)),
844 )
845 .ok_or_else(|| error!(CoreError::FailedToCalculateGlvValueForMarket))?;
846
847 msg!(
848 "[GLV] Calculating GM amount with glv_supply={}, glv_value={}, market_token_value={}",
849 glv_supply,
850 glv_value,
851 market_token_value,
852 );
853
854 let amount = get_market_token_amount_for_glv_value(
855 self.oracle,
856 op.market(),
857 market_token_value,
858 true,
859 )?
860 .try_into()
861 .map_err(|_| error!(CoreError::TokenAmountOverflow))?;
862
863 self.event_emitter.emit_cpi(&GlvPricing {
864 glv_token: self.glv_token_mint.key(),
865 market_token: market_token_mint.key(),
866 supply: glv_supply,
867 value_maximized: maximize_value,
868 value: glv_value,
869 input_amount: glv_token_amount,
870 input_value: market_token_value,
871 output_amount: amount,
872 kind: GlvPricingKind::Withdrawal,
873 })?;
874
875 amount
876 };
877
878 require_gte!(
879 self.market_token_glv_vault.amount,
880 market_token_amount,
881 CoreError::NotEnoughTokenAmount,
882 );
883
884 let executed = {
885 let mut params = WithdrawalActionParams::default();
886 params.market_token_amount = market_token_amount;
887 params.min_long_token_amount = withdrawal.params.min_final_long_token_amount;
888 params.min_short_token_amount = withdrawal.params.min_final_short_token_amount;
889
890 op.unchecked_withdraw(
891 &self.market_token_withdrawal_vault,
892 ¶ms,
893 (
894 withdrawal.tokens.final_long_token(),
895 withdrawal.tokens.final_short_token(),
896 ),
897 None,
898 )?
899 };
900
901 let amounts = executed.output;
902
903 let next_market_token_balance = self
905 .glv
906 .load()?
907 .market_config(market_token_mint.key)
908 .ok_or_else(|| error!(CoreError::NotFound))?
909 .balance()
910 .checked_sub(market_token_amount)
911 .ok_or_else(|| error!(CoreError::NotEnoughTokenAmount))?;
912
913 self.glv
914 .load_mut()?
915 .update_market_token_balance(market_token_mint.key, next_market_token_balance)?;
916
917 transfer_checked(
919 CpiContext::new(
920 self.token_program.to_account_info(),
921 TransferChecked {
922 from: self.market_token_glv_vault.to_account_info(),
923 mint: market_token_mint,
924 to: self.market_token_withdrawal_vault.to_account_info(),
925 authority: self.glv.to_account_info(),
926 },
927 )
928 .with_signer(&[&self.glv.load()?.signer_seeds()]),
929 market_token_amount,
930 market_token_decimals,
931 )
932 .expect("failed to transfer market tokens");
933
934 executed.commit();
935
936 (glv_token_amount, amounts)
937 };
938
939 {
941 self.burn_glv_tokens(&withdrawal_signer, glv_token_amount);
943 }
944
945 Ok(amounts)
946 }
947
948 fn burn_glv_tokens(&self, signer: &ActionSigner, glv_token_amount: u64) {
953 use anchor_spl::token_interface::{burn, Burn};
954
955 if glv_token_amount != 0 {
956 let ctx = CpiContext::new(
957 self.glv_token_program.to_account_info(),
958 Burn {
959 mint: self.glv_token_mint.to_account_info(),
960 from: self.glv_token_account.clone(),
961 authority: self.glv_withdrawal.to_account_info(),
962 },
963 );
964 burn(ctx.with_signer(&[&signer.as_seeds()]), glv_token_amount)
965 .expect("failed to burn GLV tokens");
966 }
967 }
968}
969
970impl ValidateOracleTime for ExecuteGlvWithdrawalOperation<'_, '_> {
971 fn oracle_updated_after(&self) -> CoreResult<Option<i64>> {
972 Ok(Some(
973 self.glv_withdrawal
974 .load()
975 .map_err(|_| CoreError::LoadAccountError)?
976 .header
977 .updated_at,
978 ))
979 }
980
981 fn oracle_updated_before(&self) -> CoreResult<Option<i64>> {
982 let ts = self
983 .store
984 .load()
985 .map_err(|_| CoreError::LoadAccountError)?
986 .request_expiration_at(
987 self.glv_withdrawal
988 .load()
989 .map_err(|_| CoreError::LoadAccountError)?
990 .header
991 .updated_at,
992 )?;
993 Ok(Some(ts))
994 }
995
996 fn oracle_updated_after_slot(&self) -> CoreResult<Option<u64>> {
997 Ok(Some(
998 self.glv_withdrawal
999 .load()
1000 .map_err(|_| CoreError::LoadAccountError)?
1001 .header
1002 .updated_at_slot,
1003 ))
1004 }
1005}
1006
1007fn unchecked_get_glv_value<'info>(
1016 glv: &Glv,
1017 oracle: &Oracle,
1018 op: &Execute<'_, 'info>,
1019 glv_markets: &'info [AccountInfo<'info>],
1020 glv_market_tokens: &'info [AccountInfo<'info>],
1021 maximize: bool,
1022) -> Result<u128> {
1023 use crate::states::market::AsLiquidityMarket;
1024
1025 let mut value = 0u128;
1026
1027 let current_market = op.market();
1028 let swap_markets = op.swap_markets();
1029
1030 let mut prices = oracle.market_prices(current_market)?;
1031
1032 for (market, market_token) in glv_markets.iter().zip(glv_market_tokens) {
1033 let key = market_token.key();
1034
1035 let balance = u128::from(
1037 glv.market_config(&key)
1038 .ok_or_else(|| error!(CoreError::NotFound))?
1039 .balance(),
1040 );
1041
1042 let value_for_market = if key == current_market.key() {
1043 let market = current_market;
1044 get_glv_value_for_market_with_new_index_price(
1046 oracle,
1047 &mut prices,
1048 market,
1049 balance,
1050 maximize,
1051 )?
1052 .0
1053 } else if let Some(market) = swap_markets.get(&key) {
1054 let mint = Account::<Mint>::try_from(market_token)?;
1055 let market = AsLiquidityMarket::new(market, &mint);
1056 get_glv_value_for_market_with_new_index_price(
1057 oracle,
1058 &mut prices,
1059 &market,
1060 balance,
1061 maximize,
1062 )?
1063 .0
1064 } else {
1065 let market = AccountLoader::<Market>::try_from(market)?;
1066 let mint = Account::<Mint>::try_from(market_token)?;
1067 let market = market.load()?;
1068 let market = market.as_liquidity_market(&mint);
1069 get_glv_value_for_market_with_new_index_price(
1070 oracle,
1071 &mut prices,
1072 &market,
1073 balance,
1074 maximize,
1075 )?
1076 .0
1077 };
1078
1079 value = value
1080 .checked_add(value_for_market)
1081 .ok_or_else(|| error!(CoreError::ValueOverflow))?;
1082 }
1083
1084 Ok(value)
1085}
1086
1087fn get_glv_value_for_market_with_new_index_price<M>(
1088 oracle: &Oracle,
1089 prices: &mut Prices<u128>,
1090 market: &M,
1091 balance: u128,
1092 maximize: bool,
1093) -> Result<(u128, i128, u128)>
1094where
1095 M: gmsol_model::LiquidityMarket<{ constants::MARKET_DECIMALS }, Num = u128, Signed = i128>,
1096 M: HasMarketMeta,
1097{
1098 let index_token_mint = market.market_meta().index_token_mint;
1099 prices.index_token_price = oracle
1100 .get_primary_price(&index_token_mint, true)
1101 .expect("must exist");
1102
1103 get_glv_value_for_market(prices, market, balance, maximize)
1104}
1105
1106fn get_glv_value_for_market<M>(
1107 prices: &Prices<u128>,
1108 market: &M,
1109 balance: u128,
1110 maximize: bool,
1111) -> Result<(u128, i128, u128)>
1112where
1113 M: gmsol_model::LiquidityMarket<{ constants::MARKET_DECIMALS }, Num = u128, Signed = i128>,
1114{
1115 use gmsol_model::{utils, LiquidityMarketExt};
1116
1117 let value = market
1118 .pool_value(prices, PnlFactorKind::MaxAfterDeposit, maximize)
1119 .map_err(ModelError::from)?;
1120
1121 let supply = market.total_supply();
1122
1123 if balance == 0 {
1124 return Ok((0, value, supply));
1125 }
1126
1127 if value.is_negative() {
1128 return err!(CoreError::GlvNegativeMarketPoolValue);
1129 }
1130
1131 let glv_value = utils::market_token_amount_to_usd(&balance, &value.unsigned_abs(), &supply)
1132 .ok_or_else(|| error!(CoreError::FailedToCalculateGlvValueForMarket))?;
1133
1134 Ok((glv_value, value, supply))
1135}
1136
1137fn get_market_token_amount_for_glv_value<M>(
1138 oracle: &Oracle,
1139 market: &M,
1140 glv_value: u128,
1141 maximize: bool,
1142) -> Result<u128>
1143where
1144 M: gmsol_model::LiquidityMarket<{ constants::MARKET_DECIMALS }, Num = u128, Signed = i128>,
1145 M: HasMarketMeta,
1146{
1147 use gmsol_model::{utils, LiquidityMarketExt};
1148
1149 let prices = oracle.market_prices(market).expect("must exist");
1150
1151 let value = market
1152 .pool_value(&prices, PnlFactorKind::MaxAfterWithdrawal, maximize)
1153 .map_err(ModelError::from)?;
1154
1155 if value.is_negative() {
1156 return err!(CoreError::GlvNegativeMarketPoolValue);
1157 }
1158
1159 let supply = market.total_supply();
1160
1161 let market_token_amount = utils::usd_to_market_token_amount(
1162 glv_value,
1163 value.unsigned_abs(),
1164 supply,
1165 constants::MARKET_USD_TO_AMOUNT_DIVISOR,
1166 )
1167 .ok_or_else(|| error!(CoreError::FailedTOCalculateMarketTokenAmountToBurn))?;
1168
1169 Ok(market_token_amount)
1170}
1171
1172#[derive(TypedBuilder)]
1174pub(crate) struct ExecuteGlvShiftOperation<'a, 'info> {
1175 glv_shift: &'a AccountLoader<'info, GlvShift>,
1176 token_program: AccountInfo<'info>,
1177 throw_on_execution_error: bool,
1178 store: &'a AccountLoader<'info, Store>,
1179 glv: &'a AccountLoader<'info, Glv>,
1180 from_market: &'a AccountLoader<'info, Market>,
1181 from_market_token_mint: &'a mut Account<'info, Mint>,
1182 from_market_token_glv_vault: &'a Account<'info, TokenAccount>,
1183 from_market_token_withdrawal_vault: AccountInfo<'info>,
1184 to_market: &'a AccountLoader<'info, Market>,
1185 to_market_token_mint: &'a mut Account<'info, Mint>,
1186 to_market_token_glv_vault: AccountInfo<'info>,
1187 oracle: &'a Oracle,
1188 #[builder(setter(into))]
1189 event_emitter: EventEmitter<'a, 'info>,
1190}
1191
1192impl ExecuteGlvShiftOperation<'_, '_> {
1193 pub(crate) fn unchecked_execute(mut self) -> Result<bool> {
1200 let throw_on_execution_error = self.throw_on_execution_error;
1201 match self.validate_oracle() {
1202 Ok(()) => {}
1203 Err(CoreError::OracleTimestampsAreLargerThanRequired) if !throw_on_execution_error => {
1204 msg!(
1205 "GLV Shift expired at {}",
1206 self.oracle_updated_before()
1207 .ok()
1208 .flatten()
1209 .expect("must have an expiration time"),
1210 );
1211 return Ok(false);
1212 }
1213 Err(err) => {
1214 return Err(error!(err));
1215 }
1216 }
1217
1218 let executed = match self.perform_glv_shift() {
1219 Ok(()) => true,
1220 Err(err) if !throw_on_execution_error => {
1221 msg!("Execute GLV shift error: {}", err);
1222 false
1223 }
1224 Err(err) => return Err(err),
1225 };
1226
1227 self.validate_after_execution()?;
1228
1229 Ok(executed)
1230 }
1231
1232 fn validate_oracle(&self) -> CoreResult<()> {
1233 self.oracle.validate_time(self)
1234 }
1235
1236 fn validate_before_execution(&self) -> Result<()> {
1237 self.glv.load()?.validate_shift_interval()?;
1238
1239 require!(
1240 self.from_market.key() != self.to_market.key(),
1241 CoreError::Internal
1242 );
1243
1244 let from_market = self.from_market.load()?;
1245 let to_market = self.to_market.load()?;
1246
1247 from_market.validate(&self.store.key())?;
1248 to_market.validate(&self.store.key())?;
1249
1250 from_market.validate_shiftable(&to_market)?;
1251
1252 let shift = self.glv_shift.load()?;
1253
1254 let amount = Borrow::<Shift>::borrow(&*shift)
1256 .params
1257 .from_market_token_amount;
1258 require_gte!(
1259 self.from_market_token_glv_vault.amount,
1260 amount,
1261 CoreError::NotEnoughTokenAmount
1262 );
1263
1264 Ok(())
1265 }
1266
1267 fn validate_after_execution(&self) -> Result<()> {
1268 use anchor_spl::token::accessor::amount;
1269
1270 let glv = self.glv.load()?;
1271
1272 let from_market_token = self.from_market_token_mint.key();
1273 let from_market_token_vault_balance =
1274 amount(&self.from_market_token_glv_vault.to_account_info())?;
1275 require_gte!(
1276 from_market_token_vault_balance,
1277 glv.market_config(&from_market_token)
1278 .ok_or_else(|| error!(CoreError::NotFound))?
1279 .balance(),
1280 CoreError::Internal
1281 );
1282
1283 let to_market_token = self.to_market_token_mint.key();
1284 let to_market_token_vault_balance =
1285 amount(&self.to_market_token_glv_vault.to_account_info())?;
1286 require_gte!(
1287 to_market_token_vault_balance,
1288 glv.market_config(&to_market_token)
1289 .ok_or_else(|| error!(CoreError::NotFound))?
1290 .balance(),
1291 CoreError::Internal
1292 );
1293
1294 Ok(())
1295 }
1296
1297 #[inline(never)]
1298 fn perform_glv_shift(&mut self) -> Result<()> {
1299 self.validate_before_execution()?;
1300
1301 let from_market_token_address = self.from_market_token_mint.key();
1302 let to_market_token_address = self.to_market_token_mint.key();
1303
1304 let from_market_token_mint = self.from_market_token_mint.to_account_info();
1305 let from_market_token_decimals = self.from_market_token_mint.decimals;
1306 let glv_shift = self.glv_shift.load()?;
1307 let shift = Borrow::<Shift>::borrow(&*glv_shift);
1308
1309 let mut from_market = RevertibleLiquidityMarketOperation::new(
1310 self.store,
1311 self.oracle,
1312 self.from_market,
1313 self.from_market_token_mint,
1314 self.token_program.clone(),
1315 None,
1316 &[],
1317 self.event_emitter,
1318 )?;
1319
1320 let mut to_market = RevertibleLiquidityMarketOperation::new(
1321 self.store,
1322 self.oracle,
1323 self.to_market,
1324 self.to_market_token_mint,
1325 self.token_program.clone(),
1326 None,
1327 &[],
1328 self.event_emitter,
1329 )?;
1330
1331 let from_market = from_market.op()?;
1332 let to_market = to_market.op()?;
1333
1334 let (from_market, to_market, received) = from_market.unchecked_shift(
1335 to_market,
1336 &shift.header().receiver(),
1337 &shift.params,
1338 &self.from_market_token_withdrawal_vault,
1339 &self.to_market_token_glv_vault,
1340 )?;
1341
1342 let next_to_market_token_balance = {
1344 let (_, market_pool_value, market_token_supply) = {
1345 let mut prices = self.oracle.market_prices(to_market.market())?;
1346 get_glv_value_for_market_with_new_index_price(
1347 self.oracle,
1348 &mut prices,
1349 to_market.market(),
1350 0,
1351 true,
1352 )?
1353 };
1354 let current_balance = self
1355 .glv
1356 .load()?
1357 .market_config(&to_market_token_address)
1358 .ok_or_else(|| error!(CoreError::NotFound))?
1359 .balance();
1360 let new_balance = current_balance
1361 .checked_add(received)
1362 .ok_or_else(|| error!(CoreError::TokenAmountOverflow))?;
1363 self.glv.load()?.validate_market_token_balance(
1364 &to_market.market().market_meta().market_token_mint,
1365 new_balance,
1366 &market_pool_value,
1367 &market_token_supply,
1368 )?;
1369
1370 new_balance
1371 };
1372
1373 {
1375 let mut prices = self.oracle.market_prices(from_market.market())?;
1376
1377 let (from_market_token_value, _, _) = get_glv_value_for_market_with_new_index_price(
1378 self.oracle,
1379 &mut prices,
1380 from_market.market(),
1381 shift.params.from_market_token_amount().into(),
1382 true,
1383 )?;
1384
1385 self.glv
1386 .load()?
1387 .validate_shift_value(from_market_token_value)?;
1388
1389 let (to_market_token_value, _, _) = get_glv_value_for_market_with_new_index_price(
1390 self.oracle,
1391 &mut prices,
1392 to_market.market(),
1393 received.into(),
1394 true,
1395 )?;
1396
1397 self.glv
1398 .load()?
1399 .validate_shift_price_impact(from_market_token_value, to_market_token_value)?;
1400 }
1401
1402 let next_from_market_token_balance = {
1404 let glv = self.glv.load()?;
1405 let seeds = glv.signer_seeds();
1406
1407 let amount = shift.params.from_market_token_amount;
1408 let next_from_market_token_balance = glv
1409 .market_config(&from_market_token_address)
1410 .ok_or_else(|| error!(CoreError::NotFound))?
1411 .balance()
1412 .checked_sub(amount)
1413 .ok_or_else(|| error!(CoreError::NotEnoughTokenAmount))?;
1414
1415 transfer_checked(
1416 CpiContext::new(
1417 self.token_program.to_account_info(),
1418 TransferChecked {
1419 from: self.from_market_token_glv_vault.to_account_info(),
1420 mint: from_market_token_mint,
1421 to: self.from_market_token_withdrawal_vault.to_account_info(),
1422 authority: self.glv.to_account_info(),
1423 },
1424 )
1425 .with_signer(&[&seeds]),
1426 amount,
1427 from_market_token_decimals,
1428 )
1429 .expect("failed to transfer from market tokens");
1430
1431 next_from_market_token_balance
1432 };
1433
1434 from_market.commit();
1436 to_market.commit();
1437
1438 {
1440 let mut glv = self.glv.load_mut().expect("must success");
1441 glv.update_shift_last_executed_ts()
1442 .expect("failed to update shift last executed ts");
1443 glv.update_market_token_balance(
1444 &from_market_token_address,
1445 next_from_market_token_balance,
1446 )
1447 .expect("failed to update from market token balance");
1448 glv.update_market_token_balance(&to_market_token_address, next_to_market_token_balance)
1449 .expect("failed to update from market token balance");
1450 }
1451
1452 Ok(())
1453 }
1454}
1455
1456impl ValidateOracleTime for ExecuteGlvShiftOperation<'_, '_> {
1457 fn oracle_updated_after(&self) -> CoreResult<Option<i64>> {
1458 Ok(Some(
1459 self.glv_shift
1460 .load()
1461 .map_err(|_| CoreError::LoadAccountError)?
1462 .header()
1463 .updated_at,
1464 ))
1465 }
1466
1467 fn oracle_updated_before(&self) -> CoreResult<Option<i64>> {
1468 let ts = self
1469 .store
1470 .load()
1471 .map_err(|_| CoreError::LoadAccountError)?
1472 .request_expiration_at(
1473 self.glv_shift
1474 .load()
1475 .map_err(|_| CoreError::LoadAccountError)?
1476 .header()
1477 .updated_at,
1478 )?;
1479 Ok(Some(ts))
1480 }
1481
1482 fn oracle_updated_after_slot(&self) -> CoreResult<Option<u64>> {
1483 Ok(Some(
1484 self.glv_shift
1485 .load()
1486 .map_err(|_| CoreError::LoadAccountError)?
1487 .header()
1488 .updated_at_slot,
1489 ))
1490 }
1491}