dloom_flow/amm/instructions/
add_liquidity.rs

1// FILE: programs/dloom_flow/src/amm/instructions/add_liquidity.rs
2
3use 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    // Update the AmmPosition state
93    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}