1use anchor_lang::prelude::*;
2use anchor_spl::{
3 associated_token::AssociatedToken,
4 token::{Mint, Token, TokenAccount},
5};
6use gmsol_store::{
7 cpi::{
8 accounts::{CloseOrder, CreateOrder, PrepareUser},
9 close_order, create_order, prepare_user,
10 },
11 ops::order::CreateOrderParams,
12 program::GmsolStore,
13 states::{common::action::Action, order::OrderKind, NonceBytes, Order},
14 utils::{CpiAuthentication, WithStore},
15 CoreError,
16};
17
18use crate::{
19 constants,
20 states::{config::ReceiverSigner, Config, TreasuryVaultConfig},
21};
22
23#[derive(Accounts)]
25pub struct CreateSwap<'info> {
26 #[account(mut)]
28 pub authority: Signer<'info>,
29 pub store: UncheckedAccount<'info>,
32 #[account(
34 has_one = store,
35 constraint = config.load()?.treasury_vault_config() == Some(&treasury_vault_config.key()) @ CoreError::InvalidArgument,
37 )]
38 pub config: AccountLoader<'info, Config>,
39 #[account(
41 has_one = config,
42 constraint = !treasury_vault_config.load()?.is_deposit_allowed(&swap_in_token.key()).unwrap_or(false) @ CoreError::InvalidArgument,
43 constraint = treasury_vault_config.load()?.is_deposit_allowed(&swap_out_token.key())? @ CoreError::InvalidArgument,
44 )]
45 pub treasury_vault_config: AccountLoader<'info, TreasuryVaultConfig>,
46 pub swap_in_token: Account<'info, Mint>,
48 #[account(constraint = swap_in_token.key() != swap_out_token.key() @ CoreError::InvalidArgument)]
50 pub swap_out_token: Account<'info, Mint>,
51 #[account(
53 mut,
54 associated_token::authority = receiver,
55 associated_token::mint = swap_in_token,
56 )]
57 pub swap_in_token_receiver_vault: Account<'info, TokenAccount>,
58 #[account(mut)]
61 pub market: UncheckedAccount<'info>,
62 #[account(
64 mut,
65 seeds = [constants::RECEIVER_SEED, config.key().as_ref()],
66 bump,
67 )]
68 pub receiver: SystemAccount<'info>,
69 #[account(mut)]
72 pub user: UncheckedAccount<'info>,
73 #[account(mut)]
76 pub swap_in_token_escrow: UncheckedAccount<'info>,
77 #[account(mut)]
80 pub swap_out_token_escrow: UncheckedAccount<'info>,
81 #[account(mut)]
84 pub order: UncheckedAccount<'info>,
85 pub store_program: Program<'info, GmsolStore>,
87 pub token_program: Program<'info, Token>,
89 pub associated_token_program: Program<'info, AssociatedToken>,
91 pub system_program: Program<'info, System>,
93}
94
95pub(crate) fn unchecked_create_swap<'info>(
99 ctx: Context<'_, '_, 'info, 'info, CreateSwap<'info>>,
100 nonce: NonceBytes,
101 swap_path_length: u8,
102 swap_in_amount: u64,
103 min_swap_out_amount: Option<u64>,
104) -> Result<()> {
105 let signer = ReceiverSigner::new(ctx.accounts.config.key(), ctx.bumps.receiver);
106
107 let cpi_ctx = ctx.accounts.prepare_user_ctx();
109 prepare_user(cpi_ctx.with_signer(&[&signer.as_seeds()]))?;
110
111 let cpi_ctx = ctx.accounts.create_order_ctx();
113 let params = CreateOrderParams {
114 kind: OrderKind::MarketSwap,
115 decrease_position_swap_type: None,
116 execution_lamports: Order::MIN_EXECUTION_LAMPORTS,
117 swap_path_length,
118 initial_collateral_delta_amount: swap_in_amount,
119 size_delta_value: 0,
120 is_long: true,
121 is_collateral_long: true,
122 min_output: min_swap_out_amount.map(u128::from),
123 trigger_price: None,
124 acceptable_price: None,
125 should_unwrap_native_token: false,
126 valid_from_ts: None,
127 };
128 create_order(
129 cpi_ctx
130 .with_signer(&[&signer.as_seeds()])
131 .with_remaining_accounts(ctx.remaining_accounts.to_vec()),
132 nonce,
133 params,
134 )?;
135 Ok(())
136}
137
138impl<'info> WithStore<'info> for CreateSwap<'info> {
139 fn store_program(&self) -> AccountInfo<'info> {
140 self.store_program.to_account_info()
141 }
142
143 fn store(&self) -> AccountInfo<'info> {
144 self.store.to_account_info()
145 }
146}
147
148impl<'info> CpiAuthentication<'info> for CreateSwap<'info> {
149 fn authority(&self) -> AccountInfo<'info> {
150 self.authority.to_account_info()
151 }
152
153 fn on_error(&self) -> Result<()> {
154 err!(CoreError::PermissionDenied)
155 }
156}
157
158impl<'info> CreateSwap<'info> {
159 fn prepare_user_ctx(&self) -> CpiContext<'_, '_, '_, 'info, PrepareUser<'info>> {
160 CpiContext::new(
161 self.store_program.to_account_info(),
162 PrepareUser {
163 owner: self.receiver.to_account_info(),
164 store: self.store.to_account_info(),
165 user: self.user.to_account_info(),
166 system_program: self.system_program.to_account_info(),
167 },
168 )
169 }
170
171 fn create_order_ctx(&self) -> CpiContext<'_, '_, '_, 'info, CreateOrder<'info>> {
172 CpiContext::new(
173 self.store_program.to_account_info(),
174 CreateOrder {
175 owner: self.receiver.to_account_info(),
176 receiver: self.receiver.to_account_info(),
177 store: self.store.to_account_info(),
178 market: self.market.to_account_info(),
179 user: self.user.to_account_info(),
180 order: self.order.to_account_info(),
181 position: None,
182 initial_collateral_token: Some(self.swap_in_token.to_account_info()),
183 final_output_token: self.swap_out_token.to_account_info(),
184 long_token: None,
185 short_token: None,
186 initial_collateral_token_escrow: Some(self.swap_in_token_escrow.to_account_info()),
187 final_output_token_escrow: Some(self.swap_out_token_escrow.to_account_info()),
188 long_token_escrow: None,
189 short_token_escrow: None,
190 initial_collateral_token_source: Some(
191 self.swap_in_token_receiver_vault.to_account_info(),
192 ),
193 system_program: self.system_program.to_account_info(),
194 token_program: self.token_program.to_account_info(),
195 associated_token_program: self.associated_token_program.to_account_info(),
196 },
197 )
198 }
199}
200
201#[derive(Accounts)]
203pub struct CancelSwap<'info> {
204 #[account(mut)]
206 pub authority: Signer<'info>,
207 #[account(mut)]
210 pub store: UncheckedAccount<'info>,
211 #[account(mut)]
214 pub store_wallet: UncheckedAccount<'info>,
215 #[account(
216 has_one = store,
217 )]
218 pub config: AccountLoader<'info, Config>,
219 #[account(
221 mut,
222 seeds = [constants::RECEIVER_SEED, config.key().as_ref()],
223 bump,
224 )]
225 pub receiver: SystemAccount<'info>,
226 #[account(mut)]
229 pub user: UncheckedAccount<'info>,
230 pub swap_in_token: UncheckedAccount<'info>,
233 pub swap_out_token: UncheckedAccount<'info>,
236 #[account(mut)]
239 pub swap_in_token_receiver_vault: UncheckedAccount<'info>,
240 #[account(mut)]
243 pub swap_out_token_receiver_vault: UncheckedAccount<'info>,
244 #[account(mut)]
247 pub swap_in_token_escrow: UncheckedAccount<'info>,
248 #[account(mut)]
251 pub swap_out_token_escrow: UncheckedAccount<'info>,
252 #[account(mut)]
255 pub order: UncheckedAccount<'info>,
256 pub event_authority: UncheckedAccount<'info>,
259 pub store_program: Program<'info, GmsolStore>,
261 pub token_program: Program<'info, Token>,
263 pub associated_token_program: Program<'info, AssociatedToken>,
265 pub system_program: Program<'info, System>,
267}
268
269pub(crate) fn unchecked_cancel_swap(ctx: Context<CancelSwap>) -> Result<()> {
273 let signer = ReceiverSigner::new(ctx.accounts.config.key(), ctx.bumps.receiver);
274 let cpi_ctx = ctx.accounts.close_order_ctx();
275 close_order(
276 cpi_ctx.with_signer(&[&signer.as_seeds()]),
277 "cancel".to_string(),
278 )?;
279 Ok(())
280}
281
282impl<'info> WithStore<'info> for CancelSwap<'info> {
283 fn store_program(&self) -> AccountInfo<'info> {
284 self.store_program.to_account_info()
285 }
286
287 fn store(&self) -> AccountInfo<'info> {
288 self.store.to_account_info()
289 }
290}
291
292impl<'info> CpiAuthentication<'info> for CancelSwap<'info> {
293 fn authority(&self) -> AccountInfo<'info> {
294 self.authority.to_account_info()
295 }
296
297 fn on_error(&self) -> Result<()> {
298 err!(CoreError::PermissionDenied)
299 }
300}
301
302impl<'info> CancelSwap<'info> {
303 fn close_order_ctx(&self) -> CpiContext<'_, '_, '_, 'info, CloseOrder<'info>> {
304 CpiContext::new(
305 self.store_program.to_account_info(),
306 CloseOrder {
307 executor: self.receiver.to_account_info(),
308 store: self.store.to_account_info(),
309 store_wallet: self.store_wallet.to_account_info(),
310 owner: self.receiver.to_account_info(),
311 receiver: self.receiver.to_account_info(),
312 rent_receiver: self.receiver.to_account_info(),
313 user: self.user.to_account_info(),
314 referrer_user: None,
315 order: self.order.to_account_info(),
316 initial_collateral_token: Some(self.swap_in_token.to_account_info()),
317 final_output_token: Some(self.swap_out_token.to_account_info()),
318 long_token: None,
319 short_token: None,
320 initial_collateral_token_escrow: Some(self.swap_in_token_escrow.to_account_info()),
321 final_output_token_escrow: Some(self.swap_out_token_escrow.to_account_info()),
322 long_token_escrow: None,
323 short_token_escrow: None,
324 initial_collateral_token_ata: Some(
325 self.swap_in_token_receiver_vault.to_account_info(),
326 ),
327 final_output_token_ata: Some(self.swap_out_token_receiver_vault.to_account_info()),
328 long_token_ata: None,
329 short_token_ata: None,
330 system_program: self.system_program.to_account_info(),
331 token_program: self.token_program.to_account_info(),
332 associated_token_program: self.associated_token_program.to_account_info(),
333 event_authority: self.event_authority.to_account_info(),
334 program: self.store_program.to_account_info(),
335 },
336 )
337 }
338}