use crate::curve::CurveCalculator;
use crate::curve::RoundDirection;
use crate::error::ErrorCode;
use crate::states::*;
use crate::utils::token::*;
use anchor_lang::prelude::*;
use anchor_spl::token::Token;
use anchor_spl::token_interface::{Mint, Token2022, TokenAccount};
#[derive(Accounts)]
pub struct Deposit<'info> {
pub owner: Signer<'info>,
#[account(
seeds = [
crate::AUTH_SEED.as_bytes(),
],
bump,
)]
pub authority: UncheckedAccount<'info>,
#[account(mut)]
pub pool_state: AccountLoader<'info, PoolState>,
#[account(mut, token::authority = owner)]
pub owner_lp_token: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
token::mint = token_0_vault.mint,
token::authority = owner
)]
pub token_0_account: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
token::mint = token_1_vault.mint,
token::authority = owner
)]
pub token_1_account: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
constraint = token_0_vault.key() == pool_state.load()?.token_0_vault
)]
pub token_0_vault: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
constraint = token_1_vault.key() == pool_state.load()?.token_1_vault
)]
pub token_1_vault: Box<InterfaceAccount<'info, TokenAccount>>,
pub token_program: Program<'info, Token>,
pub token_program_2022: Program<'info, Token2022>,
#[account(
address = token_0_vault.mint
)]
pub vault_0_mint: Box<InterfaceAccount<'info, Mint>>,
#[account(
address = token_1_vault.mint
)]
pub vault_1_mint: Box<InterfaceAccount<'info, Mint>>,
#[account(
mut,
address = pool_state.load()?.lp_mint @ ErrorCode::IncorrectLpMint)
]
pub lp_mint: Box<InterfaceAccount<'info, Mint>>,
}
pub fn deposit(
ctx: Context<Deposit>,
lp_token_amount: u64,
maximum_token_0_amount: u64,
maximum_token_1_amount: u64,
) -> Result<()> {
let pool_id = ctx.accounts.pool_state.key();
let pool_state = &mut ctx.accounts.pool_state.load_mut()?;
let mut amm = pool_state.amm;
if !pool_state.get_status_by_bit(PoolStatusBitIndex::Deposit) {
return err!(ErrorCode::NotApproved);
}
let (total_token_0_amount, total_token_1_amount) = pool_state.vault_amount_without_fee(
ctx.accounts.token_0_vault.amount,
ctx.accounts.token_1_vault.amount,
);
let results = CurveCalculator::lp_tokens_to_trading_tokens(
u128::from(lp_token_amount),
u128::from(pool_state.lp_supply),
u128::from(total_token_0_amount),
u128::from(total_token_1_amount),
RoundDirection::Ceiling,
)
.ok_or(ErrorCode::ZeroTradingTokens)?;
let token_0_amount = u64::try_from(results.token_0_amount).unwrap();
let (transfer_token_0_amount, transfer_token_0_fee) = {
let transfer_fee =
get_transfer_inverse_fee(&ctx.accounts.vault_0_mint.to_account_info(), token_0_amount)?;
(
token_0_amount.checked_add(transfer_fee).unwrap(),
transfer_fee,
)
};
let token_1_amount = u64::try_from(results.token_1_amount).unwrap();
let (transfer_token_1_amount, transfer_token_1_fee) = {
let transfer_fee =
get_transfer_inverse_fee(&ctx.accounts.vault_1_mint.to_account_info(), token_1_amount)?;
(
token_1_amount.checked_add(transfer_fee).unwrap(),
transfer_fee,
)
};
let real_liquidity = pool_state.lp_supply.checked_add(lp_token_amount).unwrap();
let new_supply = amm.apply_buy(real_liquidity.into());
if new_supply.is_none() {
return err!(ErrorCode::BuyResultNone);
}
let new_supply = new_supply.unwrap();
let current_supply = ctx.accounts.lp_mint.supply;
let mint_to = (new_supply as u64).checked_sub(current_supply).unwrap();
pool_state.amm = amm;
#[cfg(feature = "enable-log")]
msg!(
"results.token_0_amount;{}, results.token_1_amount:{},transfer_token_0_amount:{},transfer_token_0_fee:{},
transfer_token_1_amount:{},transfer_token_1_fee:{}",
results.token_0_amount,
results.token_1_amount,
transfer_token_0_amount,
transfer_token_0_fee,
transfer_token_1_amount,
transfer_token_1_fee
);
emit!(LpChangeEvent {
pool_id,
lp_amount_before: pool_state.lp_supply,
token_0_vault_before: total_token_0_amount,
token_1_vault_before: total_token_1_amount,
token_0_amount,
token_1_amount,
token_0_transfer_fee: transfer_token_0_fee,
token_1_transfer_fee: transfer_token_1_fee,
change_type: 0
});
if transfer_token_0_amount > maximum_token_0_amount
|| transfer_token_1_amount > maximum_token_1_amount
{
return Err(ErrorCode::ExceededSlippage.into());
}
transfer_from_user_to_pool_vault(
ctx.accounts.owner.to_account_info(),
ctx.accounts.token_0_account.to_account_info(),
ctx.accounts.token_0_vault.to_account_info(),
ctx.accounts.vault_0_mint.to_account_info(),
if ctx.accounts.vault_0_mint.to_account_info().owner == ctx.accounts.token_program.key {
ctx.accounts.token_program.to_account_info()
} else {
ctx.accounts.token_program_2022.to_account_info()
},
transfer_token_0_amount,
ctx.accounts.vault_0_mint.decimals,
)?;
transfer_from_user_to_pool_vault(
ctx.accounts.owner.to_account_info(),
ctx.accounts.token_1_account.to_account_info(),
ctx.accounts.token_1_vault.to_account_info(),
ctx.accounts.vault_1_mint.to_account_info(),
if ctx.accounts.vault_1_mint.to_account_info().owner == ctx.accounts.token_program.key {
ctx.accounts.token_program.to_account_info()
} else {
ctx.accounts.token_program_2022.to_account_info()
},
transfer_token_1_amount,
ctx.accounts.vault_1_mint.decimals,
)?;
pool_state.lp_supply = pool_state.lp_supply.checked_add(lp_token_amount).unwrap();
token_mint_to(
ctx.accounts.authority.to_account_info(),
ctx.accounts.token_program.to_account_info(),
ctx.accounts.lp_mint.to_account_info(),
ctx.accounts.owner_lp_token.to_account_info(),
lp_token_amount,
&[&[crate::AUTH_SEED.as_bytes(), &[pool_state.auth_bump]]],
)?;
pool_state.recent_epoch = Clock::get()?.epoch;
Ok(())
}