dloom_flow/amm/instructions/
add_liquidity.rs1use crate::{
4 amm::{
5 instructions::swap::update_oracle,
6 math,
7 state::{AmmPool, AmmPosition},
8 },
9 errors::DloomError,
10 events::AmmLiquidityAdded,
11};
12use anchor_lang::prelude::*;
13use anchor_spl::{
14 associated_token::AssociatedToken,
15 token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked},
16};
17
18pub fn handle_add_amm_liquidity(
19 ctx: Context<AddAmmLiquidity>,
20 amount_a_desired: u64,
21 amount_b_desired: u64,
22 min_lp_tokens_to_mint: u64,
23) -> Result<()> {
24 update_oracle(&mut ctx.accounts.amm_pool)?;
25 require!(
26 amount_a_desired > 0 && amount_b_desired > 0,
27 DloomError::ZeroLiquidity
28 );
29
30 let (amount_a_to_deposit, amount_b_to_deposit, lp_tokens_to_mint) =
31 math::calculate_lp_tokens_to_mint(
32 &ctx.accounts.amm_pool,
33 &ctx.accounts.lp_mint,
34 amount_a_desired,
35 amount_b_desired,
36 )?;
37
38 require!(
39 lp_tokens_to_mint >= min_lp_tokens_to_mint,
40 DloomError::SlippageExceeded
41 );
42
43 token_interface::transfer_checked(
44 CpiContext::new(
45 ctx.accounts.token_a_program.to_account_info(),
46 TransferChecked {
47 from: ctx.accounts.user_token_a_account.to_account_info(),
48 to: ctx.accounts.token_a_vault.to_account_info(),
49 authority: ctx.accounts.owner.to_account_info(),
50 mint: ctx.accounts.token_a_mint.to_account_info(),
51 },
52 ),
53 amount_a_to_deposit,
54 ctx.accounts.token_a_mint.decimals,
55 )?;
56
57 token_interface::transfer_checked(
58 CpiContext::new(
59 ctx.accounts.token_b_program.to_account_info(),
60 TransferChecked {
61 from: ctx.accounts.user_token_b_account.to_account_info(),
62 to: ctx.accounts.token_b_vault.to_account_info(),
63 authority: ctx.accounts.owner.to_account_info(),
64 mint: ctx.accounts.token_b_mint.to_account_info(),
65 },
66 ),
67 amount_b_to_deposit,
68 ctx.accounts.token_b_mint.decimals,
69 )?;
70
71 let bump = &[ctx.accounts.amm_pool.bump][..];
72 let signer_seeds = &[
73 b"amm_pool",
74 ctx.accounts.amm_pool.token_a_mint.as_ref(),
75 ctx.accounts.amm_pool.token_b_mint.as_ref(),
76 bump,
77 ][..];
78
79 token_interface::mint_to(
80 CpiContext::new_with_signer(
81 ctx.accounts.token_program.to_account_info(),
82 MintTo {
83 mint: ctx.accounts.lp_mint.to_account_info(),
84 to: ctx.accounts.user_lp_token_account.to_account_info(),
85 authority: ctx.accounts.amm_pool.to_account_info(),
86 },
87 &[signer_seeds],
88 ),
89 lp_tokens_to_mint,
90 )?;
91
92 ctx.accounts.amm_position.lp_token_amount = ctx
94 .accounts
95 .amm_position
96 .lp_token_amount
97 .checked_add(lp_tokens_to_mint)
98 .ok_or(DloomError::MathOverflow)?;
99
100 let amm_pool = &mut ctx.accounts.amm_pool;
101 amm_pool.reserves_a = amm_pool
102 .reserves_a
103 .checked_add(amount_a_to_deposit)
104 .ok_or(DloomError::MathOverflow)?;
105 amm_pool.reserves_b = amm_pool
106 .reserves_b
107 .checked_add(amount_b_to_deposit)
108 .ok_or(DloomError::MathOverflow)?;
109
110 emit!(AmmLiquidityAdded {
111 pool_address: ctx.accounts.amm_pool.key(),
112 user: ctx.accounts.owner.key(),
113 lp_tokens_minted: lp_tokens_to_mint,
114 amount_a_deposited: amount_a_to_deposit,
115 amount_b_deposited: amount_b_to_deposit,
116 });
117
118 Ok(())
119}
120
121#[derive(Accounts)]
122pub struct AddAmmLiquidity<'info> {
123 #[account(mut)]
124 pub owner: Signer<'info>,
125 #[account(
126 mut,
127 seeds = [b"amm_pool", amm_pool.token_a_mint.as_ref(), amm_pool.token_b_mint.as_ref()],
128 bump = amm_pool.bump,
129 )]
130 pub amm_pool: Box<Account<'info, AmmPool>>,
131
132 #[account(
133 mut,
134 seeds = [b"amm_position", owner.key().as_ref(), amm_pool.key().as_ref()],
135 bump,
136 has_one = owner,
137 )]
138 pub amm_position: Box<Account<'info, AmmPosition>>,
139
140 #[account(mut, address = amm_pool.lp_mint)]
141 pub lp_mint: Box<InterfaceAccount<'info, Mint>>,
142 #[account(address = amm_pool.token_a_mint)]
143 pub token_a_mint: Box<InterfaceAccount<'info, Mint>>,
144 #[account(address = amm_pool.token_b_mint)]
145 pub token_b_mint: Box<InterfaceAccount<'info, Mint>>,
146 #[account(mut, address = amm_pool.token_a_vault)]
147 pub token_a_vault: InterfaceAccount<'info, TokenAccount>,
148 #[account(mut, address = amm_pool.token_b_vault)]
149 pub token_b_vault: InterfaceAccount<'info, TokenAccount>,
150 #[account(mut, constraint = user_token_a_account.mint == amm_pool.token_a_mint, has_one = owner)]
151 pub user_token_a_account: InterfaceAccount<'info, TokenAccount>,
152 #[account(mut, constraint = user_token_b_account.mint == amm_pool.token_b_mint, has_one = owner)]
153 pub user_token_b_account: InterfaceAccount<'info, TokenAccount>,
154 #[account(
155 init_if_needed,
156 payer = owner,
157 associated_token::mint = lp_mint,
158 associated_token::authority = owner,
159 )]
160 pub user_lp_token_account: InterfaceAccount<'info, TokenAccount>,
161 pub system_program: Program<'info, System>,
162 pub token_a_program: Interface<'info, TokenInterface>,
163 pub token_b_program: Interface<'info, TokenInterface>,
164 pub token_program: Interface<'info, TokenInterface>,
165 pub associated_token_program: Program<'info, AssociatedToken>,
166}