dloom-flow 1.0.0

A Solana flow payment program created with Anchor
Documentation
// FILE: programs/dloom_flow/src/amm/instructions/add_liquidity.rs

use crate::{
    amm::{
        instructions::swap::update_oracle, 
        math,                              
        state::{AmmPool, AmmPosition}, 
    },
    errors::DloomError,
    events::AmmLiquidityAdded,
};
use anchor_lang::prelude::*;
use anchor_spl::{
    associated_token::AssociatedToken,
    token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked},
};

pub fn handle_add_amm_liquidity(
    ctx: Context<AddAmmLiquidity>,
    amount_a_desired: u64,
    amount_b_desired: u64,
    min_lp_tokens_to_mint: u64,
) -> Result<()> {
    update_oracle(&mut ctx.accounts.amm_pool)?;
    require!(
        amount_a_desired > 0 && amount_b_desired > 0,
        DloomError::ZeroLiquidity
    );

    let (amount_a_to_deposit, amount_b_to_deposit, lp_tokens_to_mint) =
        math::calculate_lp_tokens_to_mint(
            &ctx.accounts.amm_pool,
            &ctx.accounts.lp_mint,
            amount_a_desired,
            amount_b_desired,
        )?;

    require!(
        lp_tokens_to_mint >= min_lp_tokens_to_mint,
        DloomError::SlippageExceeded
    );

    token_interface::transfer_checked(
        CpiContext::new(
            ctx.accounts.token_a_program.to_account_info(),
            TransferChecked {
                from: ctx.accounts.user_token_a_account.to_account_info(),
                to: ctx.accounts.token_a_vault.to_account_info(),
                authority: ctx.accounts.owner.to_account_info(),
                mint: ctx.accounts.token_a_mint.to_account_info(),
            },
        ),
        amount_a_to_deposit,
        ctx.accounts.token_a_mint.decimals,
    )?;

    token_interface::transfer_checked(
        CpiContext::new(
            ctx.accounts.token_b_program.to_account_info(),
            TransferChecked {
                from: ctx.accounts.user_token_b_account.to_account_info(),
                to: ctx.accounts.token_b_vault.to_account_info(),
                authority: ctx.accounts.owner.to_account_info(),
                mint: ctx.accounts.token_b_mint.to_account_info(),
            },
        ),
        amount_b_to_deposit,
        ctx.accounts.token_b_mint.decimals,
    )?;

    let bump = &[ctx.accounts.amm_pool.bump][..];
    let signer_seeds = &[
        b"amm_pool",
        ctx.accounts.amm_pool.token_a_mint.as_ref(),
        ctx.accounts.amm_pool.token_b_mint.as_ref(),
        bump,
    ][..];

    token_interface::mint_to(
        CpiContext::new_with_signer(
            ctx.accounts.token_program.to_account_info(),
            MintTo {
                mint: ctx.accounts.lp_mint.to_account_info(),
                to: ctx.accounts.user_lp_token_account.to_account_info(),
                authority: ctx.accounts.amm_pool.to_account_info(),
            },
            &[signer_seeds],
        ),
        lp_tokens_to_mint,
    )?;

    // Update the AmmPosition state
    ctx.accounts.amm_position.lp_token_amount = ctx
        .accounts
        .amm_position
        .lp_token_amount
        .checked_add(lp_tokens_to_mint)
        .ok_or(DloomError::MathOverflow)?;

    let amm_pool = &mut ctx.accounts.amm_pool;
    amm_pool.reserves_a = amm_pool
        .reserves_a
        .checked_add(amount_a_to_deposit)
        .ok_or(DloomError::MathOverflow)?;
    amm_pool.reserves_b = amm_pool
        .reserves_b
        .checked_add(amount_b_to_deposit)
        .ok_or(DloomError::MathOverflow)?;

    emit!(AmmLiquidityAdded {
        pool_address: ctx.accounts.amm_pool.key(),
        user: ctx.accounts.owner.key(),
        lp_tokens_minted: lp_tokens_to_mint,
        amount_a_deposited: amount_a_to_deposit,
        amount_b_deposited: amount_b_to_deposit,
    });

    Ok(())
}

#[derive(Accounts)]
pub struct AddAmmLiquidity<'info> {
    #[account(mut)]
    pub owner: Signer<'info>,
    #[account(
        mut,
        seeds = [b"amm_pool", amm_pool.token_a_mint.as_ref(), amm_pool.token_b_mint.as_ref()],
        bump = amm_pool.bump,
    )]
    pub amm_pool: Box<Account<'info, AmmPool>>,

    #[account(
        mut,
        seeds = [b"amm_position", owner.key().as_ref(), amm_pool.key().as_ref()],
        bump,
        has_one = owner,
    )]
    pub amm_position: Box<Account<'info, AmmPosition>>,

    #[account(mut, address = amm_pool.lp_mint)]
    pub lp_mint: Box<InterfaceAccount<'info, Mint>>,
    #[account(address = amm_pool.token_a_mint)]
    pub token_a_mint: Box<InterfaceAccount<'info, Mint>>,
    #[account(address = amm_pool.token_b_mint)]
    pub token_b_mint: Box<InterfaceAccount<'info, Mint>>,
    #[account(mut, address = amm_pool.token_a_vault)]
    pub token_a_vault: InterfaceAccount<'info, TokenAccount>,
    #[account(mut, address = amm_pool.token_b_vault)]
    pub token_b_vault: InterfaceAccount<'info, TokenAccount>,
    #[account(mut, constraint = user_token_a_account.mint == amm_pool.token_a_mint, has_one = owner)]
    pub user_token_a_account: InterfaceAccount<'info, TokenAccount>,
    #[account(mut, constraint = user_token_b_account.mint == amm_pool.token_b_mint, has_one = owner)]
    pub user_token_b_account: InterfaceAccount<'info, TokenAccount>,
    #[account(
        init_if_needed,
        payer = owner,
        associated_token::mint = lp_mint,
        associated_token::authority = owner,
    )]
    pub user_lp_token_account: InterfaceAccount<'info, TokenAccount>,
    pub system_program: Program<'info, System>,
    pub token_a_program: Interface<'info, TokenInterface>,
    pub token_b_program: Interface<'info, TokenInterface>,
    pub token_program: Interface<'info, TokenInterface>,
    pub associated_token_program: Program<'info, AssociatedToken>,
}