gmsol_store/instructions/exchange/
execute_withdrawal.rs1use 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#[event_cpi]
34#[derive(Accounts)]
35pub struct ExecuteWithdrawal<'info> {
36 pub authority: Signer<'info>,
38 #[account(has_one = token_map)]
40 pub store: AccountLoader<'info, Store>,
41 #[account(has_one = store)]
43 pub token_map: AccountLoader<'info, TokenMapHeader>,
44 #[account(mut, has_one = store)]
46 pub oracle: AccountLoader<'info, Oracle>,
47 #[account(mut, has_one = store)]
49 pub market: AccountLoader<'info, Market>,
50 #[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 #[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 #[account(constraint = withdrawal.load()?.tokens.final_long_token() == final_long_token.key() @ CoreError::TokenMintMismatched)]
68 pub final_long_token: Box<Account<'info, Mint>>,
69 #[account(constraint = withdrawal.load()?.tokens.final_short_token() == final_short_token.key() @ CoreError::TokenMintMismatched)]
71 pub final_short_token: Box<Account<'info, Mint>>,
72 #[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 #[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 #[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 #[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 #[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 #[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 pub token_program: Program<'info, Token>,
133 pub system_program: Program<'info, System>,
135 pub chainlink_program: Option<Program<'info, Chainlink>>,
137}
138
139pub(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 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 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 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}