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,
)?;
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>,
}