1use std::str::FromStr;
4
5use crate::curve::curve_type::CurveType;
6use crate::curve::fees::to_bps;
7use crate::error::PoolError;
8use crate::{state::Pool, vault_utils::MercurialVault};
9use anchor_lang::prelude::*;
10use anchor_spl::associated_token::AssociatedToken;
11use anchor_spl::token::{Mint, Token, TokenAccount};
12use mercurial_vault::state::Vault;
13
14pub fn get_first_key(key1: Pubkey, key2: Pubkey) -> Pubkey {
16 if key1 > key2 {
17 return key1;
18 }
19 key2
20}
21pub fn get_second_key(key1: Pubkey, key2: Pubkey) -> Pubkey {
23 if key1 > key2 {
24 return key2;
25 }
26 key1
27}
28
29pub fn get_curve_type(curve_type: CurveType) -> u8 {
31 match curve_type {
32 CurveType::ConstantProduct {} => 0,
33 _ => 1,
34 }
35}
36
37pub fn get_admin(_payer: Pubkey) -> Pubkey {
39 Pubkey::from_str("5unTfT2kssBuNvHPY6LbJfJpLqEcdMxGYLWHwShaeTLi").unwrap()
40}
41
42pub fn get_fee_owner() -> Pubkey {
44 Pubkey::from_str("6WaLrrRfReGKBYUSkmx2K6AuT21ida4j8at2SUiZdXu8").unwrap()
45}
46
47pub fn get_lp_mint(token_a_mint_decimals: u8, token_b_mint_decimals: u8) -> u8 {
49 if token_a_mint_decimals > token_b_mint_decimals {
50 return token_a_mint_decimals;
51 }
52 token_b_mint_decimals
53}
54
55pub fn get_trade_fee_bps_bytes(curve_type: CurveType, trade_fee_bps: u64) -> Option<Vec<u8>> {
57 let default_fees = curve_type.get_default_fee();
58
59 let default_trade_fee_bps = to_bps(
60 default_fees.trade_fee_numerator.into(),
61 default_fees.trade_fee_denominator.into(),
62 )?;
63
64 if default_trade_fee_bps == trade_fee_bps {
65 return Some(vec![]);
66 }
67
68 Some(trade_fee_bps.to_le_bytes().to_vec())
69}
70
71#[derive(Accounts)]
74pub struct BootstrapLiquidity<'info> {
75 #[account(
76 mut,
77 has_one = a_vault @ PoolError::InvalidVaultAccount,
78 has_one = b_vault @ PoolError::InvalidVaultAccount,
79 has_one = a_vault_lp @ PoolError::InvalidVaultLpAccount,
80 has_one = b_vault_lp @ PoolError::InvalidVaultLpAccount,
81 has_one = lp_mint @ PoolError::InvalidPoolLpMintAccount,
82 constraint = a_vault_lp.mint == a_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
83 constraint = b_vault_lp.mint == b_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
84 constraint = pool.enabled @ PoolError::PoolDisabled
85 )]
86 pub pool: Box<Account<'info, Pool>>,
88 #[account(
89 mut,
90 constraint = lp_mint.supply == 0 @ PoolError::NonDepletedPool
91 )]
92 pub lp_mint: Account<'info, Mint>,
94 #[account(mut)]
95 pub user_pool_lp: Account<'info, TokenAccount>,
97
98 #[account(mut)]
99 pub a_vault_lp: Box<Account<'info, TokenAccount>>,
101 #[account(mut)]
102 pub b_vault_lp: Box<Account<'info, TokenAccount>>,
104
105 #[account(mut)]
106 pub a_vault: Box<Account<'info, Vault>>,
108 #[account(mut)]
109 pub b_vault: Box<Account<'info, Vault>>,
111
112 #[account(mut)]
113 pub a_vault_lp_mint: Box<Account<'info, Mint>>,
115 #[account(mut)]
116 pub b_vault_lp_mint: Box<Account<'info, Mint>>,
118
119 #[account(mut)]
120 pub a_token_vault: Box<Account<'info, TokenAccount>>,
122 #[account(mut)]
123 pub b_token_vault: Box<Account<'info, TokenAccount>>,
125 #[account(mut)]
126 pub user_a_token: Account<'info, TokenAccount>,
128 #[account(mut)]
129 pub user_b_token: Account<'info, TokenAccount>,
131 pub user: Signer<'info>,
133
134 pub vault_program: Program<'info, MercurialVault>,
136 pub token_program: Program<'info, Token>,
138}
139
140#[derive(Accounts)]
143#[instruction(curve_type: CurveType, trade_fee_bps: u64)]
144pub struct InitializePermissionlessPoolWithFeeTier<'info> {
145 #[account(
146 init,
147 seeds = [
148 &get_curve_type(curve_type).to_le_bytes(),
149 get_first_key(token_a_mint.key(), token_b_mint.key()).as_ref(),
150 get_second_key(token_a_mint.key(), token_b_mint.key()).as_ref(),
151 get_trade_fee_bps_bytes(curve_type, trade_fee_bps).ok_or(PoolError::MathOverflow)?.as_ref(), ],
153 bump,
154 payer = payer,
155 space = 8 + std::mem::size_of::<Pool>()
157 )]
158 pub pool: Box<Account<'info, Pool>>,
160
161 #[account(
163 init,
164 seeds = [
165 "lp_mint".as_ref(),
166 pool.key().as_ref()
167 ],
168 bump,
169 payer = payer,
170 mint::decimals = get_lp_mint(token_a_mint.decimals, token_b_mint.decimals),
171 mint::authority = a_vault_lp
172 )]
173 pub lp_mint: Box<Account<'info, Mint>>,
174
175 pub token_a_mint: Box<Account<'info, Mint>>,
177 #[account(
179 constraint = token_b_mint.key() != token_a_mint.key() @ PoolError::MismatchedTokenMint
180 )]
181 pub token_b_mint: Box<Account<'info, Mint>>,
182
183 #[account(
184 mut,
185 constraint = a_vault.token_mint == token_a_mint.key() @ PoolError::MismatchedTokenMint
186 )]
187 pub a_vault: Box<Account<'info, Vault>>,
189 #[account(
190 mut,
191 constraint = b_vault.token_mint == token_b_mint.key() @ PoolError::MismatchedTokenMint
192 )]
193 pub b_vault: Box<Account<'info, Vault>>,
195
196 #[account(mut)]
197 pub a_token_vault: Box<Account<'info, TokenAccount>>,
199 #[account(mut)]
200 pub b_token_vault: Box<Account<'info, TokenAccount>>,
202
203 #[account(
204 mut,
205 constraint = a_vault_lp_mint.key() == a_vault.lp_mint @ PoolError::MismatchedLpMint
206 )]
207 pub a_vault_lp_mint: Box<Account<'info, Mint>>,
209 #[account(
210 mut,
211 constraint = b_vault_lp_mint.key() == b_vault.lp_mint @ PoolError::MismatchedLpMint
212 )]
213 pub b_vault_lp_mint: Box<Account<'info, Mint>>,
215 #[account(
217 init,
218 seeds = [
219 a_vault.key().as_ref(),
220 pool.key().as_ref()
221 ],
222 bump,
223 payer = payer,
224 token::mint = a_vault_lp_mint,
225 token::authority = a_vault_lp
226 )]
227 pub a_vault_lp: Box<Account<'info, TokenAccount>>,
228 #[account(
230 init,
231 seeds = [
232 b_vault.key().as_ref(),
233 pool.key().as_ref()
234 ],
235 bump,
236 payer = payer,
237 token::mint = b_vault_lp_mint,
238 token::authority = a_vault_lp
239 )]
240 pub b_vault_lp: Box<Account<'info, TokenAccount>>,
241
242 #[account(
243 mut,
244 constraint = payer_token_a.mint == a_vault.token_mint @ PoolError::MismatchedTokenMint
245 )]
246 pub payer_token_a: Box<Account<'info, TokenAccount>>,
248 #[account(
249 mut,
250 constraint = payer_token_b.mint == b_vault.token_mint @ PoolError::MismatchedTokenMint
251 )]
252 pub payer_token_b: Box<Account<'info, TokenAccount>>,
254
255 #[account(
257 init,
258 payer = payer,
259 associated_token::mint = lp_mint,
260 associated_token::authority = payer,
261 )]
262 pub payer_pool_lp: Box<Account<'info, TokenAccount>>,
263
264 #[account(
265 init,
266 seeds = [
267 "fee".as_ref(),
268 token_a_mint.key().as_ref(),
269 pool.key().as_ref()
270 ],
271 bump,
272 payer = payer,
273 token::mint = token_a_mint,
274 token::authority = fee_owner
275 )]
276 pub admin_token_a_fee: Box<Account<'info, TokenAccount>>,
278
279 #[account(
281 init,
282 seeds = [
283 "fee".as_ref(),
284 token_b_mint.key().as_ref(),
285 pool.key().as_ref()
286 ],
287 bump,
288 payer = payer,
289 token::mint = token_b_mint,
290 token::authority = fee_owner
291 )]
292 pub admin_token_b_fee: Box<Account<'info, TokenAccount>>,
293
294 #[account(mut)]
296 pub payer: Signer<'info>,
297
298 #[account(constraint = fee_owner.key() == get_fee_owner() @ PoolError::InvalidFeeOwner)]
300 pub fee_owner: UncheckedAccount<'info>,
301
302 pub rent: Sysvar<'info, Rent>,
304
305 pub vault_program: Program<'info, MercurialVault>,
307 pub token_program: Program<'info, Token>,
309 pub associated_token_program: Program<'info, AssociatedToken>,
311 pub system_program: Program<'info, System>,
313}
314
315#[deprecated]
318#[derive(Accounts)]
319#[instruction(curve_type: CurveType)]
320pub struct InitializePermissionlessPool<'info> {
321 #[account(
322 init,
323 seeds = [
324 &get_curve_type(curve_type).to_le_bytes(),
325 get_first_key(token_a_mint.key(), token_b_mint.key()).as_ref(),
326 get_second_key(token_a_mint.key(), token_b_mint.key()).as_ref(),
327 ],
328 bump,
329 payer = payer,
330 space = 8 + std::mem::size_of::<Pool>()
332 )]
333 pub pool: Box<Account<'info, Pool>>,
335
336 #[account(
338 init,
339 seeds = [
340 "lp_mint".as_ref(),
341 pool.key().as_ref()
342 ],
343 bump,
344 payer = payer,
345 mint::decimals = get_lp_mint(token_a_mint.decimals, token_b_mint.decimals),
346 mint::authority = a_vault_lp
347 )]
348 pub lp_mint: Box<Account<'info, Mint>>,
349
350 pub token_a_mint: Box<Account<'info, Mint>>,
352 #[account(
354 constraint = token_b_mint.key() != token_a_mint.key() @ PoolError::MismatchedTokenMint
355 )]
356 pub token_b_mint: Box<Account<'info, Mint>>,
357
358 #[account(
359 mut,
360 constraint = a_vault.token_mint == token_a_mint.key() @ PoolError::MismatchedTokenMint
361 )]
362 pub a_vault: Box<Account<'info, Vault>>,
364 #[account(
365 mut,
366 constraint = b_vault.token_mint == token_b_mint.key() @ PoolError::MismatchedTokenMint
367 )]
368 pub b_vault: Box<Account<'info, Vault>>,
370
371 #[account(mut)]
372 pub a_token_vault: Box<Account<'info, TokenAccount>>,
374 #[account(mut)]
375 pub b_token_vault: Box<Account<'info, TokenAccount>>,
377
378 #[account(
379 mut,
380 constraint = a_vault_lp_mint.key() == a_vault.lp_mint @ PoolError::MismatchedLpMint
381 )]
382 pub a_vault_lp_mint: Box<Account<'info, Mint>>,
384 #[account(
385 mut,
386 constraint = b_vault_lp_mint.key() == b_vault.lp_mint @ PoolError::MismatchedLpMint
387 )]
388 pub b_vault_lp_mint: Box<Account<'info, Mint>>,
390 #[account(
392 init,
393 seeds = [
394 a_vault.key().as_ref(),
395 pool.key().as_ref()
396 ],
397 bump,
398 payer = payer,
399 token::mint = a_vault_lp_mint,
400 token::authority = a_vault_lp
401 )]
402 pub a_vault_lp: Box<Account<'info, TokenAccount>>,
403 #[account(
405 init,
406 seeds = [
407 b_vault.key().as_ref(),
408 pool.key().as_ref()
409 ],
410 bump,
411 payer = payer,
412 token::mint = b_vault_lp_mint,
413 token::authority = a_vault_lp
414 )]
415 pub b_vault_lp: Box<Account<'info, TokenAccount>>,
416
417 #[account(
418 mut,
419 constraint = payer_token_a.mint == a_vault.token_mint @ PoolError::MismatchedTokenMint
420 )]
421 pub payer_token_a: Box<Account<'info, TokenAccount>>,
423 #[account(
424 mut,
425 constraint = payer_token_b.mint == b_vault.token_mint @ PoolError::MismatchedTokenMint
426 )]
427 pub payer_token_b: Box<Account<'info, TokenAccount>>,
429
430 #[account(
432 init,
433 payer = payer,
434 associated_token::mint = lp_mint,
435 associated_token::authority = payer,
436 )]
437 pub payer_pool_lp: Box<Account<'info, TokenAccount>>,
438
439 #[account(
440 init,
441 seeds = [
442 "fee".as_ref(),
443 token_a_mint.key().as_ref(),
444 pool.key().as_ref()
445 ],
446 bump,
447 payer = payer,
448 token::mint = token_a_mint,
449 token::authority = fee_owner
450 )]
451 pub admin_token_a_fee: Box<Account<'info, TokenAccount>>,
453
454 #[account(
456 init,
457 seeds = [
458 "fee".as_ref(),
459 token_b_mint.key().as_ref(),
460 pool.key().as_ref()
461 ],
462 bump,
463 payer = payer,
464 token::mint = token_b_mint,
465 token::authority = fee_owner
466 )]
467 pub admin_token_b_fee: Box<Account<'info, TokenAccount>>,
468
469 #[account(mut)]
471 pub payer: Signer<'info>,
472
473 #[account(constraint = fee_owner.key() == get_fee_owner() @ PoolError::InvalidFeeOwner)]
475 pub fee_owner: UncheckedAccount<'info>,
476
477 pub rent: Sysvar<'info, Rent>,
479
480 pub vault_program: Program<'info, MercurialVault>,
482 pub token_program: Program<'info, Token>,
484 pub associated_token_program: Program<'info, AssociatedToken>,
486 pub system_program: Program<'info, System>,
488}
489
490#[derive(Accounts)]
492pub struct RemoveLiquiditySingleSide<'info> {
493 #[account(
494 mut,
495 has_one = a_vault @ PoolError::InvalidVaultAccount,
496 has_one = b_vault @ PoolError::InvalidVaultAccount,
497 has_one = a_vault_lp @ PoolError::InvalidVaultLpAccount,
498 has_one = b_vault_lp @ PoolError::InvalidVaultLpAccount,
499 has_one = lp_mint @ PoolError::InvalidPoolLpMintAccount,
500 constraint = a_vault_lp.mint == a_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
501 constraint = b_vault_lp.mint == b_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
502 constraint = pool.enabled @ PoolError::PoolDisabled )]
504 pub pool: Box<Account<'info, Pool>>,
506 #[account(mut)]
507 pub lp_mint: Account<'info, Mint>,
509 #[account(mut)]
510 pub user_pool_lp: Account<'info, TokenAccount>,
512
513 #[account(mut)]
514 pub a_vault_lp: Box<Account<'info, TokenAccount>>,
516 #[account(mut)]
517 pub b_vault_lp: Box<Account<'info, TokenAccount>>,
519
520 #[account(mut)]
521 pub a_vault: Box<Account<'info, Vault>>,
523 #[account(mut)]
524 pub b_vault: Box<Account<'info, Vault>>,
526
527 #[account(mut)]
528 pub a_vault_lp_mint: Box<Account<'info, Mint>>,
530 #[account(mut)]
531 pub b_vault_lp_mint: Box<Account<'info, Mint>>,
533
534 #[account(mut)]
535 pub a_token_vault: Box<Account<'info, TokenAccount>>,
537 #[account(mut)]
538 pub b_token_vault: Box<Account<'info, TokenAccount>>,
540 #[account(mut)]
541 pub user_destination_token: Account<'info, TokenAccount>,
543 pub user: Signer<'info>,
545
546 pub vault_program: Program<'info, MercurialVault>,
548 pub token_program: Program<'info, Token>,
550}
551
552#[derive(Accounts)]
554pub struct AddOrRemoveBalanceLiquidity<'info> {
555 #[account(
556 mut,
557 has_one = a_vault @ PoolError::InvalidVaultAccount,
558 has_one = b_vault @ PoolError::InvalidVaultAccount,
559 has_one = a_vault_lp @ PoolError::InvalidVaultLpAccount,
560 has_one = b_vault_lp @ PoolError::InvalidVaultLpAccount,
561 has_one = lp_mint @ PoolError::InvalidPoolLpMintAccount,
562 constraint = a_vault_lp.mint == a_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
563 constraint = b_vault_lp.mint == b_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
564 )]
565 pub pool: Box<Account<'info, Pool>>,
567 #[account(mut)]
568 pub lp_mint: Account<'info, Mint>,
570 #[account(mut)]
571 pub user_pool_lp: Account<'info, TokenAccount>,
573
574 #[account(mut)]
575 pub a_vault_lp: Box<Account<'info, TokenAccount>>,
577 #[account(mut)]
578 pub b_vault_lp: Box<Account<'info, TokenAccount>>,
580
581 #[account(mut)]
582 pub a_vault: Box<Account<'info, Vault>>,
584 #[account(mut)]
585 pub b_vault: Box<Account<'info, Vault>>,
587
588 #[account(mut)]
589 pub a_vault_lp_mint: Box<Account<'info, Mint>>,
591 #[account(mut)]
592 pub b_vault_lp_mint: Box<Account<'info, Mint>>,
594
595 #[account(mut)]
596 pub a_token_vault: Box<Account<'info, TokenAccount>>,
598 #[account(mut)]
599 pub b_token_vault: Box<Account<'info, TokenAccount>>,
601 #[account(mut)]
602 pub user_a_token: Account<'info, TokenAccount>,
604 #[account(mut)]
605 pub user_b_token: Account<'info, TokenAccount>,
607 pub user: Signer<'info>,
609
610 pub vault_program: Program<'info, MercurialVault>,
612 pub token_program: Program<'info, Token>,
614}
615
616#[derive(Accounts)]
618pub struct GetPoolInfo<'info> {
619 #[account(
620 has_one = a_vault_lp @ PoolError::InvalidVaultLpAccount,
621 has_one = b_vault_lp @ PoolError::InvalidVaultLpAccount,
622 has_one = a_vault @ PoolError::InvalidVaultAccount,
623 has_one = b_vault @ PoolError::InvalidVaultAccount,
624 has_one = lp_mint @ PoolError::InvalidPoolLpMintAccount,
625 constraint = a_vault_lp.mint == a_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
626 constraint = b_vault_lp.mint == b_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
627 )]
628 pub pool: Box<Account<'info, Pool>>,
630 pub lp_mint: Box<Account<'info, Mint>>,
632 pub a_vault_lp: Account<'info, TokenAccount>,
634 pub b_vault_lp: Account<'info, TokenAccount>,
636 pub a_vault: Box<Account<'info, Vault>>,
638 pub b_vault: Box<Account<'info, Vault>>,
640 pub a_vault_lp_mint: Box<Account<'info, Mint>>,
642 pub b_vault_lp_mint: Box<Account<'info, Mint>>,
644}
645#[derive(Accounts)]
647pub struct Swap<'info> {
648 #[account(
649 mut,
650 has_one = a_vault_lp @ PoolError::InvalidVaultLpAccount,
651 has_one = b_vault_lp @ PoolError::InvalidVaultLpAccount,
652 has_one = a_vault @ PoolError::InvalidVaultAccount,
653 has_one = b_vault @ PoolError::InvalidVaultAccount,
654 constraint = a_vault_lp.mint == a_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
655 constraint = b_vault_lp.mint == b_vault_lp_mint.key() @ PoolError::MismatchedLpMint,
656 constraint = pool.enabled @ PoolError::PoolDisabled
657 )]
658 pub pool: Box<Account<'info, Pool>>,
660
661 #[account(
662 mut,
663 constraint = user_source_token.mint != user_destination_token.mint @ PoolError::IdenticalSourceDestination,
664 constraint = user_source_token.mint == a_vault.token_mint || user_source_token.mint == b_vault.token_mint @ PoolError::MismatchedTokenMint
665 )]
666 pub user_source_token: Account<'info, TokenAccount>,
668 #[account(
669 mut,
670 constraint = user_destination_token.mint == a_vault.token_mint || user_destination_token.mint == b_vault.token_mint @ PoolError::MismatchedTokenMint
671 )]
672 pub user_destination_token: Account<'info, TokenAccount>,
674
675 #[account(mut)]
676 pub a_vault: Box<Account<'info, Vault>>,
678 #[account(mut)]
679 pub b_vault: Box<Account<'info, Vault>>,
681
682 #[account(mut)]
683 pub a_token_vault: Box<Account<'info, TokenAccount>>,
685 #[account(mut)]
686 pub b_token_vault: Box<Account<'info, TokenAccount>>,
688
689 #[account(mut)]
690 pub a_vault_lp_mint: Box<Account<'info, Mint>>,
692 #[account(mut)]
693 pub b_vault_lp_mint: Box<Account<'info, Mint>>,
695
696 #[account(mut)]
697 pub a_vault_lp: Account<'info, TokenAccount>,
699 #[account(mut)]
700 pub b_vault_lp: Account<'info, TokenAccount>,
702
703 #[account(mut)]
704 pub admin_token_fee: Box<Account<'info, TokenAccount>>,
706
707 pub user: Signer<'info>,
709
710 pub vault_program: Program<'info, MercurialVault>,
712 pub token_program: Program<'info, Token>,
714}