1use anchor_lang::prelude::*;
2use anchor_spl::{
3 associated_token::AssociatedToken,
4 token::{Mint, Token, TokenAccount},
5};
6use gmsol_utils::InitSpace;
7
8use crate::{
9 events::{DepositCreated, EventEmitter},
10 ops::deposit::{CreateDepositOperation, CreateDepositParams},
11 states::{
12 common::action::{Action, ActionExt},
13 feature::{ActionDisabledFlag, DomainDisabledFlag},
14 Deposit, Market, NonceBytes, RoleKey, Seed, Store, StoreWalletSigner,
15 },
16 utils::{
17 internal,
18 token::{is_associated_token_account, is_associated_token_account_or_owner},
19 },
20 CoreError,
21};
22
23#[derive(Accounts)]
33#[instruction(nonce: [u8; 32])]
34pub struct CreateDeposit<'info> {
35 #[account(mut)]
37 pub owner: Signer<'info>,
38 pub receiver: UncheckedAccount<'info>,
41 pub store: AccountLoader<'info, Store>,
43 #[account(mut, has_one = store)]
45 pub market: AccountLoader<'info, Market>,
46 #[account(
48 init,
49 space = 8 + Deposit::INIT_SPACE,
50 payer = owner,
51 seeds = [Deposit::SEED, store.key().as_ref(), owner.key().as_ref(), &nonce],
52 bump,
53 )]
54 pub deposit: AccountLoader<'info, Deposit>,
55 #[account(constraint = market.load()?.meta().market_token_mint == market_token.key() @ CoreError::MarketTokenMintMismatched)]
57 pub market_token: Box<Account<'info, Mint>>,
58 pub initial_long_token: Option<Box<Account<'info, Mint>>>,
60 pub initial_short_token: Option<Box<Account<'info, Mint>>>,
62 #[account(
64 mut,
65 associated_token::mint = market_token,
66 associated_token::authority = deposit,
67 )]
68 pub market_token_escrow: Box<Account<'info, TokenAccount>>,
69 #[account(
71 mut,
72 associated_token::mint = initial_long_token,
73 associated_token::authority = deposit,
74 )]
75 pub initial_long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
76 #[account(
78 mut,
79 associated_token::mint = initial_short_token,
80 associated_token::authority = deposit,
81 )]
82 pub initial_short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
83 #[account(
85 init_if_needed,
86 payer = owner,
87 associated_token::mint = market_token,
88 associated_token::authority = receiver,
89 )]
90 pub market_token_ata: Box<Account<'info, TokenAccount>>,
91 #[account(mut, token::mint = initial_long_token)]
93 pub initial_long_token_source: Option<Box<Account<'info, TokenAccount>>>,
94 #[account(mut, token::mint = initial_short_token)]
96 pub initial_short_token_source: Option<Box<Account<'info, TokenAccount>>>,
97 pub system_program: Program<'info, System>,
99 pub token_program: Program<'info, Token>,
101 pub associated_token_program: Program<'info, AssociatedToken>,
103}
104
105impl<'info> internal::Create<'info, Deposit> for CreateDeposit<'info> {
106 type CreateParams = CreateDepositParams;
107
108 fn action(&self) -> AccountInfo<'info> {
109 self.deposit.to_account_info()
110 }
111
112 fn payer(&self) -> AccountInfo<'info> {
113 self.owner.to_account_info()
114 }
115
116 fn system_program(&self) -> AccountInfo<'info> {
117 self.system_program.to_account_info()
118 }
119
120 fn validate(&self, _params: &Self::CreateParams) -> Result<()> {
121 self.store
122 .load()?
123 .validate_not_restarted()?
124 .validate_feature_enabled(DomainDisabledFlag::Deposit, ActionDisabledFlag::Create)?;
125 Ok(())
126 }
127
128 fn create_impl(
129 &mut self,
130 params: &Self::CreateParams,
131 nonce: &NonceBytes,
132 bumps: &Self::Bumps,
133 remaining_accounts: &'info [AccountInfo<'info>],
134 ) -> Result<()> {
135 self.transfer_tokens(params)?;
136 CreateDepositOperation::builder()
137 .deposit(self.deposit.clone())
138 .market(self.market.clone())
139 .store(self.store.clone())
140 .owner(&self.owner)
141 .receiver(&self.receiver)
142 .nonce(nonce)
143 .bump(bumps.deposit)
144 .initial_long_token(self.initial_long_token_escrow.as_deref())
145 .initial_short_token(self.initial_short_token_escrow.as_deref())
146 .market_token(&self.market_token_escrow)
147 .params(params)
148 .swap_paths(remaining_accounts)
149 .build()
150 .execute()?;
151 emit!(DepositCreated::new(self.store.key(), self.deposit.key())?);
152 Ok(())
153 }
154}
155
156impl CreateDeposit<'_> {
157 fn transfer_tokens(&mut self, params: &CreateDepositParams) -> Result<()> {
158 use anchor_spl::token::{transfer_checked, TransferChecked};
159
160 let amount = params.initial_long_token_amount;
161 if amount != 0 {
162 let Some(source) = self.initial_long_token_source.as_ref() else {
163 return err!(CoreError::TokenAccountNotProvided);
164 };
165 let Some(target) = self.initial_long_token_escrow.as_mut() else {
166 return err!(CoreError::TokenAccountNotProvided);
167 };
168 let Some(mint) = self.initial_long_token.as_ref() else {
169 return err!(CoreError::MintAccountNotProvided);
170 };
171 transfer_checked(
172 CpiContext::new(
173 self.token_program.to_account_info(),
174 TransferChecked {
175 from: source.to_account_info(),
176 mint: mint.to_account_info(),
177 to: target.to_account_info(),
178 authority: self.owner.to_account_info(),
179 },
180 ),
181 amount,
182 mint.decimals,
183 )?;
184 }
185
186 let amount = params.initial_short_token_amount;
187 if amount != 0 {
188 let Some(source) = self.initial_short_token_source.as_ref() else {
189 return err!(CoreError::TokenAccountNotProvided);
190 };
191 let Some(target) = self.initial_short_token_escrow.as_mut() else {
192 return err!(CoreError::TokenAccountNotProvided);
193 };
194 let Some(mint) = self.initial_short_token.as_ref() else {
195 return err!(CoreError::MintAccountNotProvided);
196 };
197 transfer_checked(
198 CpiContext::new(
199 self.token_program.to_account_info(),
200 TransferChecked {
201 from: source.to_account_info(),
202 mint: mint.to_account_info(),
203 to: target.to_account_info(),
204 authority: self.owner.to_account_info(),
205 },
206 ),
207 amount,
208 mint.decimals,
209 )?;
210 }
211
212 for escrow in self
214 .initial_long_token_escrow
215 .as_mut()
216 .into_iter()
217 .chain(self.initial_short_token_escrow.as_mut())
218 {
219 escrow.reload()?;
220 }
221 Ok(())
222 }
223}
224
225#[event_cpi]
228#[derive(Accounts)]
229pub struct CloseDeposit<'info> {
230 pub executor: Signer<'info>,
232 pub store: AccountLoader<'info, Store>,
234 #[account(mut, seeds = [Store::WALLET_SEED, store.key().as_ref()], bump)]
236 pub store_wallet: SystemAccount<'info>,
237 #[account(mut)]
240 pub owner: UncheckedAccount<'info>,
241 #[account(mut)]
244 pub receiver: UncheckedAccount<'info>,
245 #[account(
247 constraint = deposit.load()?.tokens.market_token.token().expect("must exist") == market_token.key() @ CoreError::MarketTokenMintMismatched
248 )]
249 pub market_token: Box<Account<'info, Mint>>,
250 #[account(
252 constraint = deposit.load()?.tokens.initial_long_token.token().map(|token| initial_long_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
253 )]
254 pub initial_long_token: Option<Box<Account<'info, Mint>>>,
255 #[account(
257 constraint = deposit.load()?.tokens.initial_short_token.token().map(|token| initial_short_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
258 )]
259 pub initial_short_token: Option<Box<Account<'info, Mint>>>,
260 #[account(
262 mut,
263 constraint = deposit.load()?.header.owner == owner.key() @ CoreError::OwnerMismatched,
264 constraint = deposit.load()?.header.receiver() == receiver.key() @ CoreError::ReceiverMismatched,
265 constraint = deposit.load()?.header.rent_receiver() == owner.key @ CoreError::RentReceiverMismatched,
267 constraint = deposit.load()?.header.store == store.key() @ CoreError::StoreMismatched,
268 constraint = deposit.load()?.tokens.market_token.account().expect("must exist") == market_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
269 constraint = deposit.load()?.tokens.initial_long_token.account() == initial_long_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
270 constraint = deposit.load()?.tokens.initial_short_token.account() == initial_short_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
271 seeds = [Deposit::SEED, store.key().as_ref(), owner.key().as_ref(), &deposit.load()?.header.nonce],
272 bump = deposit.load()?.header.bump,
273 )]
274 pub deposit: AccountLoader<'info, Deposit>,
275 #[account(
277 mut,
278 associated_token::mint = market_token,
279 associated_token::authority = deposit,
280 )]
281 pub market_token_escrow: Box<Account<'info, TokenAccount>>,
282 #[account(
284 mut,
285 associated_token::mint = initial_long_token,
286 associated_token::authority = deposit,
287 )]
288 pub initial_long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
289 #[account(
291 mut,
292 associated_token::mint = initial_short_token,
293 associated_token::authority = deposit,
294 )]
295 pub initial_short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
296 #[account(
299 mut,
300 constraint = is_associated_token_account(market_token_ata.key, receiver.key, &market_token.key()) @ CoreError::NotAnATA,
301 )]
302 pub market_token_ata: UncheckedAccount<'info>,
303 #[account(
306 mut,
307 constraint = is_associated_token_account_or_owner(initial_long_token_ata.key, owner.key, &initial_long_token.as_ref().expect("must provided").key()) @ CoreError::NotAnATA,
308 )]
309 pub initial_long_token_ata: Option<UncheckedAccount<'info>>,
310 #[account(
313 mut,
314 constraint = is_associated_token_account_or_owner(initial_short_token_ata.key, owner.key, &initial_short_token.as_ref().expect("must provided").key()) @ CoreError::NotAnATA,
315 )]
316 pub initial_short_token_ata: Option<UncheckedAccount<'info>>,
317 pub system_program: Program<'info, System>,
319 pub token_program: Program<'info, Token>,
321 pub associated_token_program: Program<'info, AssociatedToken>,
323}
324
325impl<'info> internal::Authentication<'info> for CloseDeposit<'info> {
326 fn authority(&self) -> &Signer<'info> {
327 &self.executor
328 }
329
330 fn store(&self) -> &AccountLoader<'info, Store> {
331 &self.store
332 }
333}
334
335impl<'info> internal::Close<'info, Deposit> for CloseDeposit<'info> {
336 fn expected_keeper_role(&self) -> &str {
337 RoleKey::ORDER_KEEPER
338 }
339
340 fn rent_receiver(&self) -> AccountInfo<'info> {
341 debug_assert!(
342 self.deposit.load().unwrap().header.rent_receiver() == self.owner.key,
343 "The rent receiver must have been checked to be the owner"
344 );
345 self.owner.to_account_info()
346 }
347
348 fn store_wallet_bump(&self, bumps: &Self::Bumps) -> u8 {
349 bumps.store_wallet
350 }
351
352 fn validate(&self) -> Result<()> {
353 let deposit = self.deposit.load()?;
354 if deposit.header.action_state()?.is_pending() {
355 self.store
356 .load()?
357 .validate_not_restarted()?
358 .validate_feature_enabled(
359 DomainDisabledFlag::Deposit,
360 ActionDisabledFlag::Cancel,
361 )?;
362 }
363 Ok(())
364 }
365
366 fn process(
367 &self,
368 init_if_needed: bool,
369 store_wallet_signer: &StoreWalletSigner,
370 _event_emitter: &EventEmitter<'_, 'info>,
371 ) -> Result<internal::Success> {
372 use crate::utils::token::TransferAllFromEscrowToATA;
373
374 let signer = self.deposit.load()?.signer();
376 let seeds = signer.as_seeds();
377
378 let builder = TransferAllFromEscrowToATA::builder()
379 .store_wallet(self.store_wallet.to_account_info())
380 .store_wallet_signer(store_wallet_signer)
381 .system_program(self.system_program.to_account_info())
382 .token_program(self.token_program.to_account_info())
383 .associated_token_program(self.associated_token_program.to_account_info())
384 .payer(self.executor.to_account_info())
385 .escrow_authority(self.deposit.to_account_info())
386 .escrow_authority_seeds(&seeds)
387 .init_if_needed(init_if_needed)
388 .rent_receiver(self.rent_receiver())
389 .should_unwrap_native(self.deposit.load()?.header().should_unwrap_native_token());
390
391 if !builder
393 .clone()
394 .mint(self.market_token.to_account_info())
395 .decimals(self.market_token.decimals)
396 .ata(self.market_token_ata.to_account_info())
397 .escrow(self.market_token_escrow.to_account_info())
398 .owner(self.receiver.to_account_info())
399 .build()
400 .unchecked_execute()?
401 {
402 return Ok(false);
403 }
404
405 let (initial_long_token_escrow, initial_short_token_escrow) =
407 if self.initial_long_token_escrow.as_ref().map(|a| a.key())
408 == self.initial_short_token_escrow.as_ref().map(|a| a.key())
409 {
410 (self.initial_long_token_escrow.as_ref(), None)
411 } else {
412 (
413 self.initial_long_token_escrow.as_ref(),
414 self.initial_short_token_escrow.as_ref(),
415 )
416 };
417
418 if let Some(escrow) = initial_long_token_escrow.as_ref() {
420 let Some(ata) = self.initial_long_token_ata.as_ref() else {
421 return err!(CoreError::TokenAccountNotProvided);
422 };
423 let Some(mint) = self.initial_long_token.as_ref() else {
424 return err!(CoreError::MintAccountNotProvided);
425 };
426 if !builder
427 .clone()
428 .mint(mint.to_account_info())
429 .decimals(mint.decimals)
430 .ata(ata.to_account_info())
431 .escrow(escrow.to_account_info())
432 .owner(self.owner.to_account_info())
433 .build()
434 .unchecked_execute()?
435 {
436 return Ok(false);
437 }
438 }
439
440 if let Some(escrow) = initial_short_token_escrow.as_ref() {
442 let Some(ata) = self.initial_short_token_ata.as_ref() else {
443 return err!(CoreError::TokenAccountNotProvided);
444 };
445 let Some(mint) = self.initial_short_token.as_ref() else {
446 return err!(CoreError::MintAccountNotProvided);
447 };
448 if !builder
449 .clone()
450 .mint(mint.to_account_info())
451 .decimals(mint.decimals)
452 .ata(ata.to_account_info())
453 .escrow(escrow.to_account_info())
454 .owner(self.owner.to_account_info())
455 .build()
456 .unchecked_execute()?
457 {
458 return Ok(false);
459 }
460 }
461
462 Ok(true)
463 }
464
465 fn event_authority(&self, bumps: &Self::Bumps) -> (AccountInfo<'info>, u8) {
466 (
467 self.event_authority.to_account_info(),
468 bumps.event_authority,
469 )
470 }
471
472 fn action(&self) -> &AccountLoader<'info, Deposit> {
473 &self.deposit
474 }
475}