gmsol_store/instructions/exchange/
execute_withdrawal.rs

1use anchor_lang::prelude::*;
2use anchor_spl::token::{transfer_checked, Mint, Token, TokenAccount, TransferChecked};
3
4use crate::{
5    constants,
6    events::EventEmitter,
7    ops::{
8        execution_fee::PayExecutionFeeOperation, market::MarketTransferOutOperation,
9        withdrawal::ExecuteWithdrawalOperation,
10    },
11    states::{
12        common::{
13            action::{ActionExt, ActionSigner},
14            swap::SwapActionParamsExt,
15        },
16        feature::{ActionDisabledFlag, DomainDisabledFlag},
17        withdrawal::Withdrawal,
18        Chainlink, Market, Oracle, Store, TokenMapHeader, TokenMapLoader,
19    },
20    utils::internal,
21    CoreError,
22};
23
24/// The accounts deifinition for the [`execute_withdrawal`](crate::gmsol_store::execute_withdrawal)
25/// instruction.
26///
27/// Remaining accounts expected by this instruction:
28///
29///   - 0..M. `[]` M feed accounts, where M represents the total number of tokens in the
30///     swap params.
31///   - M..M+N. `[writable]` N market accounts, where N represents the total number of unique
32///     markets excluding the current market in the swap params.
33#[event_cpi]
34#[derive(Accounts)]
35pub struct ExecuteWithdrawal<'info> {
36    /// Authority.
37    pub authority: Signer<'info>,
38    /// Store.
39    #[account(has_one = token_map)]
40    pub store: AccountLoader<'info, Store>,
41    /// Token map.
42    #[account(has_one = store)]
43    pub token_map: AccountLoader<'info, TokenMapHeader>,
44    /// Oracle buffer to use.
45    #[account(mut, has_one = store)]
46    pub oracle: AccountLoader<'info, Oracle>,
47    /// Market.
48    #[account(mut, has_one = store)]
49    pub market: AccountLoader<'info, Market>,
50    /// The withdrawal to execute.
51    #[account(
52        mut,
53        constraint = withdrawal.load()?.header.market == market.key() @ CoreError::MarketMismatched,
54        constraint = withdrawal.load()?.header.store == store.key() @ CoreError::StoreMismatched,
55        constraint = withdrawal.load()?.tokens.market_token_account() == market_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
56        constraint = withdrawal.load()?.tokens.final_long_token_account() == final_long_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
57        constraint = withdrawal.load()?.tokens.final_short_token_account() == final_short_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
58    )]
59    pub withdrawal: AccountLoader<'info, Withdrawal>,
60    /// Market token.
61    #[account(
62        mut,
63        constraint = withdrawal.load()?.tokens.market_token() == market_token.key() @ CoreError::MarketTokenMintMismatched
64    )]
65    pub market_token: Box<Account<'info, Mint>>,
66    /// Final long token.
67    #[account(constraint = withdrawal.load()?.tokens.final_long_token() == final_long_token.key() @ CoreError::TokenMintMismatched)]
68    pub final_long_token: Box<Account<'info, Mint>>,
69    /// Final short token.
70    #[account(constraint = withdrawal.load()?.tokens.final_short_token() == final_short_token.key() @ CoreError::TokenMintMismatched)]
71    pub final_short_token: Box<Account<'info, Mint>>,
72    /// The escrow account for receving market tokens to burn.
73    #[account(
74        mut,
75        associated_token::mint = market_token,
76        associated_token::authority = withdrawal,
77    )]
78    pub market_token_escrow: Box<Account<'info, TokenAccount>>,
79    /// The escrow account for receiving withdrawed final long tokens.
80    #[account(
81        mut,
82        associated_token::mint = final_long_token,
83        associated_token::authority = withdrawal,
84    )]
85    pub final_long_token_escrow: Box<Account<'info, TokenAccount>>,
86    /// The escrow account for receiving withdrawed final short tokens.
87    #[account(
88        mut,
89        associated_token::mint = final_short_token,
90        associated_token::authority = withdrawal,
91    )]
92    pub final_short_token_escrow: Box<Account<'info, TokenAccount>>,
93    /// Market token vault.
94    #[account(
95        mut,
96        token::mint = market_token,
97        seeds = [
98            constants::MARKET_VAULT_SEED,
99            store.key().as_ref(),
100            market_token_vault.mint.as_ref(),
101        ],
102        bump,
103    )]
104    pub market_token_vault: Box<Account<'info, TokenAccount>>,
105    /// Final long token vault.
106    #[account(
107        mut,
108        token::mint = final_long_token,
109        token::authority = store,
110        seeds = [
111            constants::MARKET_VAULT_SEED,
112            store.key().as_ref(),
113            final_long_token_vault.mint.as_ref(),
114        ],
115        bump,
116    )]
117    pub final_long_token_vault: Box<Account<'info, TokenAccount>>,
118    /// Final short token vault.
119    #[account(
120        mut,
121        token::mint = final_short_token,
122        token::authority = store,
123        seeds = [
124            constants::MARKET_VAULT_SEED,
125            store.key().as_ref(),
126            final_short_token_vault.mint.as_ref(),
127        ],
128        bump,
129    )]
130    pub final_short_token_vault: Box<Account<'info, TokenAccount>>,
131    /// The token program.
132    pub token_program: Program<'info, Token>,
133    /// The system program.
134    pub system_program: Program<'info, System>,
135    /// Chainlink Program.
136    pub chainlink_program: Option<Program<'info, Chainlink>>,
137}
138
139/// CHECK only ORDER_KEEPER can invoke this instruction.
140pub(crate) fn unchecked_execute_withdrawal<'info>(
141    ctx: Context<'_, '_, 'info, 'info, ExecuteWithdrawal<'info>>,
142    execution_fee: u64,
143    throw_on_execution_error: bool,
144) -> Result<()> {
145    let accounts = ctx.accounts;
146    let remaining_accounts = ctx.remaining_accounts;
147
148    // Validate feature enabled.
149    accounts
150        .store
151        .load()?
152        .validate_feature_enabled(DomainDisabledFlag::Withdrawal, ActionDisabledFlag::Execute)?;
153
154    let signer = accounts.withdrawal.load()?.signer();
155
156    let event_authority = accounts.event_authority.clone();
157    let event_emitter = EventEmitter::new(&event_authority, ctx.bumps.event_authority);
158
159    accounts.transfer_market_tokens_in(&signer)?;
160
161    let executed =
162        accounts.perform_execution(remaining_accounts, throw_on_execution_error, &event_emitter)?;
163
164    match executed {
165        Some((final_long_token_amount, final_short_token_amount)) => {
166            accounts.withdrawal.load_mut()?.header.completed()?;
167            accounts.transfer_tokens_out(
168                remaining_accounts,
169                final_long_token_amount,
170                final_short_token_amount,
171                &event_emitter,
172            )?;
173        }
174        None => {
175            accounts.withdrawal.load_mut()?.header.cancelled()?;
176            accounts.transfer_market_tokens_out()?;
177        }
178    }
179
180    // Is must be placed at the end to be executed correctly.
181    accounts.pay_execution_fee(execution_fee)?;
182
183    Ok(())
184}
185
186impl<'info> internal::Authentication<'info> for ExecuteWithdrawal<'info> {
187    fn authority(&self) -> &Signer<'info> {
188        &self.authority
189    }
190
191    fn store(&self) -> &AccountLoader<'info, Store> {
192        &self.store
193    }
194}
195
196impl<'info> ExecuteWithdrawal<'info> {
197    fn perform_execution(
198        &mut self,
199        remaining_accounts: &'info [AccountInfo<'info>],
200        throw_on_execution_error: bool,
201        event_emitter: &EventEmitter<'_, 'info>,
202    ) -> Result<Option<(u64, u64)>> {
203        // Note: We only need the tokens here, the feeds are not necessary.
204        let feeds = self
205            .withdrawal
206            .load()?
207            .swap()
208            .to_feeds(&self.token_map.load_token_map()?)
209            .map_err(CoreError::from)?;
210
211        let op = ExecuteWithdrawalOperation::builder()
212            .store(&self.store)
213            .market(&self.market)
214            .withdrawal(&self.withdrawal)
215            .market_token_mint(&mut self.market_token)
216            .market_token_vault(self.market_token_vault.to_account_info())
217            .token_program(self.token_program.to_account_info())
218            .throw_on_execution_error(throw_on_execution_error)
219            .event_emitter(*event_emitter);
220
221        let executed = self.oracle.load_mut()?.with_prices(
222            &self.store,
223            &self.token_map,
224            &feeds.tokens,
225            remaining_accounts,
226            self.chainlink_program.as_ref(),
227            |oracle, remaining_accounts| {
228                op.oracle(oracle)
229                    .remaining_accounts(remaining_accounts)
230                    .build()
231                    .execute()
232            },
233        )?;
234
235        Ok(executed)
236    }
237
238    fn transfer_market_tokens_in(&self, signer: &ActionSigner) -> Result<()> {
239        let seeds = signer.as_seeds();
240
241        transfer_checked(
242            CpiContext::new(
243                self.token_program.to_account_info(),
244                TransferChecked {
245                    from: self.market_token_escrow.to_account_info(),
246                    mint: self.market_token.to_account_info(),
247                    to: self.market_token_vault.to_account_info(),
248                    authority: self.withdrawal.to_account_info(),
249                },
250            )
251            .with_signer(&[&seeds]),
252            self.withdrawal.load()?.params.market_token_amount,
253            self.market_token.decimals,
254        )?;
255
256        Ok(())
257    }
258
259    fn transfer_market_tokens_out(&self) -> Result<()> {
260        use crate::internal::TransferUtils;
261
262        let amount = self.withdrawal.load()?.params.market_token_amount;
263        TransferUtils::new(
264            self.token_program.to_account_info(),
265            &self.store,
266            self.market_token.to_account_info(),
267        )
268        .transfer_out(
269            self.market_token_vault.to_account_info(),
270            self.market_token_escrow.to_account_info(),
271            amount,
272            self.market_token.decimals,
273        )?;
274
275        Ok(())
276    }
277
278    fn transfer_tokens_out(
279        &self,
280        remaining_accounts: &'info [AccountInfo<'info>],
281        final_long_token_amount: u64,
282        final_short_token_amount: u64,
283        event_emitter: &EventEmitter<'_, 'info>,
284    ) -> Result<()> {
285        let builder = MarketTransferOutOperation::builder()
286            .store(&self.store)
287            .token_program(self.token_program.to_account_info())
288            .event_emitter(*event_emitter);
289        let store = &self.store.key();
290
291        if final_long_token_amount != 0 {
292            let market = self
293                .withdrawal
294                .load()?
295                .swap
296                .find_and_unpack_last_market(store, true, remaining_accounts)?
297                .unwrap_or(self.market.clone());
298            let vault = &self.final_long_token_vault;
299            let escrow = &self.final_long_token_escrow;
300            let token = &self.final_long_token;
301            builder
302                .clone()
303                .market(&market)
304                .to(escrow.to_account_info())
305                .vault(vault.to_account_info())
306                .amount(final_long_token_amount)
307                .decimals(token.decimals)
308                .token_mint(token.to_account_info())
309                .build()
310                .execute()?;
311        }
312
313        if final_short_token_amount != 0 {
314            let market = self
315                .withdrawal
316                .load()?
317                .swap
318                .find_and_unpack_last_market(store, false, remaining_accounts)?
319                .unwrap_or(self.market.clone());
320            let vault = &self.final_short_token_vault;
321            let escrow = &self.final_short_token_escrow;
322            let token = &self.final_short_token;
323            builder
324                .market(&market)
325                .to(escrow.to_account_info())
326                .vault(vault.to_account_info())
327                .amount(final_short_token_amount)
328                .decimals(token.decimals)
329                .token_mint(token.to_account_info())
330                .build()
331                .execute()?;
332        }
333        Ok(())
334    }
335
336    fn pay_execution_fee(&self, execution_fee: u64) -> Result<()> {
337        let execution_lamports = self.withdrawal.load()?.execution_lamports(execution_fee);
338        PayExecutionFeeOperation::builder()
339            .payer(self.withdrawal.to_account_info())
340            .receiver(self.authority.to_account_info())
341            .execution_lamports(execution_lamports)
342            .build()
343            .execute()?;
344        Ok(())
345    }
346}