use crate::curve::CurveCalculator;
use crate::curve::AMM;
use crate::curve::DEFAULT_TOKEN_RESERVES;
use crate::curve::DEFAULT_VIRTUAL_SOL_RESERVE;
use crate::curve::DEFUALT_INITIAL_VIRTUAL_TOKEN_RESERVE;
use crate::curve::DEFUALT_VIRTUAL_TOKEN_RESERVE;
use crate::error::ErrorCode;
use crate::states::*;
use crate::utils::*;
use anchor_lang::{
accounts::interface_account::InterfaceAccount,
prelude::*,
solana_program::clock,
system_program,
};
use anchor_spl::metadata::create_metadata_accounts_v3;
use anchor_spl::metadata::mpl_token_metadata::types::DataV2;
use anchor_spl::metadata::CreateMetadataAccountsV3;
use anchor_spl::metadata::Metadata;
use anchor_spl::{
associated_token::AssociatedToken,
token::Token,
token_2022::spl_token_2022,
token_interface::{Mint, TokenAccount, TokenInterface},
};
use solana_program::program::invoke_signed;
use spl_associated_token_account::create_associated_token_account;
use std::ops::Deref;
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub creator: Signer<'info>,
#[account(
seeds = [
AMM_CONFIG_SEED.as_bytes(),
&(0 as u64).to_be_bytes()
],
bump,
)]
pub amm_config: Box<Account<'info, AmmConfig>>,
#[account(
seeds = [
crate::AUTH_SEED.as_bytes(),
],
bump,
)]
pub authority: UncheckedAccount<'info>,
#[account(mut)]
pub pool_state: UncheckedAccount<'info>,
#[account(
constraint = token_0_mint.key() < token_1_mint.key(),
mint::token_program = token_0_program,
)]
pub token_0_mint: Box<InterfaceAccount<'info, Mint>>,
#[account(
mint::token_program = token_1_program,
)]
pub token_1_mint: Box<InterfaceAccount<'info, Mint>>,
#[account(
init,
seeds = [
POOL_LP_MINT_SEED.as_bytes(),
pool_state.key().as_ref(),
],
bump,
mint::decimals = 9,
mint::authority = authority,
payer = creator,
mint::token_program = token_program,
)]
pub lp_mint: Box<InterfaceAccount<'info, Mint>>,
#[account(
mut,
token::mint = token_0_mint,
token::authority = creator,
)]
pub creator_token_0: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
token::mint = token_1_mint,
token::authority = creator,
)]
pub creator_token_1: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut
// associated_token::mint = lp_mint,
// associated_token::authority = creator,
// token::token_program = token_program,
)]
pub creator_lp_token: UncheckedAccount<'info>,
#[account(
mut,
seeds = [
POOL_VAULT_SEED.as_bytes(),
pool_state.key().as_ref(),
token_0_mint.key().as_ref()
],
bump,
)]
pub token_0_vault: UncheckedAccount<'info>,
#[account(
mut,
seeds = [
POOL_VAULT_SEED.as_bytes(),
pool_state.key().as_ref(),
token_1_mint.key().as_ref()
],
bump,
)]
pub token_1_vault: UncheckedAccount<'info>,
#[account(
init,
seeds = [
OBSERVATION_SEED.as_bytes(),
pool_state.key().as_ref(),
],
bump,
payer = creator,
space = ObservationState::LEN
)]
pub observation_state: AccountLoader<'info, ObservationState>,
pub token_program: Program<'info, Token>,
pub token_0_program: Interface<'info, TokenInterface>,
pub token_1_program: Interface<'info, TokenInterface>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
}
pub fn initialize(
ctx: Context<Initialize>,
init_amount_0: u64,
init_amount_1: u64,
mut open_time: u64,
) -> Result<()> {
if !(is_supported_mint(&ctx.accounts.token_0_mint).unwrap()
&& is_supported_mint(&ctx.accounts.token_1_mint).unwrap())
{
return err!(ErrorCode::NotSupportMint);
}
if ctx.accounts.amm_config.disable_create_pool {
return err!(ErrorCode::NotApproved);
}
let block_timestamp = clock::Clock::get()?.unix_timestamp as u64;
if open_time <= block_timestamp {
open_time = block_timestamp + 1;
}
create_token_account(
&ctx.accounts.authority.to_account_info(),
&ctx.accounts.creator.to_account_info(),
&ctx.accounts.token_0_vault.to_account_info(),
&ctx.accounts.token_0_mint.to_account_info(),
&ctx.accounts.system_program.to_account_info(),
&ctx.accounts.token_0_program.to_account_info(),
&[&[
POOL_VAULT_SEED.as_bytes(),
ctx.accounts.pool_state.key().as_ref(),
ctx.accounts.token_0_mint.key().as_ref(),
&[ctx.bumps.token_0_vault][..],
][..]],
)?;
create_token_account(
&ctx.accounts.authority.to_account_info(),
&ctx.accounts.creator.to_account_info(),
&ctx.accounts.token_1_vault.to_account_info(),
&ctx.accounts.token_1_mint.to_account_info(),
&ctx.accounts.system_program.to_account_info(),
&ctx.accounts.token_1_program.to_account_info(),
&[&[
POOL_VAULT_SEED.as_bytes(),
ctx.accounts.pool_state.key().as_ref(),
ctx.accounts.token_1_mint.key().as_ref(),
&[ctx.bumps.token_1_vault][..],
][..]],
)?;
custom_create_associated_token_account(
&ctx.accounts.creator.to_account_info(),
&ctx.accounts.creator.to_account_info(),
&ctx.accounts.lp_mint.to_account_info(),
&ctx.accounts.creator_lp_token.to_account_info(),
&ctx.accounts.associated_token_program.to_account_info(),
&ctx.accounts.token_program.to_account_info(),
&ctx.accounts.system_program.to_account_info(),
)?;
let pool_state_loader = create_pool(
&ctx.accounts.creator.to_account_info(),
&ctx.accounts.pool_state.to_account_info(),
&ctx.accounts.amm_config.to_account_info(),
&ctx.accounts.token_0_mint.to_account_info(),
&ctx.accounts.token_1_mint.to_account_info(),
&ctx.accounts.system_program.to_account_info(),
)?;
let pool_state = &mut pool_state_loader.load_init()?;
let mut observation_state = ctx.accounts.observation_state.load_init()?;
observation_state.pool_id = ctx.accounts.pool_state.key();
let virtual_liquidty = U128::from(init_amount_0)
.checked_mul(U128::from(init_amount_1))
.unwrap()
.checked_mul(U128::from(U128::from(2).checked_pow(U128::from(32)).unwrap()))
.unwrap()
.as_u128();
let lp_supply = U128::from(init_amount_0)
.checked_mul(U128::from(init_amount_1))
.unwrap()
.integer_sqrt()
.as_u64();
let mut amm = AMM::new(
virtual_liquidty,
DEFUALT_VIRTUAL_TOKEN_RESERVE,
0,
DEFAULT_TOKEN_RESERVES,
DEFUALT_INITIAL_VIRTUAL_TOKEN_RESERVE,
);
pool_state.amm = amm;
transfer_from_user_to_pool_vault(
ctx.accounts.creator.to_account_info(),
ctx.accounts.creator_token_0.to_account_info(),
ctx.accounts.token_0_vault.to_account_info(),
ctx.accounts.token_0_mint.to_account_info(),
ctx.accounts.token_0_program.to_account_info(),
init_amount_0,
ctx.accounts.token_0_mint.decimals,
)?;
transfer_from_user_to_pool_vault(
ctx.accounts.creator.to_account_info(),
ctx.accounts.creator_token_1.to_account_info(),
ctx.accounts.token_1_vault.to_account_info(),
ctx.accounts.token_1_mint.to_account_info(),
ctx.accounts.token_1_program.to_account_info(),
init_amount_1,
ctx.accounts.token_1_mint.decimals,
)?;
let token_0_vault =
spl_token_2022::extension::StateWithExtensions::<spl_token_2022::state::Account>::unpack(
ctx.accounts
.token_0_vault
.to_account_info()
.try_borrow_data()?
.deref(),
)?
.base;
let token_1_vault =
spl_token_2022::extension::StateWithExtensions::<spl_token_2022::state::Account>::unpack(
ctx.accounts
.token_1_vault
.to_account_info()
.try_borrow_data()?
.deref(),
)?
.base;
CurveCalculator::validate_supply(token_0_vault.amount, token_1_vault.amount)?;
let to_mint = U128::from(init_amount_0)
.checked_mul(U128::from(init_amount_1))
.unwrap()
.checked_mul(U128::from(1u128<<32))
.unwrap()
.integer_sqrt()
.as_u64();
let lock_lp_amount = 1000000000;
msg!(
"we were here"
);
token::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.creator_lp_token.to_account_info(),
to_mint
.checked_sub(lock_lp_amount)
.ok_or(ErrorCode::InitLpAmountTooLess)?,
&[&[crate::AUTH_SEED.as_bytes(), &[ctx.bumps.authority]]],
)?;
pool_state.initialize(
ctx.bumps.authority,
lp_supply,
open_time,
ctx.accounts.creator.key(),
ctx.accounts.amm_config.key(),
ctx.accounts.token_0_vault.key(),
ctx.accounts.token_1_vault.key(),
&ctx.accounts.token_0_mint,
&ctx.accounts.token_1_mint,
&ctx.accounts.lp_mint,
ctx.accounts.observation_state.key(),
);
Ok(())
}
pub fn create_pool<'info>(
payer: &AccountInfo<'info>,
pool_account_info: &AccountInfo<'info>,
amm_config: &AccountInfo<'info>,
token_0_mint: &AccountInfo<'info>,
token_1_mint: &AccountInfo<'info>,
system_program: &AccountInfo<'info>,
) -> Result<AccountLoad<'info, PoolState>> {
if pool_account_info.owner != &system_program::ID || pool_account_info.lamports() != 0 {
return err!(ErrorCode::NotApproved);
}
let (expect_pda_address, bump) = Pubkey::find_program_address(
&[
POOL_SEED.as_bytes(),
amm_config.key().as_ref(),
token_0_mint.key().as_ref(),
token_1_mint.key().as_ref(),
],
&crate::id(),
);
if pool_account_info.key() != expect_pda_address {
require_eq!(pool_account_info.is_signer, true);
}
let cpi_accounts = anchor_lang::system_program::CreateAccount {
from: payer.clone(),
to: pool_account_info.clone(),
};
let cpi_context = CpiContext::new(system_program.to_account_info(), cpi_accounts);
anchor_lang::system_program::create_account(
cpi_context.with_signer(&[&[
POOL_SEED.as_bytes(),
amm_config.key().as_ref(),
token_0_mint.key().as_ref(),
token_1_mint.key().as_ref(),
&[bump],
][..]]),
Rent::get()?.minimum_balance(PoolState::LEN),
PoolState::LEN as u64,
&crate::id(),
)?;
Ok(AccountLoad::<PoolState>::try_from_unchecked(
&crate::id(),
&pool_account_info,
)?)
}