1use anchor_lang::prelude::*;
2use anchor_spl::token::{Mint, TokenAccount};
3use gmsol_model::{
4 price::Prices, Bank, BorrowingFeeMarketMutExt, LiquidityMarketMutExt, MarketAction,
5 PerpMarketMutExt, PositionImpactMarketMutExt,
6};
7use typed_builder::TypedBuilder;
8
9use crate::{
10 events::{DepositExecuted, EventEmitter, MarketFeesUpdated, WithdrawalExecuted},
11 states::{
12 common::swap::{SwapActionParams, SwapActionParamsExt},
13 deposit::DepositActionParams,
14 market::{
15 revertible::{
16 liquidity_market::RevertibleLiquidityMarket,
17 market::SwapPricingKind,
18 swap_market::{SwapDirection, SwapMarkets},
19 Revertible, RevertibleMarket, Revision,
20 },
21 utils::ValidateMarketBalances,
22 HasMarketMeta,
23 },
24 withdrawal::WithdrawalActionParams,
25 Deposit, Market, Oracle, ShiftActionParams, Store,
26 },
27 CoreError, ModelError,
28};
29
30#[derive(TypedBuilder)]
32pub(crate) struct MarketTransferInOperation<'a, 'info> {
33 store: &'a AccountLoader<'info, Store>,
34 market: &'a AccountLoader<'info, Market>,
35 from: AccountInfo<'info>,
36 from_authority: AccountInfo<'info>,
37 vault: &'a Account<'info, TokenAccount>,
38 amount: u64,
39 token_program: AccountInfo<'info>,
40 signer_seeds: &'a [&'a [u8]],
41 #[builder(setter(into))]
42 event_emitter: EventEmitter<'a, 'info>,
43}
44
45impl MarketTransferInOperation<'_, '_> {
46 pub(crate) fn execute(self) -> Result<()> {
47 use anchor_spl::token;
48
49 self.market.load()?.validate(&self.store.key())?;
50
51 let amount = self.amount;
52 if amount != 0 {
53 token::transfer(
54 CpiContext::new(
55 self.token_program,
56 token::Transfer {
57 from: self.from,
58 to: self.vault.to_account_info(),
59 authority: self.from_authority,
60 },
61 )
62 .with_signer(&[self.signer_seeds]),
63 amount,
64 )?;
65 let token = &self.vault.mint;
66 let mut market = RevertibleMarket::new(self.market, self.event_emitter)?;
67 market
68 .record_transferred_in_by_token(token, &amount)
69 .map_err(ModelError::from)?;
70 market.commit();
71 }
72
73 Ok(())
74 }
75}
76
77#[derive(TypedBuilder)]
79pub(crate) struct MarketTransferOutOperation<'a, 'info> {
80 store: &'a AccountLoader<'info, Store>,
81 market: &'a AccountLoader<'info, Market>,
82 amount: u64,
83 decimals: u8,
84 to: AccountInfo<'info>,
85 token_mint: AccountInfo<'info>,
86 vault: AccountInfo<'info>,
87 token_program: AccountInfo<'info>,
88 #[builder(setter(into))]
89 event_emitter: EventEmitter<'a, 'info>,
90}
91
92impl MarketTransferOutOperation<'_, '_> {
93 pub(crate) fn execute(self) -> Result<()> {
94 use crate::utils::internal::TransferUtils;
95
96 {
97 let market = self.market.load()?;
98 let meta = market.validated_meta(&self.store.key())?;
99 require!(
100 meta.is_collateral_token(&self.token_mint.key()),
101 CoreError::InvalidCollateralToken
102 );
103 }
104
105 let amount = self.amount;
106 if amount != 0 {
107 let decimals = self.decimals;
108 TransferUtils::new(
109 self.token_program.to_account_info(),
110 self.store,
111 self.token_mint.to_account_info(),
112 )
113 .transfer_out(self.vault.to_account_info(), self.to, amount, decimals)?;
114 let token = &self.token_mint.key();
115 let mut market = RevertibleMarket::new(self.market, self.event_emitter)?;
116 market
117 .record_transferred_out_by_token(token, &amount)
118 .map_err(ModelError::from)?;
119 market.commit();
120 }
121
122 Ok(())
123 }
124}
125
126pub struct RevertibleLiquidityMarketOperation<'a, 'info> {
128 store: &'a AccountLoader<'info, Store>,
129 oracle: &'a Oracle,
130 market: &'a AccountLoader<'info, Market>,
131 market_token_mint: &'a mut Account<'info, Mint>,
132 token_program: AccountInfo<'info>,
133 swap: Option<&'a SwapActionParams>,
134 swap_markets: Vec<AccountLoader<'info, Market>>,
135 event_emitter: EventEmitter<'a, 'info>,
136}
137
138impl<'a, 'info> RevertibleLiquidityMarketOperation<'a, 'info> {
139 pub(crate) fn new(
140 store: &'a AccountLoader<'info, Store>,
141 oracle: &'a Oracle,
142 market: &'a AccountLoader<'info, Market>,
143 market_token_mint: &'a mut Account<'info, Mint>,
144 token_program: AccountInfo<'info>,
145 swap: Option<&'a SwapActionParams>,
146 remaining_accounts: &'info [AccountInfo<'info>],
147 event_emitter: EventEmitter<'a, 'info>,
148 ) -> Result<Self> {
149 let swap_markets = swap
150 .map(|swap| swap.unpack_markets_for_swap(&market_token_mint.key(), remaining_accounts))
151 .transpose()?
152 .unwrap_or_default();
153
154 Ok(Self {
155 store,
156 oracle,
157 market,
158 market_token_mint,
159 token_program,
160 swap,
161 swap_markets,
162 event_emitter,
163 })
164 }
165}
166
167impl<'info> RevertibleLiquidityMarketOperation<'_, 'info> {
168 pub(crate) fn op<'ctx>(&'ctx mut self) -> Result<Execute<'ctx, 'info>> {
169 let current_market_token = self.market_token_mint.key();
170 let market = RevertibleLiquidityMarket::from_revertible_market(
171 RevertibleMarket::new(self.market, self.event_emitter)?,
172 self.market_token_mint,
173 &self.token_program,
174 self.store,
175 )?;
176 let swap_markets = SwapMarkets::new(
177 &self.store.key(),
178 &self.swap_markets,
179 Some(¤t_market_token),
180 self.event_emitter,
181 )?;
182 Ok(Execute {
183 output: (),
184 oracle: self.oracle,
185 swap: self.swap,
186 market,
187 swap_markets,
188 event_emitter: self.event_emitter,
189 })
190 }
191}
192
193#[must_use = "Revertible operation must be committed to take effect"]
194pub(crate) struct Execute<'a, 'info, T = ()> {
195 pub(crate) output: T,
196 oracle: &'a Oracle,
197 swap: Option<&'a SwapActionParams>,
198 market: RevertibleLiquidityMarket<'a, 'info>,
199 swap_markets: SwapMarkets<'a, 'info>,
200 event_emitter: EventEmitter<'a, 'info>,
201}
202
203impl<'a, 'info, T> Execute<'a, 'info, T> {
204 pub(crate) fn with_output<U>(self, output: U) -> Execute<'a, 'info, U> {
205 let Self {
206 oracle,
207 swap,
208 market,
209 swap_markets,
210 event_emitter: event_authority,
211 ..
212 } = self;
213
214 Execute {
215 output,
216 oracle,
217 swap,
218 market,
219 swap_markets,
220 event_emitter: event_authority,
221 }
222 }
223
224 pub(crate) fn market(&self) -> &RevertibleLiquidityMarket<'a, 'info> {
225 &self.market
226 }
227
228 pub(crate) fn market_mut(&mut self) -> &mut RevertibleLiquidityMarket<'a, 'info> {
229 &mut self.market
230 }
231
232 pub(crate) fn swap_markets(&self) -> &SwapMarkets<'_, 'info> {
233 &self.swap_markets
234 }
235
236 fn pre_execute(&mut self, prices: &Prices<u128>) -> Result<()> {
237 let distribute_position_impact = self
239 .market
240 .distribute_position_impact()
241 .map_err(ModelError::from)?
242 .execute()
243 .map_err(ModelError::from)?;
244
245 if *distribute_position_impact.distribution_amount() != 0 {
246 msg!("[Pre-execute] position impact distributed");
247 }
248
249 let borrowing = self
251 .market
252 .base_mut()
253 .update_borrowing(prices)
254 .and_then(|a| a.execute())
255 .map_err(ModelError::from)?;
256 msg!("[Pre-execute] borrowing state updated");
257
258 let funding = self
260 .market
261 .base_mut()
262 .update_funding(prices)
263 .and_then(|a| a.execute())
264 .map_err(ModelError::from)?;
265 msg!("[Pre-execute] funding state updated");
266
267 self.event_emitter
268 .emit_cpi(&MarketFeesUpdated::from_reports(
269 self.market.rev(),
270 self.market.market_meta().market_token_mint,
271 distribute_position_impact,
272 borrowing,
273 funding,
274 ))?;
275 Ok(())
276 }
277
278 fn validate_first_deposit(
279 &self,
280 receiver: &Pubkey,
281 params: &DepositActionParams,
282 ) -> Result<()> {
283 if self.market().market_token().supply == 0 {
284 Deposit::validate_first_deposit(
285 receiver,
286 params.min_market_token_amount,
287 self.market().base().as_ref(),
288 )?;
289 }
290
291 Ok(())
292 }
293
294 #[inline(never)]
303 pub(crate) fn unchecked_deposit(
304 mut self,
305 receiver: &Pubkey,
306 market_token_receiver: &'a AccountInfo<'info>,
307 params: &DepositActionParams,
308 initial_tokens: (Option<Pubkey>, Option<Pubkey>),
309 swap_pricing_kind: Option<SwapPricingKind>,
310 ) -> Result<Execute<'a, 'info, u64>> {
311 self.validate_first_deposit(receiver, params)?;
312
313 self.market = self
314 .market
315 .enable_mint(market_token_receiver)
316 .with_swap_pricing_kind(swap_pricing_kind.unwrap_or(SwapPricingKind::Deposit));
317
318 let prices = self.oracle.market_prices(&self.market)?;
319
320 self.pre_execute(&prices)?;
321
322 let (long_token_amount, short_token_amount) = {
324 let meta = self.market.market_meta();
325 let expected_token_outs = (meta.long_token_mint, meta.short_token_mint);
326
327 match self.swap {
328 Some(swap) => self.swap_markets.revertible_swap(
329 SwapDirection::Into(&mut self.market),
330 self.oracle,
331 swap,
332 expected_token_outs,
333 initial_tokens,
334 (
335 params.initial_long_token_amount,
336 params.initial_short_token_amount,
337 ),
338 )?,
339 None => {
340 if params.initial_long_token_amount != 0 {
341 require!(initial_tokens.0.is_none(), CoreError::InvalidArgument);
342 }
343 if params.initial_short_token_amount != 0 {
344 require!(initial_tokens.1.is_none(), CoreError::InvalidArgument);
345 }
346 (
347 params.initial_long_token_amount,
348 params.initial_short_token_amount,
349 )
350 }
351 }
352 };
353
354 let minted = {
356 let report = self
357 .market
358 .deposit(long_token_amount.into(), short_token_amount.into(), prices)
359 .and_then(|d| d.execute())
360 .map_err(ModelError::from)?;
361 self.market.validate_market_balances(0, 0)?;
362
363 let minted: u64 = (*report.minted())
364 .try_into()
365 .map_err(|_| error!(CoreError::TokenAmountOverflow))?;
366
367 params.validate_market_token_amount(minted)?;
368
369 self.event_emitter.emit_cpi(&DepositExecuted::from_report(
370 self.market.rev(),
371 self.market.market_meta().market_token_mint,
372 report,
373 ))?;
374 msg!("[Deposit] executed");
375
376 minted
377 };
378
379 Ok(self.with_output(minted))
380 }
381
382 pub(crate) fn unchecked_withdraw(
389 mut self,
390 market_token_vault: &'a AccountInfo<'info>,
391 params: &WithdrawalActionParams,
392 final_tokens: (Pubkey, Pubkey),
393 swap_pricing_kind: Option<SwapPricingKind>,
394 ) -> Result<Execute<'a, 'info, (u64, u64)>> {
395 self.market = self
396 .market
397 .enable_burn(market_token_vault)
398 .with_swap_pricing_kind(swap_pricing_kind.unwrap_or(SwapPricingKind::Withdrawal));
399
400 let prices = self.oracle.market_prices(&self.market)?;
401
402 self.pre_execute(&prices)?;
403
404 let (long_amount, short_amount) = {
406 let report = self
407 .market
408 .withdraw(params.market_token_amount.into(), prices)
409 .and_then(|w| w.execute())
410 .map_err(ModelError::from)?;
411 let (long_amount, short_amount) = (
412 (*report.long_token_output())
413 .try_into()
414 .map_err(|_| CoreError::TokenAmountOverflow)?,
415 (*report.short_token_output())
416 .try_into()
417 .map_err(|_| CoreError::TokenAmountOverflow)?,
418 );
419 self.market
421 .validate_market_balances(long_amount, short_amount)?;
422
423 self.event_emitter
424 .emit_cpi(&WithdrawalExecuted::from_report(
425 self.market.rev(),
426 self.market.market_meta().market_token_mint,
427 report,
428 ))?;
429 msg!("[Withdrawal] executed");
430
431 (long_amount, short_amount)
432 };
433
434 let (final_long_amount, final_short_amount) = {
436 let meta = *self.market.market_meta();
437 match self.swap {
438 Some(swap) => self.swap_markets.revertible_swap(
439 SwapDirection::From(&mut self.market),
440 self.oracle,
441 swap,
442 final_tokens,
443 (Some(meta.long_token_mint), Some(meta.short_token_mint)),
444 (long_amount, short_amount),
445 )?,
446 None => {
447 require!(
448 final_tokens == (meta.long_token_mint, meta.short_token_mint),
449 CoreError::InvalidSwapPath
450 );
451 (long_amount, short_amount)
452 }
453 }
454 };
455
456 params.validate_output_amounts(final_long_amount, final_short_amount)?;
457
458 Ok(self.with_output((final_long_amount, final_short_amount)))
459 }
460
461 fn take_output<U>(self, new_output: U) -> (Execute<'a, 'info, U>, T) {
462 let Self {
463 output,
464 oracle,
465 swap,
466 market,
467 swap_markets,
468 event_emitter: event_authority,
469 } = self;
470
471 (
472 Execute {
473 output: new_output,
474 oracle,
475 swap,
476 market,
477 swap_markets,
478 event_emitter: event_authority,
479 },
480 output,
481 )
482 }
483
484 pub(crate) fn unchecked_shift(
490 self,
491 mut to_market: Self,
492 receiver: &Pubkey,
493 params: &ShiftActionParams,
494 from_market_token_vault: &'a AccountInfo<'info>,
495 to_market_token_account: &'a AccountInfo<'info>,
496 ) -> Result<(Self, Self, u64)> {
497 let meta = self.market().market_meta();
498 let (long_token, short_token) = (meta.long_token_mint, meta.short_token_mint);
499
500 let (mut from_market, (long_amount, short_amount)) = {
502 let (op, output) = self.take_output(());
503 let mut withdrawal_params = WithdrawalActionParams::default();
504 withdrawal_params.market_token_amount = params.from_market_token_amount;
505 op.unchecked_withdraw(
506 from_market_token_vault,
507 &withdrawal_params,
508 (long_token, short_token),
509 Some(SwapPricingKind::Shift),
510 )?
511 .take_output(output)
512 };
513
514 {
517 from_market
518 .market_mut()
519 .record_transferred_out_by_token(&long_token, &long_amount)
520 .map_err(ModelError::from)?;
521 to_market
522 .market_mut()
523 .record_transferred_in_by_token(&long_token, &long_amount)
524 .map_err(ModelError::from)?;
525
526 from_market
527 .market_mut()
528 .record_transferred_out_by_token(&short_token, &short_amount)
529 .map_err(ModelError::from)?;
530 to_market
531 .market_mut()
532 .record_transferred_in_by_token(&short_token, &short_amount)
533 .map_err(ModelError::from)?;
534 }
535
536 let (to_market, received) = {
538 let (op, output) = to_market.take_output(());
539 let mut deposit_params = DepositActionParams::default();
540 deposit_params.initial_long_token_amount = long_amount;
541 deposit_params.initial_short_token_amount = short_amount;
542 deposit_params.min_market_token_amount = params.min_to_market_token_amount;
543 op.unchecked_deposit(
544 receiver,
545 to_market_token_account,
546 &deposit_params,
547 (None, None),
548 Some(SwapPricingKind::Shift),
549 )?
550 .take_output(output)
551 };
552
553 Ok((from_market, to_market, received))
554 }
555}
556
557impl<T> Revertible for Execute<'_, '_, T> {
558 fn commit(self) {
559 self.market.commit();
560 self.swap_markets.commit();
561 }
562}