1use solana_program::account_info::{AccountInfo, next_account_info};
2use solana_program::entrypoint::ProgramResult;
3use solana_program::msg;
4use solana_program::program::{invoke, invoke_signed};
5use solana_program::pubkey::Pubkey;
6use solana_program::program_pack::Pack;
7use solana_program::rent::Rent;
8use solana_program::sysvar::Sysvar;
9use spl_token::state::Mint;
10use solana_program::program_error::ProgramError::{IllegalOwner, InvalidAccountData, InvalidInstructionData, MissingRequiredSignature};
11use crate::swap::state::{CreatorFee, SwapPool};
12use crate::swap::instruction::{calculate_deposit_amounts, calculate_swap_amounts, calculate_withdraw_amounts, SwapInstruction};
13use crate::processor::{create_spl_token_account, transfer_spl_token};
14
15pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
16 match SwapInstruction::unpack(instruction_data)? {
17 SwapInstruction::CreatePool { seed, lp_fee_rate, creator_fee_rate } => {
18 msg!("Swap:CreatePool");
19 process_create_pool(program_id, accounts, seed, lp_fee_rate, creator_fee_rate)
20 }
21 SwapInstruction::Swap { in_amount, min_out_amount } => {
22 msg!("Swap:Swap");
23 process_swap(program_id, accounts, in_amount, min_out_amount)
24 }
25 SwapInstruction::Deposit { min_a, max_a, min_b, max_b } => {
26 msg!("Swap:Deposit");
27 process_deposit(program_id, accounts, min_a, max_a, min_b, max_b)
28 }
29 SwapInstruction::Withdraw { lp_amount, min_a, min_b } => {
30 msg!("Swap:Withdraw");
31 process_wthdraw(program_id, accounts, lp_amount, min_a, min_b)
32 }
33 }
34}
35
36fn process_create_pool(program_id: &Pubkey, accounts: &[AccountInfo], seed: [u8; 32],
37 lp_fee_rate: u32, creator_fee_rate: u32) -> ProgramResult {
38 let accounts_iter = &mut accounts.iter();
39
40 let fee_payer_info = next_account_info(accounts_iter)?;
41 let swap_state_info = next_account_info(accounts_iter)?;
42 let token_a_mint_info = next_account_info(accounts_iter)?;
43 let token_a_account_info = next_account_info(accounts_iter)?;
44 let token_b_mint_info = next_account_info(accounts_iter)?;
45 let token_b_account_info = next_account_info(accounts_iter)?;
46 let lp_mint_info = next_account_info(accounts_iter)?;
47
48 let spl_token_program = next_account_info(accounts_iter)?;
49 let system_program = next_account_info(accounts_iter)?;
50
51 let rent = Rent::get()?;
52
53 if !fee_payer_info.is_signer {
54 return Err(MissingRequiredSignature);
55 }
56
57 if token_a_mint_info.key == token_b_mint_info.key {
58 return Err(InvalidAccountData);
59 }
60
61 let seeds_a = [swap_state_info.key.as_ref(), b"A"];
62 create_spl_token_account(
63 token_a_account_info,
64 token_a_mint_info,
65 swap_state_info,
66 fee_payer_info,
67 &seeds_a,
68 program_id,
69 spl_token_program,
70 system_program,
71 )?;
72
73 let seeds_b = [swap_state_info.key.as_ref(), b"B"];
74 create_spl_token_account(
75 token_b_account_info,
76 token_b_mint_info,
77 swap_state_info,
78 fee_payer_info,
79 &seeds_b,
80 program_id,
81 spl_token_program,
82 system_program,
83 )?;
84
85 let seeds_mint = [swap_state_info.key.as_ref(), b"LP"];
87 let (lp_mint_account, bump_seed) = Pubkey::find_program_address(&seeds_mint, program_id);
88
89 let create_mint_account_instruction = solana_program::system_instruction::create_account(
90 &fee_payer_info.key,
91 &lp_mint_account,
92 rent.minimum_balance(spl_token::state::Mint::LEN),
93 spl_token::state::Mint::LEN as u64,
94 &spl_token::id(),
95 );
96 invoke_signed(
97 &create_mint_account_instruction,
98 &[
99 system_program.clone(),
100 fee_payer_info.clone(),
101 lp_mint_info.clone(),
102 ],
103 &[&[&seeds_mint[0], &seeds_mint[1], &[bump_seed]]], )?;
105
106 let initialize_mint_instruction = spl_token::instruction::initialize_mint2(
107 &spl_token_program.key,
108 &lp_mint_account,
109 &swap_state_info.key,
110 None,
111 6,
112 )?;
113
114 invoke(
115 &initialize_mint_instruction,
116 &[
117 spl_token_program.clone(),
118 lp_mint_info.clone(),
119 ],
120 )?;
121
122 let (creator_fee, state_size) = {
123 if creator_fee_rate > 0 {
124 (Some(CreatorFee {
125 rate: creator_fee_rate,
126 balance_a: 0,
127 balance_b: 0,
128 withdraw_authority: fee_payer_info.key.clone(),
129 }), SwapPool::WITH_CREATOR_FEE_SIZE)
130 } else {
131 (None, SwapPool::BASE_SIZE)
132 }
133 };
134
135
136 let create_state_account_instruction = solana_program::system_instruction::create_account(
137 &fee_payer_info.key,
138 &swap_state_info.key,
139 rent.minimum_balance(state_size),
140 state_size as u64,
141 &program_id,
142 );
143
144 invoke_signed(
146 &create_state_account_instruction,
147 &[
148 system_program.clone(),
149 fee_payer_info.clone(),
150 swap_state_info.clone(),
151 ],
152 &[&[&seed]],
153 )?;
154
155 SwapPool {
156 seed: seed,
157 token_account_a: *token_a_account_info.key,
158 token_account_b: *token_b_account_info.key,
159 balance_a: 0,
160 balance_b: 0,
161 lp_mint: lp_mint_account,
162 lp_fee_rate,
163 creator_fee: creator_fee,
164 }.pack(&mut swap_state_info.try_borrow_mut_data()?)?;
165
166 Ok(())
167}
168
169
170fn process_deposit(program_id: &Pubkey, accounts: &[AccountInfo], min_a: u64, max_a: u64, min_b: u64, max_b: u64)
171 -> ProgramResult {
172 let accounts_iter = &mut accounts.iter();
173 let owner_info = next_account_info(accounts_iter)?;
174 let swap_pool_state_info = next_account_info(accounts_iter)?;
175 let source_a_info = next_account_info(accounts_iter)?;
176 let destination_a_info = next_account_info(accounts_iter)?;
177 let source_b_info = next_account_info(accounts_iter)?;
178 let destination_b_info = next_account_info(accounts_iter)?;
179 let lp_mint_info = next_account_info(accounts_iter)?;
180 let destination_lp_info = next_account_info(accounts_iter)?;
181
182 let spl_token_program = next_account_info(accounts_iter)?;
183
184 if !owner_info.is_signer {
185 return Err(MissingRequiredSignature);
186 }
187
188 if swap_pool_state_info.owner != program_id {
189 return Err(IllegalOwner);
190 }
191
192 let mut swap_pool_state = SwapPool::unpack(&swap_pool_state_info.try_borrow_data()?)?;
193 if swap_pool_state.token_account_a != *destination_a_info.key
194 || swap_pool_state.token_account_b != *destination_b_info.key
195 || swap_pool_state.lp_mint != *lp_mint_info.key {
196 return Err(InvalidAccountData);
197 }
198
199 let lp_mint_state = Mint::unpack(&lp_mint_info.try_borrow_data()?)?;
200
201 let (token_a_transfer_amount, token_b_transfer_amount, lp_mint_amount) = calculate_deposit_amounts(
202 swap_pool_state.balance_a,
203 swap_pool_state.balance_b,
204 lp_mint_state.supply,
205 max_a,
206 max_b).ok_or(InvalidInstructionData)?;
207
208 if token_a_transfer_amount < min_a || token_b_transfer_amount < min_b {
209 return Err(InvalidInstructionData);
211 }
212
213 transfer_spl_token(
214 source_a_info,
215 destination_a_info,
216 owner_info,
217 spl_token_program,
218 token_a_transfer_amount,
219 )?;
220
221 transfer_spl_token(
222 source_b_info,
223 destination_b_info,
224 owner_info,
225 spl_token_program,
226 token_b_transfer_amount,
227 )?;
228
229 let mint_instruction = spl_token::instruction::mint_to(
230 spl_token_program.key,
231 &swap_pool_state.lp_mint,
232 destination_lp_info.key,
233 swap_pool_state_info.key,
234 &[],
235 lp_mint_amount,
236 )?;
237
238 invoke_signed(
239 &mint_instruction,
240 &[
241 spl_token_program.clone(),
242 lp_mint_info.clone(),
243 destination_lp_info.clone(),
244 swap_pool_state_info.clone(),
245 ],
246 &[&[&swap_pool_state.seed]],
247 )?;
248
249
250 swap_pool_state.balance_a = swap_pool_state.balance_a
251 .checked_add(token_a_transfer_amount)
252 .ok_or(InvalidInstructionData)?;
253 swap_pool_state.balance_b = swap_pool_state.balance_b
254 .checked_add(token_b_transfer_amount)
255 .ok_or(InvalidInstructionData)?;
256 swap_pool_state.pack(&mut swap_pool_state_info.try_borrow_mut_data()?)?;
257
258 Ok(())
259}
260
261fn process_swap(program_id: &Pubkey, accounts: &[AccountInfo], in_amount: u64, min_out_amount: u64) -> ProgramResult {
262 let accounts_iter = &mut accounts.iter();
263 let owner_info = next_account_info(accounts_iter)?;
264 let swap_pool_state_info = next_account_info(accounts_iter)?;
265 let input_source_info = next_account_info(accounts_iter)?;
266 let input_destination_info = next_account_info(accounts_iter)?;
267 let output_source_info = next_account_info(accounts_iter)?;
268 let output_destination_info = next_account_info(accounts_iter)?;
269
270 let spl_token_program = next_account_info(accounts_iter)?;
271
272 if !owner_info.is_signer {
273 return Err(MissingRequiredSignature);
274 }
275
276 if swap_pool_state_info.owner != program_id {
277 return Err(IllegalOwner);
278 }
279
280 if in_amount == 0 {
281 return Err(InvalidInstructionData);
282 }
283
284 let mut swap_pool_state = SwapPool::unpack(&swap_pool_state_info.try_borrow_data()?)?;
285
286 let is_a_to_b = {
288 if *input_destination_info.key == swap_pool_state.token_account_a
289 && *output_source_info.key == swap_pool_state.token_account_b {
290 true
291 } else if *input_destination_info.key == swap_pool_state.token_account_b
292 && *output_source_info.key == swap_pool_state.token_account_a {
293 false
294 } else {
295 return Err(InvalidAccountData);
296 }
297 };
298
299 let (pool_balance_in_token, pool_balance_out_token) = if is_a_to_b {
300 (swap_pool_state.balance_a, swap_pool_state.balance_b)
301 } else {
302 (swap_pool_state.balance_b, swap_pool_state.balance_a)
303 };
304
305 let (out_amount, dao_fee_amount, _lp_fee_amount, creator_fee_amount) = calculate_swap_amounts(
306 pool_balance_in_token,
307 pool_balance_out_token,
308 in_amount,
309 50_000, swap_pool_state.lp_fee_rate,
311 swap_pool_state.creator_fee.as_ref()
312 .map_or(0, |cf| cf.rate),
313 ).ok_or(InvalidInstructionData)?;
314
315 if out_amount < min_out_amount || out_amount == 0 {
316 return Err(InvalidInstructionData);
318 }
319
320 transfer_spl_token(
321 input_source_info,
322 input_destination_info,
323 owner_info,
324 spl_token_program,
325 in_amount,
326 )?;
327
328 let withdraw_transfer_instruction = spl_token::instruction::transfer(
329 spl_token_program.key,
330 output_source_info.key,
331 output_destination_info.key,
332 swap_pool_state_info.key,
333 &[],
334 out_amount,
335 )?;
336 invoke_signed(
337 &withdraw_transfer_instruction,
338 &[
339 spl_token_program.clone(),
340 output_source_info.clone(),
341 output_destination_info.clone(),
342 swap_pool_state_info.clone(),
343 ],
344 &[&[&swap_pool_state.seed]],
345 )?;
346
347 let pool_deposit_amount = in_amount
348 .checked_sub(dao_fee_amount)
349 .ok_or(InvalidInstructionData)?
350 .checked_sub(creator_fee_amount)
351 .ok_or(InvalidInstructionData)?;
352
353 if is_a_to_b {
354 swap_pool_state.balance_a = swap_pool_state.balance_a
355 .checked_add(pool_deposit_amount)
356 .ok_or(InvalidInstructionData)?;
357 swap_pool_state.balance_b = swap_pool_state.balance_b
358 .checked_sub(out_amount)
359 .ok_or(InvalidInstructionData)?;
360 } else {
361 swap_pool_state.balance_b = swap_pool_state.balance_b
362 .checked_add(pool_deposit_amount)
363 .ok_or(InvalidInstructionData)?;
364 swap_pool_state.balance_a = swap_pool_state.balance_a
365 .checked_sub(out_amount)
366 .ok_or(InvalidInstructionData)?;
367 }
368
369 if let Some(creator_fee) = &mut swap_pool_state.creator_fee {
370 if is_a_to_b {
371 creator_fee.balance_a = creator_fee.balance_a.checked_add(creator_fee_amount)
372 .ok_or(InvalidInstructionData)?;
373 } else {
374 creator_fee.balance_b = creator_fee.balance_b.checked_add(creator_fee_amount)
375 .ok_or(InvalidInstructionData)?;
376 }
377 }
378 swap_pool_state.pack(&mut swap_pool_state_info.try_borrow_mut_data()?)?;
379
380 Ok(())
381}
382
383fn process_wthdraw(program_id: &Pubkey, accounts: &[AccountInfo], lp_amount: u64, min_a: u64, min_b: u64) -> ProgramResult {
384 let accounts_iter = &mut accounts.iter();
385 let owner_info = next_account_info(accounts_iter)?;
386 let swap_pool_state_info = next_account_info(accounts_iter)?;
387 let source_a_info = next_account_info(accounts_iter)?;
388 let destination_a_info = next_account_info(accounts_iter)?;
389 let source_b_info = next_account_info(accounts_iter)?;
390 let destination_b_info = next_account_info(accounts_iter)?;
391 let lp_mint_info = next_account_info(accounts_iter)?;
392 let source_lp_info = next_account_info(accounts_iter)?;
393
394 let spl_token_program = next_account_info(accounts_iter)?;
395
396 if !owner_info.is_signer {
397 return Err(MissingRequiredSignature);
398 }
399
400 if swap_pool_state_info.owner != program_id {
401 return Err(IllegalOwner);
402 }
403
404 let mut swap_pool_state = SwapPool::unpack(&swap_pool_state_info.try_borrow_data()?)?;
405 if swap_pool_state.token_account_a != *source_a_info.key
406 || swap_pool_state.token_account_b != *source_b_info.key
407 || swap_pool_state.lp_mint != *lp_mint_info.key {
408 return Err(InvalidAccountData);
409 }
410
411 let lp_mint_state = Mint::unpack(&lp_mint_info.try_borrow_data()?)?;
412
413 let (withdraw_a_amount, withdraw_b_amount) = calculate_withdraw_amounts(
414 swap_pool_state.balance_a,
415 swap_pool_state.balance_b,
416 lp_mint_state.supply,
417 lp_amount,
418 ).ok_or(InvalidInstructionData)?;
419
420 if withdraw_a_amount < min_a || withdraw_b_amount < min_b {
421 return Err(InvalidInstructionData);
423 }
424
425 let burn_instruction = spl_token::instruction::burn(
427 spl_token_program.key,
428 source_lp_info.key,
429 &swap_pool_state.lp_mint,
430 owner_info.key,
431 &[owner_info.key],
432 lp_amount,
433 )?;
434
435 invoke(
436 &burn_instruction,
437 &[
438 spl_token_program.clone(),
439 source_lp_info.clone(),
440 lp_mint_info.clone(),
441 owner_info.clone(),
442 ],
443 )?;
444
445 let transfer_a_instruction = spl_token::instruction::transfer(
446 spl_token_program.key,
447 &swap_pool_state.token_account_a,
448 destination_a_info.key,
449 swap_pool_state_info.key,
450 &[],
451 withdraw_a_amount,
452 )?;
453 invoke_signed(
454 &transfer_a_instruction,
455 &[
456 spl_token_program.clone(),
457 source_a_info.clone(),
458 destination_a_info.clone(),
459 swap_pool_state_info.clone(),
460 ],
461 &[&[&swap_pool_state.seed]],
462 )?;
463
464 let transfer_b_instruction = spl_token::instruction::transfer(
465 spl_token_program.key,
466 &swap_pool_state.token_account_b,
467 destination_b_info.key,
468 swap_pool_state_info.key,
469 &[],
470 withdraw_b_amount,
471 )?;
472 invoke_signed(
473 &transfer_b_instruction,
474 &[
475 spl_token_program.clone(),
476 source_b_info.clone(),
477 destination_b_info.clone(),
478 swap_pool_state_info.clone(),
479 ],
480 &[&[&swap_pool_state.seed]],
481 )?;
482
483 swap_pool_state.balance_a = swap_pool_state.balance_a
484 .checked_sub(withdraw_a_amount)
485 .ok_or(InvalidInstructionData)?;
486 swap_pool_state.balance_b = swap_pool_state.balance_b
487 .checked_sub(withdraw_b_amount)
488 .ok_or(InvalidInstructionData)?;
489 swap_pool_state.pack(&mut swap_pool_state_info.try_borrow_mut_data()?)?;
490
491 Ok(())
492}