Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00035.parquet:48326

e5224259a15916a5d8e317f1
turn 1/26gpt-4o-mini-2024-07-18EnglishUnited States194 words
degenerate_repetitionAbsentFinal dense release
USER
hello we were working on snyching my project up all the files to work as a unit can u help me finish up and tell me what files would aid u in this process to keep it going
ASSISTANT
Hello! I'd be happy to help you with your project synchronization. To assist you effectively, it would be beneficial to know the following:

1. **Project Type**: What kind of project is it (e.g., web application, mobile app, desktop software, etc.)?
  
2. **Programming Language**: Which programming languages or frameworks are you using?

3. **File Structure**: Could you provide an overview of your project's folder structure and which files you have so far?

4. **Version Control**: Are you using any version control system (like Git)? If so, are you considering syncing it with a remote repository?

5. **Dependencies**: Are there any dependencies or libraries that the project needs?

6. **Specific Issues**: Are there any particular problems or challenges you are facing with synchronization?

7. **Environment**: What operating system or development environment are you using?

Once I have this information, I can guide you on the files you might need and the best practices to sync your project effectively.

turns-00035.parquet:48327

9d3a58d3631d9b415ee6d7d9
turn 2/26gpt-4o-mini-2024-07-18EnglishUnited States17185 words
degenerate_repetitionAbsentFinal dense release
USER
//! Program state processor
#![allow(deprecated)]
use crate::{
    error::AmmError,
    instruction::{
        AdminCancelOrdersInstruction, AmmInstruction, ConfigArgs, DepositInstruction,
        InitializeInstruction2, MonitorStepInstruction, SetParamsInstruction, SimulateInstruction,
        SwapInstructionBaseIn, SwapInstructionBaseOut, WithdrawInstruction, WithdrawSrmInstruction,
    },
    invokers::Invokers,
    math::{
        Calculator, CheckedCeilDiv, InvariantPool, InvariantToken, RoundDirection, SwapDirection,
        U128, U256,
    },
    state::{
        AmmConfig, AmmInfo, AmmParams, AmmResetFlag, AmmState, AmmStatus, GetPoolData,
        GetSwapBaseInData, GetSwapBaseOutData, Loadable, RunCrankData, SimulateParams,
        TargetOrders, MAX_ORDER_LIMIT, TEN_THOUSAND,
    },
};

use serum_dex::{
    critbit::{LeafNode, Slab, SlabView},
    matching::{OrderType, Side},
    state::{Market, MarketState, OpenOrders, ToAlignedBytes},
};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    clock::Clock,
    entrypoint::ProgramResult,
    msg,
    program::{invoke, invoke_signed},
    // log::sol_log_compute_units,
    program_error::ProgramError,
    program_option::COption,
    program_pack::Pack,
    pubkey::Pubkey,
    rent::Rent,
    system_instruction,
    sysvar::{clock, Sysvar},
};

use super::log::*;
use arrayref::{array_ref, array_refs};
use arrform::{arrform, ArrForm};
use bytemuck::from_bytes_mut;
use std::{
    cell::RefMut, collections::VecDeque, convert::identity, convert::TryFrom, mem::size_of,
    num::NonZeroU64, ops::Deref,
};

pub mod srm_token {
    solana_program::declare_id!("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt");
}

pub mod msrm_token {
    solana_program::declare_id!("MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L");
}

#[cfg(feature = "localnet")]
pub mod config_feature {
    pub mod amm_owner {
        solana_program::declare_id!("75KWb5XcqPTgacQyNw9P5QU2HL3xpezEVcgsFCiJgTT");
    }
    pub mod openbook_program {
        solana_program::declare_id!("kGeitTdTHT1WdpUScdm8yxUAirZwbnQtqrpzvAm1p98");
    }
    pub mod referrer_pc_wallet {
        solana_program::declare_id!("75KWb5XcqPTgacQyNw9P5QU2HL3xpezEVcgsFCiJgTT");
    }
    pub mod create_pool_fee_address {
        solana_program::declare_id!("75KWb5XcqPTgacQyNw9P5QU2HL3xpezEVcgsFCiJgTT");
    }
}
#[cfg(feature = "devnet")]
pub mod config_feature {
    pub mod amm_owner {
        solana_program::declare_id!("Adm29NctkKwJGaaiU8CXqdV6WDTwR81JbxV8zoxn745Y");
    }
    pub mod openbook_program {
        solana_program::declare_id!("EoTcMgcDRTJVZDMZWBoU6rhYHZfkNTVEAfz3uUJRcYGj");
    }
    pub mod referrer_pc_wallet {
        solana_program::declare_id!("4NpMfWThvJQsV9VLjUXXpn3tPv1zoQpib8wCBDc1EBzD");
    }
    pub mod create_pool_fee_address {
        solana_program::declare_id!("3XMrhbv989VxAMi3DErLV9eJht1pHppW5LbKxe9fkEFR");
    }
}
#[cfg(not(any(feature = "localnet", feature = "devnet")))]
pub mod config_feature {
    pub mod amm_owner {
        solana_program::declare_id!("GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ");
    }
    pub mod openbook_program {
        solana_program::declare_id!("srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX");
    }
    pub mod referrer_pc_wallet {
        solana_program::declare_id!("FCxGKqGSVeV1d3WsmAXt45A5iQdCS6kKCeJy3EUBigMG");
    }
    pub mod create_pool_fee_address {
        solana_program::declare_id!("7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5");
    }
}

/// Suffix for amm authority seed
pub const AUTHORITY_AMM: &'static [u8] = b"amm authority";
/// Suffix for amm associated seed
pub const AMM_ASSOCIATED_SEED: &'static [u8] = b"amm_associated_seed";
/// Suffix for target associated seed
pub const TARGET_ASSOCIATED_SEED: &'static [u8] = b"target_associated_seed";
/// Suffix for amm open order associated seed
pub const OPEN_ORDER_ASSOCIATED_SEED: &'static [u8] = b"open_order_associated_seed";
/// Suffix for coin vault associated seed
pub const COIN_VAULT_ASSOCIATED_SEED: &'static [u8] = b"coin_vault_associated_seed";
/// Suffix for pc vault associated seed
pub const PC_VAULT_ASSOCIATED_SEED: &'static [u8] = b"pc_vault_associated_seed";
/// Suffix for lp mint associated seed
pub const LP_MINT_ASSOCIATED_SEED: &'static [u8] = b"lp_mint_associated_seed";
/// Amm config seed
pub const AMM_CONFIG_SEED: &'static [u8] = b"amm_config_account_seed";

pub fn get_associated_address_and_bump_seed(
    info_id: &Pubkey,
    market_address: &Pubkey,
    associated_seed: &[u8],
    program_id: &Pubkey,
) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[
            &info_id.to_bytes(),
            &market_address.to_bytes(),
            &associated_seed,
        ],
        program_id,
    )
}

/// Program state handler.
pub struct Processor {}
impl Processor {
    #[inline]
    fn check_account_readonly(account_info: &AccountInfo) -> ProgramResult {
        if account_info.is_writable {
            return Err(AmmError::AccountNeedReadOnly.into());
        }
        return Ok(());
    }

    /// Unpacks a spl_token `Account`.
    #[inline]
    pub fn unpack_token_account(
        account_info: &AccountInfo,
        token_program_id: &Pubkey,
    ) -> Result<spl_token::state::Account, AmmError> {
        if account_info.owner != token_program_id {
            Err(AmmError::InvalidSplTokenProgram)
        } else {
            spl_token::state::Account::unpack(&account_info.data.borrow())
                .map_err(|_| AmmError::ExpectedAccount)
        }
    }

    /// Unpacks a spl_token `Mint`.
    #[inline]
    pub fn unpack_mint(
        account_info: &AccountInfo,
        token_program_id: &Pubkey,
    ) -> Result<spl_token::state::Mint, AmmError> {
        if account_info.owner != token_program_id {
            Err(AmmError::InvalidSplTokenProgram)
        } else {
            spl_token::state::Mint::unpack(&account_info.data.borrow())
                .map_err(|_| AmmError::ExpectedMint)
        }
    }

    fn load_orders<'a>(
        orders_account: &'a AccountInfo,
    ) -> Result<RefMut<'a, OpenOrders>, ProgramError> {
        let (_, data) = serum_dex::state::strip_header::<[u8; 0], u8>(orders_account, false)?;
        let open_orders: RefMut<'a, OpenOrders>;
        open_orders = RefMut::map(data, |data| from_bytes_mut(data));
        Ok(open_orders)
    }

    pub fn load_serum_market_order<'a>(
        market_acc: &AccountInfo<'a>,
        open_orders_acc: &AccountInfo<'a>,
        authority_acc: &AccountInfo<'a>,
        amm: &AmmInfo,
        // Allow for the market flag to be set to AccountFlag::Disabled
        allow_disabled: bool,
    ) -> Result<(Box<MarketState>, Box<OpenOrders>), ProgramError> {
        let market_state = Market::load(market_acc, &amm.market_program, allow_disabled)?;
        let open_orders = market_state.load_orders_mut(
            open_orders_acc,
            Some(authority_acc),
            &amm.market_program,
            None,
            None,
        )?;
        if identity(open_orders.market) != market_acc.key.to_aligned_bytes() {
            return Err(AmmError::InvalidMarket.into());
        }
        if identity(open_orders.owner) != authority_acc.key.to_aligned_bytes() {
            return Err(AmmError::InvalidOwner.into());
        }
        if *open_orders_acc.key != amm.open_orders {
            return Err(AmmError::InvalidOpenOrders.into());
        }
        return Ok((
            Box::new(*market_state.deref()),
            Box::new(*open_orders.deref()),
        ));
    }

    fn _get_dex_best_price(slab: &RefMut<serum_dex::critbit::Slab>, side: Side) -> Option<u64> {
        if slab.is_empty() {
            None
        } else {
            match side {
                Side::Bid => {
                    let best_bid_h = slab.find_max().unwrap();
                    let best_bid_px = slab
                        .get(best_bid_h)
                        .unwrap()
                        .as_leaf()
                        .unwrap()
                        .price()
                        .get();
                    Some(best_bid_px)
                }
                Side::Ask => {
                    let best_ask_h = slab.find_min().unwrap();
                    let best_ask_px = slab
                        .get(best_ask_h)
                        .unwrap()
                        .as_leaf()
                        .unwrap()
                        .price()
                        .get();
                    Some(best_ask_px)
                }
            }
        }
    }

    fn get_amm_orders(
        open_orders: &OpenOrders,
        bids: RefMut<Slab>,
        asks: RefMut<Slab>,
    ) -> Result<(Vec<LeafNode>, Vec<LeafNode>), ProgramError> {
        let orders_number = open_orders.free_slot_bits.count_zeros();
        let mut bids_orders: Vec<LeafNode> = Vec::new();
        let mut asks_orders: Vec<LeafNode> = Vec::new();
        if orders_number != 0 {
            for i in 0..128 {
                let slot_mask = 1u128 << i;
                if open_orders.free_slot_bits & slot_mask != 0 {
                    // means slot is free
                    continue;
                }
                if open_orders.is_bid_bits & slot_mask != 0 {
                    match bids.find_by_key(open_orders.orders[i]) {
                        None => continue,
                        Some(handle_bid) => {
                            let handle_bid_ref = bids.get(handle_bid).unwrap().as_leaf().unwrap();
                            bids_orders.push(*handle_bid_ref);
                        }
                    }
                } else {
                    match asks.find_by_key(open_orders.orders[i]) {
                        None => continue,
                        Some(handle_ask) => {
                            let handle_ask_ref = asks.get(handle_ask).unwrap().as_leaf().unwrap();
                            asks_orders.push(*handle_ask_ref);
                        }
                    }
                };
            }
        }
        bids_orders.sort_by(|a, b| b.price().get().cmp(&a.price().get()));
        asks_orders.sort_by(|a, b| a.price().get().cmp(&b.price().get()));
        Ok((bids_orders, asks_orders))
    }

    pub fn get_amm_best_price(
        market_state: &MarketState,
        open_orders: &OpenOrders,
        bids_account: &AccountInfo,
        asks_account: &AccountInfo,
    ) -> Result<(Option<u64>, Option<u64>), ProgramError> {
        let bids = market_state.load_bids_mut(&bids_account)?;
        let asks = market_state.load_asks_mut(&asks_account)?;
        let (bids_orders, asks_orders) = Self::get_amm_orders(open_orders, bids, asks)?;
        let mut bid_best_price = None;
        let mut ask_best_price = None;
        if bids_orders.len() != 0 {
            bid_best_price = Some(bids_orders.first().unwrap().price().get());
        }
        if asks_orders.len() != 0 {
            ask_best_price = Some(asks_orders.first().unwrap().price().get());
        }
        Ok((bid_best_price, ask_best_price))
    }

    pub fn get_amm_worst_price(
        market_state: &MarketState,
        open_orders: &OpenOrders,
        bids_account: &AccountInfo,
        asks_account: &AccountInfo,
    ) -> Result<(Option<u64>, Option<u64>), ProgramError> {
        let bids = market_state.load_bids_mut(&bids_account)?;
        let asks = market_state.load_asks_mut(&asks_account)?;
        let (bids_orders, asks_orders) = Self::get_amm_orders(open_orders, bids, asks)?;
        let mut bid_best_price = None;
        let mut ask_best_price = None;
        if bids_orders.len() != 0 {
            bid_best_price = Some(bids_orders.last().unwrap().price().get());
        }
        if asks_orders.len() != 0 {
            ask_best_price = Some(asks_orders.last().unwrap().price().get());
        }
        Ok((bid_best_price, ask_best_price))
    }

    /// The Detailed calculation of pnl
    /// 1. calc last_k witch dose not take pnl: last_k = calc_pnl_x * calc_pnl_y;
    /// 2. calc current price: current_price = current_x / current_y;
    /// 3. calc x after take pnl: x_after_take_pnl = sqrt(last_k * current_price);
    /// 4. calc y after take pnl: y_after_take_pnl = x_after_take_pnl / current_price;
    ///                           y_after_take_pnl = x_after_take_pnl * current_y / current_x;
    /// 5. calc pnl_x & pnl_y:  pnl_x = current_x - x_after_take_pnl;
    ///                         pnl_y = current_y - y_after_take_pnl;
    pub fn calc_take_pnl(
        target: &TargetOrders,
        amm: &mut AmmInfo,
        total_pc_without_take_pnl: &mut u64,
        total_coin_without_take_pnl: &mut u64,
        x1: U256,
        y1: U256,
    ) -> Result<(u128, u128), ProgramError> {
        // calc pnl
        let mut delta_x: u128;
        let mut delta_y: u128;
        let calc_pc_amount = Calculator::restore_decimal(
            target.calc_pnl_x.into(),
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let calc_coin_amount = Calculator::restore_decimal(
            target.calc_pnl_y.into(),
            amm.coin_decimals,
            amm.sys_decimal_value,
        );
        let pool_pc_amount = U128::from(*total_pc_without_take_pnl);
        let pool_coin_amount = U128::from(*total_coin_without_take_pnl);
        if pool_pc_amount.checked_mul(pool_coin_amount).unwrap()
            >= (calc_pc_amount).checked_mul(calc_coin_amount).unwrap()
        {
            // last k is
            // let last_k: u128 = (target.calc_pnl_x as u128).checked_mul(target.calc_pnl_y as u128).unwrap();
            // current k is
            // let current_k: u128 = (x1 as u128).checked_mul(y1 as u128).unwrap();
            // current p is
            // let current_p: u128 = (x1 as u128).checked_div(y1 as u128).unwrap();
            let x2_power = Calculator::calc_x_power(
                target.calc_pnl_x.into(),
                target.calc_pnl_y.into(),
                x1,
                y1,
            );
            // let x2 = Calculator::sqrt(x2_power).unwrap();
            let x2 = x2_power.integer_sqrt();
            // msg!(arrform!(LOG_SIZE, "calc_take_pnl x2_power:{}, x2:{}", x2_power, x2).as_str());
            let y2 = x2.checked_mul(y1).unwrap().checked_div(x1).unwrap();
            // msg!(arrform!(LOG_SIZE, "calc_take_pnl y2:{}", y2).as_str());

            // transfer to token_coin_pnl and token_pc_pnl
            // (x1 -x2) * pnl / sys_decimal_value
            let diff_x = U128::from(x1.checked_sub(x2).unwrap().as_u128());
            let diff_y = U128::from(y1.checked_sub(y2).unwrap().as_u128());
            delta_x = diff_x
                .checked_mul(amm.fees.pnl_numerator.into())
                .unwrap()
                .checked_div(amm.fees.pnl_denominator.into())
                .unwrap()
                .as_u128();
            delta_y = diff_y
                .checked_mul(amm.fees.pnl_numerator.into())
                .unwrap()
                .checked_div(amm.fees.pnl_denominator.into())
                .unwrap()
                .as_u128();

            let diff_pc_pnl_amount =
                Calculator::restore_decimal(diff_x, amm.pc_decimals, amm.sys_decimal_value);
            let diff_coin_pnl_amount =
                Calculator::restore_decimal(diff_y, amm.coin_decimals, amm.sys_decimal_value);
            let pc_pnl_amount = diff_pc_pnl_amount
                .checked_mul(amm.fees.pnl_numerator.into())
                .unwrap()
                .checked_div(amm.fees.pnl_denominator.into())
                .unwrap()
                .as_u64();
            let coin_pnl_amount = diff_coin_pnl_amount
                .checked_mul(amm.fees.pnl_numerator.into())
                .unwrap()
                .checked_div(amm.fees.pnl_denominator.into())
                .unwrap()
                .as_u64();
            if pc_pnl_amount != 0 && coin_pnl_amount != 0 {
                // step2: save total_pnl_pc & total_pnl_coin
                amm.state_data.total_pnl_pc = amm
                    .state_data
                    .total_pnl_pc
                    .checked_add(diff_pc_pnl_amount.as_u64())
                    .unwrap();
                amm.state_data.total_pnl_coin = amm
                    .state_data
                    .total_pnl_coin
                    .checked_add(diff_coin_pnl_amount.as_u64())
                    .unwrap();
                amm.state_data.need_take_pnl_pc = amm
                    .state_data
                    .need_take_pnl_pc
                    .checked_add(pc_pnl_amount)
                    .unwrap();
                amm.state_data.need_take_pnl_coin = amm
                    .state_data
                    .need_take_pnl_coin
                    .checked_add(coin_pnl_amount)
                    .unwrap();

                // step3: update total_coin and total_pc without pnl
                *total_pc_without_take_pnl = (*total_pc_without_take_pnl)
                    .checked_sub(pc_pnl_amount)
                    .unwrap();
                *total_coin_without_take_pnl = (*total_coin_without_take_pnl)
                    .checked_sub(coin_pnl_amount)
                    .unwrap();
            } else {
                delta_x = 0;
                delta_y = 0;
            }
        } else {
            msg!(arrform!(
                LOG_SIZE,
                "calc_take_pnl error x:{}, y:{}, calc_pnl_x:{}, calc_pnl_y:{}",
                x1,
                y1,
                target.calc_pnl_x,
                target.calc_pnl_y
            )
            .as_str());
            return Err(AmmError::CalcPnlError.into());
        }

        Ok((delta_x, delta_y))
    }

    /// Calculates the authority id by generating a program address.
    pub fn authority_id(
        program_id: &Pubkey,
        amm_seed: &[u8],
        nonce: u8,
    ) -> Result<Pubkey, AmmError> {
        Pubkey::create_program_address(&[amm_seed, &[nonce]], program_id)
            .map_err(|_| AmmError::InvalidProgramAddress.into())
    }

    #[allow(clippy::too_many_arguments)]
    fn check_accounts(
        program_id: &Pubkey,
        amm: &AmmInfo,
        amm_info: &AccountInfo,
        token_program_info: &AccountInfo,
        clock_info: &AccountInfo,
        market_program_info: &AccountInfo,
        amm_authority_info: &AccountInfo,
        market_info: &AccountInfo,
        amm_open_orders_info: &AccountInfo,
        amm_coin_vault_info: &AccountInfo,
        amm_pc_vault_info: &AccountInfo,
        amm_target_orders_info: &AccountInfo,
        srm_token_info: Option<&AccountInfo>,
        referrer_pc_info: Option<&AccountInfo>,
    ) -> ProgramResult {
        if amm.status == AmmStatus::Uninitialized.into_u64() {
            return Err(AmmError::InvalidStatus.into());
        }
        if *amm_authority_info.key
            != Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?
        {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        check_assert_eq!(
            *amm_info.owner,
            *program_id,
            "amm_owner",
            AmmError::InvalidOwner
        );
        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let token_program_id = token_program_info.key;
        check_assert_eq!(
            *clock_info.key,
            clock::id(),
            "clock",
            AmmError::InvalidProgramAddress
        );
        check_assert_eq!(
            *market_program_info.key,
            amm.market_program,
            "market_program",
            AmmError::InvalidMarketProgram
        );
        check_assert_eq!(
            *market_info.key,
            amm.market,
            "market_info",
            AmmError::InvalidMarket
        );
        check_assert_eq!(
            *amm_open_orders_info.key,
            amm.open_orders,
            "open_order",
            AmmError::InvalidOpenOrders
        );
        check_assert_eq!(
            *amm_coin_vault_info.key,
            amm.coin_vault,
            "coin_vault",
            AmmError::InvalidCoinVault
        );
        check_assert_eq!(
            *amm_pc_vault_info.key,
            amm.pc_vault,
            "pc_vault",
            AmmError::InvalidPCVault
        );
        check_assert_eq!(
            *amm_target_orders_info.key,
            amm.target_orders,
            "target_orders",
            AmmError::InvalidTargetOrders
        );
        if let Some(srm_token_account) = srm_token_info {
            let srm_token = Self::unpack_token_account(&srm_token_account, token_program_id)?;
            check_assert_eq!(
                srm_token.owner,
                *amm_authority_info.key,
                "srm_token_owner",
                AmmError::InvalidOwner
            );
            if srm_token.mint != srm_token::id() && srm_token.mint != msrm_token::id() {
                return Err(AmmError::InvalidSrmMint.into());
            }
        }
        if let Some(referrer_pc_account) = referrer_pc_info {
            let referrer_pc_token =
                Self::unpack_token_account(&referrer_pc_account, token_program_id)?;
            check_assert_eq!(
                referrer_pc_token.owner,
                config_feature::referrer_pc_wallet::id(),
                "referrer_pc_owner",
                AmmError::InvalidOwner
            );
            check_assert_eq!(
                referrer_pc_token.mint,
                amm.pc_vault_mint,
                "referrer_pc",
                AmmError::InvalidReferPCMint
            );
        }
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    fn generate_amm_associated_spl_token<'a, 'b: 'a>(
        program_id: &Pubkey,
        spl_token_program_id: &Pubkey,
        market_account: &'a AccountInfo<'b>,
        associated_token_account: &'a AccountInfo<'b>,
        token_mint_account: &'a AccountInfo<'b>,
        user_wallet_account: &'a AccountInfo<'b>,
        system_program_account: &'a AccountInfo<'b>,
        rent_sysvar_account: &'a AccountInfo<'b>,
        spl_token_program_account: &'a AccountInfo<'b>,
        associated_owner_account: &'a AccountInfo<'b>,
        associated_seed: &[u8],
    ) -> ProgramResult {
        let (associated_token_address, bump_seed) = get_associated_address_and_bump_seed(
            program_id,
            &market_account.key,
            associated_seed,
            program_id,
        );
        if associated_token_address != *associated_token_account.key {
            msg!("Error: Associated token address does not match seed derivation");
            return Err(AmmError::ExpectedAccount.into());
        }
        if associated_token_account.owner == system_program_account.key {
            let associated_account_signer_seeds: &[&[_]] = &[
                &program_id.to_bytes(),
                &market_account.key.to_bytes(),
                associated_seed,
                &[bump_seed],
            ];
            let rent = &Rent::from_account_info(rent_sysvar_account)?;
            let required_lamports = rent
                .minimum_balance(spl_token::state::Account::LEN)
                .max(1)
                .saturating_sub(associated_token_account.lamports());
            if required_lamports > 0 {
                invoke(
                    &system_instruction::transfer(
                        user_wallet_account.key,
                        associated_token_account.key,
                        required_lamports,
                    ),
                    &[
                        user_wallet_account.clone(),
                        associated_token_account.clone(),
                        system_program_account.clone(),
                    ],
                )?;
            }
            invoke_signed(
                &system_instruction::allocate(
                    associated_token_account.key,
                    spl_token::state::Account::LEN as u64,
                ),
                &[
                    associated_token_account.clone(),
                    system_program_account.clone(),
                ],
                &[&associated_account_signer_seeds],
            )?;
            invoke_signed(
                &system_instruction::assign(associated_token_account.key, spl_token_program_id),
                &[
                    associated_token_account.clone(),
                    system_program_account.clone(),
                ],
                &[&associated_account_signer_seeds],
            )?;

            invoke(
                &spl_token::instruction::initialize_account(
                    spl_token_program_id,
                    associated_token_account.key,
                    token_mint_account.key,
                    associated_owner_account.key,
                )?,
                &[
                    associated_token_account.clone(),
                    token_mint_account.clone(),
                    associated_owner_account.clone(),
                    rent_sysvar_account.clone(),
                    spl_token_program_account.clone(),
                ],
            )?;
        } else {
            associated_token_address.log();
            return Err(AmmError::RepeatCreateAmm.into());
        }
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    fn generate_amm_associated_spl_mint<'a, 'b: 'a>(
        program_id: &Pubkey,
        spl_token_program_id: &Pubkey,
        market_account: &'a AccountInfo<'b>,
        associated_token_account: &'a AccountInfo<'b>,
        user_wallet_account: &'a AccountInfo<'b>,
        system_program_account: &'a AccountInfo<'b>,
        rent_sysvar_account: &'a AccountInfo<'b>,
        spl_token_program_account: &'a AccountInfo<'b>,
        associated_owner_account: &'a AccountInfo<'b>,
        associated_seed: &[u8],
        mint_decimals: u8,
    ) -> ProgramResult {
        let (associated_token_address, bump_seed) = get_associated_address_and_bump_seed(
            program_id,
            &market_account.key,
            associated_seed,
            program_id,
        );
        if associated_token_address != *associated_token_account.key {
            msg!("Error: Associated mint address does not match seed derivation");
            return Err(AmmError::ExpectedMint.into());
        }
        if associated_token_account.owner == system_program_account.key {
            let associated_account_signer_seeds: &[&[_]] = &[
                &program_id.to_bytes(),
                &market_account.key.to_bytes(),
                associated_seed,
                &[bump_seed],
            ];
            let rent = &Rent::from_account_info(rent_sysvar_account)?;
            let required_lamports = rent
                .minimum_balance(spl_token::state::Mint::LEN)
                .max(1)
                .saturating_sub(associated_token_account.lamports());
            if required_lamports > 0 {
                invoke(
                    &system_instruction::transfer(
                        user_wallet_account.key,
                        associated_token_account.key,
                        required_lamports,
                    ),
                    &[
                        user_wallet_account.clone(),
                        associated_token_account.clone(),
                        system_program_account.clone(),
                    ],
                )?;
            }
            invoke_signed(
                &system_instruction::allocate(
                    associated_token_account.key,
                    spl_token::state::Mint::LEN as u64,
                ),
                &[
                    associated_token_account.clone(),
                    system_program_account.clone(),
                ],
                &[&associated_account_signer_seeds],
            )?;
            invoke_signed(
                &system_instruction::assign(associated_token_account.key, spl_token_program_id),
                &[
                    associated_token_account.clone(),
                    system_program_account.clone(),
                ],
                &[&associated_account_signer_seeds],
            )?;

            invoke(
                &spl_token::instruction::initialize_mint(
                    spl_token_program_id,
                    associated_token_account.key,
                    associated_owner_account.key,
                    None,
                    mint_decimals,
                )?,
                &[
                    associated_token_account.clone(),
                    associated_owner_account.clone(),
                    rent_sysvar_account.clone(),
                    spl_token_program_account.clone(),
                ],
            )?;
        } else {
            associated_token_address.log();
            return Err(AmmError::RepeatCreateAmm.into());
        }
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    fn generate_amm_associated_account<'a, 'b: 'a>(
        program_id: &Pubkey,
        assign_to: &Pubkey,
        market_account: &'a AccountInfo<'b>,
        associated_token_account: &'a AccountInfo<'b>,
        user_wallet_account: &'a AccountInfo<'b>,
        system_program_account: &'a AccountInfo<'b>,
        rent_sysvar_account: &'a AccountInfo<'b>,
        associated_seed: &[u8],
        data_size: usize,
    ) -> ProgramResult {
        let (associated_token_address, bump_seed) = get_associated_address_and_bump_seed(
            &program_id,
            &market_account.key,
            associated_seed,
            program_id,
        );
        if associated_token_address != *associated_token_account.key {
            msg!("Error: Associated token address does not match seed derivation");
            return Err(AmmError::ExpectedAccount.into());
        }
        if associated_token_account.owner == system_program_account.key {
            let associated_account_signer_seeds: &[&[_]] = &[
                &program_id.to_bytes(),
                &market_account.key.to_bytes(),
                associated_seed,
                &[bump_seed],
            ];
            let rent = &Rent::from_account_info(rent_sysvar_account)?;
            let required_lamports = rent
                .minimum_balance(data_size)
                .max(1)
                .saturating_sub(associated_token_account.lamports());
            if required_lamports > 0 {
                invoke(
                    &system_instruction::transfer(
                        user_wallet_account.key,
                        associated_token_account.key,
                        required_lamports,
                    ),
                    &[
                        user_wallet_account.clone(),
                        associated_token_account.clone(),
                        system_program_account.clone(),
                    ],
                )?;
            }
            invoke_signed(
                &system_instruction::allocate(associated_token_account.key, data_size as u64),
                &[
                    associated_token_account.clone(),
                    system_program_account.clone(),
                ],
                &[&associated_account_signer_seeds],
            )?;
            invoke_signed(
                &system_instruction::assign(associated_token_account.key, assign_to),
                &[
                    associated_token_account.clone(),
                    system_program_account.clone(),
                ],
                &[&associated_account_signer_seeds],
            )?;
        } else {
            associated_token_address.log();
            return Err(AmmError::RepeatCreateAmm.into());
        }
        Ok(())
    }

    /// Processes an [Initialize](enum.Instruction.html).
    pub fn process_initialize2(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        init: InitializeInstruction2,
    ) -> ProgramResult {
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;
        let ata_token_program_info = next_account_info(account_info_iter)?;
        let system_program_info = next_account_info(account_info_iter)?;
        let rent_sysvar_info = next_account_info(account_info_iter)?;
        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_lp_mint_info = next_account_info(account_info_iter)?;
        let amm_coin_mint_info = next_account_info(account_info_iter)?;
        let amm_pc_mint_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_config_info = next_account_info(account_info_iter)?;
        let create_fee_destination_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;

        let user_wallet_info = next_account_info(account_info_iter)?;
        let user_token_coin_info = next_account_info(account_info_iter)?;
        let user_token_pc_info = next_account_info(account_info_iter)?;
        let user_token_lp_info = next_account_info(account_info_iter)?;

        let (pda, _) = Pubkey::find_program_address(&[&AMM_CONFIG_SEED], program_id);
        if pda != *amm_config_info.key || amm_config_info.owner != program_id {
            return Err(AmmError::InvalidConfigAccount.into());
        }

        msg!(arrform!(LOG_SIZE, "initialize2: {:?}", init).as_str());
        if !user_wallet_info.is_signer {
            return Err(AmmError::InvalidSignAccount.into());
        }
        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let spl_token_program_id = token_program_info.key;
        check_assert_eq!(
            *ata_token_program_info.key,
            spl_associated_token_account::id(),
            "spl_associated_token_account",
            AmmError::InvalidSplTokenProgram
        );
        check_assert_eq!(
            *market_program_info.key,
            config_feature::openbook_program::id(),
            "market_program",
            AmmError::InvalidMarketProgram
        );
        check_assert_eq!(
            *system_program_info.key,
            solana_program::system_program::id(),
            "sys_program",
            AmmError::InvalidSysProgramAddress
        );
        let (expect_amm_authority, expect_nonce) =
            Pubkey::find_program_address(&[&AUTHORITY_AMM], program_id);
        if *amm_authority_info.key != expect_amm_authority || init.nonce != expect_nonce {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        if *create_fee_destination_info.key != config_feature::create_pool_fee_address::id() {
            return Err(AmmError::InvalidFee.into());
        }
        let amm_config = AmmConfig::load_checked(&amm_config_info, program_id)?;
        // Charge the fee to create a pool
        if amm_config.create_pool_fee != 0 {
            invoke(
                &system_instruction::transfer(
                    user_wallet_info.key,
                    create_fee_destination_info.key,
                    amm_config.create_pool_fee,
                ),
                &[
                    user_wallet_info.clone(),
                    create_fee_destination_info.clone(),
                    system_program_info.clone(),
                ],
            )?;
            invoke(
                &spl_token::instruction::sync_native(
                    token_program_info.key,
                    create_fee_destination_info.key,
                )?,
                &[
                    token_program_info.clone(),
                    create_fee_destination_info.clone(),
                ],
            )?;
        }

        // unpack and check coin_mint
        let coin_mint = Self::unpack_mint(&amm_coin_mint_info, spl_token_program_id)?;
        // unpack and check pc_mint
        let pc_mint = Self::unpack_mint(&amm_pc_mint_info, spl_token_program_id)?;

        // create target_order account
        Self::generate_amm_associated_account(
            program_id,
            program_id,
            market_info,
            amm_target_orders_info,
            user_wallet_info,
            system_program_info,
            rent_sysvar_info,
            TARGET_ASSOCIATED_SEED,
            size_of::<TargetOrders>(),
        )?;

        // create lp mint account
        let lp_decimals = coin_mint.decimals;
        Self::generate_amm_associated_spl_mint(
            program_id,
            spl_token_program_id,
            market_info,
            amm_lp_mint_info,
            user_wallet_info,
            system_program_info,
            rent_sysvar_info,
            token_program_info,
            amm_authority_info,
            LP_MINT_ASSOCIATED_SEED,
            lp_decimals,
        )?;
        // create coin vault account
        Self::generate_amm_associated_spl_token(
            program_id,
            spl_token_program_id,
            market_info,
            amm_coin_vault_info,
            amm_coin_mint_info,
            user_wallet_info,
            system_program_info,
            rent_sysvar_info,
            token_program_info,
            amm_authority_info,
            COIN_VAULT_ASSOCIATED_SEED,
        )?;
        // create pc vault account
        Self::generate_amm_associated_spl_token(
            program_id,
            spl_token_program_id,
            market_info,
            amm_pc_vault_info,
            amm_pc_mint_info,
            user_wallet_info,
            system_program_info,
            rent_sysvar_info,
            token_program_info,
            amm_authority_info,
            PC_VAULT_ASSOCIATED_SEED,
        )?;
        // create amm account
        Self::generate_amm_associated_account(
            program_id,
            program_id,
            market_info,
            amm_info,
            user_wallet_info,
            system_program_info,
            rent_sysvar_info,
            AMM_ASSOCIATED_SEED,
            size_of::<AmmInfo>(),
        )?;

        // create amm open order account
        Self::generate_amm_associated_account(
            program_id,
            market_program_info.key,
            market_info,
            amm_open_orders_info,
            user_wallet_info,
            system_program_info,
            rent_sysvar_info,
            OPEN_ORDER_ASSOCIATED_SEED,
            size_of::<serum_dex::state::OpenOrders>() + 12,
        )?;
        // init open orders account
        Invokers::invoke_dex_init_open_orders(
            market_program_info.clone(),
            amm_open_orders_info.clone(),
            amm_authority_info.clone(),
            market_info.clone(),
            rent_sysvar_info.clone(),
            AUTHORITY_AMM,
            init.nonce as u8,
        )?;

        // create user ata lp token
        Invokers::create_ata_spl_token(
            user_token_lp_info.clone(),
            user_wallet_info.clone(),
            user_wallet_info.clone(),
            amm_lp_mint_info.clone(),
            token_program_info.clone(),
            ata_token_program_info.clone(),
            system_program_info.clone(),
        )?;

        // transfer user tokens to vault
        Invokers::token_transfer(
            token_program_info.clone(),
            user_token_coin_info.clone(),
            amm_coin_vault_info.clone(),
            user_wallet_info.clone(),
            init.init_coin_amount,
        )?;
        Invokers::token_transfer(
            token_program_info.clone(),
            user_token_pc_info.clone(),
            amm_pc_vault_info.clone(),
            user_wallet_info.clone(),
            init.init_pc_amount,
        )?;

        // load AmmInfo
        let mut amm = AmmInfo::load_mut(&amm_info)?;
        if amm.status != AmmStatus::Uninitialized.into_u64() {
            return Err(AmmError::AlreadyInUse.into());
        }

        // unpack and check token_coin
        let amm_coin_vault =
            Self::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
        check_assert_eq!(
            amm_coin_vault.owner,
            *amm_authority_info.key,
            "coin_vault_owner",
            AmmError::InvalidOwner
        );
        if amm_coin_vault.amount == 0 {
            return Err(AmmError::InvalidSupply.into());
        }
        if amm_coin_vault.delegate.is_some() {
            return Err(AmmError::InvalidDelegate.into());
        }
        if amm_coin_vault.close_authority.is_some() {
            return Err(AmmError::InvalidCloseAuthority.into());
        }
        check_assert_eq!(
            *amm_coin_mint_info.key,
            amm_coin_vault.mint,
            "coin_mint",
            AmmError::InvalidCoinMint
        );
        // unpack and check token_pc
        let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
        check_assert_eq!(
            amm_pc_vault.owner,
            *amm_authority_info.key,
            "pc_vault_owner",
            AmmError::InvalidOwner
        );
        if amm_pc_vault.amount == 0 {
            return Err(AmmError::InvalidSupply.into());
        }
        if amm_pc_vault.delegate.is_some() {
            return Err(AmmError::InvalidDelegate.into());
        }
        if amm_pc_vault.close_authority.is_some() {
            return Err(AmmError::InvalidCloseAuthority.into());
        }
        check_assert_eq!(
            *amm_pc_mint_info.key,
            amm_pc_vault.mint,
            "pc_mint",
            AmmError::InvalidPCMint
        );

        // load and check market
        let market_state =
            Market::load(market_info, &config_feature::openbook_program::id(), false)?;
        if identity(market_state.coin_mint) != amm_coin_vault.mint.to_aligned_bytes()
            || identity(market_state.coin_mint) != (*amm_coin_mint_info.key).to_aligned_bytes()
        {
            return Err(AmmError::InvalidCoinMint.into());
        }
        if identity(market_state.pc_mint) != amm_pc_vault.mint.to_aligned_bytes()
            || identity(market_state.pc_mint) != (*amm_pc_mint_info.key).to_aligned_bytes()
        {
            return Err(AmmError::InvalidPCMint.into());
        }
        if market_state.pc_lot_size == 0 || market_state.coin_lot_size == 0 {
            msg!(
                "pc_lot_size:{}, coin_lot_size:{}",
                identity(market_state.pc_lot_size),
                identity(market_state.coin_lot_size)
            );
            return Err(AmmError::InvalidMarket.into());
        }

        let lp_mint = Self::unpack_mint(&amm_lp_mint_info, spl_token_program_id)?;
        if lp_mint.supply != 0 {
            return Err(AmmError::InvalidSupply.into());
        }
        if COption::Some(*amm_authority_info.key) != lp_mint.mint_authority {
            return Err(AmmError::InvalidOwner.into());
        }
        if lp_mint.freeze_authority.is_some() {
            return Err(AmmError::InvalidFreezeAuthority.into());
        }

        let liquidity = Calculator::to_u64(
            U128::from(amm_pc_vault.amount)
                .checked_mul(amm_coin_vault.amount.into())
                .unwrap()
                .integer_sqrt()
                .as_u128(),
        )?;
        let user_lp_amount = liquidity
            .checked_sub((10u64).checked_pow(lp_mint.decimals.into()).unwrap())
            .ok_or(AmmError::InitLpAmountTooLess)?;

        // liquidity is measured in terms of token_a's value since both sides of
        // the pool are equal
        Invokers::token_mint_to(
            token_program_info.clone(),
            amm_lp_mint_info.clone(),
            user_token_lp_info.clone(),
            amm_authority_info.clone(),
            AUTHORITY_AMM,
            init.nonce,
            user_lp_amount,
        )?;

        amm.initialize(
            init.nonce,
            init.open_time,
            coin_mint.decimals,
            pc_mint.decimals,
            market_state.coin_lot_size,
            market_state.pc_lot_size,
        )?;
        encode_ray_log(InitLog {
            log_type: LogType::Init.into_u8(),
            time: init.open_time,
            pc_decimals: amm.pc_decimals as u8,
            coin_decimals: amm.coin_decimals as u8,
            pc_lot_size: market_state.pc_lot_size,
            coin_lot_size: market_state.coin_lot_size,
            pc_amount: amm_pc_vault.amount,
            coin_amount: amm_coin_vault.amount,
            market: *market_info.key,
        });
        let x = Calculator::normalize_decimal_v2(
            amm_pc_vault.amount,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y = Calculator::normalize_decimal_v2(
            amm_coin_vault.amount,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );
        // check and init target orders account
        if amm_target_orders_info.owner != program_id {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        let mut target_order = TargetOrders::load_mut(amm_target_orders_info)?;
        target_order.check_init(x.as_u128(), y.as_u128(), amm_info.key)?;

        amm.coin_vault = *amm_coin_vault_info.key;
        amm.pc_vault = *amm_pc_vault_info.key;
        amm.coin_vault_mint = *amm_coin_mint_info.key;
        amm.pc_vault_mint = *amm_pc_mint_info.key;
        amm.lp_mint = *amm_lp_mint_info.key;
        amm.open_orders = *amm_open_orders_info.key;
        amm.market = *market_info.key;
        amm.market_program = *market_program_info.key;
        amm.target_orders = *amm_target_orders_info.key;
        amm.amm_owner = config_feature::amm_owner::ID;
        amm.lp_amount = liquidity;
        amm.status = if init.open_time > (Clock::get()?.unix_timestamp as u64) {
            AmmStatus::WaitingTrade.into_u64()
        } else {
            AmmStatus::SwapOnly.into_u64()
        };
        amm.reset_flag = AmmResetFlag::ResetYes.into_u64();

        Ok(())
    }

    /// Processes an [Deposit](enum.Instruction.html).
    pub fn process_deposit(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        deposit: DepositInstruction,
    ) -> ProgramResult {
        const ACCOUNT_LEN: usize = 14;
        let input_account_len = accounts.len();
        if input_account_len != ACCOUNT_LEN && input_account_len != ACCOUNT_LEN + 1 {
            return Err(AmmError::WrongAccountsNumber.into());
        }
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_lp_mint_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;

        let market_info = next_account_info(account_info_iter)?;

        let user_source_coin_info = next_account_info(account_info_iter)?;
        let user_source_pc_info = next_account_info(account_info_iter)?;
        let user_dest_lp_info = next_account_info(account_info_iter)?;
        let source_owner_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;
        let mut amm = AmmInfo::load_mut_checked(&amm_info, program_id)?;
        if deposit.max_coin_amount == 0 || deposit.max_pc_amount == 0 {
            encode_ray_log(DepositLog {
                log_type: LogType::Deposit.into_u8(),
                max_coin: deposit.max_coin_amount,
                max_pc: deposit.max_pc_amount,
                base: deposit.base_side,
                pool_coin: 0,
                pool_pc: 0,
                pool_lp: 0,
                calc_pnl_x: 0,
                calc_pnl_y: 0,
                deduct_coin: 0,
                deduct_pc: 0,
                mint_lp: 0,
            });
            return Err(AmmError::InvalidInput.into());
        }
        if !source_owner_info.is_signer {
            return Err(AmmError::InvalidSignAccount.into());
        }

        if !AmmStatus::from_u64(amm.status).deposit_permission() {
            return Err(AmmError::InvalidStatus.into());
        }
        if *amm_authority_info.key
            != Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?
        {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        let enable_orderbook;
        if AmmStatus::from_u64(amm.status).orderbook_permission() {
            enable_orderbook = true;
        } else {
            enable_orderbook = false;
        }
        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let spl_token_program_id = token_program_info.key;
        // token_coin must be amm.coin_vault or token_source_coin must not be amm.coin_vault
        if *amm_coin_vault_info.key != amm.coin_vault
            || *user_source_coin_info.key == amm.coin_vault
        {
            return Err(AmmError::InvalidCoinVault.into());
        }
        // token_pc must be amm.pc_vault or token_source_pc must not be amm.pc_vault
        if *amm_pc_vault_info.key != amm.pc_vault || *user_source_pc_info.key == amm.pc_vault {
            return Err(AmmError::InvalidPCVault.into());
        }
        check_assert_eq!(
            *amm_lp_mint_info.key,
            amm.lp_mint,
            "lp_mint",
            AmmError::InvalidPoolMint
        );
        check_assert_eq!(
            *amm_target_orders_info.key,
            amm.target_orders,
            "target_orders",
            AmmError::InvalidTargetOrders
        );
        let amm_coin_vault =
            Self::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
        let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
        let user_source_coin =
            Self::unpack_token_account(&user_source_coin_info, spl_token_program_id)?;
        let user_source_pc =
            Self::unpack_token_account(&user_source_pc_info, spl_token_program_id)?;
        let mut target_orders =
            TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
        // calc the remaining total_pc & total_coin
        let (mut total_pc_without_take_pnl, mut total_coin_without_take_pnl) = if enable_orderbook {
            check_assert_eq!(
                *market_info.key,
                amm.market,
                "market",
                AmmError::InvalidMarket
            );
            check_assert_eq!(
                *amm_open_orders_info.key,
                amm.open_orders,
                "open_orders",
                AmmError::InvalidOpenOrders
            );
            let (market_state, open_orders) = Self::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;
            if identity(market_state.coin_mint) != amm_coin_vault.mint.to_aligned_bytes()
                || identity(market_state.coin_mint) != user_source_coin.mint.to_aligned_bytes()
            {
                return Err(AmmError::InvalidCoinMint.into());
            }
            if identity(market_state.pc_mint) != amm_pc_vault.mint.to_aligned_bytes()
                || identity(market_state.pc_mint) != user_source_pc.mint.to_aligned_bytes()
            {
                return Err(AmmError::InvalidPCMint.into());
            }
            Calculator::calc_total_without_take_pnl(
                amm_pc_vault.amount,
                amm_coin_vault.amount,
                &open_orders,
                &amm,
                &market_state,
                &market_event_queue_info,
                &amm_open_orders_info,
            )?
        } else {
            Calculator::calc_total_without_take_pnl_no_orderbook(
                amm_pc_vault.amount,
                amm_coin_vault.amount,
                &amm,
            )?
        };

        let x1 = Calculator::normalize_decimal_v2(
            total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y1 = Calculator::normalize_decimal_v2(
            total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );
        // calc and update pnl
        let (delta_x, delta_y) = Self::calc_take_pnl(
            &target_orders,
            &mut amm,
            &mut total_pc_without_take_pnl,
            &mut total_coin_without_take_pnl,
            x1.as_u128().into(),
            y1.as_u128().into(),
        )?;
        let invariant = InvariantToken {
            token_coin: total_coin_without_take_pnl,
            token_pc: total_pc_without_take_pnl,
        };

        // let lp_mint  = Self::unpack_mint(&lp_mint_info, spl_token_program_id)?;
        if amm.lp_amount == 0 {
            encode_ray_log(DepositLog {
                log_type: LogType::Deposit.into_u8(),
                max_coin: deposit.max_coin_amount,
                max_pc: deposit.max_pc_amount,
                base: deposit.base_side,
                pool_coin: total_coin_without_take_pnl,
                pool_pc: total_pc_without_take_pnl,
                pool_lp: amm.lp_amount,
                calc_pnl_x: target_orders.calc_pnl_x,
                calc_pnl_y: target_orders.calc_pnl_y,
                deduct_coin: 0,
                deduct_pc: 0,
                mint_lp: 0,
            });
            return Err(AmmError::NotAllowZeroLP.into());
        }
        let deduct_pc_amount;
        let deduct_coin_amount;
        let mint_lp_amount;
        if deposit.base_side == 0 {
            // base coin
            deduct_pc_amount = invariant
                .exchange_coin_to_pc(deposit.max_coin_amount, RoundDirection::Ceiling)
                .ok_or(AmmError::CalculationExRateFailure)?;
            deduct_coin_amount = deposit.max_coin_amount;
            if deduct_pc_amount > deposit.max_pc_amount {
                encode_ray_log(DepositLog {
                    log_type: LogType::Deposit.into_u8(),
                    max_coin: deposit.max_coin_amount,
                    max_pc: deposit.max_pc_amount,
                    base: deposit.base_side,
                    pool_coin: total_coin_without_take_pnl,
                    pool_pc: total_pc_without_take_pnl,
                    pool_lp: amm.lp_amount,
                    calc_pnl_x: target_orders.calc_pnl_x,
                    calc_pnl_y: target_orders.calc_pnl_y,
                    deduct_coin: deduct_coin_amount,
                    deduct_pc: deduct_pc_amount,
                    mint_lp: 0,
                });
                return Err(AmmError::ExceededSlippage.into());
            }
            // base coin, check other_amount_min if need
            if deposit.other_amount_min.is_some() {
                if deduct_pc_amount < deposit.other_amount_min.unwrap() {
                    encode_ray_log(DepositLog {
                        log_type: LogType::Deposit.into_u8(),
                        max_coin: deposit.max_coin_amount,
                        max_pc: deposit.max_pc_amount,
                        base: deposit.base_side,
                        pool_coin: total_coin_without_take_pnl,
                        pool_pc: total_pc_without_take_pnl,
                        pool_lp: amm.lp_amount,
                        calc_pnl_x: target_orders.calc_pnl_x,
                        calc_pnl_y: target_orders.calc_pnl_y,
                        deduct_coin: deduct_coin_amount,
                        deduct_pc: deduct_pc_amount,
                        mint_lp: 0,
                    });
                    return Err(AmmError::ExceededSlippage.into());
                }
            }
            // coin_amount/ (total_coin_amount + coin_amount)  = output / (lp_mint.supply + output) =>  output = coin_amount / total_coin_amount * lp_mint.supply
            let invariant_coin = InvariantPool {
                token_input: deduct_coin_amount,
                token_total: total_coin_without_take_pnl,
            };
            mint_lp_amount = invariant_coin
                .exchange_token_to_pool(amm.lp_amount, RoundDirection::Floor)
                .ok_or(AmmError::CalculationExRateFailure)?;
        } else {
            // base pc
            deduct_coin_amount = invariant
                .exchange_pc_to_coin(deposit.max_pc_amount, RoundDirection::Ceiling)
                .ok_or(AmmError::CalculationExRateFailure)?;
            deduct_pc_amount = deposit.max_pc_amount;
            if deduct_coin_amount > deposit.max_coin_amount {
                encode_ray_log(DepositLog {
                    log_type: LogType::Deposit.into_u8(),
                    max_coin: deposit.max_coin_amount,
                    max_pc: deposit.max_pc_amount,
                    base: deposit.base_side,
                    pool_coin: total_coin_without_take_pnl,
                    pool_pc: total_pc_without_take_pnl,
                    pool_lp: amm.lp_amount,
                    calc_pnl_x: target_orders.calc_pnl_x,
                    calc_pnl_y: target_orders.calc_pnl_y,
                    deduct_coin: deduct_coin_amount,
                    deduct_pc: deduct_pc_amount,
                    mint_lp: 0,
                });
                return Err(AmmError::ExceededSlippage.into());
            }
            // base pc, check other_amount_min if need
            if deposit.other_amount_min.is_some() {
                if deduct_coin_amount < deposit.other_amount_min.unwrap() {
                    encode_ray_log(DepositLog {
                        log_type: LogType::Deposit.into_u8(),
                        max_coin: deposit.max_coin_amount,
                        max_pc: deposit.max_pc_amount,
                        base: deposit.base_side,
                        pool_coin: total_coin_without_take_pnl,
                        pool_pc: total_pc_without_take_pnl,
                        pool_lp: amm.lp_amount,
                        calc_pnl_x: target_orders.calc_pnl_x,
                        calc_pnl_y: target_orders.calc_pnl_y,
                        deduct_coin: deduct_coin_amount,
                        deduct_pc: deduct_pc_amount,
                        mint_lp: 0,
                    });
                    return Err(AmmError::ExceededSlippage.into());
                }
            }

            let invariant_pc = InvariantPool {
                token_input: deduct_pc_amount,
                token_total: total_pc_without_take_pnl,
            };
            // pc_amount/ (total_pc_amount + pc_amount)  = output / (lp_mint.supply + output) =>  output = pc_amount / total_pc_amount * lp_mint.supply
            mint_lp_amount = invariant_pc
                .exchange_token_to_pool(amm.lp_amount, RoundDirection::Floor)
                .ok_or(AmmError::CalculationExRateFailure)?;
        }
        encode_ray_log(DepositLog {
            log_type: LogType::Deposit.into_u8(),
            max_coin: deposit.max_coin_amount,
            max_pc: deposit.max_pc_amount,
            base: deposit.base_side,
            pool_coin: total_coin_without_take_pnl,
            pool_pc: total_pc_without_take_pnl,
            pool_lp: amm.lp_amount,
            calc_pnl_x: target_orders.calc_pnl_x,
            calc_pnl_y: target_orders.calc_pnl_y,
            deduct_coin: deduct_coin_amount,
            deduct_pc: deduct_pc_amount,
            mint_lp: mint_lp_amount,
        });

        if deduct_coin_amount > user_source_coin.amount || deduct_pc_amount > user_source_pc.amount
        {
            return Err(AmmError::InsufficientFunds.into());
        }
        if mint_lp_amount == 0 || deduct_coin_amount == 0 || deduct_pc_amount == 0 {
            return Err(AmmError::InvalidInput.into());
        }

        Invokers::token_transfer(
            token_program_info.clone(),
            user_source_coin_info.clone(),
            amm_coin_vault_info.clone(),
            source_owner_info.clone(),
            deduct_coin_amount,
        )?;
        Invokers::token_transfer(
            token_program_info.clone(),
            user_source_pc_info.clone(),
            amm_pc_vault_info.clone(),
            source_owner_info.clone(),
            deduct_pc_amount,
        )?;
        Invokers::token_mint_to(
            token_program_info.clone(),
            amm_lp_mint_info.clone(),
            user_dest_lp_info.clone(),
            amm_authority_info.clone(),
            AUTHORITY_AMM,
            amm.nonce as u8,
            mint_lp_amount,
        )?;
        amm.lp_amount = amm.lp_amount.checked_add(mint_lp_amount).unwrap();

        target_orders.calc_pnl_x = x1
            .checked_add(Calculator::normalize_decimal_v2(
                deduct_pc_amount,
                amm.pc_decimals,
                amm.sys_decimal_value,
            ))
            .unwrap()
            .checked_sub(U128::from(delta_x))
            .unwrap()
            .as_u128();
        target_orders.calc_pnl_y = y1
            .checked_add(Calculator::normalize_decimal_v2(
                deduct_coin_amount,
                amm.coin_decimals,
                amm.sys_decimal_value,
            ))
            .unwrap()
            .checked_sub(U128::from(delta_y))
            .unwrap()
            .as_u128();
        amm.recent_epoch = Clock::get()?.epoch;
        Ok(())
    }

    pub fn process_withdrawpnl(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
        const ACCOUNT_LEN: usize = 17;
        let input_account_len = accounts.len();
        if input_account_len != ACCOUNT_LEN && input_account_len != ACCOUNT_LEN + 1 {
            return Err(AmmError::WrongAccountsNumber.into());
        }
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_config_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        let user_pnl_coin_info = next_account_info(account_info_iter)?;
        let user_pnl_pc_info = next_account_info(account_info_iter)?;
        let pnl_owner_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;
        let market_coin_vault_info = next_account_info(account_info_iter)?;
        let market_pc_vault_info = next_account_info(account_info_iter)?;
        let market_vault_signer = next_account_info(account_info_iter)?;
        let mut referrer_pc_wallet = None;
        if input_account_len == ACCOUNT_LEN + 1 {
            referrer_pc_wallet = Some(next_account_info(account_info_iter)?);
            let referrer_pc_token =
                Self::unpack_token_account(&referrer_pc_wallet.unwrap(), token_program_info.key)?;
            check_assert_eq!(
                referrer_pc_token.owner,
                config_feature::referrer_pc_wallet::id(),
                "referrer_pc_owner",
                AmmError::InvalidOwner
            );
        }

        let mut amm = AmmInfo::load_mut_checked(&amm_info, program_id)?;
        if *amm_authority_info.key
            != Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?
        {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        if amm_info.owner != program_id {
            return Err(AmmError::InvalidOwner.into());
        }

        let (pda, _) = Pubkey::find_program_address(&[&AMM_CONFIG_SEED], program_id);
        if pda != *amm_config_info.key || amm_config_info.owner != program_id {
            return Err(AmmError::InvalidConfigAccount.into());
        }
        let amm_config = AmmConfig::load_checked(&amm_config_info, program_id)?;

        if !pnl_owner_info.is_signer
            || (*pnl_owner_info.key != config_feature::amm_owner::ID
                && *pnl_owner_info.key != amm_config.pnl_owner)
        {
            return Err(AmmError::InvalidSignAccount.into());
        }
        // withdrawpnl in all status except Uninitialized
        if amm.status == AmmStatus::Uninitialized.into_u64() {
            msg!(&format!("withdrawpnl: status {}", amm.status));
            return Err(AmmError::InvalidStatus.into());
        }
        check_assert_eq!(
            *market_info.key,
            amm.market,
            "market",
            AmmError::InvalidMarket
        );
        check_assert_eq!(
            *amm_coin_vault_info.key,
            amm.coin_vault,
            "coin_vault",
            AmmError::InvalidCoinVault
        );
        check_assert_eq!(
            *amm_pc_vault_info.key,
            amm.pc_vault,
            "pc_vault",
            AmmError::InvalidPCVault
        );
        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let spl_token_program_id = token_program_info.key;
        check_assert_eq!(
            *market_program_info.key,
            amm.market_program,
            "market_program",
            AmmError::InvalidMarketProgram
        );
        check_assert_eq!(
            *amm_open_orders_info.key,
            amm.open_orders,
            "open_orders",
            AmmError::InvalidOpenOrders
        );
        check_assert_eq!(
            *amm_target_orders_info.key,
            amm.target_orders,
            "target_orders",
            AmmError::InvalidTargetOrders
        );
        let amm_coin_vault =
            Self::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
        let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
        let user_pnl_coin = Self::unpack_token_account(&user_pnl_coin_info, spl_token_program_id)?;
        let user_pnl_pc = Self::unpack_token_account(&user_pnl_pc_info, spl_token_program_id)?;
        let mut target_orders =
            TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
        if amm_coin_vault.mint != amm.coin_vault_mint || user_pnl_coin.mint != amm.coin_vault_mint {
            return Err(AmmError::InvalidCoinMint.into());
        }
        if amm_pc_vault.mint != amm.pc_vault_mint || user_pnl_pc.mint != amm.pc_vault_mint {
            return Err(AmmError::InvalidPCMint.into());
        }

        let (market_state, open_orders) = Self::load_serum_market_order(
            market_info,
            amm_open_orders_info,
            amm_authority_info,
            &amm,
            false,
        )?;

        msg!(arrform!(
            LOG_SIZE,
            "withdrawpnl need_take_coin:{}, need_take_pc:{}",
            amm.state_data.need_take_pnl_coin,
            amm.state_data.need_take_pnl_pc
        )
        .as_str());

        let (mut total_pc_without_take_pnl, mut total_coin_without_take_pnl) =
            Calculator::calc_total_without_take_pnl(
                amm_pc_vault.amount,
                amm_coin_vault.amount,
                &open_orders,
                &amm,
                &market_state,
                &market_event_queue_info,
                &amm_open_orders_info,
            )?;
        let x1 = Calculator::normalize_decimal_v2(
            total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y1 = Calculator::normalize_decimal_v2(
            total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );
        msg!(arrform!(
            LOG_SIZE,
            "withdrawpnl total_pc:{}, total_pc:{}, x:{}, y:{}",
            total_pc_without_take_pnl,
            total_coin_without_take_pnl,
            x1,
            y1
        )
        .as_str());

        // calc and update pnl
        let (delta_x, delta_y) = Self::calc_take_pnl(
            &target_orders,
            &mut amm,
            &mut total_pc_without_take_pnl,
            &mut total_coin_without_take_pnl,
            x1.as_u128().into(),
            y1.as_u128().into(),
        )?;
        msg!(arrform!(LOG_SIZE, "withdrawpnl total_pc:{}, total_pc:{}, delta_x:{}, delta_y:{}, need_take_coin:{}, need_take_pc:{}",total_pc_without_take_pnl, total_coin_without_take_pnl, delta_x, delta_y, amm.state_data.need_take_pnl_coin, amm.state_data.need_take_pnl_pc).as_str());

        if amm.state_data.need_take_pnl_coin <= amm_coin_vault.amount
            && amm.state_data.need_take_pnl_pc <= amm_pc_vault.amount
        {
            // coin & pc is enough, transfer directly
            Invokers::token_transfer_with_authority(
                token_program_info.clone(),
                amm_coin_vault_info.clone(),
                user_pnl_coin_info.clone(),
                amm_authority_info.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
                amm.state_data.need_take_pnl_coin,
            )?;
            Invokers::token_transfer_with_authority(
                token_program_info.clone(),
                amm_pc_vault_info.clone(),
                user_pnl_pc_info.clone(),
                amm_authority_info.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
                amm.state_data.need_take_pnl_pc,
            )?;
            // clear need take pnl
            amm.state_data.need_take_pnl_coin = 0u64;
            amm.state_data.need_take_pnl_pc = 0u64;
            // update target_orders.calc_pnl_x & target_orders.calc_pnl_y
            target_orders.calc_pnl_x = x1.checked_sub(U128::from(delta_x)).unwrap().as_u128();
            target_orders.calc_pnl_y = y1.checked_sub(U128::from(delta_y)).unwrap().as_u128();
        } else if amm.state_data.need_take_pnl_coin
            <= amm_coin_vault
                .amount
                .checked_add(open_orders.native_coin_free)
                .unwrap()
            && amm.state_data.need_take_pnl_pc
                <= amm_pc_vault
                    .amount
                    .checked_add(open_orders.native_pc_free)
                    .unwrap()
        {
            // settle & transfer
            Invokers::invoke_dex_settle_funds(
                market_program_info.clone(),
                market_info.clone(),
                amm_open_orders_info.clone(),
                amm_authority_info.clone(),
                market_coin_vault_info.clone(),
                market_pc_vault_info.clone(),
                amm_coin_vault_info.clone(),
                amm_pc_vault_info.clone(),
                market_vault_signer.clone(),
                token_program_info.clone(),
                referrer_pc_wallet.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
            )?;
            Invokers::token_transfer_with_authority(
                token_program_info.clone(),
                amm_coin_vault_info.clone(),
                user_pnl_coin_info.clone(),
                amm_authority_info.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
                amm.state_data.need_take_pnl_coin,
            )?;
            Invokers::token_transfer_with_authority(
                token_program_info.clone(),
                amm_pc_vault_info.clone(),
                user_pnl_pc_info.clone(),
                amm_authority_info.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
                amm.state_data.need_take_pnl_pc,
            )?;
            // clear need take pnl
            amm.state_data.need_take_pnl_coin = 0u64;
            amm.state_data.need_take_pnl_pc = 0u64;
            // update target_orders.calc_pnl_x & target_orders.calc_pnl_y
            target_orders.calc_pnl_x = x1.checked_sub(U128::from(delta_x)).unwrap().as_u128();
            target_orders.calc_pnl_y = y1.checked_sub(U128::from(delta_y)).unwrap().as_u128();
        } else if amm.state_data.need_take_pnl_coin
            >= amm_coin_vault
                .amount
                .checked_add(open_orders.native_coin_total)
                .unwrap()
            || amm.state_data.need_take_pnl_pc
                >= amm_pc_vault
                    .amount
                    .checked_add(open_orders.native_pc_total)
                    .unwrap()
        {
            // calc error need to disable
            amm.state = AmmState::CancelAllOrdersState.into_u64();
            amm.status = AmmStatus::Disabled.into_u64();
        }
        amm.recent_epoch = Clock::get()?.epoch;

        Ok(())
    }

    /// Processes an [Withdraw](enum.Instruction.html).
    pub fn process_withdraw(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        withdraw: WithdrawInstruction,
    ) -> ProgramResult {
        const ACCOUNT_LEN: usize = 20;
        let input_account_len = accounts.len();
        if input_account_len != ACCOUNT_LEN
            && input_account_len != ACCOUNT_LEN + 1
            && input_account_len != ACCOUNT_LEN + 2
            && input_account_len != ACCOUNT_LEN + 3
        {
            return Err(AmmError::WrongAccountsNumber.into());
        }
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_lp_mint_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        if input_account_len == ACCOUNT_LEN + 2 || input_account_len == ACCOUNT_LEN + 3 {
            let _padding_account_info1 = next_account_info(account_info_iter)?;
            let _padding_account_info2 = next_account_info(account_info_iter)?;
        }

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_coin_vault_info = next_account_info(account_info_iter)?;
        let market_pc_vault_info = next_account_info(account_info_iter)?;
        let market_vault_signer = next_account_info(account_info_iter)?;

        let user_source_lp_info = next_account_info(account_info_iter)?;
        let user_dest_coin_info = next_account_info(account_info_iter)?;
        let user_dest_pc_info = next_account_info(account_info_iter)?;
        let source_lp_owner_info = next_account_info(account_info_iter)?;

        let market_event_q_info = next_account_info(account_info_iter)?;
        let market_bids_info = next_account_info(account_info_iter)?;
        let market_asks_info = next_account_info(account_info_iter)?;

        let mut referrer_pc_wallet = None;
        if input_account_len == ACCOUNT_LEN + 1 || input_account_len == ACCOUNT_LEN + 3 {
            referrer_pc_wallet = Some(next_account_info(account_info_iter)?);
            if *referrer_pc_wallet.unwrap().key != Pubkey::default() {
                let referrer_pc_token = Self::unpack_token_account(
                    &referrer_pc_wallet.unwrap(),
                    token_program_info.key,
                )?;
                check_assert_eq!(
                    referrer_pc_token.owner,
                    config_feature::referrer_pc_wallet::id(),
                    "referrer_pc_owner",
                    AmmError::InvalidOwner
                );
            }
        }

        if referrer_pc_wallet.is_none() {
            referrer_pc_wallet = Some(amm_pc_vault_info);
        }
        if !source_lp_owner_info.is_signer {
            return Err(AmmError::InvalidSignAccount.into());
        }
        let mut amm = AmmInfo::load_mut_checked(&amm_info, program_id)?;
        let mut target_orders =
            TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;

        if !AmmStatus::from_u64(amm.status).withdraw_permission() {
            return Err(AmmError::InvalidStatus.into());
        }
        if *amm_authority_info.key
            != Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?
        {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        let enable_orderbook;
        if AmmStatus::from_u64(amm.status).orderbook_permission() {
            enable_orderbook = true;
        } else {
            enable_orderbook = false;
        }
        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let spl_token_program_id = token_program_info.key;
        // token_coin must be amm.coin_vault or token_dest_coin must not be amm.coin_vault
        if *amm_coin_vault_info.key != amm.coin_vault || *user_dest_coin_info.key == amm.coin_vault
        {
            return Err(AmmError::InvalidCoinVault.into());
        }
        // token_pc must be amm.pc_vault or token_dest_pc must not be amm.pc_vault
        if *amm_pc_vault_info.key != amm.pc_vault || *user_dest_pc_info.key == amm.pc_vault {
            return Err(AmmError::InvalidPCVault.into());
        }
        check_assert_eq!(
            *amm_target_orders_info.key,
            amm.target_orders,
            "target_orders",
            AmmError::InvalidTargetOrders
        );
        check_assert_eq!(
            *amm_lp_mint_info.key,
            amm.lp_mint,
            "lp_mint",
            AmmError::InvalidPoolMint
        );

        let amm_coin_vault =
            Self::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
        let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
        let user_dest_coin =
            Self::unpack_token_account(&user_dest_coin_info, spl_token_program_id)?;
        let user_dest_pc = Self::unpack_token_account(&user_dest_pc_info, spl_token_program_id)?;

        let lp_mint = Self::unpack_mint(&amm_lp_mint_info, spl_token_program_id)?;
        let user_source_lp =
            Self::unpack_token_account(&user_source_lp_info, spl_token_program_id)?;
        if user_source_lp.mint != *amm_lp_mint_info.key {
            return Err(AmmError::InvalidTokenLP.into());
        }
        if withdraw.amount > user_source_lp.amount {
            return Err(AmmError::InsufficientFunds.into());
        }
        if withdraw.amount > lp_mint.supply || withdraw.amount >= amm.lp_amount {
            return Err(AmmError::NotAllowZeroLP.into());
        }
        let (mut total_pc_without_take_pnl, mut total_coin_without_take_pnl) = if enable_orderbook {
            // check account
            check_assert_eq!(
                *market_info.key,
                amm.market,
                "market",
                AmmError::InvalidMarket
            );
            check_assert_eq!(
                *market_program_info.key,
                amm.market_program,
                "market_program",
                AmmError::InvalidMarketProgram
            );
            check_assert_eq!(
                *amm_open_orders_info.key,
                amm.open_orders,
                "open_orders",
                AmmError::InvalidOpenOrders
            );
            // load
            let (market_state, open_orders) = Self::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            let (bids, asks) = Self::get_amm_orders(&open_orders, bids_orders, asks_orders)?;
            // cancel all orders
            let mut amm_order_ids_vec = Vec::new();
            let mut order_ids = [0u64; 8];
            let mut count = 0;
            for i in 0..std::cmp::max(bids.len(), asks.len()) {
                if i < bids.len() {
                    order_ids[count] = bids[i].client_order_id();
                    count += 1;
                }
                if i < asks.len() {
                    order_ids[count] = asks[i].client_order_id();
                    count += 1;
                }
                if count == 8 {
                    amm_order_ids_vec.push(order_ids);
                    order_ids = [0u64; 8];
                    count = 0;
                }
            }
            if count != 0 {
                amm_order_ids_vec.push(order_ids);
            }
            for ids in amm_order_ids_vec.iter() {
                Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                    market_program_info.clone(),
                    market_info.clone(),
                    market_bids_info.clone(),
                    market_asks_info.clone(),
                    amm_open_orders_info.clone(),
                    amm_authority_info.clone(),
                    market_event_q_info.clone(),
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                    *ids,
                )?;
            }
            Invokers::invoke_dex_settle_funds(
                market_program_info.clone(),
                market_info.clone(),
                amm_open_orders_info.clone(),
                amm_authority_info.clone(),
                market_coin_vault_info.clone(),
                market_pc_vault_info.clone(),
                amm_coin_vault_info.clone(),
                amm_pc_vault_info.clone(),
                market_vault_signer.clone(),
                token_program_info.clone(),
                referrer_pc_wallet.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
            )?;

            if identity(market_state.coin_mint) != amm_coin_vault.mint.to_aligned_bytes()
                || identity(market_state.coin_mint) != user_dest_coin.mint.to_aligned_bytes()
            {
                return Err(AmmError::InvalidCoinMint.into());
            }
            if identity(market_state.pc_mint) != amm_pc_vault.mint.to_aligned_bytes()
                || identity(market_state.pc_mint) != user_dest_pc.mint.to_aligned_bytes()
            {
                return Err(AmmError::InvalidPCMint.into());
            }
            Calculator::calc_total_without_take_pnl(
                amm_pc_vault.amount,
                amm_coin_vault.amount,
                &open_orders,
                &amm,
                &market_state,
                &market_event_q_info,
                &amm_open_orders_info,
            )?
        } else {
            Calculator::calc_total_without_take_pnl_no_orderbook(
                amm_pc_vault.amount,
                amm_coin_vault.amount,
                &amm,
            )?
        };

        let x1 = Calculator::normalize_decimal_v2(
            total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y1 = Calculator::normalize_decimal_v2(
            total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );

        // calc and update pnl
        let mut delta_x: u128 = 0;
        let mut delta_y: u128 = 0;
        if amm.status != AmmStatus::WithdrawOnly.into_u64() {
            (delta_x, delta_y) = Self::calc_take_pnl(
                &target_orders,
                &mut amm,
                &mut total_pc_without_take_pnl,
                &mut total_coin_without_take_pnl,
                x1.as_u128().into(),
                y1.as_u128().into(),
            )?;
        }

        // coin_amount / total_coin_amount = amount / lp_mint.supply => coin_amount = total_coin_amount * amount / pool_mint.supply
        let invariant = InvariantPool {
            token_input: withdraw.amount,
            token_total: amm.lp_amount,
        };
        let coin_amount = invariant
            .exchange_pool_to_token(total_coin_without_take_pnl, RoundDirection::Floor)
            .ok_or(AmmError::CalculationExRateFailure)?;
        let pc_amount = invariant
            .exchange_pool_to_token(total_pc_without_take_pnl, RoundDirection::Floor)
            .ok_or(AmmError::CalculationExRateFailure)?;

        encode_ray_log(WithdrawLog {
            log_type: LogType::Withdraw.into_u8(),
            withdraw_lp: withdraw.amount,
            user_lp: user_source_lp.amount,
            pool_coin: total_coin_without_take_pnl,
            pool_pc: total_pc_without_take_pnl,
            pool_lp: amm.lp_amount,
            calc_pnl_x: target_orders.calc_pnl_x,
            calc_pnl_y: target_orders.calc_pnl_y,
            out_coin: coin_amount,
            out_pc: pc_amount,
        });
        if withdraw.amount == 0 || coin_amount == 0 || pc_amount == 0 {
            return Err(AmmError::InvalidInput.into());
        }

        if coin_amount < amm_coin_vault.amount && pc_amount < amm_pc_vault.amount {
            if withdraw.min_coin_amount.is_some() && withdraw.min_pc_amount.is_some() {
                if withdraw.min_coin_amount.unwrap() > coin_amount
                    || withdraw.min_pc_amount.unwrap() > pc_amount
                {
                    return Err(AmmError::ExceededSlippage.into());
                }
            }
            Invokers::token_transfer_with_authority(
                token_program_info.clone(),
                amm_coin_vault_info.clone(),
                user_dest_coin_info.clone(),
                amm_authority_info.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
                coin_amount,
            )?;
            Invokers::token_transfer_with_authority(
                token_program_info.clone(),
                amm_pc_vault_info.clone(),
                user_dest_pc_info.clone(),
                amm_authority_info.clone(),
                AUTHORITY_AMM,
                amm.nonce as u8,
                pc_amount,
            )?;
            Invokers::token_burn(
                token_program_info.clone(),
                user_source_lp_info.clone(),
                amm_lp_mint_info.clone(),
                source_lp_owner_info.clone(),
                withdraw.amount,
            )?;
            amm.lp_amount = amm.lp_amount.checked_sub(withdraw.amount).unwrap();
        } else {
            // calc error
            return Err(AmmError::TakePnlError.into());
        }

        // step4: update target_orders.calc_pnl_x & target_orders.calc_pnl_y
        target_orders.calc_pnl_x = x1
            .checked_sub(Calculator::normalize_decimal_v2(
                pc_amount,
                amm.pc_decimals,
                amm.sys_decimal_value,
            ))
            .unwrap()
            .checked_sub(U128::from(delta_x))
            .unwrap()
            .as_u128();
        target_orders.calc_pnl_y = y1
            .checked_sub(Calculator::normalize_decimal_v2(
                coin_amount,
                amm.coin_decimals,
                amm.sys_decimal_value,
            ))
            .unwrap()
            .checked_sub(U128::from(delta_y))
            .unwrap()
            .as_u128();
        amm.recent_epoch = Clock::get()?.epoch;
        Ok(())
    }

    pub fn process_swap_base_in(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        swap: SwapInstructionBaseIn,
    ) -> ProgramResult {
        const ACCOUNT_LEN: usize = 17;
        let input_account_len = accounts.len();
        if input_account_len != ACCOUNT_LEN && input_account_len != ACCOUNT_LEN + 1 {
            return Err(AmmError::WrongAccountsNumber.into());
        }
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        if input_account_len == ACCOUNT_LEN + 1 {
            let _amm_target_orders_info = next_account_info(account_info_iter)?;
        }
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;

        let mut amm = AmmInfo::load_mut_checked(&amm_info, program_id)?;
        let enable_orderbook;
        if AmmStatus::from_u64(amm.status).orderbook_permission() {
            enable_orderbook = true;
        } else {
            enable_orderbook = false;
        }
        let market_info = next_account_info(account_info_iter)?;
        let market_bids_info = next_account_info(account_info_iter)?;
        let market_asks_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;
        let market_coin_vault_info = next_account_info(account_info_iter)?;
        let market_pc_vault_info = next_account_info(account_info_iter)?;
        let market_vault_signer = next_account_info(account_info_iter)?;

        let user_source_info = next_account_info(account_info_iter)?;
        let user_destination_info = next_account_info(account_info_iter)?;
        let user_source_owner = next_account_info(account_info_iter)?;
        if !user_source_owner.is_signer {
            return Err(AmmError::InvalidSignAccount.into());
        }
        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let spl_token_program_id = token_program_info.key;
        if *amm_authority_info.key
            != Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?
        {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        check_assert_eq!(
            *amm_coin_vault_info.key,
            amm.coin_vault,
            "coin_vault",
            AmmError::InvalidCoinVault
        );
        check_assert_eq!(
            *amm_pc_vault_info.key,
            amm.pc_vault,
            "pc_vault",
            AmmError::InvalidPCVault
        );

        if *user_source_info.key == amm.pc_vault || *user_source_info.key == amm.coin_vault {
            return Err(AmmError::InvalidUserToken.into());
        }
        if *user_destination_info.key == amm.pc_vault
            || *user_destination_info.key == amm.coin_vault
        {
            return Err(AmmError::InvalidUserToken.into());
        }

        let amm_coin_vault =
            Self::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
        let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;

        let user_source = Self::unpack_token_account(&user_source_info, spl_token_program_id)?;
        let user_destination =
            Self::unpack_token_account(&user_destination_info, spl_token_program_id)?;

        if !AmmStatus::from_u64(amm.status).swap_permission() {
            msg!(&format!("swap_base_in: status {}", amm.status));
            let clock = Clock::get()?;
            if amm.status == AmmStatus::OrderBookOnly.into_u64()
                && (clock.unix_timestamp as u64) >= amm.state_data.orderbook_to_init_time
            {
                amm.status = AmmStatus::Initialized.into_u64();
                msg!("swap_base_in: OrderBook to Initialized");
            } else {
                return Err(AmmError::InvalidStatus.into());
            }
        } else if amm.status == AmmStatus::WaitingTrade.into_u64() {
            let clock = Clock::get()?;
            if (clock.unix_timestamp as u64) < amm.state_data.pool_open_time {
                return Err(AmmError::InvalidStatus.into());
            } else {
                amm.status = AmmStatus::SwapOnly.into_u64();
                msg!("swap_base_in: WaitingTrade to SwapOnly");
            }
        }

        let total_pc_without_take_pnl;
        let total_coin_without_take_pnl;
        let mut bids: Vec<LeafNode> = Vec::new();
        let mut asks: Vec<LeafNode> = Vec::new();
        if enable_orderbook {
            check_assert_eq!(
                *amm_open_orders_info.key,
                amm.open_orders,
                "open_orders",
                AmmError::InvalidOpenOrders
            );
            check_assert_eq!(
                *market_program_info.key,
                amm.market_program,
                "market_program",
                AmmError::InvalidMarketProgram
            );
            check_assert_eq!(
                *market_info.key,
                amm.market,
                "market",
                AmmError::InvalidMarket
            );
            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            (bids, asks) = Self::get_amm_orders(&open_orders, bids_orders, asks_orders)?;
            (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &open_orders,
                    &amm,
                    &market_state,
                    &market_event_queue_info,
                    &amm_open_orders_info,
                )?;
        } else {
            (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl_no_orderbook(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &amm,
                )?;
        }

        let swap_direction;
        if user_source.mint == amm_coin_vault.mint && user_destination.mint == amm_pc_vault.mint {
            swap_direction = SwapDirection::Coin2PC
        } else if user_source.mint == amm_pc_vault.mint
            && user_destination.mint == amm_coin_vault.mint
        {
            swap_direction = SwapDirection::PC2Coin
        } else {
            return Err(AmmError::InvalidUserToken.into());
        }
        if user_source.amount < swap.amount_in {
            encode_ray_log(SwapBaseInLog {
                log_type: LogType::SwapBaseIn.into_u8(),
                amount_in: swap.amount_in,
                minimum_out: swap.minimum_amount_out,
                direction: swap_direction as u64,
                user_source: user_source.amount,
                pool_coin: total_coin_without_take_pnl,
                pool_pc: total_pc_without_take_pnl,
                out_amount: 0,
            });
            return Err(AmmError::InsufficientFunds.into());
        }
        let swap_fee = U128::from(swap.amount_in)
            .checked_mul(amm.fees.swap_fee_numerator.into())
            .unwrap()
            .checked_ceil_div(amm.fees.swap_fee_denominator.into())
            .unwrap()
            .0;
        let swap_in_after_deduct_fee = U128::from(swap.amount_in).checked_sub(swap_fee).unwrap();
        let swap_amount_out = Calculator::swap_token_amount_base_in(
            swap_in_after_deduct_fee,
            total_pc_without_take_pnl.into(),
            total_coin_without_take_pnl.into(),
            swap_direction,
        )
        .as_u64();
        encode_ray_log(SwapBaseInLog {
            log_type: LogType::SwapBaseIn.into_u8(),
            amount_in: swap.amount_in,
            minimum_out: swap.minimum_amount_out,
            direction: swap_direction as u64,
            user_source: user_source.amount,
            pool_coin: total_coin_without_take_pnl,
            pool_pc: total_pc_without_take_pnl,
            out_amount: swap_amount_out,
        });
        if swap_amount_out < swap.minimum_amount_out {
            return Err(AmmError::ExceededSlippage.into());
        }
        if swap_amount_out == 0 || swap.amount_in == 0 {
            return Err(AmmError::InvalidInput.into());
        }

        match swap_direction {
            SwapDirection::Coin2PC => {
                if swap_amount_out >= total_pc_without_take_pnl {
                    return Err(AmmError::InsufficientFunds.into());
                }

                if enable_orderbook {
                    // coin -> pc, need cancel buy order
                    if !bids.is_empty() {
                        let mut amm_order_ids_vec = Vec::new();
                        let mut order_ids = [0u64; 8];
                        let mut count = 0;
                        // fetch cancel order ids{
                        for order in bids.into_iter() {
                            order_ids[count] = order.client_order_id();
                            count += 1;
                            if count == 8 {
                                amm_order_ids_vec.push(order_ids);
                                order_ids = [0u64; 8];
                                count = 0;
                            }
                        }
                        if count != 0 {
                            amm_order_ids_vec.push(order_ids);
                        }
                        for ids in amm_order_ids_vec.iter() {
                            Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                                market_program_info.clone(),
                                market_info.clone(),
                                market_bids_info.clone(),
                                market_asks_info.clone(),
                                amm_open_orders_info.clone(),
                                amm_authority_info.clone(),
                                market_event_queue_info.clone(),
                                AUTHORITY_AMM,
                                amm.nonce as u8,
                                *ids,
                            )?;
                        }
                    }

                    if swap_amount_out > amm_pc_vault.amount {
                        // need settle funds
                        Invokers::invoke_dex_settle_funds(
                            market_program_info.clone(),
                            market_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_authority_info.clone(),
                            market_coin_vault_info.clone(),
                            market_pc_vault_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            market_vault_signer.clone(),
                            token_program_info.clone(),
                            Some(&amm_pc_vault_info.clone()),
                            AUTHORITY_AMM,
                            amm.nonce as u8,
                        )?;
                    }
                }
                // deposit source coin to amm_coin_vault
                Invokers::token_transfer(
                    token_program_info.clone(),
                    user_source_info.clone(),
                    amm_coin_vault_info.clone(),
                    user_source_owner.clone(),
                    swap.amount_in,
                )?;
                // withdraw amm_pc_vault to destination pc
                Invokers::token_transfer_with_authority(
                    token_program_info.clone(),
                    amm_pc_vault_info.clone(),
                    user_destination_info.clone(),
                    amm_authority_info.clone(),
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                    swap_amount_out,
                )?;
                // update state_data data
                amm.state_data.swap_coin_in_amount = amm
                    .state_data
                    .swap_coin_in_amount
                    .checked_add(swap.amount_in.into())
                    .unwrap();
                amm.state_data.swap_pc_out_amount = amm
                    .state_data
                    .swap_pc_out_amount
                    .checked_add(swap_amount_out.into())
                    .unwrap();
                // charge coin as swap fee
                amm.state_data.swap_acc_coin_fee = amm
                    .state_data
                    .swap_acc_coin_fee
                    .checked_add(swap_fee.as_u64())
                    .unwrap();
            }
            SwapDirection::PC2Coin => {
                if swap_amount_out >= total_coin_without_take_pnl {
                    return Err(AmmError::InsufficientFunds.into());
                }

                if enable_orderbook {
                    // pc -> coin, need cancel sell order
                    if !asks.is_empty() {
                        let mut amm_order_ids_vec = Vec::new();
                        let mut order_ids = [0u64; 8];
                        let mut count = 0;
                        // fetch cancel order ids{
                        for order in asks.into_iter() {
                            order_ids[count] = order.client_order_id();
                            count += 1;
                            if count == 8 {
                                amm_order_ids_vec.push(order_ids);
                                order_ids = [0u64; 8];
                                count = 0;
                            }
                        }
                        if count != 0 {
                            amm_order_ids_vec.push(order_ids);
                        }
                        for ids in amm_order_ids_vec.iter() {
                            Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                                market_program_info.clone(),
                                market_info.clone(),
                                market_bids_info.clone(),
                                market_asks_info.clone(),
                                amm_open_orders_info.clone(),
                                amm_authority_info.clone(),
                                market_event_queue_info.clone(),
                                AUTHORITY_AMM,
                                amm.nonce as u8,
                                *ids,
                            )?;
                        }
                    }

                    if swap_amount_out > amm_coin_vault.amount {
                        Invokers::invoke_dex_settle_funds(
                            market_program_info.clone(),
                            market_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_authority_info.clone(),
                            market_coin_vault_info.clone(),
                            market_pc_vault_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            market_vault_signer.clone(),
                            token_program_info.clone(),
                            Some(&amm_pc_vault_info.clone()),
                            AUTHORITY_AMM,
                            amm.nonce as u8,
                        )?;
                    }
                }
                // deposit source pc to amm_pc_vault
                Invokers::token_transfer(
                    token_program_info.clone(),
                    user_source_info.clone(),
                    amm_pc_vault_info.clone(),
                    user_source_owner.clone(),
                    swap.amount_in,
                )?;
                // withdraw amm_coin_vault to destination coin
                Invokers::token_transfer_with_authority(
                    token_program_info.clone(),
                    amm_coin_vault_info.clone(),
                    user_destination_info.clone(),
                    amm_authority_info.clone(),
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                    swap_amount_out,
                )?;
                // update state_data data
                amm.state_data.swap_pc_in_amount = amm
                    .state_data
                    .swap_pc_in_amount
                    .checked_add(swap.amount_in.into())
                    .unwrap();
                amm.state_data.swap_coin_out_amount = amm
                    .state_data
                    .swap_coin_out_amount
                    .checked_add(swap_amount_out.into())
                    .unwrap();
                // charge pc as swap fee
                amm.state_data.swap_acc_pc_fee = amm
                    .state_data
                    .swap_acc_pc_fee
                    .checked_add(swap_fee.as_u64())
                    .unwrap();
            }
        };
        amm.recent_epoch = Clock::get()?.epoch;

        Ok(())
    }

    pub fn process_swap_base_out(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        swap: SwapInstructionBaseOut,
    ) -> ProgramResult {
        const SWAP_ACCOUNT_NUM: usize = 17;
        let input_account_len = accounts.len();
        if input_account_len != SWAP_ACCOUNT_NUM && input_account_len != SWAP_ACCOUNT_NUM + 1 {
            return Err(AmmError::WrongAccountsNumber.into());
        }
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        if input_account_len == SWAP_ACCOUNT_NUM + 1 {
            let _amm_target_orders_info = next_account_info(account_info_iter)?;
        }
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;

        let mut amm = AmmInfo::load_mut_checked(&amm_info, program_id)?;
        let enable_orderbook;
        if AmmStatus::from_u64(amm.status).orderbook_permission() {
            enable_orderbook = true;
        } else {
            enable_orderbook = false;
        }

        let market_info = next_account_info(account_info_iter)?;
        let market_bids_info = next_account_info(account_info_iter)?;
        let market_asks_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;
        let market_coin_vault_info = next_account_info(account_info_iter)?;
        let market_pc_vault_info = next_account_info(account_info_iter)?;
        let market_vault_signer = next_account_info(account_info_iter)?;

        let user_source_info = next_account_info(account_info_iter)?;
        let user_destination_info = next_account_info(account_info_iter)?;
        let user_source_owner = next_account_info(account_info_iter)?;
        if !user_source_owner.is_signer {
            return Err(AmmError::InvalidSignAccount.into());
        }

        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let spl_token_program_id = token_program_info.key;
        let authority = Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?;
        check_assert_eq!(
            *amm_authority_info.key,
            authority,
            "authority",
            AmmError::InvalidProgramAddress
        );
        check_assert_eq!(
            *amm_coin_vault_info.key,
            amm.coin_vault,
            "coin_vault",
            AmmError::InvalidCoinVault
        );
        check_assert_eq!(
            *amm_pc_vault_info.key,
            amm.pc_vault,
            "pc_vault",
            AmmError::InvalidPCVault
        );

        if *user_source_info.key == amm.pc_vault || *user_source_info.key == amm.coin_vault {
            return Err(AmmError::InvalidUserToken.into());
        }
        if *user_destination_info.key == amm.pc_vault
            || *user_destination_info.key == amm.coin_vault
        {
            return Err(AmmError::InvalidUserToken.into());
        }

        let amm_coin_vault =
            Self::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
        let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;

        let user_source = Self::unpack_token_account(&user_source_info, spl_token_program_id)?;
        let user_destination =
            Self::unpack_token_account(&user_destination_info, spl_token_program_id)?;

        if !AmmStatus::from_u64(amm.status).swap_permission() {
            msg!(&format!("swap_base_out: status {}", amm.status));
            let clock = Clock::get()?;
            if amm.status == AmmStatus::OrderBookOnly.into_u64()
                && (clock.unix_timestamp as u64) >= amm.state_data.orderbook_to_init_time
            {
                amm.status = AmmStatus::Initialized.into_u64();
                msg!("swap_base_out: OrderBook to Initialized");
            } else {
                return Err(AmmError::InvalidStatus.into());
            }
        } else if amm.status == AmmStatus::WaitingTrade.into_u64() {
            let clock = Clock::get()?;
            if (clock.unix_timestamp as u64) < amm.state_data.pool_open_time {
                return Err(AmmError::InvalidStatus.into());
            } else {
                amm.status = AmmStatus::SwapOnly.into_u64();
                msg!("swap_base_out: WaitingTrade to SwapOnly");
            }
        }

        let total_pc_without_take_pnl;
        let total_coin_without_take_pnl;
        let mut bids: Vec<LeafNode> = Vec::new();
        let mut asks: Vec<LeafNode> = Vec::new();
        if enable_orderbook {
            check_assert_eq!(
                *amm_open_orders_info.key,
                amm.open_orders,
                "open_orders",
                AmmError::InvalidOpenOrders
            );
            check_assert_eq!(
                *market_program_info.key,
                amm.market_program,
                "market_program",
                AmmError::InvalidMarketProgram
            );
            check_assert_eq!(
                *market_info.key,
                amm.market,
                "market",
                AmmError::InvalidMarket
            );
            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            (bids, asks) = Self::get_amm_orders(&open_orders, bids_orders, asks_orders)?;
            (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &open_orders,
                    &amm,
                    &market_state,
                    &market_event_queue_info,
                    &amm_open_orders_info,
                )?;
        } else {
            (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl_no_orderbook(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &amm,
                )?;
        }

        let swap_direction;
        if user_source.mint == amm_coin_vault.mint && user_destination.mint == amm_pc_vault.mint {
            swap_direction = SwapDirection::Coin2PC
        } else if user_source.mint == amm_pc_vault.mint
            && user_destination.mint == amm_coin_vault.mint
        {
            swap_direction = SwapDirection::PC2Coin
        } else {
            return Err(AmmError::InvalidUserToken.into());
        }

        let swap_in_before_add_fee = Calculator::swap_token_amount_base_out(
            swap.amount_out.into(),
            total_pc_without_take_pnl.into(),
            total_coin_without_take_pnl.into(),
            swap_direction,
        );
        // swap_in_after_add_fee * (1 - 0.0025) = swap_in_before_add_fee
        // swap_in_after_add_fee = swap_in_before_add_fee / (1 - 0.0025)
        let swap_in_after_add_fee = swap_in_before_add_fee
            .checked_mul(amm.fees.swap_fee_denominator.into())
            .unwrap()
            .checked_ceil_div(
                (amm.fees
                    .swap_fee_denominator
                    .checked_sub(amm.fees.swap_fee_numerator)
                    .unwrap())
                .into(),
            )
            .unwrap()
            .0
            .as_u64();
        let swap_fee = swap_in_after_add_fee
            .checked_sub(swap_in_before_add_fee.as_u64())
            .unwrap();
        encode_ray_log(SwapBaseOutLog {
            log_type: LogType::SwapBaseOut.into_u8(),
            max_in: swap.max_amount_in,
            amount_out: swap.amount_out,
            direction: swap_direction as u64,
            user_source: user_source.amount,
            pool_coin: total_coin_without_take_pnl,
            pool_pc: total_pc_without_take_pnl,
            deduct_in: swap_in_after_add_fee,
        });
        if user_source.amount < swap_in_after_add_fee {
            return Err(AmmError::InsufficientFunds.into());
        }
        if swap.max_amount_in < swap_in_after_add_fee {
            return Err(AmmError::ExceededSlippage.into());
        }
        if swap_in_after_add_fee == 0 || swap.amount_out == 0 {
            return Err(AmmError::InvalidInput.into());
        }

        match swap_direction {
            SwapDirection::Coin2PC => {
                if swap.amount_out >= total_pc_without_take_pnl {
                    return Err(AmmError::InsufficientFunds.into());
                }

                if enable_orderbook {
                    // coin -> pc, need cancel buy order
                    if !bids.is_empty() {
                        let mut amm_order_ids_vec = Vec::new();
                        let mut order_ids = [0u64; 8];
                        let mut count = 0;
                        // fetch cancel order ids
                        for order in bids.into_iter() {
                            order_ids[count] = order.client_order_id();
                            count += 1;
                            if count == 8 {
                                amm_order_ids_vec.push(order_ids);
                                order_ids = [0u64; 8];
                                count = 0;
                            }
                        }
                        if count != 0 {
                            amm_order_ids_vec.push(order_ids);
                        }
                        for ids in amm_order_ids_vec.iter() {
                            Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                                market_program_info.clone(),
                                market_info.clone(),
                                market_bids_info.clone(),
                                market_asks_info.clone(),
                                amm_open_orders_info.clone(),
                                amm_authority_info.clone(),
                                market_event_queue_info.clone(),
                                AUTHORITY_AMM,
                                amm.nonce as u8,
                                *ids,
                            )?;
                        }
                    }
                    if swap.amount_out > amm_pc_vault.amount {
                        // need settle funds
                        Invokers::invoke_dex_settle_funds(
                            market_program_info.clone(),
                            market_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_authority_info.clone(),
                            market_coin_vault_info.clone(),
                            market_pc_vault_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            market_vault_signer.clone(),
                            token_program_info.clone(),
                            Some(&amm_pc_vault_info.clone()),
                            AUTHORITY_AMM,
                            amm.nonce as u8,
                        )?;
                    }
                }
                // deposit source coin to amm_coin_vault
                Invokers::token_transfer(
                    token_program_info.clone(),
                    user_source_info.clone(),
                    amm_coin_vault_info.clone(),
                    user_source_owner.clone(),
                    swap_in_after_add_fee,
                )?;
                // withdraw amm_pc_vault to destination pc
                Invokers::token_transfer_with_authority(
                    token_program_info.clone(),
                    amm_pc_vault_info.clone(),
                    user_destination_info.clone(),
                    amm_authority_info.clone(),
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                    swap.amount_out,
                )?;
                // update state_data data
                amm.state_data.swap_coin_in_amount = amm
                    .state_data
                    .swap_coin_in_amount
                    .checked_add(swap_in_after_add_fee.into())
                    .unwrap();
                amm.state_data.swap_pc_out_amount = amm
                    .state_data
                    .swap_pc_out_amount
                    .checked_add(Calculator::to_u128(swap.amount_out)?)
                    .unwrap();
                // charge coin as swap fee
                amm.state_data.swap_acc_coin_fee = amm
                    .state_data
                    .swap_acc_coin_fee
                    .checked_add(swap_fee)
                    .unwrap();
            }
            SwapDirection::PC2Coin => {
                if swap.amount_out >= total_coin_without_take_pnl {
                    return Err(AmmError::InsufficientFunds.into());
                }

                if enable_orderbook {
                    // pc -> coin, need cancel sell order
                    if !asks.is_empty() {
                        let mut amm_order_ids_vec = Vec::new();
                        let mut order_ids = [0u64; 8];
                        let mut count = 0;
                        // fetch cancel order ids
                        for order in asks.into_iter() {
                            order_ids[count] = order.client_order_id();
                            count += 1;
                            if count == 8 {
                                amm_order_ids_vec.push(order_ids);
                                order_ids = [0u64; 8];
                                count = 0;
                            }
                        }
                        if count != 0 {
                            amm_order_ids_vec.push(order_ids);
                        }
                        for ids in amm_order_ids_vec.iter() {
                            Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                                market_program_info.clone(),
                                market_info.clone(),
                                market_bids_info.clone(),
                                market_asks_info.clone(),
                                amm_open_orders_info.clone(),
                                amm_authority_info.clone(),
                                market_event_queue_info.clone(),
                                AUTHORITY_AMM,
                                amm.nonce as u8,
                                *ids,
                            )?;
                        }
                    }
                    if swap.amount_out > amm_coin_vault.amount {
                        Invokers::invoke_dex_settle_funds(
                            market_program_info.clone(),
                            market_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_authority_info.clone(),
                            market_asks_info.clone(),
                            market_pc_vault_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            market_vault_signer.clone(),
                            token_program_info.clone(),
                            Some(&amm_pc_vault_info.clone()),
                            AUTHORITY_AMM,
                            amm.nonce as u8,
                        )?;
                    }
                }

                // deposit source pc to amm_pc_vault
                Invokers::token_transfer(
                    token_program_info.clone(),
                    user_source_info.clone(),
                    amm_pc_vault_info.clone(),
                    user_source_owner.clone(),
                    swap_in_after_add_fee,
                )?;
                // withdraw amm_coin_vault to destination coin
                Invokers::token_transfer_with_authority(
                    token_program_info.clone(),
                    amm_coin_vault_info.clone(),
                    user_destination_info.clone(),
                    amm_authority_info.clone(),
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                    swap.amount_out,
                )?;
                // update state_data data
                amm.state_data.swap_pc_in_amount = amm
                    .state_data
                    .swap_pc_in_amount
                    .checked_add(swap_in_after_add_fee.into())
                    .unwrap();
                amm.state_data.swap_coin_out_amount = amm
                    .state_data
                    .swap_coin_out_amount
                    .checked_add(swap.amount_out.into())
                    .unwrap();
                // charge pc as swap fee
                amm.state_data.swap_acc_pc_fee = amm
                    .state_data
                    .swap_acc_pc_fee
                    .checked_add(swap_fee)
                    .unwrap();
            }
        };
        amm.recent_epoch = Clock::get()?.epoch;

        Ok(())
    }

    pub fn process_migrate_to_openbook(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
    ) -> ProgramResult {
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;
        let system_program_info = next_account_info(account_info_iter)?;
        let rent_sysvar_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_bids_info = next_account_info(account_info_iter)?;
        let market_asks_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;
        let market_coin_vault_info = next_account_info(account_info_iter)?;
        let market_pc_vault_info = next_account_info(account_info_iter)?;
        let market_vault_signer = next_account_info(account_info_iter)?;

        let new_amm_open_orders_info = next_account_info(account_info_iter)?;
        let new_market_program_info = next_account_info(account_info_iter)?;
        let new_market_info = next_account_info(account_info_iter)?;
        let admin_info = next_account_info(account_info_iter)?;
        let mut amm = AmmInfo::load_mut_checked(&amm_info, program_id)?;
        if !admin_info.is_signer || *admin_info.key != config_feature::amm_owner::ID {
            return Err(AmmError::InvalidSignAccount.into());
        }
        let authority = Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?;
        check_assert_eq!(
            *amm_authority_info.key,
            authority,
            "authority",
            AmmError::InvalidProgramAddress
        );
        let spl_token_program_id = token_program_info.key;
        check_assert_eq!(
            *spl_token_program_id,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        check_assert_eq!(
            *system_program_info.key,
            solana_program::system_program::id(),
            "sys_program",
            AmmError::InvalidSysProgramAddress
        );
        // old account check
        check_assert_eq!(
            *amm_coin_vault_info.key,
            amm.coin_vault,
            "coin_vault",
            AmmError::InvalidCoinVault
        );
        check_assert_eq!(
            *amm_pc_vault_info.key,
            amm.pc_vault,
            "pc_vault",
            AmmError::InvalidPCVault
        );
        check_assert_eq!(
            *amm_target_orders_info.key,
            amm.target_orders,
            "target_orders",
            AmmError::InvalidPCVault
        );
        check_assert_eq!(
            *market_info.key,
            amm.market,
            "market",
            AmmError::InvalidMarket
        );
        check_assert_eq!(
            *market_program_info.key,
            amm.market_program,
            "market_program",
            AmmError::InvalidMarketProgram
        );
        check_assert_eq!(
            *amm_open_orders_info.key,
            amm.open_orders,
            "open_orders",
            AmmError::InvalidOpenOrders
        );
        let mut target =
            TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
        let pnl_pc_amount = Calculator::restore_decimal(
            target.calc_pnl_x.into(),
            amm.pc_decimals,
            amm.sys_decimal_value,
        )
        .as_u64();
        let pnl_coin_amount = Calculator::restore_decimal(
            target.calc_pnl_y.into(),
            amm.coin_decimals,
            amm.sys_decimal_value,
        )
        .as_u64();
        // cancel amm orders in old market
        Self::do_cancel_amm_orders(
            &amm,
            amm_authority_info,
            amm_open_orders_info,
            market_program_info,
            market_info,
            market_bids_info,
            market_asks_info,
            market_event_queue_info,
            AUTHORITY_AMM,
        )?;
        // settle assets
        Invokers::invoke_dex_settle_funds(
            market_program_info.clone(),
            market_info.clone(),
            amm_open_orders_info.clone(),
            amm_authority_info.clone(),
            market_coin_vault_info.clone(),
            market_pc_vault_info.clone(),
            amm_coin_vault_info.clone(),
            amm_pc_vault_info.clone(),
            market_vault_signer.clone(),
            token_program_info.clone(),
            None,
            AUTHORITY_AMM,
            amm.nonce as u8,
        )?;
        let (market_state, open_orders) = Self::load_serum_market_order(
            market_info,
            amm_open_orders_info,
            amm_authority_info,
            &amm,
            false,
        )?;
        if identity(market_state.coin_mint) != amm.coin_vault_mint.to_aligned_bytes() {
            return Err(AmmError::InvalidCoinMint.into());
        }
        if identity(market_state.pc_mint) != amm.pc_vault_mint.to_aligned_bytes() {
            return Err(AmmError::InvalidPCMint.into());
        }
        // Partial filled without consumed assets exclude native total in open_orders will not be settled back to amm.
        // It is necessary to check that all assets have been indeed settled back to amm
        let (pc_total_in_serum, coin_total_in_serum) = Calculator::calc_exact_vault_in_serum(
            &open_orders,
            &market_state,
            market_event_queue_info,
            amm_open_orders_info,
        )?;
        if pc_total_in_serum != 0 || coin_total_in_serum != 0 {
            // Invokers::invoke_dex_close_open_orders(
            //     serum_dex_info.clone(),
            //     amm_open_orders_info.clone(),
            //     authority_info.clone(),
            //     admin_info.clone(),
            //     market_info.clone(),
            //     AUTHORITY_AMM,
            //     amm.nonce as u8,
            // )?;
            msg!(
                "{}, {}, {}, {}, {:?}",
                pc_total_in_serum,
                coin_total_in_serum,
                identity(open_orders.native_pc_total),
                identity(open_orders.native_coin_total),
                identity(open_orders.free_slot_bits)
            );
            return Err(AmmError::UnknownAmmError.into());
        }
        // check new dex
        check_assert_eq!(
            *new_market_program_info.key,
            config_feature::openbook_program::id(),
            "new_market_program",
            AmmError::InvalidMarketProgram
        );
        let new_market_pc_lot_size;
        let new_market_coin_lot_size;
        {
            let new_market_state = Market::load(
                new_market_info,
                &config_feature::openbook_program::id(),
                false,
            )?;
            new_market_pc_lot_size = new_market_state.pc_lot_size;
            new_market_coin_lot_size = new_market_state.coin_lot_size;
            if identity(new_market_state.coin_mint) != amm.coin_vault_mint.to_aligned_bytes() {
                return Err(AmmError::InvalidCoinMint.into());
            }
            if identity(new_market_state.pc_mint) != amm.pc_vault_mint.to_aligned_bytes() {
                return Err(AmmError::InvalidPCMint.into());
            }
        }
        // create amm open order account
        Self::generate_amm_associated_account(
            program_id,
            new_market_program_info.key,
            new_market_info,
            new_amm_open_orders_info,
            admin_info,
            system_program_info,
            rent_sysvar_info,
            OPEN_ORDER_ASSOCIATED_SEED,
            size_of::<serum_dex::state::OpenOrders>() + 12,
        )?;
        // init open orders account
        Invokers::invoke_dex_init_open_orders(
            new_market_program_info.clone(),
            new_amm_open_orders_info.clone(),
            amm_authority_info.clone(),
            new_market_info.clone(),
            rent_sysvar_info.clone(),
            AUTHORITY_AMM,
            amm.nonce as u8,
        )?;
        // update pool
        amm.market_program = *new_market_program_info.key;
        amm.market = *new_market_info.key;
        amm.open_orders = *new_amm_open_orders_info.key;
        let nonce = amm.nonce as u8;
        let pool_open_time = amm.state_data.pool_open_time;
        let coin_decimals = amm.coin_decimals as u8;
        let pc_decimals = amm.pc_decimals as u8;
        amm.initialize(
            nonce,
            pool_open_time,
            coin_decimals,
            pc_decimals,
            new_market_coin_lot_size,
            new_market_pc_lot_size,
        )?;
        amm.status = AmmStatus::WaitingTrade.into_u64();
        amm.reset_flag = AmmResetFlag::ResetYes.into_u64();

        if new_market_coin_lot_size != market_state.coin_lot_size
            || new_market_pc_lot_size != market_state.pc_lot_size
        {
            // market lot size is different, calc_pnl_x & calc_pnl_x must update
            target.calc_pnl_x = Calculator::normalize_decimal_v2(
                pnl_pc_amount,
                amm.pc_decimals,
                amm.sys_decimal_value,
            )
            .as_u128();
            target.calc_pnl_y = Calculator::normalize_decimal_v2(
                pnl_coin_amount,
                amm.coin_decimals,
                amm.sys_decimal_value,
            )
            .as_u128();
        }
        amm.recent_epoch = Clock::get()?.epoch;

        Ok(())
    }

    /// withdraw_srm
    pub fn process_withdraw_srm(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        withdrawsrm: WithdrawSrmInstruction,
    ) -> ProgramResult {
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_owner_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let srm_token_info = next_account_info(account_info_iter)?;
        let dest_srm_token_info = next_account_info(account_info_iter)?;

        msg!("withdraw_srm: {}", withdrawsrm.amount);
        let amm = AmmInfo::load_checked(&amm_info, program_id)?;
        if amm.status == AmmStatus::Uninitialized.into_u64() {
            msg!(&format!("withdraw_srm: status {}", amm.status));
            return Err(AmmError::InvalidStatus.into());
        }
        if !amm_owner_info.is_signer || *amm_owner_info.key != config_feature::amm_owner::ID {
            return Err(AmmError::InvalidSignAccount.into());
        }
        // check_assert_eq!(
        //     *amm_owner_info.key,
        //     amm.amm_owner,
        //     "amm_owner",
        //     AmmError::InvalidOwner
        // );
        check_assert_eq!(
            *token_program_info.key,
            spl_token::id(),
            "spl_token_program",
            AmmError::InvalidSplTokenProgram
        );
        let spl_token_program_id = token_program_info.key;
        let authority = Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?;
        check_assert_eq!(
            *amm_authority_info.key,
            authority,
            "authority",
            AmmError::InvalidProgramAddress
        );
        let srm_token = Self::unpack_token_account(&srm_token_info, spl_token_program_id)?;
        check_assert_eq!(
            srm_token.owner,
            *amm_authority_info.key,
            "srm_token_owner",
            AmmError::InvalidOwner
        );
        if srm_token.mint != srm_token::id() && srm_token.mint != msrm_token::id() {
            return Err(AmmError::InvalidSrmMint.into());
        }
        let dest_srm_token =
            Self::unpack_token_account(&dest_srm_token_info, spl_token_program_id)?;
        check_assert_eq!(
            srm_token.mint,
            dest_srm_token.mint,
            "srm_token_mint",
            AmmError::InvalidInput
        );
        if withdrawsrm.amount > srm_token.amount {
            return Err(AmmError::InsufficientFunds.into());
        }

        Invokers::token_transfer_with_authority(
            token_program_info.clone(),
            srm_token_info.clone(),
            dest_srm_token_info.clone(),
            amm_authority_info.clone(),
            AUTHORITY_AMM,
            amm.nonce as u8,
            withdrawsrm.amount,
        )?;

        Ok(())
    }

    fn simulate_pool_info(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
    ) -> Result<GetPoolData, ProgramError> {
        const ACCOUNT_LEN: usize = 8;
        let input_account_len = accounts.len();
        let account_info_iter = &mut accounts.iter();
        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        let amm_lp_mint_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;

        let amm = AmmInfo::load_checked(&amm_info, program_id)?;
        let authority = Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?;
        Self::check_account_readonly(amm_info)?;
        Self::check_account_readonly(amm_open_orders_info)?;
        Self::check_account_readonly(amm_coin_vault_info)?;
        Self::check_account_readonly(amm_pc_vault_info)?;
        Self::check_account_readonly(amm_lp_mint_info)?;
        Self::check_account_readonly(market_info)?;
        Self::check_account_readonly(market_event_queue_info)?;
        check_assert_eq!(
            *amm_authority_info.key,
            authority,
            "authority",
            AmmError::InvalidProgramAddress
        );
        check_assert_eq!(
            *amm_coin_vault_info.key,
            amm.coin_vault,
            "coin_vault",
            AmmError::InvalidCoinVault
        );
        check_assert_eq!(
            *amm_pc_vault_info.key,
            amm.pc_vault,
            "pc_vault",
            AmmError::InvalidPCVault
        );
        check_assert_eq!(
            *amm_lp_mint_info.key,
            amm.lp_mint,
            "lp_mint",
            AmmError::InvalidPoolMint
        );
        check_assert_eq!(
            *amm_open_orders_info.key,
            amm.open_orders,
            "open_orders",
            AmmError::InvalidOpenOrders
        );
        check_assert_eq!(
            *market_info.key,
            amm.market,
            "market",
            AmmError::InvalidMarket
        );
        let pnl_pc_amount;
        let pnl_coin_amount;
        if input_account_len == ACCOUNT_LEN + 1 {
            let target_orders_info = next_account_info(account_info_iter)?;
            check_assert_eq!(
                *target_orders_info.key,
                amm.target_orders,
                "target_orders",
                AmmError::InvalidTargetOrders
            );
            let target = TargetOrders::load_checked(&target_orders_info, program_id, amm_info.key)?;
            pnl_pc_amount = Calculator::restore_decimal(
                target.calc_pnl_x.into(),
                amm.pc_decimals,
                amm.sys_decimal_value,
            )
            .as_u64();
            pnl_coin_amount = Calculator::restore_decimal(
                target.calc_pnl_y.into(),
                amm.coin_decimals,
                amm.sys_decimal_value,
            )
            .as_u64();
        } else {
            pnl_pc_amount = 0;
            pnl_coin_amount = 0;
        }

        let amm_coin_vault = Self::unpack_token_account(&amm_coin_vault_info, &spl_token::id())?;
        let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, &spl_token::id())?;
        let lp_mint = Self::unpack_mint(&amm_lp_mint_info, &spl_token::id())?;
        let (market_state, open_orders) = Self::load_serum_market_order(
            market_info,
            amm_open_orders_info,
            amm_authority_info,
            &amm,
            false,
        )?;
        if identity(market_state.coin_mint) != amm_coin_vault.mint.to_aligned_bytes() {
            return Err(AmmError::InvalidCoinMint.into());
        }
        if identity(market_state.pc_mint) != amm_pc_vault.mint.to_aligned_bytes() {
            return Err(AmmError::InvalidPCMint.into());
        }
        let (total_pc_without_take_pnl, total_coin_without_take_pnl) =
            Calculator::calc_total_without_take_pnl(
                amm_pc_vault.amount,
                amm_coin_vault.amount,
                &open_orders,
                &amm,
                &market_state,
                &market_event_queue_info,
                &amm_open_orders_info,
            )?;
        let pool_info_data = GetPoolData {
            status: amm.status,
            coin_decimals: amm.coin_decimals,
            pc_decimals: amm.pc_decimals,
            lp_decimals: lp_mint.decimals.into(),
            pool_pc_amount: total_pc_without_take_pnl,
            pool_coin_amount: total_coin_without_take_pnl,
            pnl_pc_amount,
            pnl_coin_amount,
            pool_lp_supply: amm.lp_amount,
            pool_open_time: amm.state_data.pool_open_time,
            amm_id: amm_info.key.to_string(),
        };
        return Ok(pool_info_data);
    }

    fn simulate_swap_base_in(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        simulate: SimulateInstruction,
    ) -> Result<GetSwapBaseInData, ProgramError> {
        let account_info_iter = &mut accounts.iter();

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        let amm_lp_mint_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;

        let user_source_info = next_account_info(account_info_iter)?;
        let user_destination_info = next_account_info(account_info_iter)?;
        let user_source_owner = next_account_info(account_info_iter)?;

        Self::check_account_readonly(amm_info)?;
        Self::check_account_readonly(amm_open_orders_info)?;
        Self::check_account_readonly(amm_target_orders_info)?;
        Self::check_account_readonly(amm_coin_vault_info)?;
        Self::check_account_readonly(amm_pc_vault_info)?;
        Self::check_account_readonly(amm_lp_mint_info)?;
        Self::check_account_readonly(market_info)?;
        Self::check_account_readonly(market_event_queue_info)?;
        Self::check_account_readonly(user_source_info)?;
        Self::check_account_readonly(user_destination_info)?;
        Self::check_account_readonly(user_source_owner)?;

        let mut swap_base_in: GetSwapBaseInData = Default::default();
        if let Some(swap) = simulate.swap_base_in_value {
            swap_base_in.amount_in = swap.amount_in;

            if !user_source_owner.is_signer {
                return Err(AmmError::InvalidSignAccount.into());
            }
            let amm = AmmInfo::load_checked(&amm_info, program_id)?;

            if !AmmStatus::from_u64(amm.status).swap_permission() {
                msg!("simulate_swap_base_in: status {}", amm.status);
                return Err(AmmError::InvalidStatus.into());
            }
            let authority = Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?;
            check_assert_eq!(
                *amm_authority_info.key,
                authority,
                "authority",
                AmmError::InvalidProgramAddress
            );
            check_assert_eq!(
                *amm_coin_vault_info.key,
                amm.coin_vault,
                "coin_vault",
                AmmError::InvalidCoinVault
            );
            check_assert_eq!(
                *amm_pc_vault_info.key,
                amm.pc_vault,
                "pc_vault",
                AmmError::InvalidPCVault
            );
            check_assert_eq!(
                *amm_open_orders_info.key,
                amm.open_orders,
                "open_orders",
                AmmError::InvalidOpenOrders
            );
            check_assert_eq!(
                *amm_target_orders_info.key,
                amm.target_orders,
                "target_orders",
                AmmError::InvalidTargetOrders
            );
            check_assert_eq!(
                *amm_lp_mint_info.key,
                amm.lp_mint,
                "lp_mint",
                AmmError::InvalidPoolMint
            );
            check_assert_eq!(
                *market_program_info.key,
                amm.market_program,
                "market_program",
                AmmError::InvalidMarketProgram
            );
            check_assert_eq!(
                *market_info.key,
                amm.market,
                "market",
                AmmError::InvalidMarket
            );

            if *user_source_info.key == amm.pc_vault || *user_source_info.key == amm.coin_vault {
                return Err(AmmError::InvalidInput.into());
            }
            if *user_destination_info.key == amm.pc_vault
                || *user_destination_info.key == amm.coin_vault
            {
                return Err(AmmError::InvalidInput.into());
            }

            let (market_state, open_orders) = Self::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;

            let amm_coin_vault =
                Self::unpack_token_account(&amm_coin_vault_info, &spl_token::id())?;
            let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, &spl_token::id())?;
            let lp_mint = Self::unpack_mint(&amm_lp_mint_info, &spl_token::id())?;

            let user_source = Self::unpack_token_account(&user_source_info, &spl_token::id())?;
            let user_destination =
                Self::unpack_token_account(&user_destination_info, &spl_token::id())?;

            // let target_orders = TargetOrders::load_mut_checked(&target_orders_info, program_id, amm_info.key)?;

            let swap_direction;
            if user_source.mint == amm_coin_vault.mint && user_destination.mint == amm_pc_vault.mint
            {
                swap_direction = SwapDirection::Coin2PC
            } else if user_source.mint == amm_pc_vault.mint
                && user_destination.mint == amm_coin_vault.mint
            {
                swap_direction = SwapDirection::PC2Coin
            } else {
                return Err(AmmError::InvalidInput.into());
            }
            let (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &open_orders,
                    &amm,
                    &market_state,
                    &market_event_queue_info,
                    &amm_open_orders_info,
                )?;
            swap_base_in.pool_data.status = amm.status;
            swap_base_in.pool_data.coin_decimals = amm.coin_decimals;
            swap_base_in.pool_data.pc_decimals = amm.pc_decimals;
            swap_base_in.pool_data.lp_decimals = lp_mint.decimals.into();
            swap_base_in.pool_data.pool_lp_supply = amm.lp_amount;
            swap_base_in.pool_data.pool_open_time = amm.state_data.pool_open_time;
            swap_base_in.pool_data.pool_pc_amount = total_pc_without_take_pnl;
            swap_base_in.pool_data.pool_coin_amount = total_coin_without_take_pnl;
            swap_base_in.pool_data.amm_id = amm_info.key.to_string();

            let swap_fee = U128::from(swap.amount_in)
                .checked_mul(amm.fees.swap_fee_numerator.into())
                .unwrap()
                .checked_ceil_div(amm.fees.swap_fee_denominator.into())
                .unwrap()
                .0;
            let swap_in_after_deduct_fee =
                U128::from(swap.amount_in).checked_sub(swap_fee).unwrap();
            let swap_amount_out = Calculator::swap_token_amount_base_in(
                swap_in_after_deduct_fee,
                total_pc_without_take_pnl.into(),
                total_coin_without_take_pnl.into(),
                swap_direction,
            )
            .as_u64();
            swap_base_in.minimum_amount_out = swap_amount_out;
            match swap_direction {
                SwapDirection::Coin2PC => {
                    // coin -> pc, need cancel buy order
                    let token_pc_after_swap = total_pc_without_take_pnl
                        .checked_sub(swap_amount_out)
                        .unwrap();
                    let token_coin_after_swap = total_coin_without_take_pnl
                        .checked_add(swap.amount_in)
                        .unwrap();

                    let swap_price_before = total_pc_without_take_pnl
                        .checked_div(total_coin_without_take_pnl)
                        .unwrap();
                    let swap_price_after = token_pc_after_swap
                        .checked_div(token_coin_after_swap)
                        .unwrap();
                    swap_base_in.price_impact =
                        (swap_price_before.checked_sub(swap_price_after).unwrap())
                            .checked_mul(1000000)
                            .unwrap()
                            .checked_div(swap_price_before)
                            .unwrap();
                }
                SwapDirection::PC2Coin => {
                    // pc -> coin, need cancel sell order
                    let token_pc_after_swap = total_pc_without_take_pnl
                        .checked_add(swap.amount_in)
                        .unwrap();
                    let token_coin_after_swap = total_coin_without_take_pnl
                        .checked_sub(swap_amount_out)
                        .unwrap();

                    let swap_price_before = total_pc_without_take_pnl
                        .checked_div(total_coin_without_take_pnl)
                        .unwrap();
                    let swap_price_after = token_pc_after_swap
                        .checked_div(token_coin_after_swap)
                        .unwrap();
                    swap_base_in.price_impact =
                        (swap_price_after.checked_sub(swap_price_before).unwrap())
                            .checked_mul(1000000)
                            .unwrap()
                            .checked_div(swap_price_before)
                            .unwrap();
                }
            }
        }
        return Ok(swap_base_in);
    }

    fn simulate_swap_base_out(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        simulate: SimulateInstruction,
    ) -> Result<GetSwapBaseOutData, ProgramError> {
        let account_info_iter = &mut accounts.iter();

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        let amm_lp_mint_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;

        let user_source_info = next_account_info(account_info_iter)?;
        let user_destination_info = next_account_info(account_info_iter)?;
        let user_source_owner = next_account_info(account_info_iter)?;

        Self::check_account_readonly(amm_info)?;
        Self::check_account_readonly(amm_open_orders_info)?;
        Self::check_account_readonly(amm_target_orders_info)?;
        Self::check_account_readonly(amm_coin_vault_info)?;
        Self::check_account_readonly(amm_pc_vault_info)?;
        Self::check_account_readonly(amm_lp_mint_info)?;
        Self::check_account_readonly(market_info)?;
        Self::check_account_readonly(market_event_queue_info)?;
        Self::check_account_readonly(user_source_info)?;
        Self::check_account_readonly(user_destination_info)?;
        Self::check_account_readonly(user_source_owner)?;

        let mut swap_base_out: GetSwapBaseOutData = Default::default();
        if let Some(swap) = simulate.swap_base_out_value {
            swap_base_out.amount_out = swap.amount_out;

            if !user_source_owner.is_signer {
                return Err(AmmError::InvalidSignAccount.into());
            }
            let amm = AmmInfo::load_checked(&amm_info, program_id)?;
            if !AmmStatus::from_u64(amm.status).swap_permission() {
                msg!("simulate_swap_base_out: status {}", amm.status);
                return Err(AmmError::InvalidStatus.into());
            }
            let authority = Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?;
            check_assert_eq!(
                *amm_authority_info.key,
                authority,
                "authority",
                AmmError::InvalidProgramAddress
            );
            check_assert_eq!(
                *amm_coin_vault_info.key,
                amm.coin_vault,
                "coin_vault",
                AmmError::InvalidCoinVault
            );
            check_assert_eq!(
                *amm_pc_vault_info.key,
                amm.pc_vault,
                "pc_vault",
                AmmError::InvalidPCVault
            );
            check_assert_eq!(
                *amm_open_orders_info.key,
                amm.open_orders,
                "open_orders",
                AmmError::InvalidOpenOrders
            );
            check_assert_eq!(
                *amm_target_orders_info.key,
                amm.target_orders,
                "target_orders",
                AmmError::InvalidTargetOrders
            );
            check_assert_eq!(
                *amm_lp_mint_info.key,
                amm.lp_mint,
                "lp_mint",
                AmmError::InvalidPoolMint
            );
            check_assert_eq!(
                *market_program_info.key,
                amm.market_program,
                "market_program",
                AmmError::InvalidMarketProgram
            );
            check_assert_eq!(
                *market_info.key,
                amm.market,
                "market",
                AmmError::InvalidMarket
            );

            if *user_source_info.key == amm.pc_vault || *user_source_info.key == amm.coin_vault {
                return Err(AmmError::InvalidInput.into());
            }
            if *user_destination_info.key == amm.pc_vault
                || *user_destination_info.key == amm.coin_vault
            {
                return Err(AmmError::InvalidInput.into());
            }

            let (market_state, open_orders) = Self::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;

            let amm_coin_vault =
                Self::unpack_token_account(&amm_coin_vault_info, &spl_token::id())?;
            let amm_pc_vault = Self::unpack_token_account(&amm_pc_vault_info, &spl_token::id())?;
            let lp_mint = Self::unpack_mint(&amm_lp_mint_info, &spl_token::id())?;

            let user_swap_source = Self::unpack_token_account(&user_source_info, &spl_token::id())?;
            let user_swap_destination =
                Self::unpack_token_account(&user_destination_info, &spl_token::id())?;

            // let target_orders = TargetOrders::load_mut_checked(&target_orders_info, program_id, amm_info.key)?;

            let swap_direction;
            if user_swap_source.mint == amm_coin_vault.mint
                && user_swap_destination.mint == amm_pc_vault.mint
            {
                swap_direction = SwapDirection::Coin2PC
            } else if user_swap_source.mint == amm_pc_vault.mint
                && user_swap_destination.mint == amm_coin_vault.mint
            {
                swap_direction = SwapDirection::PC2Coin
            } else {
                return Err(AmmError::InvalidInput.into());
            }
            let (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &open_orders,
                    &amm,
                    &market_state,
                    &market_event_queue_info,
                    &amm_open_orders_info,
                )?;
            swap_base_out.pool_data.status = amm.status;
            swap_base_out.pool_data.coin_decimals = amm.coin_decimals;
            swap_base_out.pool_data.pc_decimals = amm.pc_decimals;
            swap_base_out.pool_data.lp_decimals = lp_mint.decimals.into();
            swap_base_out.pool_data.pool_lp_supply = amm.lp_amount;
            swap_base_out.pool_data.pool_open_time = amm.state_data.pool_open_time;
            swap_base_out.pool_data.pool_pc_amount = total_pc_without_take_pnl;
            swap_base_out.pool_data.pool_coin_amount = total_coin_without_take_pnl;
            swap_base_out.pool_data.amm_id = amm_info.key.to_string();

            let swap_in_before_add_fee = Calculator::swap_token_amount_base_out(
                swap.amount_out.into(),
                total_pc_without_take_pnl.into(),
                total_coin_without_take_pnl.into(),
                swap_direction,
            );

            // swap_in_after_add_fee * (1 - 0.0025) = swap_in_before_add_fee
            // swap_in_after_add_fee = swap_in_before_add_fee / (1 - 0.0025)
            let swap_in_after_add_fee = swap_in_before_add_fee
                .checked_mul(amm.fees.swap_fee_denominator.into())
                .unwrap()
                .checked_ceil_div(
                    (amm.fees
                        .swap_fee_denominator
                        .checked_sub(amm.fees.swap_fee_numerator)
                        .unwrap())
                    .into(),
                )
                .unwrap()
                .0
                .as_u64();
            swap_base_out.max_amount_in = swap_in_after_add_fee;

            match swap_direction {
                SwapDirection::Coin2PC => {
                    // coin -> pc, need cancel buy order
                    let token_pc_after_swap = total_pc_without_take_pnl
                        .checked_sub(swap.amount_out)
                        .unwrap();
                    let token_coin_after_swap = total_coin_without_take_pnl
                        .checked_add(swap_in_after_add_fee)
                        .unwrap();

                    let swap_price_before = total_pc_without_take_pnl
                        .checked_div(total_coin_without_take_pnl)
                        .unwrap();
                    let swap_price_after = token_pc_after_swap
                        .checked_div(token_coin_after_swap)
                        .unwrap();
                    swap_base_out.price_impact =
                        (swap_price_before.checked_sub(swap_price_after).unwrap())
                            .checked_mul(1000000)
                            .unwrap()
                            .checked_div(swap_price_before)
                            .unwrap();
                }
                SwapDirection::PC2Coin => {
                    // pc -> coin, need cancel sell order
                    let token_pc_after_swap = total_pc_without_take_pnl
                        .checked_add(swap_in_after_add_fee)
                        .unwrap();
                    let token_coin_after_swap = total_coin_without_take_pnl
                        .checked_sub(swap.amount_out)
                        .unwrap();

                    let swap_price_before = total_pc_without_take_pnl
                        .checked_div(total_coin_without_take_pnl)
                        .unwrap();
                    let swap_price_after = token_pc_after_swap
                        .checked_div(token_coin_after_swap)
                        .unwrap();
                    swap_base_out.price_impact =
                        (swap_price_after.checked_sub(swap_price_before).unwrap())
                            .checked_mul(1000000)
                            .unwrap()
                            .checked_div(swap_price_before)
                            .unwrap();
                }
            }
        }
        return Ok(swap_base_out);
    }

    fn simulate_run_crank(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
    ) -> Result<RunCrankData, ProgramError> {
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;
        let clock_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_bids_info = next_account_info(account_info_iter)?;
        let market_asks_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;

        Self::check_account_readonly(amm_info)?;
        Self::check_account_readonly(amm_open_orders_info)?;
        Self::check_account_readonly(amm_target_orders_info)?;
        Self::check_account_readonly(amm_coin_vault_info)?;
        Self::check_account_readonly(amm_pc_vault_info)?;
        Self::check_account_readonly(market_info)?;
        Self::check_account_readonly(market_bids_info)?;
        Self::check_account_readonly(market_asks_info)?;
        Self::check_account_readonly(market_event_queue_info)?;

        let amm = AmmInfo::load_checked(amm_info, program_id)?;
        Self::check_accounts(
            program_id,
            &amm,
            amm_info,
            token_program_info,
            clock_info,
            market_program_info,
            amm_authority_info,
            market_info,
            amm_open_orders_info,
            amm_coin_vault_info,
            amm_pc_vault_info,
            amm_target_orders_info,
            None,
            None,
        )?;
        let spl_token_program_id = token_program_info.key;
        let amm_status = AmmStatus::from_u64(amm.status);
        let amm_state = AmmState::from_u64(amm.state);
        let mut run_crank_data = RunCrankData {
            status: amm.status,
            state: amm.state,
            run_crank: false,
        };

        if amm.reset_flag == AmmResetFlag::ResetYes.into_u64() {
            run_crank_data.run_crank = true;
        } else if amm.order_num == 0 {
            run_crank_data.run_crank = false;
        } else {
            match amm_status {
                AmmStatus::Uninitialized
                | AmmStatus::Disabled
                | AmmStatus::WithdrawOnly
                | AmmStatus::LiquidityOnly
                | AmmStatus::SwapOnly
                | AmmStatus::WaitingTrade => {
                    run_crank_data.run_crank = false;
                }
                AmmStatus::Initialized | AmmStatus::OrderBookOnly => match amm_state {
                    AmmState::IdleState => {
                        let amm_coin_vault = Processor::unpack_token_account(
                            &amm_coin_vault_info,
                            spl_token_program_id,
                        )?;
                        let amm_pc_vault = Processor::unpack_token_account(
                            &amm_pc_vault_info,
                            spl_token_program_id,
                        )?;
                        let target = TargetOrders::load_checked(
                            &amm_target_orders_info,
                            program_id,
                            amm_info.key,
                        )?;
                        let (market_state, open_orders) = Processor::load_serum_market_order(
                            market_info,
                            amm_open_orders_info,
                            amm_authority_info,
                            &amm,
                            false,
                        )?;
                        let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
                        let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
                        let (bids, asks) =
                            Self::get_amm_orders(&open_orders, bids_orders, asks_orders)?;
                        let native_pc_total = open_orders.native_pc_total;
                        let native_coin_total = open_orders.native_coin_total;
                        msg!(&format!("simulate_run_crank pc_amount:{}, native_pc_total:{}, coin_amount:{}, native_coin_total:{}, need_take_pnl_pc:{}, need_take_pnl_coin:{}", amm_pc_vault.amount, native_pc_total, amm_coin_vault.amount, native_coin_total, amm.state_data.need_take_pnl_pc, amm.state_data.need_take_pnl_coin));
                        let (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                            Calculator::calc_total_without_take_pnl(
                                amm_pc_vault.amount,
                                amm_coin_vault.amount,
                                &open_orders,
                                &amm,
                                &market_state,
                                &market_event_queue_info,
                                &amm_open_orders_info,
                            )?;
                        let x = Calculator::normalize_decimal_v2(
                            total_pc_without_take_pnl,
                            amm.pc_decimals,
                            amm.sys_decimal_value,
                        );
                        let y = Calculator::normalize_decimal_v2(
                            total_coin_without_take_pnl,
                            amm.coin_decimals,
                            amm.sys_decimal_value,
                        );
                        msg!(&format!(
                            "simulate_run_crank x:{}, y:{}, place_x:{}, place_y:{}",
                            x, y, target.placed_x, target.placed_y
                        ));
                        if x.is_zero() || y.is_zero() {
                            run_crank_data.run_crank = false;
                        }

                        let valid_buy_order_num = target.valid_buy_order_num as usize;
                        let valid_sell_order_num = target.valid_sell_order_num as usize;
                        msg!(&format!("simulate_run_crank buy_len:{}, sell_len:{}, plan_buy_order:{}, plan_sell_order:{}", bids.len(), asks.len(), valid_buy_order_num, valid_sell_order_num));
                        if bids.len() < valid_buy_order_num
                            || asks.len() < valid_sell_order_num
                            || (bids.is_empty() && asks.is_empty())
                            || x != target.placed_x.into()
                            || y != target.placed_y.into()
                        {
                            run_crank_data.run_crank = true;
                        }

                        if (x.checked_mul(y).unwrap()
                            < U128::from(target.calc_pnl_x)
                                .checked_mul(target.calc_pnl_y.into())
                                .unwrap())
                            && bids.is_empty()
                            && asks.is_empty()
                        {
                            msg!(arrform!(
                                LOG_SIZE,
                                "{}, {}, {}, {}",
                                x,
                                y,
                                target.calc_pnl_x,
                                target.calc_pnl_y
                            )
                            .as_str());
                            run_crank_data.run_crank = false;
                        }

                        let cur_price: u64 = u64::try_from(
                            (x).checked_mul(amm.sys_decimal_value.into())
                                .unwrap()
                                .checked_div(y)
                                .unwrap()
                                .as_u128(),
                        )
                        .unwrap();
                        let mux_cur_price = (amm.pc_lot_size as u128)
                            .checked_mul(amm.max_price_multiplier as u128)
                            .unwrap();
                        let min_cur_price = (amm.pc_lot_size as u128)
                            .checked_mul(amm.min_price_multiplier as u128)
                            .unwrap();
                        if (cur_price as u128) < min_cur_price
                            || (cur_price as u128) > mux_cur_price
                        {
                            msg!(&format!("simulate_run_crank cur_price:{}, min_cur_price:{}, mux_cur_price:{}", cur_price, min_cur_price, mux_cur_price));
                            run_crank_data.run_crank = false;
                        }
                    }
                    AmmState::CancelAllOrdersState => {
                        let amm_coin_vault = Processor::unpack_token_account(
                            &amm_coin_vault_info,
                            spl_token_program_id,
                        )?;
                        let amm_pc_vault = Processor::unpack_token_account(
                            &amm_pc_vault_info,
                            spl_token_program_id,
                        )?;
                        let target = TargetOrders::load_checked(
                            &amm_target_orders_info,
                            program_id,
                            amm_info.key,
                        )?;
                        let (market_state, open_orders) = Processor::load_serum_market_order(
                            market_info,
                            amm_open_orders_info,
                            amm_authority_info,
                            &amm,
                            false,
                        )?;
                        let (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                            Calculator::calc_total_without_take_pnl(
                                amm_pc_vault.amount,
                                amm_coin_vault.amount,
                                &open_orders,
                                &amm,
                                &market_state,
                                &market_event_queue_info,
                                &amm_open_orders_info,
                            )?;
                        let x = Calculator::normalize_decimal_v2(
                            total_pc_without_take_pnl,
                            amm.pc_decimals,
                            amm.sys_decimal_value,
                        );
                        let y = Calculator::normalize_decimal_v2(
                            total_coin_without_take_pnl,
                            amm.coin_decimals,
                            amm.sys_decimal_value,
                        );
                        let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
                        let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
                        let (bids, asks) =
                            Self::get_amm_orders(&open_orders, bids_orders, asks_orders)?;
                        if open_orders.free_slot_bits.count_zeros() > 100
                            && bids.is_empty()
                            && asks.is_empty()
                        {
                            run_crank_data.run_crank = false;
                        } else {
                            run_crank_data.run_crank = true;
                        }
                        if (x.checked_mul(y).unwrap()
                            < U128::from(target.calc_pnl_x)
                                .checked_mul(target.calc_pnl_y.into())
                                .unwrap())
                            && bids.is_empty()
                            && asks.is_empty()
                        {
                            msg!(arrform!(
                                LOG_SIZE,
                                "{}, {}, {}, {}",
                                x,
                                y,
                                target.calc_pnl_x,
                                target.calc_pnl_y
                            )
                            .as_str());
                            run_crank_data.run_crank = false;
                        }
                    }
                    AmmState::PlanOrdersState
                    | AmmState::CancelOrderState
                    | AmmState::PlaceOrdersState
                    | AmmState::PurgeOrderState => {
                        run_crank_data.run_crank = true;
                    }
                    _ => {
                        run_crank_data.run_crank = false;
                    }
                },
            }
        }

        return Ok(run_crank_data);
    }

    /// simulate_info
    pub fn process_simulate_info(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        simulate: SimulateInstruction,
    ) -> ProgramResult {
        let param = simulate.param;
        match SimulateParams::from_u64(param as u64) {
            SimulateParams::PoolInfo => {
                let pool_info_data = Self::simulate_pool_info(program_id, accounts).unwrap();
                msg!("GetPoolData: {}", pool_info_data.to_json());
            }
            SimulateParams::RunCrankInfo => {
                let run_crank_data = Self::simulate_run_crank(program_id, accounts).unwrap();
                msg!("RunCrankData: {}", run_crank_data.to_json());
            }
            SimulateParams::SwapBaseInInfo => {
                let swap_base_in_data =
                    Self::simulate_swap_base_in(program_id, accounts, simulate).unwrap();
                msg!("GetSwapBaseInData: {}", swap_base_in_data.to_json());
            }
            SimulateParams::SwapBaseOutInfo => {
                let swap_base_out_data =
                    Self::simulate_swap_base_out(program_id, accounts, simulate).unwrap();
                msg!("GetSwapBaseOutData: {}", swap_base_out_data.to_json());
            }
        }
        return Ok(());
    }

    pub fn do_idle_state(args: account_parser::IdleArgs) -> ProgramResult {
        let account_parser::IdleArgs {
            program_id: _,
            total_coin_without_take_pnl,
            total_pc_without_take_pnl,
            amm,
            bids,
            asks,
            target,
        } = args;

        let x = Calculator::normalize_decimal_v2(
            *total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y = Calculator::normalize_decimal_v2(
            *total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );
        if x.is_zero() || y.is_zero() {
            return Err(AmmError::CheckedEmptyFunds.into());
        }

        let valid_buy_order_num = target.valid_buy_order_num as usize;
        let valid_sell_order_num = target.valid_sell_order_num as usize;
        if bids.len() < valid_buy_order_num
            || asks.len() < valid_sell_order_num
            || (bids.is_empty() && asks.is_empty())
            || x != U128::from(target.placed_x)
            || y != U128::from(target.placed_y)
        {
            // clear plan pos before plan
            target.plan_orders_cur = 0;
            amm.state = AmmState::PlanOrdersState.into_u64();
        } else {
            // no order traded
            amm.state = AmmState::IdleState.into_u64();
        }

        let cur_price = U128::from(x)
            .checked_mul(amm.sys_decimal_value.into())
            .unwrap()
            .checked_div(y.into())
            .unwrap();
        let mux_cur_price = U128::from(amm.pc_lot_size)
            .checked_mul(amm.max_price_multiplier.into())
            .unwrap();
        let min_cur_price = U128::from(amm.pc_lot_size)
            .checked_mul(amm.min_price_multiplier.into())
            .unwrap();
        if cur_price < min_cur_price || cur_price > mux_cur_price {
            msg!(arrform!(
                LOG_SIZE,
                "do_idle cur_price:{}, min_cur_price:{}, mux_cur_price:{}",
                cur_price,
                min_cur_price,
                mux_cur_price
            )
            .as_str());
            if bids.is_empty() && asks.is_empty() {
                amm.state = AmmState::IdleState.into_u64();
            } else {
                amm.state = AmmState::CancelAllOrdersState.into_u64();
            }
        }

        if x.checked_mul(y).unwrap()
            < U128::from(target.calc_pnl_x)
                .checked_mul(target.calc_pnl_y.into())
                .unwrap()
        {
            amm.state = AmmState::CancelAllOrdersState.into_u64();
        } else {
            // calc and update pnl
            let (delta_x, delta_y) = Self::calc_take_pnl(
                &target,
                amm,
                total_pc_without_take_pnl,
                total_coin_without_take_pnl,
                x.as_u128().into(),
                y.as_u128().into(),
            )?;
            if delta_x != 0 && delta_y != 0 {
                target.calc_pnl_x = x.checked_sub(U128::from(delta_x)).unwrap().as_u128();
                target.calc_pnl_y = y.checked_sub(U128::from(delta_y)).unwrap().as_u128();
            }
        }

        msg!("do_idle to_state {}", amm.state);
        Ok(())
    }

    /// plan orderbook
    pub fn do_plan_orderbook(args: account_parser::PlanOrderBookArgs) -> ProgramResult {
        let account_parser::PlanOrderBookArgs {
            program_id: _,
            limit,
            total_coin_without_take_pnl,
            total_pc_without_take_pnl,
            amm,
            target,
        } = args;

        if amm.order_num == target.plan_orders_cur {
            // all orders have been planed
            // change to next state
            target.place_orders_cur = 0;
            target.replace_buy_client_id = [0u64; 10];
            target.replace_sell_client_id = [0u64; 10];
            target.placed_x = 0;
            target.placed_y = 0;
            amm.state = AmmState::PlaceOrdersState.into_u64();
        } else {
            let x = Calculator::normalize_decimal_v2(
                total_pc_without_take_pnl,
                amm.pc_decimals,
                amm.sys_decimal_value,
            );
            let y = Calculator::normalize_decimal_v2(
                total_coin_without_take_pnl,
                amm.coin_decimals,
                amm.sys_decimal_value,
            );
            if target.plan_orders_cur == 0 {
                // save tartget_x and target_y
                target.target_x = x.as_u128();
                target.target_y = y.as_u128();
                target.plan_x_buy = x.as_u128();
                target.plan_y_buy = y.as_u128();
                target.plan_x_sell = x.as_u128();
                target.plan_y_sell = y.as_u128();
                target.valid_buy_order_num = 0;
                target.valid_sell_order_num = 0;
            }
            if x.is_zero() || y.is_zero() {
                return Err(AmmError::CheckedEmptyFunds.into());
            }
            if x != target.target_x.into() || y != target.target_y.into() {
                amm.state = AmmState::IdleState.into_u64();
                msg!("do_plan: to_state {}", amm.state);
                return Ok(());
            }
            let min_size: u64 = amm.min_size as u64;
            let cur_price: u64 = u64::try_from(
                (x).checked_mul(amm.sys_decimal_value.into())
                    .unwrap()
                    .checked_div(y)
                    .unwrap()
                    .as_u128(),
            )
            .unwrap();
            let mux_cur_price = (amm.pc_lot_size as u128)
                .checked_mul(amm.max_price_multiplier as u128)
                .unwrap();
            let min_cur_price = (amm.pc_lot_size as u128)
                .checked_mul(amm.min_price_multiplier as u128)
                .unwrap();
            if (cur_price as u128) < min_cur_price || (cur_price as u128) > mux_cur_price {
                msg!(arrform!(
                    LOG_SIZE,
                    "do_plan cur_price:{}, min_cur_price:{}, mux_cur_price:{}",
                    cur_price,
                    min_cur_price,
                    mux_cur_price
                )
                .as_str());
                amm.state = AmmState::IdleState.into_u64();
            } else {
                //let max_bid: u64 = cur_price * (amm.sys_decimal_value - (amm.min_separate + amm.fee)) / amm.sys_decimal_value;
                let max_bid: u64 = U128::from(cur_price)
                    .checked_mul(
                        (amm.fees.trade_fee_denominator
                            - (amm.fees.min_separate_numerator + amm.fees.trade_fee_numerator))
                            .into(),
                    )
                    .unwrap()
                    .checked_div(amm.fees.trade_fee_denominator.into())
                    .unwrap()
                    .as_u64();
                //let min_ask: u64 = cur_price * (amm.sys_decimal_value + amm.min_separate + amm.fee) / amm.sys_decimal_value;
                let min_ask: u64 = U128::from(cur_price)
                    .checked_mul(
                        (amm.fees.trade_fee_denominator
                            + (amm.fees.min_separate_numerator + amm.fees.trade_fee_numerator))
                            .into(),
                    )
                    .unwrap()
                    .checked_ceil_div(amm.fees.trade_fee_denominator.into())
                    .unwrap()
                    .0
                    .as_u64();

                //let grid: u64 = cur_price * (amm.depth as u64) / (100) / (amm.order_num as u64); // percent: e.g., 5*10**6 is 5%
                let mut grid: u64 = cur_price
                    .checked_mul(amm.depth)
                    .unwrap()
                    .checked_div(100)
                    .unwrap()
                    .checked_div(amm.order_num)
                    .unwrap();
                if grid < amm.pc_lot_size {
                    grid = amm.pc_lot_size;
                }
                // msg!("max_bid:{}, min_ask:{}, grid:{}", max_bid, min_ask, grid);
                let plan_orders_num;
                let plan_orders_cur = target.plan_orders_cur;
                if plan_orders_cur + limit as u64 > amm.order_num {
                    plan_orders_num = amm.order_num;
                } else {
                    plan_orders_num = plan_orders_cur + limit as u64;
                }
                let fb = Calculator::fibonacci(amm.order_num);
                // To plan orders and update target
                for i in plan_orders_cur..plan_orders_num {
                    let distance = grid.checked_mul(fb[i as usize]).unwrap();
                    // plan buy side
                    // buy_price = max_bid - grid * i;
                    let mut buy_price = max_bid.saturating_sub(distance);
                    // change the grid distance for last buy order
                    if i == amm.order_num.checked_sub(1).unwrap()
                        && target.last_order_denominator != 0
                        && target.last_order_numerator != 0
                    {
                        let target_buy_price = cur_price
                            .checked_mul(target.last_order_denominator)
                            .unwrap()
                            .checked_div(target.last_order_numerator)
                            .unwrap();
                        if buy_price > target_buy_price {
                            buy_price = target_buy_price;
                        }
                        // change the price to ticksize if the cur price is invalid and the last price is valid
                        if buy_price < amm.pc_lot_size
                            && target.buy_orders[i as usize - 2].price != 0
                            && target.buy_orders[i as usize - 2].price != grid
                        {
                            buy_price = grid;
                        }
                    }

                    let mut buy_vol = 0u64;
                    buy_price = Calculator::floor_lot(buy_price, amm.pc_lot_size as u64);
                    if buy_price != 0u64 {
                        let max_buy_vol = Calculator::get_max_buy_size_at_price(
                            buy_price,
                            target.plan_x_buy,
                            target.plan_y_buy,
                            &amm,
                        );
                        // msg!("buy_price:{}, max_buy_vol:{}", buy_price, max_buy_vol);
                        buy_vol = max_buy_vol
                            .checked_sub(Calculator::to_u64(
                                U128::from(amm.vol_max_cut_ratio)
                                    .checked_mul(max_buy_vol.into())
                                    .unwrap()
                                    .checked_div(U128::from(TEN_THOUSAND))
                                    .unwrap()
                                    .as_u128(),
                            )?)
                            .unwrap();
                    }
                    if buy_vol < min_size {
                        buy_vol = 0u64;
                    }
                    let max_buy_qty_u64 = Calculator::floor_lot(
                        buy_vol,
                        Calculator::normalize_decimal(
                            amm.coin_lot_size as u64,
                            amm.coin_decimals,
                            amm.sys_decimal_value,
                        ),
                    );
                    target.plan_x_buy = target
                        .plan_x_buy
                        .checked_sub(
                            U128::from(max_buy_qty_u64)
                                .checked_mul(buy_price.into())
                                .unwrap()
                                .checked_div(amm.sys_decimal_value.into())
                                .unwrap()
                                .as_u128(),
                        )
                        .unwrap();
                    target.plan_y_buy = target
                        .plan_y_buy
                        .checked_add(max_buy_qty_u64.into())
                        .unwrap();
                    // update tartget buy orders
                    target.buy_orders[i as usize].price = buy_price;
                    target.buy_orders[i as usize].vol = max_buy_qty_u64;

                    // plan sell side
                    // sell_price = min_ask + grid * i
                    let mut sell_price = min_ask.checked_add(distance).unwrap();
                    // change the last sell order price
                    if i == amm.order_num.checked_sub(1).unwrap()
                        && target.last_order_denominator != 0
                        && target.last_order_numerator != 0
                    {
                        let target_sell_price = cur_price
                            .checked_mul(target.last_order_numerator)
                            .unwrap()
                            .checked_div(target.last_order_denominator)
                            .unwrap();
                        if sell_price < target_sell_price {
                            sell_price = target_sell_price;
                        }
                    }
                    sell_price = Calculator::ceil_lot(sell_price, amm.pc_lot_size);
                    let max_sell_vol = Calculator::get_max_sell_size_at_price(
                        sell_price,
                        target.plan_x_sell,
                        target.plan_y_sell,
                        &amm,
                    );
                    // msg!("sell_price:{}, max_sell_vol:{}", sell_price, max_sell_vol);
                    let mut sell_vol = max_sell_vol
                        .checked_sub(Calculator::to_u64(
                            U128::from(amm.vol_max_cut_ratio)
                                .checked_mul(max_sell_vol.into())
                                .unwrap()
                                .checked_div(U128::from(TEN_THOUSAND))
                                .unwrap()
                                .as_u128(),
                        )?)
                        .unwrap();
                    if sell_vol < min_size {
                        sell_vol = 0u64;
                    }
                    let max_sell_qty_u64 = Calculator::floor_lot(
                        sell_vol,
                        Calculator::normalize_decimal(
                            amm.coin_lot_size as u64,
                            amm.coin_decimals,
                            amm.sys_decimal_value,
                        ),
                    );
                    target.plan_y_sell = target
                        .plan_y_sell
                        .checked_sub(max_sell_qty_u64.into())
                        .unwrap();
                    target.plan_x_sell = target
                        .plan_x_sell
                        .checked_add(
                            U128::from(max_sell_qty_u64)
                                .checked_mul(sell_price.into())
                                .unwrap()
                                .checked_div(amm.sys_decimal_value.into())
                                .unwrap()
                                .as_u128(),
                        )
                        .unwrap();
                    target.sell_orders[i as usize].price = sell_price;
                    target.sell_orders[i as usize].vol = max_sell_qty_u64;
                    // msg!(arrform!(LOG_SIZE, "do_plan i:{}, {}, {}, {}, {}", i, target.buy_orders[i as usize].price, target.buy_orders[i as usize].vol, target.sell_orders[i as usize].price, target.sell_orders[i as usize].vol).as_str());

                    // update plan_orders_cur
                    target.plan_orders_cur += 1;
                }
                if amm.order_num == target.plan_orders_cur {
                    // all orders have been planed
                    // change to next state
                    target.place_orders_cur = 0;
                    target.replace_buy_client_id = [0u64; 10];
                    target.replace_sell_client_id = [0u64; 10];
                    target.placed_x = 0;
                    target.placed_y = 0;
                    amm.state = AmmState::PlaceOrdersState.into_u64();
                }
            }
        }
        Ok(())
    }

    pub fn do_cancel_order(args: account_parser::CancelOrderArgs) -> ProgramResult {
        let account_parser::CancelOrderArgs {
            program_id: _,
            limit: _,
            market_program_info: _,
            market_info: _,
            amm_open_orders_info: _,
            amm_authority_info: _,
            market_event_queue_info: _,
            market_bids_info: _,
            market_asks_info: _,
            amm,
            open_orders: _,
            target: _,
            bids: _,
            asks: _,

            coin_amount: _,
            pc_amount: _,
        } = args;

        amm.state = AmmState::IdleState.into_u64();
        Ok(())
    }

    pub fn do_place_orders(args: account_parser::PlaceOrdersArgs) -> ProgramResult {
        let account_parser::PlaceOrdersArgs {
            program_id: _,
            limit,

            amm_authority_info,
            amm_open_orders_info,
            market_program_info,
            market_info,
            market_request_queue_info,
            amm_coin_vault_info,
            amm_pc_vault_info,
            market_coin_vault_info,
            market_pc_vault_info,
            token_program_info,
            rent_info,

            market_event_queue_info,
            market_bids_info,
            market_asks_info,

            srm_token_account,

            amm,
            open_orders,
            bids,
            asks,
            target,

            total_coin_without_take_pnl,
            total_pc_without_take_pnl,

            coin_vault_amount,
            pc_vault_amount,
        } = args;

        if open_orders.free_slot_bits.count_zeros() > 100 {
            // TooManyOpenOrders
            amm.state = AmmState::CancelAllOrdersState.into_u64();
            return Ok(());
        }
        let x = Calculator::normalize_decimal_v2(
            total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y = Calculator::normalize_decimal_v2(
            total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );
        if x.is_zero() || y.is_zero() {
            return Err(AmmError::CheckedEmptyFunds.into());
        }
        if x != target.target_x.into() || y != target.target_y.into() {
            amm.state = AmmState::IdleState.into_u64();
            return Ok(());
        }
        let place_orders_num;
        let place_orders_cur = target.place_orders_cur;
        let target_orders_buy = target.buy_orders;
        let target_orders_sell = target.sell_orders;
        if place_orders_cur + limit as u64 > amm.order_num {
            place_orders_num = amm.order_num
        } else {
            place_orders_num = place_orders_cur + limit as u64;
        }
        // msg!(arrform!(LOG_SIZE, "do_place num:{}, cur:{}, limit:{}, buy_len:{}, sell_len:{}", place_orders_num, place_orders_cur, limit, bids.len(), asks.len()).as_str());
        let mut bids_client_ids: VecDeque<u64> = bids
            .into_iter()
            .map(|item| item.client_order_id())
            .collect();
        let mut asks_client_ids: VecDeque<u64> = asks
            .into_iter()
            .map(|item| item.client_order_id())
            .collect();
        bids_client_ids.retain(|x| !target.replace_buy_client_id.contains(&x));
        asks_client_ids.retain(|x| !target.replace_sell_client_id.contains(&x));

        let mut pc_avaliable = pc_vault_amount
            .checked_add(open_orders.native_pc_free)
            .unwrap()
            .checked_sub(amm.state_data.need_take_pnl_pc)
            .unwrap();
        let mut coin_avaliable = coin_vault_amount
            .checked_add(open_orders.native_coin_free)
            .unwrap()
            .checked_sub(amm.state_data.need_take_pnl_coin)
            .unwrap();
        for i in place_orders_cur..place_orders_num {
            let payer = amm_pc_vault_info.clone();
            let side = Side::Bid;
            let limit_price_u64 =
                Calculator::convert_price_out(target_orders_buy[i as usize].price, amm.pc_lot_size);
            let max_coin_qty = Calculator::convert_vol_out(
                target_orders_buy[i as usize].vol,
                amm.coin_decimals,
                amm.coin_lot_size,
                amm.sys_decimal_value,
            );
            let order_type = OrderType::Limit;

            let out_pc_lot_size = Calculator::convert_out_pc_lot_size(
                amm.pc_decimals as u8,
                amm.coin_decimals as u8,
                amm.pc_lot_size,
                amm.coin_lot_size,
                amm.sys_decimal_value,
            );
            let max_native_pc_qty_including_fees = max_coin_qty
                .checked_mul(limit_price_u64)
                .unwrap()
                .checked_mul(out_pc_lot_size)
                .unwrap();
            if max_native_pc_qty_including_fees > pc_avaliable {
                // pc amount is InsufficientFunds
                amm.state = AmmState::CancelAllOrdersState.into_u64();
                return Ok(());
            }
            if limit_price_u64 != 0 && max_coin_qty != 0 {
                if bids_client_ids.is_empty() {
                    let client_order_id = amm.incr_client_order_id();
                    // msg!("new buy order:{}, {}, {}", client_order_id, limit_price_u64, max_coin_qty);
                    target.replace_buy_client_id[target.valid_buy_order_num as usize] =
                        client_order_id;
                    Invokers::invoke_dex_new_order_v3(
                        market_program_info.clone(),
                        market_info.clone(),
                        amm_open_orders_info.clone(),
                        market_request_queue_info.clone(),
                        market_event_queue_info.clone(),
                        market_bids_info.clone(),
                        market_asks_info.clone(),
                        payer,
                        amm_authority_info.clone(),
                        market_coin_vault_info.clone(),
                        market_pc_vault_info.clone(),
                        token_program_info.clone(),
                        rent_info.clone(),
                        srm_token_account.clone(),
                        AUTHORITY_AMM,
                        amm.nonce as u8,
                        side,
                        NonZeroU64::new(limit_price_u64).unwrap(),
                        NonZeroU64::new(max_coin_qty).unwrap(),
                        NonZeroU64::new(max_native_pc_qty_including_fees).unwrap(),
                        order_type,
                        client_order_id,
                        std::u16::MAX,
                    )?;
                } else {
                    let client_order_id = if target.valid_buy_order_num == 0 {
                        bids_client_ids.pop_back().unwrap()
                    } else {
                        bids_client_ids.pop_front().unwrap()
                    };
                    target.replace_buy_client_id[target.valid_buy_order_num as usize] =
                        client_order_id;
                    // msg!("replace buy order:{}, {}, {}", client_order_id, limit_price_u64, max_coin_qty);
                    Invokers::invoke_dex_replace_order_by_client_id(
                        market_program_info.clone(),
                        market_info.clone(),
                        amm_open_orders_info.clone(),
                        market_request_queue_info.clone(),
                        market_event_queue_info.clone(),
                        market_bids_info.clone(),
                        market_asks_info.clone(),
                        payer,
                        amm_authority_info.clone(),
                        market_coin_vault_info.clone(),
                        market_pc_vault_info.clone(),
                        token_program_info.clone(),
                        rent_info.clone(),
                        srm_token_account.clone(),
                        AUTHORITY_AMM,
                        amm.nonce as u8,
                        side,
                        NonZeroU64::new(limit_price_u64).unwrap(),
                        NonZeroU64::new(max_coin_qty).unwrap(),
                        NonZeroU64::new(max_native_pc_qty_including_fees).unwrap(),
                        order_type,
                        client_order_id,
                        std::u16::MAX,
                    )?;
                }
                target.valid_buy_order_num += 1;
            }

            let payer = amm_coin_vault_info.clone();
            let side = Side::Ask;
            let limit_price_u64 = Calculator::convert_price_out(
                target_orders_sell[i as usize].price,
                amm.pc_lot_size,
            );
            let max_coin_qty = Calculator::convert_vol_out(
                target_orders_sell[i as usize].vol,
                amm.coin_decimals,
                amm.coin_lot_size,
                amm.sys_decimal_value,
            );
            let order_type = OrderType::Limit;

            if max_coin_qty.checked_mul(amm.coin_lot_size).unwrap() > coin_avaliable {
                // coin amount is InsufficientFunds
                amm.state = AmmState::CancelAllOrdersState.into_u64();
                return Ok(());
            }

            let max_native_pc_qty_including_fees = 1u64;
            if limit_price_u64 != 0 && max_coin_qty != 0 {
                if asks_client_ids.is_empty() {
                    let client_order_id = amm.incr_client_order_id();
                    target.replace_sell_client_id[target.valid_sell_order_num as usize] =
                        client_order_id;
                    // msg!("new sell order:{}, {}, {}", client_order_id, limit_price_u64, max_coin_qty);
                    Invokers::invoke_dex_new_order_v3(
                        market_program_info.clone(),
                        market_info.clone(),
                        amm_open_orders_info.clone(),
                        market_request_queue_info.clone(),
                        market_event_queue_info.clone(),
                        market_bids_info.clone(),
                        market_asks_info.clone(),
                        payer,
                        amm_authority_info.clone(),
                        market_coin_vault_info.clone(),
                        market_pc_vault_info.clone(),
                        token_program_info.clone(),
                        rent_info.clone(),
                        srm_token_account.clone(),
                        AUTHORITY_AMM,
                        amm.nonce as u8,
                        side,
                        NonZeroU64::new(limit_price_u64).unwrap(),
                        NonZeroU64::new(max_coin_qty).unwrap(),
                        NonZeroU64::new(max_native_pc_qty_including_fees).unwrap(),
                        order_type,
                        client_order_id,
                        std::u16::MAX,
                    )?;
                } else {
                    let client_order_id = if target.valid_sell_order_num == 0 {
                        asks_client_ids.pop_back().unwrap()
                    } else {
                        asks_client_ids.pop_front().unwrap()
                    };
                    target.replace_sell_client_id[target.valid_sell_order_num as usize] =
                        client_order_id;
                    // msg!("replace sell order:{}, {}, {}", client_order_id, limit_price_u64, max_coin_qty);
                    Invokers::invoke_dex_replace_order_by_client_id(
                        market_program_info.clone(),
                        market_info.clone(),
                        amm_open_orders_info.clone(),
                        market_request_queue_info.clone(),
                        market_event_queue_info.clone(),
                        market_bids_info.clone(),
                        market_asks_info.clone(),
                        payer,
                        amm_authority_info.clone(),
                        market_coin_vault_info.clone(),
                        market_pc_vault_info.clone(),
                        token_program_info.clone(),
                        rent_info.clone(),
                        srm_token_account.clone(),
                        AUTHORITY_AMM,
                        amm.nonce as u8,
                        side,
                        NonZeroU64::new(limit_price_u64).unwrap(),
                        NonZeroU64::new(max_coin_qty).unwrap(),
                        NonZeroU64::new(max_native_pc_qty_including_fees).unwrap(),
                        order_type,
                        client_order_id,
                        std::u16::MAX,
                    )?;
                }
                target.valid_sell_order_num += 1;
            }
            // update place_orders_cur
            target.place_orders_cur += 1;
            {
                // reload account data to check avaliable pc and avaliable coin is enough to place order
                let open_orders = Self::load_orders(amm_open_orders_info)?;
                let pc_vault = Self::unpack_token_account(&amm_pc_vault_info, &spl_token::id())?;
                let coin_vault = Self::unpack_token_account(&amm_pc_vault_info, &spl_token::id())?;
                pc_avaliable = pc_vault
                    .amount
                    .checked_add(open_orders.native_pc_free)
                    .unwrap()
                    .checked_sub(amm.state_data.need_take_pnl_pc)
                    .unwrap();
                coin_avaliable = coin_vault
                    .amount
                    .checked_add(open_orders.native_coin_free)
                    .unwrap()
                    .checked_sub(amm.state_data.need_take_pnl_coin)
                    .unwrap();
            }
        }
        if target.place_orders_cur == amm.order_num {
            // save placed_x & placed_y
            target.placed_x = x.as_u128();
            target.placed_y = y.as_u128();
        }
        if target.place_orders_cur >= amm.order_num {
            amm.state = AmmState::PurgeOrderState.into_u64();
            return Ok(());
        }
        Ok(())
    }

    pub fn do_purge_orders(args: account_parser::PurgeOrderArgs) -> ProgramResult {
        let account_parser::PurgeOrderArgs {
            program_id: _,
            limit: _,
            market_program_info,
            market_info,
            amm_open_orders_info,
            amm_authority_info,
            market_event_queue_info,
            market_bids_info,
            market_asks_info,
            amm,
            target,
            bids,
            asks,
        } = args;

        if bids.len() <= target.valid_buy_order_num as usize
            && asks.len() <= target.valid_sell_order_num as usize
        {
            // no order to purge
            amm.state = AmmState::IdleState.into_u64();
        } else {
            let mut cancel_client_order_ids = Vec::new();
            for i in target.valid_buy_order_num as usize..bids.len() {
                cancel_client_order_ids.push(bids[i].client_order_id());
            }
            for i in target.valid_sell_order_num as usize..asks.len() {
                cancel_client_order_ids.push(asks[i].client_order_id());
            }

            let mut order_ids_vec = Vec::new();
            let mut order_ids = [0u64; 8];
            let mut count = 0;
            for i in 0..cancel_client_order_ids.len() {
                order_ids[count] = cancel_client_order_ids[i];
                count += 1;
                if count == 8 {
                    order_ids_vec.push(order_ids);
                    order_ids = [0u64; 8];
                    count = 0;
                }
            }
            if count != 0 {
                order_ids_vec.push(order_ids);
            }
            for ids in order_ids_vec.iter() {
                Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                    market_program_info.clone(),
                    market_info.clone(),
                    market_bids_info.clone(),
                    market_asks_info.clone(),
                    amm_open_orders_info.clone(),
                    amm_authority_info.clone(),
                    market_event_queue_info.clone(),
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                    *ids,
                )?;
            }
        }
        msg!("do_purge to_state {}", amm.state);
        Ok(())
    }

    pub fn do_cancel_all_orders_state(args: account_parser::CancelAllOrdersArgs) -> ProgramResult {
        let account_parser::CancelAllOrdersArgs {
            program_id: _,
            limit: _,
            market_program_info,
            market_info,
            amm_open_orders_info,
            amm_authority_info,
            market_event_queue_info,
            market_coin_vault_info,
            market_pc_vault_info,
            market_bids_info,
            market_asks_info,
            market_vault_signer,
            token_program_info,
            amm_coin_vault_info,
            amm_pc_vault_info,
            referrer_pc_account,
            amm,
            open_orders,
            target,
            bids,
            asks,
        } = args;

        msg!(arrform!(
            LOG_SIZE,
            "do_cancel_all o_bits:{:x}",
            identity(open_orders.free_slot_bits)
        )
        .as_str());
        // to decide whether all orders are really canceled
        if open_orders.free_slot_bits.count_zeros() > 100 && bids.is_empty() && asks.is_empty() {
            msg!(
                "do_cancel_all to state :{}, count_zeros:{}",
                amm.state,
                open_orders.free_slot_bits.count_zeros()
            );
            return Ok(());
        } else {
            let mut amm_order_ids_vec = Vec::new();
            let mut order_ids = [0u64; 8];
            let mut count = 0;
            for i in 0..std::cmp::max(bids.len(), asks.len()) {
                if i < bids.len() {
                    order_ids[count] = bids[i].client_order_id();
                    count += 1;
                }
                if i < asks.len() {
                    order_ids[count] = asks[i].client_order_id();
                    count += 1;
                }
                if count == 8 {
                    amm_order_ids_vec.push(order_ids);
                    order_ids = [0u64; 8];
                    count = 0;
                }
            }
            if count != 0 {
                amm_order_ids_vec.push(order_ids);
            }
            for ids in amm_order_ids_vec.iter() {
                Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                    market_program_info.clone(),
                    market_info.clone(),
                    market_bids_info.clone(),
                    market_asks_info.clone(),
                    amm_open_orders_info.clone(),
                    amm_authority_info.clone(),
                    market_event_queue_info.clone(),
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                    *ids,
                )?;
            }
            if open_orders.native_coin_total != 0 || open_orders.native_pc_total != 0 {
                Invokers::invoke_dex_settle_funds(
                    market_program_info.clone(),
                    market_info.clone(),
                    amm_open_orders_info.clone(),
                    amm_authority_info.clone(),
                    market_coin_vault_info.clone(),
                    market_pc_vault_info.clone(),
                    amm_coin_vault_info.clone(),
                    amm_pc_vault_info.clone(),
                    market_vault_signer.clone(),
                    token_program_info.clone(),
                    referrer_pc_account,
                    AUTHORITY_AMM,
                    amm.nonce as u8,
                )?;
            }
            if amm.reset_flag == AmmResetFlag::ResetYes.into_u64() {
                amm.reset_flag = AmmResetFlag::ResetNo.into_u64();
            }
            amm.state = AmmState::IdleState.into_u64();
            target.plan_orders_cur = 0;
            target.place_orders_cur = 0;
        }
        Ok(())
    }

    pub fn process_set_params(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        setparams: SetParamsInstruction,
    ) -> ProgramResult {
        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_coin_vault_info = next_account_info(account_info_iter)?;
        let market_pc_vault_info = next_account_info(account_info_iter)?;
        let market_vault_signer = next_account_info(account_info_iter)?;
        let market_event_q_info = next_account_info(account_info_iter)?;
        let market_bids_info = next_account_info(account_info_iter)?;
        let market_asks_info = next_account_info(account_info_iter)?;

        let amm_owner_info = next_account_info(account_info_iter)?;

        if *token_program_info.key != spl_token::ID {
            return Err(AmmError::InvalidSplTokenProgram.into());
        }
        let mut amm = AmmInfo::load_mut_checked(&amm_info, program_id)?;
        if *amm_authority_info.key
            != Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?
        {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        if amm_info.owner != program_id {
            return Err(AmmError::InvalidOwner.into());
        }
        if !amm_owner_info.is_signer || *amm_owner_info.key != config_feature::amm_owner::ID {
            return Err(AmmError::InvalidSignAccount.into());
        }
        if *market_program_info.key != amm.market_program {
            return Err(AmmError::InvalidMarketProgram.into());
        }
        if *market_info.key != amm.market {
            return Err(AmmError::InvalidMarket.into());
        }
        if *amm_open_orders_info.key != amm.open_orders {
            return Err(AmmError::InvalidOpenOrders.into());
        }
        if amm.coin_vault != *amm_coin_vault_info.key {
            return Err(AmmError::InvalidCoinVault.into());
        }
        if amm.pc_vault != *amm_pc_vault_info.key {
            return Err(AmmError::InvalidPCVault.into());
        }
        if amm.target_orders != *amm_target_orders_info.key {
            return Err(AmmError::InvalidTargetOrders.into());
        }
        // cancel amm orders in openbook
        Self::do_cancel_amm_orders(
            &amm,
            amm_authority_info,
            amm_open_orders_info,
            market_program_info,
            market_info,
            market_bids_info,
            market_asks_info,
            market_event_q_info,
            AUTHORITY_AMM,
        )?;
        Invokers::invoke_dex_settle_funds(
            market_program_info.clone(),
            market_info.clone(),
            amm_open_orders_info.clone(),
            amm_authority_info.clone(),
            market_coin_vault_info.clone(),
            market_pc_vault_info.clone(),
            amm_coin_vault_info.clone(),
            amm_pc_vault_info.clone(),
            market_vault_signer.clone(),
            token_program_info.clone(),
            None,
            AUTHORITY_AMM,
            amm.nonce as u8,
        )?;

        let param = setparams.param;
        let mut set_valid = false;
        match AmmParams::from_u64(param as u64) {
            AmmParams::Status => {
                {
                    let (market_state, open_orders) = Processor::load_serum_market_order(
                        market_info,
                        amm_open_orders_info,
                        amm_authority_info,
                        &amm,
                        false,
                    )?;
                    let (shared_pc, shared_coin) = Calculator::calc_exact_vault_in_serum(
                        &open_orders,
                        &market_state,
                        market_event_q_info,
                        amm_open_orders_info,
                    )
                    .unwrap();
                    if shared_pc != 0 || shared_coin != 0 {
                        msg!("shared_pc:{}, shared_coin:{}", shared_pc, shared_coin);
                        return Err(AmmError::InvalidInput.into());
                    }
                }
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if AmmStatus::valid_status(value) {
                    amm.status = value as u64;
                    set_valid = true;
                }
            }
            AmmParams::State => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if AmmState::valid_state(value) {
                    amm.state = value as u64;
                    set_valid = true;
                }
            }
            AmmParams::OrderNum => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if value > MAX_ORDER_LIMIT as u64 {
                    return Err(AmmError::InvalidInput.into());
                }
                amm.order_num = value as u64;
                set_valid = true;
            }
            AmmParams::Depth => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if value > 0 && value < 100 {
                    amm.depth = value as u64;
                    set_valid = true;
                }
            }
            AmmParams::AmountWave => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                amm.amount_wave = value;
                set_valid = true;
            }
            AmmParams::MinPriceMultiplier => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if value < amm.max_price_multiplier {
                    amm.min_price_multiplier = value;
                    set_valid = true;
                }
            }
            AmmParams::MaxPriceMultiplier => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if value > amm.max_price_multiplier {
                    amm.max_price_multiplier = value;
                    set_valid = true;
                }
            }
            AmmParams::VolMaxCutRatio => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if value <= TEN_THOUSAND {
                    amm.vol_max_cut_ratio = value;
                    set_valid = true;
                }
            }
            AmmParams::Seperate => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                if value <= TEN_THOUSAND {
                    amm.fees.min_separate_numerator = value;
                    set_valid = true;
                }
            }
            AmmParams::Fees => {
                let fees = match setparams.fees {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                fees.validate()?;
                amm.fees = fees;
                set_valid = true;
            }
            AmmParams::AmmOwner => {
                let new_pubkey = match setparams.new_pubkey {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                amm.amm_owner = new_pubkey;
                set_valid = true;
            }
            AmmParams::SetOpenTime => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                amm.state_data.pool_open_time = value as u64;
                set_valid = true;
            }
            AmmParams::LastOrderDistance => {
                let mut target = TargetOrders::load_mut_checked(
                    &amm_target_orders_info,
                    program_id,
                    amm_info.key,
                )?;
                let distance = match setparams.last_order_distance {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                target.last_order_numerator = distance.last_order_numerator;
                target.last_order_denominator = distance.last_order_denominator;
                set_valid = true;
            }
            AmmParams::InitOrderDepth => {
                amm.order_num = 7u64;
                amm.depth = 3u64;
                set_valid = true;
            }
            AmmParams::SetSwitchTime => {
                let value = match setparams.value {
                    Some(a) => a,
                    None => return Err(AmmError::InvalidInput.into()),
                };
                amm.state_data.orderbook_to_init_time = value as u64;
                set_valid = true;
            }
            AmmParams::ClearOpenTime => {
                amm.state_data.pool_open_time = 0;
                amm.state_data.orderbook_to_init_time = 0;
                set_valid = true;
            }
            AmmParams::UpdateOpenOrder => {
                let new_open_orders_info = next_account_info(account_info_iter)?;
                amm.open_orders = *new_open_orders_info.key;
                let (_market_state, _open_orders) = Processor::load_serum_market_order(
                    market_info,
                    new_open_orders_info,
                    amm_authority_info,
                    &amm,
                    false,
                )?;
                set_valid = true;
            }
            _ => {
                return Err(AmmError::InvalidInput.into());
            }
        }
        if set_valid {
            amm.state = AmmState::CancelAllOrdersState.into_u64();
            amm.reset_flag = AmmResetFlag::ResetYes.into_u64();
        } else {
            return Err(AmmError::InvalidParamsSet.into());
        }
        amm.recent_epoch = Clock::get()?.epoch;
        Ok(())
    }

    pub fn process_monitor_step(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        monitor: MonitorStepInstruction,
    ) -> ProgramResult {
        const MIN_ACCOUNTS: usize = 18;
        let input_account_len = accounts.len();
        if input_account_len != MIN_ACCOUNTS
            && input_account_len != MIN_ACCOUNTS + 1
            && input_account_len != MIN_ACCOUNTS + 2
        {
            return Err(AmmError::WrongAccountsNumber.into());
        }
        let (fixed_accounts, extra_token_accounts) = array_refs![accounts, MIN_ACCOUNTS; .. ;];
        let [token_program_info, rent_info, clock_info, amm_info, amm_authority_info, amm_open_orders_info, amm_target_orders_info, amm_coin_vault_info, amm_pc_vault_info, market_program_info, market_info, market_coin_vault_info, market_pc_vault_info, market_vault_signer, market_request_queue_info, market_event_queue_info, market_bids_info, market_asks_info] =
            fixed_accounts;
        let mut srm_token_info = None;
        let mut referrer_pc_info = None;
        if input_account_len == MIN_ACCOUNTS + 1 {
            if let [srm_token_acc] = extra_token_accounts {
                srm_token_info = Some(srm_token_acc);
            }
        } else if input_account_len == MIN_ACCOUNTS + 2 {
            if let [srm_token_acc, referrer_pc_acc] = extra_token_accounts {
                srm_token_info = Some(srm_token_acc);
                referrer_pc_info = Some(referrer_pc_acc);
            }
        }

        let mut amm = AmmInfo::load_mut_checked(amm_info, program_id)?;
        Self::check_accounts(
            program_id,
            &amm,
            amm_info,
            token_program_info,
            clock_info,
            market_program_info,
            amm_authority_info,
            market_info,
            amm_open_orders_info,
            amm_coin_vault_info,
            amm_pc_vault_info,
            amm_target_orders_info,
            srm_token_info,
            referrer_pc_info,
        )?;
        check_assert_eq!(
            *market_program_info.key,
            config_feature::openbook_program::id(),
            "market_program",
            AmmError::InvalidMarketProgram
        );
        if amm_info.owner != program_id {
            return Err(AmmError::InvalidOwner.into());
        }
        if amm.min_size == 0 {
            return Err(AmmError::MarketLotSizeIsTooLarge.into());
        }
        let amm_status = AmmStatus::from_u64(amm.status);
        let amm_state = AmmState::from_u64(amm.state);
        let spl_token_program_id = token_program_info.key;

        if amm.reset_flag == AmmResetFlag::ResetYes.into_u64() {
            msg!("monitor_step: ResetYes");
            let cancel_all_orders_accounts: &[AccountInfo] = &[
                amm_info.clone(),
                market_program_info.clone(),
                market_info.clone(),
                amm_open_orders_info.clone(),
                amm_authority_info.clone(),
                market_event_queue_info.clone(),
                market_coin_vault_info.clone(),
                market_pc_vault_info.clone(),
                market_bids_info.clone(),
                market_asks_info.clone(),
                market_vault_signer.clone(),
                token_program_info.clone(),
                amm_coin_vault_info.clone(),
                amm_pc_vault_info.clone(),
                amm_target_orders_info.clone(),
            ];
            account_parser::CancelAllOrdersArgs::with_parsed_args(
                program_id,
                monitor.cancel_order_limit,
                &mut amm,
                cancel_all_orders_accounts,
                referrer_pc_info,
                Self::do_cancel_all_orders_state,
            )
            .unwrap();
        } else if amm.order_num == 0 {
            msg!(arrform!(
                LOG_SIZE,
                "monitor_step Status:{}, order_num:{}",
                amm.status,
                amm.order_num
            )
            .as_str());
        } else {
            match amm_status {
                AmmStatus::Uninitialized
                | AmmStatus::Disabled
                | AmmStatus::WithdrawOnly
                | AmmStatus::LiquidityOnly
                | AmmStatus::SwapOnly
                | AmmStatus::WaitingTrade => {
                    msg!("monitor_step: AmmStatus:{}", amm.status);
                    return Err(AmmError::InvalidStatus.into());
                }
                AmmStatus::Initialized | AmmStatus::OrderBookOnly => match amm_state {
                    AmmState::IdleState => {
                        msg!("monitor_step IdleState:{}", amm.state);
                        let idle_accounts: &[AccountInfo] = &[
                            amm_info.clone(),
                            market_program_info.clone(),
                            market_info.clone(),
                            market_bids_info.clone(),
                            market_asks_info.clone(),
                            market_event_queue_info.clone(),
                            amm_authority_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            amm_target_orders_info.clone(),
                        ];
                        account_parser::IdleArgs::with_parsed_args(
                            program_id,
                            spl_token_program_id,
                            &mut amm,
                            idle_accounts,
                            Self::do_idle_state,
                        )
                        .unwrap();
                    }
                    AmmState::CancelAllOrdersState => {
                        msg!("monitor_step CancelAllOrdersState:{}", amm.state);
                        let cancel_all_orders_accounts: &[AccountInfo] = &[
                            amm_info.clone(),
                            market_program_info.clone(),
                            market_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_authority_info.clone(),
                            market_event_queue_info.clone(),
                            market_coin_vault_info.clone(),
                            market_pc_vault_info.clone(),
                            market_bids_info.clone(),
                            market_asks_info.clone(),
                            market_vault_signer.clone(),
                            token_program_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            amm_target_orders_info.clone(),
                        ];
                        account_parser::CancelAllOrdersArgs::with_parsed_args(
                            program_id,
                            monitor.cancel_order_limit,
                            &mut amm,
                            cancel_all_orders_accounts,
                            referrer_pc_info,
                            Self::do_cancel_all_orders_state,
                        )
                        .unwrap();
                    }
                    AmmState::PlanOrdersState => {
                        msg!("monitor_step PlanOrdersState:{}", amm.state);
                        let plan_buy_accounts: &[AccountInfo] = &[
                            amm_info.clone(),
                            market_info.clone(),
                            market_event_queue_info.clone(),
                            amm_authority_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            amm_target_orders_info.clone(),
                        ];
                        account_parser::PlanOrderBookArgs::with_parsed_args(
                            program_id,
                            spl_token_program_id,
                            monitor.plan_order_limit,
                            &mut amm,
                            plan_buy_accounts,
                            Self::do_plan_orderbook,
                        )
                        .unwrap();
                    }
                    AmmState::CancelOrderState => {
                        msg!("monitor_step CancelOrderState:{}", amm.state);
                        let cancel_order_accounts: &[AccountInfo] = &[
                            amm_info.clone(),
                            market_program_info.clone(),
                            market_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_authority_info.clone(),
                            market_event_queue_info.clone(),
                            market_bids_info.clone(),
                            market_asks_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            amm_target_orders_info.clone(),
                        ];
                        account_parser::CancelOrderArgs::with_parsed_args(
                            program_id,
                            spl_token_program_id,
                            monitor.cancel_order_limit,
                            &mut amm,
                            cancel_order_accounts,
                            Self::do_cancel_order,
                        )
                        .unwrap();
                    }
                    AmmState::PlaceOrdersState => {
                        msg!("monitor_step PlaceOrdersState:{}", amm.state);
                        let place_order_accounts: &[AccountInfo] = &[
                            amm_info.clone(),
                            amm_authority_info.clone(),
                            amm_open_orders_info.clone(),
                            market_program_info.clone(),
                            market_info.clone(),
                            market_request_queue_info.clone(),
                            amm_coin_vault_info.clone(),
                            amm_pc_vault_info.clone(),
                            market_coin_vault_info.clone(),
                            market_pc_vault_info.clone(),
                            token_program_info.clone(),
                            rent_info.clone(),
                            market_event_queue_info.clone(),
                            market_bids_info.clone(),
                            market_asks_info.clone(),
                            amm_target_orders_info.clone(),
                            clock_info.clone(),
                        ];
                        account_parser::PlaceOrdersArgs::with_parsed_args(
                            program_id,
                            spl_token_program_id,
                            monitor.place_order_limit,
                            &mut amm,
                            place_order_accounts,
                            srm_token_info,
                            Self::do_place_orders,
                        )
                        .unwrap();
                    }
                    AmmState::PurgeOrderState => {
                        msg!("monitor_step PurgeOrderState:{}", amm.state);
                        let purge_orders_accounts: &[AccountInfo] = &[
                            amm_info.clone(),
                            market_program_info.clone(),
                            market_info.clone(),
                            amm_open_orders_info.clone(),
                            amm_authority_info.clone(),
                            market_event_queue_info.clone(),
                            market_bids_info.clone(),
                            market_asks_info.clone(),
                            amm_target_orders_info.clone(),
                        ];
                        account_parser::PurgeOrderArgs::with_parsed_args(
                            program_id,
                            spl_token_program_id,
                            monitor.cancel_order_limit,
                            &mut amm,
                            purge_orders_accounts,
                            Self::do_purge_orders,
                        )
                        .unwrap();
                    }
                    _ => {
                        msg!("monitor_step InvalidState:{}", amm.state);
                    }
                },
            }
        }
        msg!("monitor_step end");
        Ok(())
    }

    fn do_cancel_amm_orders<'a>(
        amm: &AmmInfo,
        amm_authority_info: &AccountInfo<'a>,
        amm_open_orders_info: &AccountInfo<'a>,
        market_program_info: &AccountInfo<'a>,
        market_info: &AccountInfo<'a>,
        market_bids_info: &AccountInfo<'a>,
        market_asks_info: &AccountInfo<'a>,
        market_event_queue_info: &AccountInfo<'a>,
        amm_seed: &[u8],
    ) -> ProgramResult {
        let (market_state, open_orders) = Processor::load_serum_market_order(
            market_info,
            amm_open_orders_info,
            amm_authority_info,
            amm,
            false,
        )?;
        let bids_orders = market_state.load_bids_mut(market_bids_info)?;
        let asks_orders = market_state.load_asks_mut(market_asks_info)?;
        let (bids, asks) = Processor::get_amm_orders(&open_orders, bids_orders, asks_orders)?;

        let mut amm_order_ids_vec = Vec::new();
        let mut order_ids = [0u64; 8];
        let mut count = 0;
        for i in 0..std::cmp::max(bids.len(), asks.len()) {
            if i < bids.len() {
                order_ids[count] = bids[i].client_order_id();
                count += 1;
            }
            if i < asks.len() {
                order_ids[count] = asks[i].client_order_id();
                count += 1;
            }
            if count == 8 {
                amm_order_ids_vec.push(order_ids);
                order_ids = [0u64; 8];
                count = 0;
            }
        }
        if count != 0 {
            amm_order_ids_vec.push(order_ids);
        }
        for ids in amm_order_ids_vec.iter() {
            Invokers::invoke_dex_cancel_orders_by_client_order_ids(
                market_program_info.clone(),
                market_info.clone(),
                market_bids_info.clone(),
                market_asks_info.clone(),
                amm_open_orders_info.clone(),
                amm_authority_info.clone(),
                market_event_queue_info.clone(),
                amm_seed,
                amm.nonce as u8,
                *ids,
            )?;
        }

        Ok(())
    }

    pub fn process_admin_cancel_orders(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        _cancel: AdminCancelOrdersInstruction,
    ) -> ProgramResult {
        const MIN_ACCOUNTS: usize = 17;
        let input_account_len = accounts.len();
        if input_account_len != MIN_ACCOUNTS
            && input_account_len != MIN_ACCOUNTS + 1
            && input_account_len != MIN_ACCOUNTS + 2
        {
            return Err(AmmError::WrongAccountsNumber.into());
        }

        let account_info_iter = &mut accounts.iter();
        let token_program_info = next_account_info(account_info_iter)?;

        let amm_info = next_account_info(account_info_iter)?;
        let amm_authority_info = next_account_info(account_info_iter)?;
        let amm_open_orders_info = next_account_info(account_info_iter)?;
        let amm_target_orders_info = next_account_info(account_info_iter)?;
        let amm_coin_vault_info = next_account_info(account_info_iter)?;
        let amm_pc_vault_info = next_account_info(account_info_iter)?;
        let amm_owner_info = next_account_info(account_info_iter)?;
        let amm_config_info = next_account_info(account_info_iter)?;

        let market_program_info = next_account_info(account_info_iter)?;
        let market_info = next_account_info(account_info_iter)?;
        let market_coin_vault_info = next_account_info(account_info_iter)?;
        let market_pc_vault_info = next_account_info(account_info_iter)?;
        let market_vault_signer_info = next_account_info(account_info_iter)?;
        let market_event_queue_info = next_account_info(account_info_iter)?;
        let market_bids_info = next_account_info(account_info_iter)?;
        let market_asks_info = next_account_info(account_info_iter)?;

        let mut srm_token_account = None;
        let mut referrer_pc_wallet = None;
        if input_account_len == MIN_ACCOUNTS + 1 {
            let srm_token_info = next_account_info(account_info_iter)?;
            srm_token_account = Some(srm_token_info);
        } else if input_account_len == MIN_ACCOUNTS + 2 {
            let srm_token_info = next_account_info(account_info_iter)?;
            let referrer_pc_info = next_account_info(account_info_iter)?;
            srm_token_account = Some(srm_token_info);
            referrer_pc_wallet = Some(referrer_pc_info);
        }

        if *token_program_info.key != spl_token::ID {
            return Err(AmmError::InvalidSplTokenProgram.into());
        }
        let amm = AmmInfo::load_checked(&amm_info, program_id)?;
        if *amm_authority_info.key
            != Self::authority_id(program_id, AUTHORITY_AMM, amm.nonce as u8)?
        {
            return Err(AmmError::InvalidProgramAddress.into());
        }
        if amm_info.owner != program_id {
            return Err(AmmError::InvalidOwner.into());
        }
        let (pda, _) = Pubkey::find_program_address(&[&AMM_CONFIG_SEED], program_id);
        if pda != *amm_config_info.key {
            return Err(AmmError::InvalidConfigAccount.into());
        }
        let amm_config = AmmConfig::load_checked(&amm_config_info, program_id)?;
        if !amm_owner_info.is_signer
            || (*amm_owner_info.key != config_feature::amm_owner::ID
                && *amm_owner_info.key != amm_config.cancel_owner)
        {
            return Err(AmmError::InvalidSignAccount.into());
        }

        if amm.status == AmmStatus::Uninitialized.into_u64() {
            return Err(AmmError::InvalidStatus.into());
        }
        if *market_program_info.key != amm.market_program {
            return Err(AmmError::InvalidMarketProgram.into());
        }
        if *market_info.key != amm.market {
            return Err(AmmError::InvalidMarket.into());
        }
        if *amm_open_orders_info.key != amm.open_orders {
            return Err(AmmError::InvalidOpenOrders.into());
        }
        if amm.coin_vault != *amm_coin_vault_info.key {
            return Err(AmmError::InvalidCoinVault.into());
        }
        if amm.pc_vault != *amm_pc_vault_info.key {
            return Err(AmmError::InvalidPCVault.into());
        }
        if amm.target_orders != *amm_target_orders_info.key {
            return Err(AmmError::InvalidTargetOrders.into());
        }
        if let Some(srm_token_info) = srm_token_account {
            let srm_token = Self::unpack_token_account(&srm_token_info, &spl_token::id())?;
            if *amm_authority_info.key != srm_token.owner {
                return Err(AmmError::InvalidOwner.into());
            }
            if srm_token.mint != srm_token::ID && srm_token.mint != msrm_token::ID {
                return Err(AmmError::InvalidSrmMint.into());
            }
        }
        if let Some(referrer_pc_info) = referrer_pc_wallet {
            let referrer_pc_token =
                Self::unpack_token_account(&referrer_pc_info, &spl_token::id())?;
            if referrer_pc_token.owner != config_feature::referrer_pc_wallet::id() {
                return Err(AmmError::InvalidOwner.into());
            }
            if referrer_pc_token.mint != amm.pc_vault_mint {
                return Err(AmmError::InvalidReferPCMint.into());
            }
        }
        Self::do_cancel_amm_orders(
            &amm,
            amm_authority_info,
            amm_open_orders_info,
            market_program_info,
            market_info,
            market_bids_info,
            market_asks_info,
            market_event_queue_info,
            AUTHORITY_AMM,
        )?;
        let (open_order_coin_free, open_order_pc_free) = {
            let (_market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                true,
            )?;
            (open_orders.native_coin_free, open_orders.native_pc_free)
        };
        if open_order_coin_free == 0 && open_order_pc_free == 0 {
            return Ok(());
        }
        Invokers::invoke_dex_settle_funds(
            market_program_info.clone(),
            market_info.clone(),
            amm_open_orders_info.clone(),
            amm_authority_info.clone(),
            market_coin_vault_info.clone(),
            market_pc_vault_info.clone(),
            amm_coin_vault_info.clone(),
            amm_pc_vault_info.clone(),
            market_vault_signer_info.clone(),
            token_program_info.clone(),
            referrer_pc_wallet,
            AUTHORITY_AMM,
            amm.nonce as u8,
        )?;
        Ok(())
    }

    /// Processes `process_create_config` instruction.
    pub fn process_create_config(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
        let account_info_iter = &mut accounts.iter();
        let admin_info = next_account_info(account_info_iter)?;
        let amm_config_info = next_account_info(account_info_iter)?;
        let pnl_owner_info = next_account_info(account_info_iter)?;
        let system_program_info = next_account_info(account_info_iter)?;
        let rent_sysvar_info = next_account_info(account_info_iter)?;

        if !admin_info.is_signer || config_feature::amm_owner::id() != *admin_info.key {
            return Err(AmmError::InvalidSignAccount.into());
        }
        if *system_program_info.key != solana_program::system_program::id() {
            return Err(AmmError::InvalidSysProgramAddress.into());
        }

        let (pda, bump_seed) = Pubkey::find_program_address(&[&&AMM_CONFIG_SEED], program_id);
        if pda != *amm_config_info.key {
            return Err(AmmError::InvalidConfigAccount.into());
        }
        if amm_config_info.owner != system_program_info.key {
            return Err(AmmError::RepeatCreateConfigAccount.into());
        }
        let pda_signer_seeds: &[&[_]] = &[&AMM_CONFIG_SEED, &[bump_seed]];
        let rent = &Rent::from_account_info(rent_sysvar_info)?;
        let data_size = size_of::<AmmConfig>();
        let required_lamports = rent
            .minimum_balance(data_size)
            .max(1)
            .saturating_sub(amm_config_info.lamports());
        if required_lamports > 0 {
            invoke(
                &system_instruction::transfer(
                    admin_info.key,
                    amm_config_info.key,
                    required_lamports,
                ),
                &[
                    admin_info.clone(),
                    amm_config_info.clone(),
                    system_program_info.clone(),
                ],
            )?;
        }
        invoke_signed(
            &system_instruction::allocate(amm_config_info.key, data_size as u64),
            &[amm_config_info.clone(), system_program_info.clone()],
            &[&pda_signer_seeds],
        )?;
        invoke_signed(
            &system_instruction::assign(amm_config_info.key, &program_id),
            &[amm_config_info.clone(), system_program_info.clone()],
            &[&pda_signer_seeds],
        )?;

        let mut amm_config = AmmConfig::load_mut_checked(&amm_config_info, program_id)?;
        amm_config.pnl_owner = *pnl_owner_info.key;
        amm_config.create_pool_fee = 0;

        Ok(())
    }

    /// Processes `process_update_config` instruction.
    pub fn process_update_config(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        config_args: ConfigArgs,
    ) -> ProgramResult {
        let account_info_iter = &mut accounts.iter();
        let admin_info = next_account_info(account_info_iter)?;
        let amm_config_info = next_account_info(account_info_iter)?;
        if !admin_info.is_signer || config_feature::amm_owner::id() != *admin_info.key {
            return Err(AmmError::InvalidSignAccount.into());
        }
        let (pda, _) = Pubkey::find_program_address(&[&AMM_CONFIG_SEED], program_id);
        if pda != *amm_config_info.key || amm_config_info.owner != program_id {
            return Err(AmmError::InvalidConfigAccount.into());
        }

        let mut amm_config = AmmConfig::load_mut_checked(&amm_config_info, program_id)?;
        match config_args.param {
            0 => {
                let pnl_owner = config_args.owner.unwrap();
                if pnl_owner == Pubkey::default() {
                    return Err(AmmError::InvalidInput.into());
                }
                amm_config.pnl_owner = pnl_owner;
            }
            1 => {
                let cancel_owner = config_args.owner.unwrap();
                if cancel_owner == Pubkey::default() {
                    return Err(AmmError::InvalidInput.into());
                }
                amm_config.cancel_owner = cancel_owner;
            }
            2 => {
                let create_pool_fee = config_args.create_pool_fee.unwrap();
                amm_config.create_pool_fee = create_pool_fee;
            }
            _ => {
                return Err(AmmError::InvalidInput.into());
            }
        }

        return Ok(());
    }

    /// Processes an [Instruction](enum.Instruction.html).
    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
        let instruction = AmmInstruction::unpack(input)?;
        match instruction {
            AmmInstruction::PreInitialize(_init_arg) => {
                unimplemented!("This instruction is not supported, please use Initialize2")
            }
            AmmInstruction::Initialize(_init1) => {
                unimplemented!("This instruction is not supported, please use Initialize2")
            }
            AmmInstruction::Initialize2(init2) => {
                Self::process_initialize2(program_id, accounts, init2)
            }
            AmmInstruction::MonitorStep(monitor) => {
                Self::process_monitor_step(program_id, accounts, monitor)
            }
            AmmInstruction::Deposit(deposit) => {
                Self::process_deposit(program_id, accounts, deposit)
            }
            AmmInstruction::Withdraw(withdraw) => {
                Self::process_withdraw(program_id, accounts, withdraw)
            }
            AmmInstruction::MigrateToOpenBook => {
                Self::process_migrate_to_openbook(program_id, accounts)
            }
            AmmInstruction::SetParams(setparams) => {
                Self::process_set_params(program_id, accounts, setparams)
            }
            AmmInstruction::WithdrawPnl => Self::process_withdrawpnl(program_id, accounts),
            AmmInstruction::WithdrawSrm(withdrawsrm) => {
                Self::process_withdraw_srm(program_id, accounts, withdrawsrm)
            }
            AmmInstruction::SwapBaseIn(swap) => {
                Self::process_swap_base_in(program_id, accounts, swap)
            }
            AmmInstruction::SwapBaseOut(swap) => {
                Self::process_swap_base_out(program_id, accounts, swap)
            }
            AmmInstruction::SimulateInfo(simulate) => {
                Self::process_simulate_info(program_id, accounts, simulate)
            }
            AmmInstruction::AdminCancelOrders(cancel) => {
                Self::process_admin_cancel_orders(program_id, accounts, cancel)
            }
            AmmInstruction::CreateConfigAccount => {
                Self::process_create_config(program_id, accounts)
            }
            AmmInstruction::UpdateConfigAccount(config_args) => {
                Self::process_update_config(program_id, accounts, config_args)
            }
        }
    }
}

pub mod account_parser {
    use super::*;
    pub struct IdleArgs<'a> {
        pub program_id: &'a Pubkey,
        pub total_coin_without_take_pnl: &'a mut u64,
        pub total_pc_without_take_pnl: &'a mut u64,
        pub amm: &'a mut AmmInfo,
        pub bids: &'a Vec<LeafNode>,
        pub asks: &'a Vec<LeafNode>,
        pub target: &'a mut TargetOrders,
    }
    impl<'a> IdleArgs<'a> {
        pub fn with_parsed_args(
            program_id: &'a Pubkey,
            spl_token_program_id: &'a Pubkey,
            amm: &mut AmmInfo,
            accounts: &'a [AccountInfo],
            f: impl FnOnce(IdleArgs) -> ProgramResult,
        ) -> ProgramResult {
            if accounts.len() != 11 {
                return Err(AmmError::WrongAccountsNumber.into());
            }
            let &[ref amm_info, ref _market_program_info, ref market_info, ref market_bids_info, ref market_asks_info, ref market_event_queue_info, ref market_authority_info, ref amm_open_orders_info, ref amm_coin_vault_info, ref amm_pc_vault_info, ref amm_target_orders_info] =
                array_ref![accounts, 0, 11];

            let amm_coin_vault =
                Processor::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
            let amm_pc_vault =
                Processor::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
            let mut target =
                TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;

            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                market_authority_info,
                &amm,
                false,
            )?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            let (bids, asks) = Processor::get_amm_orders(&open_orders, bids_orders, asks_orders)?;

            let (mut total_pc_without_take_pnl, mut total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &open_orders,
                    &amm,
                    &market_state,
                    &market_event_queue_info,
                    &amm_open_orders_info,
                )?;

            let args = IdleArgs {
                program_id,
                total_coin_without_take_pnl: &mut total_coin_without_take_pnl,
                total_pc_without_take_pnl: &mut total_pc_without_take_pnl,
                amm,
                bids: &bids,
                asks: &asks,
                target: &mut target,
            };
            f(args)
        }
    }

    pub struct CancelAllOrdersArgs<'a, 'b: 'a> {
        pub program_id: &'a Pubkey,
        pub limit: u16,
        pub market_program_info: &'a AccountInfo<'b>,
        pub market_info: &'a AccountInfo<'b>,
        pub amm_open_orders_info: &'a AccountInfo<'b>,
        pub amm_authority_info: &'a AccountInfo<'b>,
        pub market_event_queue_info: &'a AccountInfo<'b>,
        pub market_coin_vault_info: &'a AccountInfo<'b>,
        pub market_pc_vault_info: &'a AccountInfo<'b>,
        pub market_bids_info: &'a AccountInfo<'b>,
        pub market_asks_info: &'a AccountInfo<'b>,
        pub market_vault_signer: &'a AccountInfo<'b>,
        pub token_program_info: &'a AccountInfo<'b>,
        pub amm_coin_vault_info: &'a AccountInfo<'b>,
        pub amm_pc_vault_info: &'a AccountInfo<'b>,

        pub referrer_pc_account: Option<&'a AccountInfo<'b>>,

        pub amm: &'a mut AmmInfo,
        pub open_orders: &'a OpenOrders,
        pub target: &'a mut TargetOrders,
        pub bids: &'a Vec<LeafNode>,
        pub asks: &'a Vec<LeafNode>,
    }
    impl<'a, 'b: 'a> CancelAllOrdersArgs<'a, 'b> {
        pub fn with_parsed_args(
            program_id: &'a Pubkey,
            limit: u16,
            amm: &mut AmmInfo,
            accounts: &'a [AccountInfo<'b>],
            referrer_pc_wallet: Option<&'a AccountInfo<'b>>,
            f: impl FnOnce(CancelAllOrdersArgs) -> ProgramResult,
        ) -> ProgramResult {
            if accounts.len() != 15 {
                return Err(AmmError::WrongAccountsNumber.into());
            }
            let &[ref amm_info, ref market_program_info, ref market_info, ref amm_open_orders_info, ref amm_authority_info, ref market_event_queue_info, ref market_coin_vault_info, ref market_pc_vault_info, ref market_bids_info, ref market_asks_info, ref market_vault_signer, ref token_program_info, ref amm_coin_vault_info, ref amm_pc_vault_info, ref amm_target_orders_info] =
                array_ref![accounts, 0, 15];

            let mut target =
                TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                true,
            )?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            let (bids, asks) = Processor::get_amm_orders(&open_orders, bids_orders, asks_orders)?;

            let args = CancelAllOrdersArgs {
                program_id,
                limit,
                market_program_info,
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                market_event_queue_info,
                market_coin_vault_info,
                market_pc_vault_info,
                market_bids_info,
                market_asks_info,
                market_vault_signer,
                token_program_info,
                amm_coin_vault_info,
                amm_pc_vault_info,
                referrer_pc_account: referrer_pc_wallet,
                amm,
                open_orders: &open_orders,
                target: &mut target,
                bids: &bids,
                asks: &asks,
            };
            f(args)
        }
    }

    pub struct PlanOrderBookArgs<'a> {
        pub program_id: &'a Pubkey,
        pub limit: u16,
        pub total_coin_without_take_pnl: u64,
        pub total_pc_without_take_pnl: u64,
        pub amm: &'a mut AmmInfo,
        pub target: &'a mut TargetOrders,
    }
    impl<'a> PlanOrderBookArgs<'a> {
        pub fn with_parsed_args(
            program_id: &'a Pubkey,
            spl_token_program_id: &'a Pubkey,
            limit: u16,
            amm: &mut AmmInfo,
            accounts: &'a [AccountInfo],
            f: impl FnOnce(PlanOrderBookArgs) -> ProgramResult,
        ) -> ProgramResult {
            if accounts.len() != 8 {
                return Err(AmmError::WrongAccountsNumber.into());
            }
            let &[ref amm_info, ref market_info, ref market_event_queue_info, ref amm_authority_info, ref amm_open_orders_info, ref amm_coin_vault_info, ref amm_pc_vault_info, ref amm_target_orders_info] =
                array_ref![accounts, 0, 8];

            let amm_coin_vault =
                Processor::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
            let amm_pc_vault =
                Processor::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
            let mut target =
                TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;
            let (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &open_orders,
                    &amm,
                    &market_state,
                    &market_event_queue_info,
                    &amm_open_orders_info,
                )?;

            let args = PlanOrderBookArgs {
                program_id,
                limit,
                total_coin_without_take_pnl,
                total_pc_without_take_pnl,
                amm,
                target: &mut target,
            };
            f(args)
        }
    }

    pub struct PlaceOrdersArgs<'a, 'b: 'a> {
        pub program_id: &'a Pubkey,
        pub limit: u16,

        pub amm_authority_info: &'a AccountInfo<'b>,
        pub amm_open_orders_info: &'a AccountInfo<'b>,
        pub market_program_info: &'a AccountInfo<'b>,
        pub market_info: &'a AccountInfo<'b>,
        pub market_request_queue_info: &'a AccountInfo<'b>,
        pub amm_coin_vault_info: &'a AccountInfo<'b>,
        pub amm_pc_vault_info: &'a AccountInfo<'b>,
        pub market_coin_vault_info: &'a AccountInfo<'b>,
        pub market_pc_vault_info: &'a AccountInfo<'b>,
        pub token_program_info: &'a AccountInfo<'b>,
        pub rent_info: &'a AccountInfo<'b>,

        pub market_event_queue_info: &'a AccountInfo<'b>,
        pub market_bids_info: &'a AccountInfo<'b>,
        pub market_asks_info: &'a AccountInfo<'b>,

        pub srm_token_account: Option<&'a AccountInfo<'b>>,

        pub amm: &'a mut AmmInfo,
        pub open_orders: &'a OpenOrders,
        pub bids: &'a Vec<LeafNode>,
        pub asks: &'a Vec<LeafNode>,
        pub target: &'a mut TargetOrders,

        pub total_coin_without_take_pnl: u64,
        pub total_pc_without_take_pnl: u64,
        pub coin_vault_amount: u64,
        pub pc_vault_amount: u64,
    }
    impl<'a, 'b: 'a> PlaceOrdersArgs<'a, 'b> {
        pub fn with_parsed_args(
            program_id: &'a Pubkey,
            spl_token_program_id: &'a Pubkey,
            limit: u16,
            amm: &mut AmmInfo,
            accounts: &'a [AccountInfo<'b>],
            srm_token_account: Option<&'a AccountInfo<'b>>,
            f: impl FnOnce(PlaceOrdersArgs) -> ProgramResult,
        ) -> ProgramResult {
            if accounts.len() != 17 {
                return Err(AmmError::WrongAccountsNumber.into());
            }
            let accounts = array_ref![accounts, 0, 17];
            let (new_orders_accounts, data_accounts) = array_refs![accounts, 15, 2];
            let &[ref amm_info, ref amm_authority_info, ref amm_open_orders_info, ref market_program_info, ref market_info, ref market_request_queue_info, ref amm_coin_vault_info, ref amm_pc_vault_info, ref market_coin_vault_info, ref market_pc_vault_info, ref token_program_info, ref rent_info, ref market_event_queue_info, ref market_bids_info, ref market_asks_info] =
                array_ref![new_orders_accounts, 0, 15];
            let &[ref amm_target_orders_info, ref _clock_info] = array_ref![data_accounts, 0, 2];

            let amm_coin_vault =
                Processor::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
            let amm_pc_vault =
                Processor::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
            let mut target =
                TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                false,
            )?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            let (bids, asks) = Processor::get_amm_orders(&open_orders, bids_orders, asks_orders)?;
            let (total_pc_without_take_pnl, total_coin_without_take_pnl) =
                Calculator::calc_total_without_take_pnl(
                    amm_pc_vault.amount,
                    amm_coin_vault.amount,
                    &open_orders,
                    &amm,
                    &market_state,
                    &market_event_queue_info,
                    &amm_open_orders_info,
                )?;

            let args = PlaceOrdersArgs {
                program_id,
                limit,

                amm_authority_info,
                amm_open_orders_info,
                market_program_info,
                market_info,
                market_request_queue_info,
                amm_coin_vault_info,
                amm_pc_vault_info,
                market_coin_vault_info,
                market_pc_vault_info,
                token_program_info,
                rent_info,

                market_event_queue_info,
                market_bids_info,
                market_asks_info,
                srm_token_account,

                amm,
                open_orders: &open_orders,
                bids: &bids,
                asks: &asks,
                target: &mut target,

                total_pc_without_take_pnl,
                total_coin_without_take_pnl,

                coin_vault_amount: amm_coin_vault.amount,
                pc_vault_amount: amm_pc_vault.amount,
            };
            f(args)
        }
    }

    pub struct PurgeOrderArgs<'a, 'b: 'a> {
        pub program_id: &'a Pubkey,
        pub limit: u16,
        pub market_program_info: &'a AccountInfo<'b>,
        pub market_info: &'a AccountInfo<'b>,
        pub amm_open_orders_info: &'a AccountInfo<'b>,
        pub amm_authority_info: &'a AccountInfo<'b>,
        pub market_event_queue_info: &'a AccountInfo<'b>,
        pub market_bids_info: &'a AccountInfo<'b>,
        pub market_asks_info: &'a AccountInfo<'b>,
        pub amm: &'a mut AmmInfo,
        pub target: &'a TargetOrders,
        pub bids: &'a Vec<LeafNode>,
        pub asks: &'a Vec<LeafNode>,
    }
    impl<'a, 'b: 'a> PurgeOrderArgs<'a, 'b> {
        pub fn with_parsed_args(
            program_id: &'a Pubkey,
            _spl_token_program_id: &'a Pubkey,
            limit: u16,
            amm: &mut AmmInfo,
            accounts: &'a [AccountInfo<'b>],
            f: impl FnOnce(PurgeOrderArgs) -> ProgramResult,
        ) -> ProgramResult {
            if accounts.len() != 9 {
                return Err(AmmError::WrongAccountsNumber.into());
            }
            let &[ref amm_info, ref market_program_info, ref market_info, ref amm_open_orders_info, ref amm_authority_info, ref market_event_queue_info, ref market_bids_info, ref market_asks_info, ref amm_target_orders_info] =
                array_ref![accounts, 0, 9];

            let target =
                TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                true,
            )?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            let (bids, asks) = Processor::get_amm_orders(&open_orders, bids_orders, asks_orders)?;

            let args = PurgeOrderArgs {
                program_id,
                limit,
                market_program_info,
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                market_event_queue_info,
                market_bids_info,
                market_asks_info,
                amm,
                target: &target,
                bids: &bids,
                asks: &asks,
            };
            f(args)
        }
    }

    pub struct CancelOrderArgs<'a, 'b: 'a> {
        pub program_id: &'a Pubkey,
        pub limit: u16,
        pub market_program_info: &'a AccountInfo<'b>,
        pub market_info: &'a AccountInfo<'b>,
        pub amm_open_orders_info: &'a AccountInfo<'b>,
        pub amm_authority_info: &'a AccountInfo<'b>,
        pub market_event_queue_info: &'a AccountInfo<'b>,
        pub market_bids_info: &'a AccountInfo<'b>,
        pub market_asks_info: &'a AccountInfo<'b>,
        pub amm: &'a mut AmmInfo,
        pub open_orders: &'a OpenOrders,
        pub target: &'a mut TargetOrders,
        pub bids: &'a Vec<LeafNode>,
        pub asks: &'a Vec<LeafNode>,

        pub coin_amount: u64,
        pub pc_amount: u64,
    }
    impl<'a, 'b: 'a> CancelOrderArgs<'a, 'b> {
        pub fn with_parsed_args(
            program_id: &'a Pubkey,
            spl_token_program_id: &'a Pubkey,
            limit: u16,
            amm: &mut AmmInfo,
            accounts: &'a [AccountInfo<'b>],
            f: impl FnOnce(CancelOrderArgs) -> ProgramResult,
        ) -> ProgramResult {
            if accounts.len() != 11 {
                return Err(AmmError::WrongAccountsNumber.into());
            }
            let &[ref amm_info, ref market_program_info, ref market_info, ref amm_open_orders_info, ref amm_authority_info, ref market_event_queue_info, ref market_bids_info, ref market_asks_info, ref amm_coin_vault_info, ref amm_pc_vault_info, ref amm_target_orders_info] =
                array_ref![accounts, 0, 11];

            let amm_coin_vault =
                Processor::unpack_token_account(&amm_coin_vault_info, spl_token_program_id)?;
            let amm_pc_vault =
                Processor::unpack_token_account(&amm_pc_vault_info, spl_token_program_id)?;
            let mut target =
                TargetOrders::load_mut_checked(&amm_target_orders_info, program_id, amm_info.key)?;
            let (market_state, open_orders) = Processor::load_serum_market_order(
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                &amm,
                true,
            )?;
            // let (myorders, _max_bid, _min_ask) = Processor::get_amm_orders_and_dex_best_price(&market_state, &open_orders, bids_acc, asks_acc)?;
            let bids_orders = market_state.load_bids_mut(&market_bids_info)?;
            let asks_orders = market_state.load_asks_mut(&market_asks_info)?;
            let (bids, asks) = Processor::get_amm_orders(&open_orders, bids_orders, asks_orders)?;

            let args = CancelOrderArgs {
                program_id,
                limit,
                market_program_info,
                market_info,
                amm_open_orders_info,
                amm_authority_info,
                market_event_queue_info,
                market_bids_info,
                market_asks_info,
                amm,
                open_orders: &open_orders,
                target: &mut target,
                bids: &bids,
                asks: &asks,
                coin_amount: amm_coin_vault.amount,
                pc_amount: amm_pc_vault.amount,
            };
            f(args)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_calc_take_pnl() {
        let mut amm = AmmInfo::default();
        amm.initialize(0, 0, 2, 9, 1000000, 1).unwrap();
        let mut target = TargetOrders::default();
        target.calc_pnl_x = 900000000000000;
        target.calc_pnl_y = 150000000000000000000000000;

        let mut total_pc_without_take_pnl = 1343675125663;
        let mut total_coin_without_take_pnl = 117837534493793;
        let x1 = Calculator::normalize_decimal_v2(
            total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y1 = Calculator::normalize_decimal_v2(
            total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );

        let (delta_x, delta_y) = Processor::calc_take_pnl(
            &target,
            &mut amm,
            &mut total_pc_without_take_pnl,
            &mut total_coin_without_take_pnl,
            x1.as_u128().into(),
            y1.as_u128().into(),
        )
        .unwrap();
        println!("delta_x:{}, delta_y:{}", delta_x, delta_y);
    }

    #[test]
    fn test_calc_pnl_precision() {
        // init
        let mut amm = AmmInfo::default();
        let init_pc_amount = 5434000000u64;
        let init_coin_amount = 100000000000000u64;
        let liquidity = Calculator::to_u64(
            U128::from(init_pc_amount)
                .checked_mul(init_coin_amount.into())
                .unwrap()
                .integer_sqrt()
                .as_u128(),
        )
        .unwrap();
        amm.initialize(0, 0, 5, 9, 1000000000, 7803).unwrap();
        amm.lp_amount = liquidity;

        let x =
            Calculator::normalize_decimal_v2(5434000000, amm.pc_decimals, amm.sys_decimal_value);
        let y = Calculator::normalize_decimal_v2(
            100000000000000,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );
        let mut target = TargetOrders::default();
        target.calc_pnl_x = x.as_u128();
        target.calc_pnl_y = y.as_u128();
        println!(
             "init_pc_amount:{}, init_coin_amount:{}, liquidity:{}, sys_decimal_value:{}, calc_pnl_x:{}, calc_pnl_y:{}",
             init_pc_amount, init_coin_amount, liquidity, amm.sys_decimal_value, target.calc_pnl_x, target.calc_pnl_y
         );

        // withdraw
        let withdraw_lp = 2577470628u64;
        let mut total_pc_without_take_pnl = init_pc_amount;
        let mut total_coin_without_take_pnl = init_coin_amount;
        let x1 = Calculator::normalize_decimal_v2(
            total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y1 = Calculator::normalize_decimal_v2(
            total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );

        let (delta_x, delta_y) = Processor::calc_take_pnl(
            &target,
            &mut amm,
            &mut total_pc_without_take_pnl,
            &mut total_coin_without_take_pnl,
            x1.as_u128().into(),
            y1.as_u128().into(),
        )
        .unwrap();
        println!("delta_x:{}, delta_y:{}", delta_x, delta_y);
        // coin_amount / total_coin_amount = amount / lp_mint.supply => coin_amount = total_coin_amount * amount / pool_mint.supply
        let invariant = InvariantPool {
            token_input: withdraw_lp,
            token_total: amm.lp_amount,
        };
        let coin_amount = invariant
            .exchange_pool_to_token(total_coin_without_take_pnl, RoundDirection::Floor)
            .ok_or(AmmError::CalculationExRateFailure)
            .unwrap();
        let pc_amount = invariant
            .exchange_pool_to_token(total_pc_without_take_pnl, RoundDirection::Floor)
            .ok_or(AmmError::CalculationExRateFailure)
            .unwrap();

        amm.lp_amount = amm.lp_amount.checked_sub(withdraw_lp).unwrap();
        target.calc_pnl_x = x1
            .checked_sub(Calculator::normalize_decimal_v2(
                pc_amount,
                amm.pc_decimals,
                amm.sys_decimal_value,
            ))
            .unwrap()
            .checked_sub(U128::from(delta_x))
            .unwrap()
            .as_u128();
        target.calc_pnl_y = y1
            .checked_sub(Calculator::normalize_decimal_v2(
                coin_amount,
                amm.coin_decimals,
                amm.sys_decimal_value,
            ))
            .unwrap()
            .checked_sub(U128::from(delta_y))
            .unwrap()
            .as_u128();
        total_pc_without_take_pnl = total_pc_without_take_pnl.checked_sub(pc_amount).unwrap();
        total_coin_without_take_pnl = total_coin_without_take_pnl
            .checked_sub(coin_amount)
            .unwrap();
        println!(
             "withdraw calc_pnl_x:{}, calc_pnl_y:{}, total_pc_without_take_pnl:{}, total_coin_without_take_pnl:{}",
             target.calc_pnl_x, target.calc_pnl_y, total_pc_without_take_pnl, total_coin_without_take_pnl
         );

        // withdraw 2
        let x1 = Calculator::normalize_decimal_v2(
            total_pc_without_take_pnl,
            amm.pc_decimals,
            amm.sys_decimal_value,
        );
        let y1 = Calculator::normalize_decimal_v2(
            total_coin_without_take_pnl,
            amm.coin_decimals,
            amm.sys_decimal_value,
        );

        let (delta_x, delta_y) = Processor::calc_take_pnl(
            &target,
            &mut amm,
            &mut total_pc_without_take_pnl,
            &mut total_coin_without_take_pnl,
            x1.as_u128().into(),
            y1.as_u128().into(),
        )
        .unwrap();
        println!("delta_x:{}, delta_y:{}", delta_x, delta_y);
    }

    #[test]
    fn test_swap_base_in() {
        let amount_in = 212854295571_u64;
        let total_coin_without_take_pnl = 77043918330755_u64;
        let total_pc_without_take_pnl = 1511361338135_u64;

        let swap_direction = SwapDirection::Coin2PC;
        let mut amm = AmmInfo::default();
        amm.initialize(0, 0, 2, 9, 1000000, 1).unwrap();

        let swap_fee = U128::from(amount_in)
            .checked_mul(amm.fees.swap_fee_numerator.into())
            .unwrap()
            .checked_ceil_div(amm.fees.swap_fee_denominator.into())
            .unwrap()
            .0;

        let swap_in_after_deduct_fee = U128::from(amount_in).checked_sub(swap_fee).unwrap();
        let swap_amount_out = Calculator::swap_token_amount_base_in(
            swap_in_after_deduct_fee,
            total_pc_without_take_pnl.into(),
            total_coin_without_take_pnl.into(),
            swap_direction,
        )
        .as_u64();

        println!("swap_amount_out:{}", swap_amount_out);
    }
}
 C:\solana_rust_bot\src\raydium-amm\program\src\processor.rs     ///////       C:\solana_rust_bot\src\raydium-amm\program\src\invokers.rs    //! Program state invoker

use solana_program::{
    account_info::AccountInfo,
    instruction::{AccountMeta, Instruction},
    program_error::ProgramError,
    pubkey::Pubkey,
};
use std::num::NonZeroU64;

pub struct Invokers {}

impl Invokers {
    /// Issue a associated_spl_token `create_associated_token_account` instruction
    pub fn create_ata_spl_token<'a>(
        associated_account: AccountInfo<'a>,
        funding_account: AccountInfo<'a>,
        wallet_account: AccountInfo<'a>,
        token_mint_account: AccountInfo<'a>,
        token_program_account: AccountInfo<'a>,
        ata_program_account: AccountInfo<'a>,
        system_program_account: AccountInfo<'a>,
    ) -> Result<(), ProgramError> {
        let ix = spl_associated_token_account::instruction::create_associated_token_account(
            funding_account.key,
            wallet_account.key,
            token_mint_account.key,
            token_program_account.key,
        );
        solana_program::program::invoke_signed(
            &ix,
            &[
                associated_account,
                funding_account,
                wallet_account,
                token_mint_account,
                token_program_account,
                ata_program_account,
                system_program_account,
            ],
            &[],
        )
    }
    /// Issue a spl_token `Burn` instruction.
    pub fn token_burn<'a>(
        token_program: AccountInfo<'a>,
        burn_account: AccountInfo<'a>,
        mint: AccountInfo<'a>,
        owner: AccountInfo<'a>,
        burn_amount: u64,
    ) -> Result<(), ProgramError> {
        let ix = spl_token::instruction::burn(
            token_program.key,
            burn_account.key,
            mint.key,
            owner.key,
            &[],
            burn_amount,
        )?;

        solana_program::program::invoke_signed(
            &ix,
            &[burn_account, mint, owner, token_program],
            &[],
        )
    }

    /// Close Account
    pub fn token_close_with_authority<'a>(
        token_program: AccountInfo<'a>,
        close_account: AccountInfo<'a>,
        destination_account: AccountInfo<'a>,
        authority: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];
        let ix = spl_token::instruction::close_account(
            token_program.key,
            close_account.key,
            destination_account.key,
            authority.key,
            &[],
        )?;

        solana_program::program::invoke_signed(
            &ix,
            &[close_account, destination_account, authority, token_program],
            signers,
        )
    }

    /// Issue a spl_token `Burn` instruction.
    pub fn token_burn_with_authority<'a>(
        token_program: AccountInfo<'a>,
        burn_account: AccountInfo<'a>,
        mint: AccountInfo<'a>,
        authority: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,
        burn_amount: u64,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];
        let ix = spl_token::instruction::burn(
            token_program.key,
            burn_account.key,
            mint.key,
            authority.key,
            &[],
            burn_amount,
        )?;

        solana_program::program::invoke_signed(
            &ix,
            &[burn_account, mint, authority, token_program],
            signers,
        )
    }

    /// Issue a spl_token `MintTo` instruction.
    pub fn token_mint_to<'a>(
        token_program: AccountInfo<'a>,
        mint: AccountInfo<'a>,
        destination: AccountInfo<'a>,
        authority: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,
        amount: u64,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];
        let ix = spl_token::instruction::mint_to(
            token_program.key,
            mint.key,
            destination.key,
            authority.key,
            &[],
            amount,
        )?;

        solana_program::program::invoke_signed(
            &ix,
            &[mint, destination, authority, token_program],
            signers,
        )
    }

    /// Issue a spl_token `Transfer` instruction.
    pub fn token_transfer<'a>(
        token_program: AccountInfo<'a>,
        source: AccountInfo<'a>,
        destination: AccountInfo<'a>,
        owner: AccountInfo<'a>,
        deposit_amount: u64,
    ) -> Result<(), ProgramError> {
        let ix = spl_token::instruction::transfer(
            token_program.key,
            source.key,
            destination.key,
            owner.key,
            &[],
            deposit_amount,
        )?;
        solana_program::program::invoke_signed(
            &ix,
            &[source, destination, owner, token_program],
            &[],
        )
    }

    /// Issue a spl_token `Transfer` instruction.
    pub fn token_transfer_with_authority<'a>(
        token_program: AccountInfo<'a>,
        source: AccountInfo<'a>,
        destination: AccountInfo<'a>,
        authority: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,
        amount: u64,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];
        let ix = spl_token::instruction::transfer(
            token_program.key,
            source.key,
            destination.key,
            authority.key,
            &[],
            amount,
        )?;
        solana_program::program::invoke_signed(
            &ix,
            &[source, destination, authority, token_program],
            signers,
        )
    }

    pub fn token_set_authority<'a>(
        token_program: AccountInfo<'a>,
        account: AccountInfo<'a>, // mint or token account
        authority: AccountInfo<'a>,
        new_authority: AccountInfo<'a>,
        amm_seed: &[u8],
        authority_nonce: u8,
        authority_type: spl_token::instruction::AuthorityType,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[authority_nonce]];
        let signers = &[&authority_signature_seeds[..]];
        let ix = spl_token::instruction::set_authority(
            token_program.key,
            account.key,
            Some(new_authority.key),
            authority_type,
            authority.key,
            &[],
        )?;
        solana_program::program::invoke_signed(&ix, &[account, authority, token_program], signers)
    }

    /// Issue a dex `InitOpenOrders` instruction
    pub fn invoke_dex_init_open_orders<'a>(
        dex_program: AccountInfo<'a>,
        open_orders: AccountInfo<'a>,
        open_orders_owner: AccountInfo<'a>,
        market: AccountInfo<'a>,
        rent_sysvar: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];

        let ix = serum_dex::instruction::init_open_orders(
            dex_program.key,
            open_orders.key,
            open_orders_owner.key,
            market.key,
            None,
        )?;

        let accounts = vec![
            dex_program,
            open_orders,
            open_orders_owner,
            market,
            rent_sysvar,
        ];
        solana_program::program::invoke_signed(&ix, &accounts, signers)
    }

    pub fn invoke_dex_close_open_orders<'a>(
        dex_program: AccountInfo<'a>,
        open_orders: AccountInfo<'a>,
        open_orders_owner: AccountInfo<'a>,
        destination: AccountInfo<'a>,
        market: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];

        let ix = serum_dex::instruction::close_open_orders(
            dex_program.key,
            open_orders.key,
            open_orders_owner.key,
            destination.key,
            market.key,
        )?;
        let accounts = vec![
            dex_program,
            open_orders,
            open_orders_owner,
            destination,
            market,
        ];
        solana_program::program::invoke_signed(&ix, &accounts, signers)
    }

    pub fn replace_order_by_client_id(
        market: &Pubkey,
        open_orders_account: &Pubkey,
        request_queue: &Pubkey,
        event_queue: &Pubkey,
        market_bids: &Pubkey,
        market_asks: &Pubkey,
        order_payer: &Pubkey,
        open_orders_account_owner: &Pubkey,
        coin_vault: &Pubkey,
        pc_vault: &Pubkey,
        spl_token_program_id: &Pubkey,
        rent_sysvar_id: &Pubkey,
        srm_account_referral: Option<&Pubkey>,
        program_id: &Pubkey,
        side: serum_dex::matching::Side,
        limit_price: NonZeroU64,
        max_coin_qty: NonZeroU64,
        order_type: serum_dex::matching::OrderType,
        client_order_id: u64,
        self_trade_behavior: serum_dex::instruction::SelfTradeBehavior,
        limit: u16,
        max_native_pc_qty_including_fees: NonZeroU64,
        max_ts: i64,
    ) -> Result<Instruction, serum_dex::error::DexError> {
        let data = serum_dex::instruction::MarketInstruction::ReplaceOrderByClientId(
            serum_dex::instruction::NewOrderInstructionV3 {
                side,
                limit_price,
                max_coin_qty,
                order_type,
                client_order_id,
                self_trade_behavior,
                limit,
                max_native_pc_qty_including_fees,
                max_ts,
            },
        )
        .pack();
        let mut accounts = vec![
            AccountMeta::new(*market, false),
            AccountMeta::new(*open_orders_account, false),
            AccountMeta::new(*request_queue, false),
            AccountMeta::new(*event_queue, false),
            AccountMeta::new(*market_bids, false),
            AccountMeta::new(*market_asks, false),
            AccountMeta::new(*order_payer, false),
            AccountMeta::new_readonly(*open_orders_account_owner, true),
            AccountMeta::new(*coin_vault, false),
            AccountMeta::new(*pc_vault, false),
            AccountMeta::new_readonly(*spl_token_program_id, false),
            AccountMeta::new_readonly(*rent_sysvar_id, false),
        ];
        if let Some(key) = srm_account_referral {
            accounts.push(AccountMeta::new_readonly(*key, false))
        }
        Ok(Instruction {
            program_id: *program_id,
            data,
            accounts,
        })
    }
    /// Issue a dex `ReplaceOrderByClientId` instruction.
    pub fn invoke_dex_replace_order_by_client_id<'a>(
        dex_program: AccountInfo<'a>,
        market: AccountInfo<'a>,
        open_orders: AccountInfo<'a>,
        req_q: AccountInfo<'a>,
        event_q: AccountInfo<'a>,
        bids: AccountInfo<'a>,
        asks: AccountInfo<'a>,
        payer: AccountInfo<'a>,
        open_orders_owner: AccountInfo<'a>,
        coin_vault: AccountInfo<'a>,
        pc_vault: AccountInfo<'a>,
        token_program: AccountInfo<'a>,
        rent_account: AccountInfo<'a>,
        srm_account_referral: Option<&AccountInfo<'a>>,
        amm_seed: &[u8],
        nonce: u8,

        side: serum_dex::matching::Side,
        limit_price: NonZeroU64,
        max_coin_qty: NonZeroU64,
        max_native_pc_qty_including_fees: NonZeroU64,
        order_type: serum_dex::matching::OrderType,
        client_order_id: u64,
        limit: u16,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];

        let mut srm_account_referral_key = None;
        if let Some(srm_account_referral_account) = srm_account_referral {
            srm_account_referral_key = Some(srm_account_referral_account.key);
        }

        let ix = Self::replace_order_by_client_id(
            market.key,
            open_orders.key,
            req_q.key,
            event_q.key,
            bids.key,
            asks.key,
            payer.key,
            open_orders_owner.key,
            coin_vault.key,
            pc_vault.key,
            token_program.key,
            rent_account.key,
            srm_account_referral_key,
            dex_program.key,
            side,
            limit_price,
            max_coin_qty,
            order_type,
            client_order_id,
            serum_dex::instruction::SelfTradeBehavior::CancelProvide,
            limit,
            max_native_pc_qty_including_fees,
            i64::MAX,
        )?;

        let mut accounts = vec![
            dex_program,
            market,
            open_orders,
            req_q,
            event_q,
            bids,
            asks,
            payer,
            open_orders_owner,
            coin_vault,
            pc_vault,
            token_program,
            rent_account,
        ];
        if let Some(srm_account) = srm_account_referral {
            accounts.push(srm_account.clone());
        }

        solana_program::program::invoke_signed(&ix, &accounts, signers)
    }

    /// Issue a dex `NewOrder` instruction.
    pub fn invoke_dex_new_order_v3<'a>(
        dex_program: AccountInfo<'a>,
        market: AccountInfo<'a>,
        open_orders: AccountInfo<'a>,
        req_q: AccountInfo<'a>,
        event_q: AccountInfo<'a>,
        bids: AccountInfo<'a>,
        asks: AccountInfo<'a>,
        payer: AccountInfo<'a>,
        open_orders_owner: AccountInfo<'a>,
        coin_vault: AccountInfo<'a>,
        pc_vault: AccountInfo<'a>,
        token_program: AccountInfo<'a>,
        rent_account: AccountInfo<'a>,
        srm_account_referral: Option<&AccountInfo<'a>>,
        amm_seed: &[u8],
        nonce: u8,

        side: serum_dex::matching::Side,
        limit_price: NonZeroU64,
        max_coin_qty: NonZeroU64,
        max_native_pc_qty_including_fees: NonZeroU64,
        order_type: serum_dex::matching::OrderType,
        client_order_id: u64,
        limit: u16,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];

        let mut srm_account_referral_key = None;
        if let Some(srm_account_referral_account) = srm_account_referral {
            srm_account_referral_key = Some(srm_account_referral_account.key);
        }

        let ix = serum_dex::instruction::new_order(
            market.key,
            open_orders.key,
            req_q.key,
            event_q.key,
            bids.key,
            asks.key,
            payer.key,
            open_orders_owner.key,
            coin_vault.key,
            pc_vault.key,
            token_program.key,
            rent_account.key,
            srm_account_referral_key,
            dex_program.key,
            side,
            limit_price,
            max_coin_qty,
            order_type,
            client_order_id,
            serum_dex::instruction::SelfTradeBehavior::CancelProvide,
            limit,
            max_native_pc_qty_including_fees,
            i64::MAX,
        )?;

        let mut accounts = vec![
            dex_program,
            market,
            open_orders,
            req_q,
            event_q,
            bids,
            asks,
            payer,
            open_orders_owner,
            coin_vault,
            pc_vault,
            token_program,
            rent_account,
        ];
        if let Some(srm_account) = srm_account_referral {
            accounts.push(srm_account.clone());
        }

        solana_program::program::invoke_signed(&ix, &accounts, signers)
    }

    /// Issue a dex `CancelOrder` instruction.
    pub fn invoke_dex_cancel_order_v2<'a>(
        dex_program: AccountInfo<'a>,
        market: AccountInfo<'a>,
        bids: AccountInfo<'a>,
        asks: AccountInfo<'a>,
        open_orders: AccountInfo<'a>,
        open_orders_owner: AccountInfo<'a>,
        event_q: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,

        side: serum_dex::matching::Side,
        order_id: u128,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];

        let ix = serum_dex::instruction::cancel_order(
            dex_program.key,
            market.key,
            bids.key,
            asks.key,
            open_orders.key,
            open_orders_owner.key,
            event_q.key,
            side,
            order_id,
        )?;
        let accounts = [
            dex_program,
            market,
            bids,
            asks,
            open_orders,
            open_orders_owner,
            event_q,
        ];
        solana_program::program::invoke_signed(&ix, &accounts, signers)
    }

    /// Issue a dex `CancelOrdersByClientIds` instruction.
    pub fn invoke_dex_cancel_orders_by_client_order_ids<'a>(
        dex_program: AccountInfo<'a>,
        market: AccountInfo<'a>,
        bids: AccountInfo<'a>,
        asks: AccountInfo<'a>,
        open_orders: AccountInfo<'a>,
        open_orders_owner: AccountInfo<'a>,
        event_q: AccountInfo<'a>,
        amm_seed: &[u8],
        nonce: u8,

        client_order_ids: [u64; 8],
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];

        let ix = serum_dex::instruction::cancel_orders_by_client_order_ids(
            dex_program.key,
            market.key,
            bids.key,
            asks.key,
            open_orders.key,
            open_orders_owner.key,
            event_q.key,
            client_order_ids,
        )?;
        let accounts = [
            dex_program,
            market,
            bids,
            asks,
            open_orders,
            open_orders_owner,
            event_q,
        ];
        solana_program::program::invoke_signed(&ix, &accounts, signers)
    }

    /// Issue a dex `SettleFunds` instruction.
    pub fn invoke_dex_settle_funds<'a>(
        dex_program: AccountInfo<'a>,
        market: AccountInfo<'a>,
        open_orders: AccountInfo<'a>,
        owner: AccountInfo<'a>, //open_orders.owner
        coin_vault: AccountInfo<'a>,
        pc_vault: AccountInfo<'a>,
        coin_wallet: AccountInfo<'a>,
        pc_wallet: AccountInfo<'a>,
        vault_signer: AccountInfo<'a>,
        spl_token_program: AccountInfo<'a>,
        referrer_pc_wallet: Option<&AccountInfo<'a>>,
        amm_seed: &[u8],
        nonce: u8,
    ) -> Result<(), ProgramError> {
        let authority_signature_seeds = [amm_seed, &[nonce]];
        let signers = &[&authority_signature_seeds[..]];

        let mut referrer_pc_wallet_key = None;
        if let Some(referrer_pc_wallet_account) = referrer_pc_wallet {
            referrer_pc_wallet_key = Some(referrer_pc_wallet_account.key);
        }

        let ix = serum_dex::instruction::settle_funds(
            dex_program.key,
            market.key,
            spl_token_program.key,
            open_orders.key,
            owner.key,
            coin_vault.key,
            coin_wallet.key,
            pc_vault.key,
            pc_wallet.key,
            referrer_pc_wallet_key,
            vault_signer.key,
        )?;

        let mut accounts = vec![
            dex_program,
            market,
            open_orders,
            owner,
            coin_vault,
            pc_vault,
            coin_wallet,
            pc_wallet,
            vault_signer,
            spl_token_program,
        ];
        if let Some(referrer_pc_account) = referrer_pc_wallet {
            accounts.push(referrer_pc_account.clone());
        }
        solana_program::program::invoke_signed(&ix, &accounts, signers)
    }
}
 these 2 files are left to me edited and i can share a few of my others so u get a better idea   main.rs use anyhow::{Result, Context};
use dotenv::dotenv;
use raydium_amm::amm;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    pubkey::Pubkey,
    signature::{Keypair, read_keypair_file},
    transaction::Transaction,
};
use std::{str::FromStr, env};

async fn perform_swap(
    client: &RpcClient,
    keypair: &Keypair,
    token_a: &Pubkey,
    token_b: &Pubkey,
    amount_in: u64,
    is_buy: bool
) -> Result<String> {
    let amm_program_id = Pubkey::from_str("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")?;
    let amm_pool_id = Pubkey::from_str("58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2")?;

    // Load AMM keys and market keys
    let amm_keys = amm::utils::load_amm_keys(client, &amm_program_id, &amm_pool_id)?;
    let market_keys = amm::openbook::get_keys_for_market(
        client,
        &amm_keys.market_program,
        &amm_keys.market,
    )?;

    // Determine swap direction
    let direction = if is_buy {
        amm::utils::SwapDirection::PC2Coin
    } else {
        amm::utils::SwapDirection::Coin2PC
    };

    // Calculate amounts
    let result = amm::calculate_pool_vault_amounts(
        client,
        &amm_program_id,
        &amm_pool_id,
        &amm_keys,
        &market_keys,
        amm::utils::CalculateMethod::Simulate,
        Some(&keypair.pubkey()),
    )?;

    let minimum_amount_out = amm::swap_with_slippage(
        result.pool_pc_vault_amount,
        result.pool_coin_vault_amount,
        result.swap_fee_numerator,
        result.swap_fee_denominator,
        direction,
        amount_in,
        true,
        100, // 1% slippage
    )?;

    // Create swap instruction
    let swap_ix = amm::commands::swap(
        &amm_program_id,
        &amm_keys,
        &market_keys,
        &keypair.pubkey(),
        &spl_associated_token_account::get_associated_token_address(
            &keypair.pubkey(),
            token_a,
        ),
        &spl_associated_token_account::get_associated_token_address(
            &keypair.pubkey(),
            token_b,
        ),
        amount_in,
        minimum_amount_out,
        true, // swap_base_in
    )?;

    // Create and send transaction
    let recent_blockhash = client.get_latest_blockhash()?;
    let transaction = Transaction::new_signed_with_payer(
        &[swap_ix],
        Some(&keypair.pubkey()),
        &[keypair],
        recent_blockhash,
    );

    let signature = client.send_and_confirm_transaction(&transaction)?;
    Ok(signature.to_string())
}

#[tokio::main]
async fn main() -> Result<()> {
    dotenv().ok();
    
    let rpc_url = env::var("RPC_URL").expect("RPC_URL not set in .env file");
    let client = RpcClient::new(rpc_url);

    let keypair = read_keypair_file(env::var("KEYPAIR_PATH").expect("KEYPAIR_PATH not set in .env file"))
        .context("Failed to load keypair")?;

    let token_a = Pubkey::from_str("So11111111111111111111111111111111111111112")?; // WSOL
    let token_b = Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")?; // USDC
    let amount_in = 100_000_000; // 0.1 SOL

    println!("Performing buy...");
    let buy_result = perform_swap(&client, &keypair, &token_a, &token_b, amount_in, true).await?;
    println!("Buy transaction signature: {}", buy_result);

    // Wait for a bit before selling
    println!("Waiting for 5 seconds before selling...");
    std::thread::sleep(std::time::Duration::from_secs(5));

    println!("Performing sell...");
    let sell_result = perform_swap(&client, &keypair, &token_b, &token_a, amount_in, false).await?;
    println!("Sell transaction signature: {}", sell_result);

    Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
    dotenv().ok();
    
    let rpc_url = env::var("RPC_URL").expect("RPC_URL not set in .env file");
    let client = RpcClient::new(rpc_url);

    let keypair = read_keypair_file(env::var("KEYPAIR_PATH").expect("KEYPAIR_PATH not set in .env file"))
        .context("Failed to load keypair")?;

    let token_a = Pubkey::from_str("So11111111111111111111111111111111111111112")?; // WSOL
    let token_b = Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")?; // USDC
    let amount_in = 100_000_000; // 0.1 SOL

    println!("Performing buy...");
    let buy_result = perform_swap(&client, &keypair, &token_a, &token_b, amount_in, true).await?;
    println!("Buy transaction signature: {}", buy_result);

    // Wait for a bit before selling
    println!("Waiting for 5 seconds before selling...");
    std::thread::sleep(std::time::Duration::from_secs(5));

    println!("Performing sell...");
    let sell_result = perform_swap(&client, &keypair, &token_b, &token_a, amount_in, false).await?;
    println!("Sell transaction signature: {}", sell_result);

    Ok(())
}
ASSISTANT
Thank you for sharing the code files! It looks like you're working with a Solana-based decentralized exchange (DEX) implementation, particularly an Automated Market Maker (AMM). From the files you've provided, it seems you're dealing with a structure that incorporates Swapping functionalities and Account Invokers within a Raydium-like ecosystem. 

To help you synchronize your files and understand what might be needed to finish up your project, here’s a breakdown of the important files and components involved, as well as additional files you may want to consider sharing:

### Key Files Involved

1. **Processor.rs**: This file contains the core logic for processing transactions related to the AMM, including initialization, deposits, withdrawals, swaps, and simulation of actions. You’ll likely need to ensure that transaction processing aligns with the business logic you want.

2. **Invokers.rs**: This file handles invoking instructions for placing orders, transferring tokens, burning tokens, and other operations using Solana's instruction execution features. This file is crucial to ensure you're correctly interacting with the Solana Program Library (SPL) and any other necessary programs.

3. **main.rs**: This file handles the client-side functionality for your AMM, likely forming the entry point of your application. It includes the `perform_swap` function which carries out the actual transactions (swaps) between tokens and reports the results.

4. **Utils/Library Files**: Depending on your project structure, there may be utility files or libraries containing reusable functions or modules for common operations like token calculations, validation checks, etc.

### Files You May Consider Sharing

- **Cargo.toml**: This file outlines your project dependencies, allows others to see what external crates (like Solana SDK or Raydium-specific libraries) you are using.

- **Any module files related to `amm`, `utils`, or any custom libraries** you've created for calculations or token management—these would give insights on specific logic related to the business needs of your AMM.

- **Tests**: If you have any automated tests, either in a dedicated `tests` directory or inline with your code, these can help verify that everything works as expected.

- **Any configuration files or `.env` settings** (without sensitive data) to show how the application is set up or initialized.

### Steps to Synchronize Project Files

1. **Run Tests**: Ensure all your tests pass to validate that the functionalities implemented in `processor.rs` and `invokers.rs` are working as expected.

2. **Check for Consistencies**: Make sure that the data structures being passed around match across files. For instance, ensure that the AMM info structure in `main.rs` corresponds to what's expected in `processor.rs`.

3. **Document**: Ensure your code is well-commented, especially if there are complex calculations or decision points within the processor logic.

4. **Version Control**: If you're not already using Git (or any version control platform), consider integrating that into your workflow to easily manage changes and updates.

5. **Integration**: If you have a front-end or UI layer, ensure that it aligns with the back-end operations laid out in the processor and invokers files.

6. **Configuration**: Verify that any adjustable parameters (like fees, minimum amounts, etc.) are correctly set, so that operations comply with your business logic or market conditions.

If you have any specific questions or sections from your other files that you need help with, feel free to ask! Let’s get this project moving forward together!

turns-00035.parquet:48328

e98beacc249d00a44c8947eb
turn 3/26gpt-4o-mini-2024-07-18EnglishUnited States1665 words
degenerate_repetitionAbsentFinal dense release
USER
ill share a few more 1 second error.rs Here’s the structured representation of your error.rs file. This file defines the various error types that can occur in your Raydium AMM program, allowing for effective error management and handling.
C:\solana_rust_bot\src\raydium-amm\program\src\error.rs

//! Error types

use num_derive::FromPrimitive;
use solana_program::{
    decode_error::DecodeError,
    msg,
    program_error::{PrintProgramError, ProgramError},
};
use thiserror::Error;

/// Errors that may be returned by the TokenAmm program.
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
pub enum AmmError {
    // 0
    /// The account cannot be initialized because it is already being used.
    #[error("AlreadyInUse")]
    AlreadyInUse,
    /// The program address provided doesn't match value generated by the program.
    #[error("InvalidProgramAddress")]
    InvalidProgramAddress,
    /// The deserialization of the Token state returned something besides State::Token.
    #[error("ExpectedMint")]
    ExpectedMint,
    /// The deserialization of the Token state returned something besides State::Account.
    #[error("ExpectedAccount")]
    ExpectedAccount,
    /// The coin vault provided doesn't match the coin vault in the AmmInfo.
    #[error("InvalidCoinVault")]
    InvalidCoinVault,

    // 5
    /// The pc vault provided doesn't match the pc vault in the AmmInfo.
    #[error("InvalidPCVault")]
    InvalidPCVault,
    /// The token_lp provided doesn't match the token_lp in the AmmInfo.
    #[error("InvalidTokenLP")]
    InvalidTokenLP,
    /// The dest_token_coin provided doesn't match the dest_token_coin in WithdrawTokenInfo.
    #[error("InvalidDestTokenCoin")]
    InvalidDestTokenCoin,
    /// The dest_token_pc provided doesn't match the dest_token_pc in WithdrawTokenInfo.
    #[error("InvalidDestTokenPC")]
    InvalidDestTokenPC,
    /// The pool_mint provided doesn't match the pool_mint in the AmmInfo.
    #[error("InvalidPoolMint")]
    InvalidPoolMint,

    // 10
    /// The open_orders provided doesn't match the open_orders in the AmmInfo.
    #[error("InvalidOpenOrders")]
    InvalidOpenOrders,
    /// The market provided doesn't match the market in the AmmInfo.
    #[error("InvalidMarket")]
    InvalidMarket,
    /// The market program provided doesn't match the market program in the AmmInfo.
    #[error("InvalidMarketProgram")]
    InvalidMarketProgram,
    /// The target_orders provided doesn't match the target_orders in the AmmInfo.
    #[error("InvalidTargetOrders")]
    InvalidTargetOrders,
    /// The Account provided must be writable.
    #[error("AccountNeedWriteable")]
    AccountNeedWriteable,

    // 15
    /// The Account provided must be read-only.
    #[error("AccountNeedReadOnly")]
    AccountNeedReadOnly,
    /// The token_coin's mint provided doesn't match the coin_mint's key.
    #[error("InvalidCoinMint")]
    InvalidCoinMint,
    /// The token_pc's mint provided doesn't match the pc_mint's key.
    #[error("InvalidPCMint")]
    InvalidPCMint,
    /// The owner of the input isn't set to the program address generated by the program.
    #[error("InvalidOwner")]
    InvalidOwner,
    /// The initialized pool had a non-zero supply.
    #[error("InvalidSupply")]
    InvalidSupply,

    // 20
    /// The initialized token has a delegate.
    #[error("InvalidDelegate")]
    InvalidDelegate,
    /// Invalid Sign Account
    #[error("Invalid Sign Account")]
    InvalidSignAccount,
    /// The amm status is invalid.
    #[error("InvalidStatus")]
    InvalidStatus,
    /// Invalid instruction number passed in
    #[error("Invalid instruction")]
    InvalidInstruction,
    /// The number of accounts provided does not match the expectations
    #[error("Wrong accounts number")]
    WrongAccountsNumber,

    // 25
    /// The target account owner does not match with this program
    #[error("The target account owner is not match with this program")]
    InvalidTargetAccountOwner,
    /// The owner saved in target does not match with this amm pool
    #[error("The owner saved in target is not match with this amm pool")]
    InvalidTargetOwner,
    /// The amm account owner does not match with this program
    #[error("The amm account owner is not match with this program")]
    InvalidAmmAccountOwner,
    /// The params set is invalid
    #[error("Params Set is invalid")]
    InvalidParamsSet,
    /// The params input is invalid.
    #[error("InvalidInput")]
    InvalidInput,

    // 30
    /// Instruction exceeds desired slippage limit
    #[error("Instruction exceeds desired slippage limit")]
    ExceededSlippage,
    /// The calculation exchange rate failed.
    #[error("CalculationExRateFailure")]
    CalculationExRateFailure,
    /// Checked_Sub Overflow
    #[error("Checked_Sub Overflow")]
    CheckedSubOverflow,
    /// Checked_Add Overflow
    #[error("Checked_Add Overflow")]
    CheckedAddOverflow,
    /// Checked_Mul Overflow
    #[error("Checked_Mul Overflow")]
    CheckedMulOverflow,

    // 35
    /// Checked_Div Overflow
    #[error("Checked_Div Overflow")]
    CheckedDivOverflow,
    /// Empty Funds
    #[error("Empty Funds")]
    CheckedEmptyFunds,
    /// Calc pnl error
    #[error("Calc pnl error")]
    CalcPnlError,
    /// Invalid SPL Token Program
    #[error("InvalidSplTokenProgram")]
    InvalidSplTokenProgram,
    /// Take PnL Error
    #[error("Take Pnl error")]
    TakePnlError,

    // 40
    /// Insufficient funds
    #[error("Insufficient funds")]
    InsufficientFunds,
    /// Conversion failure
    #[error("Conversion to u64 failed with an overflow or underflow")]
    ConversionFailure,
    /// The user token input does not match amm
    #[error("user token input does not match amm")]
    InvalidUserToken,
    /// The SRM token's mint does not match the pc_mint's key.
    #[error("InvalidSrmMint")]
    InvalidSrmMint,
    /// The SRM token provided does not match the SRM token in the program.
    #[error("InvalidSrmToken")]
    InvalidSrmToken,

    // 45
    /// Too Many Open Orders
    #[error("TooManyOpenOrders")]
    TooManyOpenOrders,
    /// Order at Slot Is Placed
    #[error("OrderAtSlotIsPlaced")]
    OrderAtSlotIsPlaced,
    /// Invalid System Program Address
    #[error("InvalidSysProgramAddress")]
    InvalidSysProgramAddress,
    /// The provided fee does not match the program owner's constraints
    #[error("The provided fee does not match the program owner's constraints")]
    InvalidFee,
    /// Repeat create amm about market
    #[error("Repeat create amm about market")]
    RepeatCreateAmm,

    // 50
    /// Not Allow Zero LP
    #[error("Not allow Zero LP")]
    NotAllowZeroLP,
    /// The provided token account has a close authority.
    #[error("Token account has a close authority")]
    InvalidCloseAuthority,
    /// The pool token mint has a freeze authority.
    #[error("Pool token mint has a freeze authority")]
    InvalidFreezeAuthority,
    /// The referrer pc wallet's mint provided does not match the pc_mint's key.
    #[error("InvalidReferPCMint")]
    InvalidReferPCMint,
    /// Invalid Config Account
    #[error("InvalidConfigAccount")]
    InvalidConfigAccount,

    // 55
    /// Repeat Create Config Account
    #[error("Repeat create config account")]
    RepeatCreateConfigAccount,
    /// Market Lot Size Is Too Large
    #[error("Market lotSize is too large")]
    MarketLotSizeIsTooLarge,
    /// Init LP amount is too less.
    #[error("Init lp amount is too less (Because 10**lp_decimals amount lp will be locked)")]
    InitLpAmountTooLess,
    /// Unknown Amm Error
    #[error("Unknown Amm Error")]
    UnknownAmmError,
}

impl From<AmmError> for ProgramError {
    fn from(e: AmmError) -> Self {
        ProgramError::Custom(e as u32)
    }
}

impl<T> DecodeError<T> for AmmError {
    fn type_of() -> &'static str {
        "Amm Error"
    }
}

impl PrintProgramError for AmmError {
    fn print<E>(&self)
    where
        E: 'static
            + std::error::Error
            + DecodeError<E>
            + PrintProgramError
            + num_traits::FromPrimitive,
    {
        match self {
            AmmError::AlreadyInUse => msg!("Error: AlreadyInUse"),
            AmmError::InvalidProgramAddress => msg!("Error: InvalidProgramAddress"),
            AmmError::ExpectedMint => msg!("Error: ExpectedMint"),
            AmmError::ExpectedAccount => msg!("Error: ExpectedAccount"),
            AmmError::InvalidCoinVault => msg!("Error: InvalidCoinVault"),

            AmmError::InvalidPCVault => msg!("Error: InvalidPCVault"),
            AmmError::InvalidTokenLP => msg!("Error: InvalidTokenLP"),
            AmmError::InvalidDestTokenCoin => msg!("Error: InvalidDestTokenCoin"),
            AmmError::InvalidDestTokenPC => msg!("Error: InvalidDestTokenPC"),
            AmmError::InvalidPoolMint => msg!("Error: InvalidPoolMint"),
            AmmError::InvalidOpenOrders => msg!("Error: InvalidOpenOrders"),
            AmmError::InvalidMarket => msg!("Error: InvalidMarket"),
            AmmError::InvalidMarketProgram => msg!("Error: InvalidMarketProgram"),

            AmmError::InvalidTargetOrders => msg!("Error: InvalidTargetOrders"),
            AmmError::AccountNeedWriteable => msg!("Error: AccountNeedWriteable"),
            AmmError::AccountNeedReadOnly => msg!("Error: AccountNeedReadOnly"),
            AmmError::InvalidCoinMint => msg!("Error: InvalidCoinMint"),
            AmmError::InvalidPCMint => msg!("Error: InvalidPCMint"),

            AmmError::InvalidOwner => msg!("Error: InvalidOwner"),
            AmmError::InvalidSupply => msg!("Error: InvalidSupply"),
            AmmError::InvalidDelegate => msg!("Error: InvalidDelegate"),
            AmmError::InvalidSignAccount => msg!("Error: Invalid Sign Account"),
            AmmError::InvalidStatus => msg!("Error: InvalidStatus"),

            AmmError::InvalidInstruction => msg!("Error: InvalidInstruction"),
            AmmError::WrongAccountsNumber => msg!("Error: WrongAccountsNumber"),
            AmmError::InvalidTargetAccountOwner => msg!("Error: The target account owner is not match with this program"),
            AmmError::InvalidTargetOwner => msg!("Error: The owner saved in target is not match with this amm pool"),
            AmmError::InvalidAmmAccountOwner => msg!("Error: The amm account owner is not match with this program"),

            AmmError::InvalidParamsSet => msg!("Error: Params Set is Invalid"),
            AmmError::InvalidInput => msg!("Error: InvalidInput"),
            AmmError::ExceededSlippage => msg!("Error: exceeds desired slippage limit"),
            AmmError::CalculationExRateFailure => msg!("Error: CalculationExRateFailure"),
            AmmError::CheckedSubOverflow => msg!("Error: Checked_Sub Overflow"),

            AmmError::CheckedAddOverflow => msg!("Error: Checked_Add Overflow"),
            AmmError::CheckedMulOverflow => msg!("Error: Checked_Mul Overflow"),
            AmmError::CheckedDivOverflow => msg!("Error: Checked_Div Overflow"),
            AmmError::CheckedEmptyFunds => msg!("Error: CheckedEmptyFunds"),
            AmmError::CalcPnlError => msg!("Error: Calc pnl error"),

            AmmError::InvalidSplTokenProgram => msg!("Error: InvalidSplTokenProgram"),
            AmmError::TakePnlError => msg!("Error: Take Pnl error"),
            AmmError::InsufficientFunds => msg!("Error: insufficient funds"),
            AmmError::ConversionFailure => msg!("Error: Conversion to u64 failed with an overflow or underflow"),
            AmmError::InvalidUserToken => msg!("Error: user token input does not match amm"),

            AmmError::InvalidSrmMint => msg!("Error: InvalidSrmMint"),
            AmmError::InvalidSrmToken => msg!("Error: InvalidSrmToken"),
            AmmError::TooManyOpenOrders => msg!("Error: TooManyOpenOrders"),
            AmmError::OrderAtSlotIsPlaced => msg!("Error: OrderAtSlotIsPlaced"),
            AmmError::InvalidSysProgramAddress => msg!("Error: InvalidSysProgramAddress"),

            AmmError::InvalidFee => msg!("Error: InvalidFee"),
            AmmError::RepeatCreateAmm => msg!("Error: RepeatCreateAmm"),
            AmmError::NotAllowZeroLP => msg!("Error: Not allow Zero LP"),
            AmmError::InvalidCloseAuthority => msg!("Error: Token account has a close authority"),
            AmmError::InvalidFreezeAuthority => {
                msg!("Error: Pool token mint has a freeze authority")
            }
            AmmError::InvalidReferPCMint => msg!("Error: InvalidReferPCMint"),
            AmmError::InvalidConfigAccount => msg!("Error: InvalidConfigAccount"),
            AmmError::RepeatCreateConfigAccount => msg!("Error: Repeat create config account"),
            AmmError::MarketLotSizeIsTooLarge => msg!("Error: Market lotSize is too large"),
            AmmError::InitLpAmountTooLess => msg!("Error: Init lp amount is too less (Because 10**lp_decimals amount lp will be locked)"),
            AmmError::UnknownAmmError => msg!("Error: Unknown Amm Error"),
        }
    }
}
ASSISTANT
Thank you for sharing your `error.rs` file. This file plays an important role in defining error types for your AMM application, improving the reliability and maintainability of your code. Here’s a quick overview of the key components in your `error.rs` file:

### Key Components of `error.rs`

1. **Error Enum**: 
   - The `AmmError` enum represents various errors that may occur in your AMM program. Each variant corresponds to a specific error case, providing descriptive error messages.
   - The use of the `thiserror` crate makes defining error types cleaner and more structured.

2. **Implementations**:
   - Implementations for `From<AmmError> for ProgramError`, allowing for easy conversion between your custom error types and the generic program error type used in Solana.
   - Implements `DecodeError<T>` for encoding/decoding error types between your program and client.
   - Implements `PrintProgramError` to allow the program to log error messages clearly, which helps during debugging.

### Suggestions for Improvement or Usage

1. **Consistency in Error Handling**: Ensure that whenever you're returning or propagating errors in your application, you're using the appropriate `AmmError` variants. This will make debugging much easier down the line.

2. **Extend Error Coverage**: If there are additional edge cases or error scenarios in your AMM program that are not covered by the existing error enum, consider adding more variants to improve granularity.

3. **Logging and Metrics**: If you implement a logging framework later, you might want to log occurrences of each error type for better monitoring of your application, giving insight into common failure points.

4. **Testing Errors**: Ensure that your unit tests include cases where these errors are expected. This helps verify that your error handling logic behaves as intended.

### Next Steps for Synchronization

To proceed with the synchronization and integration of your project files, consider the following steps:

1. **Make Sure Errors Are Used**: Go through your `processor.rs` and `invokers.rs` files to ensure that all the relevant error handling mechanisms utilize the defined errors in `error.rs`. Whenever you encounter a scenario that leads to an error, return an appropriate variant from `AmmError`.

2. **Test Cases**: Develop unit tests to simulate various erroneous scenarios and check that your application responds with the expected errors. 

3. **Share Related Files**: If you have unit tests (`tests` directory) or a configuration file (`Cargo.toml`), it would be beneficial to see those as well to get a comprehensive understanding of how your program is structured.

4. **Integrate with Main**: Ensure that the `main.rs` file properly initializes and uses your `error.rs` types throughout the swap operations, reflecting any potential error scenarios that may arise.

5. **Documentation**: Update any comments or documentation to clarify where specific errors may be triggered within the AMM program for any future developers or users.

If you would like to continue sharing other files or have specific questions regarding what needs to be done, feel free to let me know!

turns-00035.parquet:48329

c4d03e948ab2209800e0a219
turn 4/26gpt-4o-mini-2024-07-18EnglishUnited States513 words
degenerate_repetitionAbsentFinal dense release
USER
//! Program entrypoint definitions

#![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]

use crate::{error::AmmError, processor::Processor};
use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    program_error::PrintProgramError,
    pubkey::Pubkey,
};

entrypoint!(process_instruction);
fn process_instruction<'a>(
    program_id: &Pubkey,
    accounts: &'a [AccountInfo<'a>],
    instruction_data: &[u8],
) -> ProgramResult {
    if let Err(error) = Processor::process(program_id, accounts, instruction_data) {
        // Catch the error so we can print it
        error.print::<AmmError>();
        return Err(error);
    }
    Ok(())
}
ASSISTANT
Thank you for sharing your `entrypoint.rs` file. This file serves as the main entry point for your Solana program, handling incoming instructions from clients and invoking the associated logic in the processor. Here’s an overview of the components in your `entrypoint.rs` file and some suggestions for improvements.

### Overview of `entrypoint.rs`
1. **Conditional Compilation**:
   - The `#![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]` directive ensures that this code is only compiled for the correct target architecture (BPF) and when the `no-entrypoint` feature is not enabled. This is essential for maintaining the modularity and configurability of your program.

2. **Imports**:
   - You’re importing `AmmError` to utilize your custom error types for error handling.
   - The `Processor` module is imported to handle the business logic of processing instructions.
   - You also import necessary types from the `solana_program` crate, including `AccountInfo`, `entrypoint`, and related traits for program functionality.

3. **Entry Point Function**:
   - The `process_instruction` function is defined as the entry point for the program.
   - It takes three parameters:
     - `program_id`: The public key of the program instance.
     - `accounts`: A slice of `AccountInfo` structs representing the accounts involved in the instruction.
     - `instruction_data`: The raw byte data that contains the instruction to be executed.

4. **Error Handling**:
   - The function invokes `Processor::process()` and will catch any errors that arise.
   - Upon encountering an error, the code utilizes the custom `print()` method from `PrintProgramError` to log the error details, which is very helpful for debugging and monitoring.

### Suggestions for Improvements
1. **Error Messaging**: 
   - You may want to enhance the detail of the error printed by including contextual information, such as the program ID and instruction data. This can be useful for debugging in a production environment.

2. **Instruction Validation**: 
   - Although this is handled in the processor, consider briefly checking that the instruction data is valid and conforms to expected formats before processing. If not, return an appropriate error.

3. **Documentation**: 
   - Add comments to further explain what happens within the `process_instruction` function. Since this is the entry point of your program, clarity is important for maintainability.

4. **Adjustable Log Levels**: 
   - You could integrate a log level control to manage the verbosity of printed logs, which can be particularly helpful when transitioning between development and production environments.

### Next Steps:
- **Integration Confirmation**: Ensure that the `main.rs` file correctly initializes and calls this entry point, facilitating the instruction processing pipeline. 
- **Definition in Cargo.toml**: Confirm that this entry point is defined correctly in your `Cargo.toml`, and it specifies the required dependencies correctly.

If you have more files to share or specific queries regarding the logic in this or other components, please feel free to continue! Your progress is moving well, and I’m here to help!

turns-00035.parquet:48330

cebc63a1d0e9fb0b0c5e4137
turn 5/26gpt-4o-mini-2024-07-18EnglishUnited States761 words
degenerate_repetitionAbsentFinal dense release
USER
use anyhow::Result;
use reqwest::Client;
use serde_json::Value;

pub struct HeliusRpcClient {
    client: Client,
    base_url: String,
}

impl HeliusRpcClient {
    pub fn new(base_url: &str) -> Self {
        Self {
            client: Client::new(),
            base_url: base_url.to_string(),
        }
    }

    pub async fn fetch_token_price(&self, token_address: &str) -> Result<f64> {
        let url = format!("{}/v0/token-price", self.base_url);
        let response: Value = self.client.post(&url)
            .json(&serde_json::json!({ "tokenAddress": token_address }))
            .send()
            .await?
            .json()
            .await?;
        
        Ok(response["price"]
            .as_f64()
            .ok_or_else(|| anyhow::anyhow!("No price found"))?)
    }
} use anyhow::Result;
use futures_util::stream::StreamExt; // Make sure to include this for the stream handling
use tokio_tungstenite::{connect_async, tungstenite::Error as WsError};
use url::Url;

pub struct HeliusWebSocketListener {
    url: String,
}

impl HeliusWebSocketListener {
    pub fn new(url: &str) -> Self {
        Self { url: url.to_string() }
    }

    pub async fn start(&self) -> Result<(), WsError> {
        let (ws_stream, _) = connect_async(Url::parse(&self.url)?).await?;
        
        // Handle WebSocket messages
        let mut ws_stream = ws_stream; // Ensure you're storing the stream
        while let Some(msg) = ws_stream.next().await {
            match msg {
                Ok(m) => {
                    // Process the message here
                    println!("Received message: {:?}", m);
                },
                Err(e) => {
                    println!("Error receiving message: {:?}", e);
                    break;
                }
            }
        }
        Ok(())
    }
}
ASSISTANT
You've provided two components for a client that interfaces with a service called Helius: one for regular HTTP RPC calls to fetch token prices, and another for a WebSocket listener for real-time updates. Below is a summary of both components, along with some suggestions for improvements and potential integration steps.

### Overview of Helius RPC Client

#### HeliusRpcClient
1. **Structure**:
   - The `HeliusRpcClient` struct holds an instance of `reqwest::Client` and a base URL for making requests to the Helius API.

2. **Methods**:
   - `new(base_url: &str)`: Initializes a new instance of the client with the provided base URL.
   - `fetch_token_price(&self, token_address: &str)`: Asynchronously makes a POST request to fetch the price of a token based on its address.

3. **Error Handling**:
   - Uses the `anyhow` crate for error handling, allowing for easy propagation of errors from async calls.

4. **JSON Handling**:
   - The response is expected to return a JSON object containing the price of the token, with error handling to provide a user-friendly message if the price key is not found.

### Suggestions for Improvements and Integration

1. **Environment Variables**:
   - Consider allowing the `base_url` to be configurable through environment variables, which can be set through a `.env` file, especially for different environments (development vs production).

2. **Logging**:
   - Implement logging to record API request details—including success and failure responses. This can help during debugging and in production.

3. **Timeouts**:
   - You can configure your `reqwest` client to set timeouts for requests, ensuring that your application doesn’t hang due to slow network responses.

4. **Testing & Mocking**:
   - Write unit tests to verify behavior, including success and failure cases when fetching token prices. You may want to use mocking libraries to simulate API responses.

### Overview of Helius WebSocket Listener

#### HeliusWebSocketListener
1. **Structure**:
   - The `HeliusWebSocketListener` struct holds a URL for the WebSocket connection.

2. **Methods**:
   - `new(url: &str)`: Initializes a new instance with the provided URL.
   - `start(&self)`: Asynchronously connects to the WebSocket and listens for incoming messages.

3. **Message Handling**:
   - The listener uses `futures_util::stream::StreamExt` to handle the incoming messages and print them out, enabling real-time processing.

### Suggestions for Improvements and Integration

1. **Graceful Shutdown**:
   - Implement a method to gracefully shut down the WebSocket listener. This will help to clean up resources or save state when stopping the listener.

2. **Error Handling**:
   - Use more detailed error handling when establishing a connection and receiving messages. For example, you might want to implement exponential backoff for reconnect attempts after errors.

3. **Message Processing**:
   - Instead of only printing received messages, you may want to create a dedicated handler method to process incoming messages based on their content.

4. **Multithreading**:
   - If this listener will be used alongside other parts of your application, ensure to manage the task as a background task, perhaps using a `tokio::spawn` method so it does not block the main execution thread.

### Next Steps
- **Integration**: Ensure that these two components can work together efficiently. For example, the WebSocket listener could respond based on price updates fetched by the `HeliusRpcClient`.
  
- **Complete Project Configuration**: Ensure that you have appropriate error handling across the board and that you are using best practices for Rust's async programming model.

- **Environmental Setup**: Test the relational integrity between these components, along with potential integration points with `Processor` or `Invokers` from your previous code files.

Feel free to ask about any specific areas you want to explore further or if you have other code files and concepts you’d like to discuss!

turns-00035.parquet:48331

0e212955b160cdb96721ee4e
turn 6/26gpt-4o-mini-2024-07-18EnglishUnited States670 words
degenerate_repetitionAbsentFinal dense release
USER
can u synch the processor and invoker now or u need to see more scripts still??? C:\solana_rust_bot\src\raydium-amm\program\src\lib.rs use anyhow::Result;
use futures_util::stream::StreamExt; // Make sure to include this for the stream handling
use tokio_tungstenite::{connect_async, tungstenite::Error as WsError};
use url::Url;

pub struct HeliusWebSocketListener {
    url: String,
}

impl HeliusWebSocketListener {
    pub fn new(url: &str) -> Self {
        Self { url: url.to_string() }
    }

    pub async fn start(&self) -> Result<(), WsError> {
        let (ws_stream, _) = connect_async(Url::parse(&self.url)?).await?;
        
        // Handle WebSocket messages
        let mut ws_stream = ws_stream; // Ensure you're storing the stream
        while let Some(msg) = ws_stream.next().await {
            match msg {
                Ok(m) => {
                    // Process the message here
                    println!("Received message: {:?}", m);
                },
                Err(e) => {
                    println!("Error receiving message: {:?}", e);
                    break;
                }
            }
        }
        Ok(())
    }
}
ASSISTANT
Thank you for sharing the `lib.rs` file, which appears to define the `HeliusWebSocketListener` that interacts with the Helius service via WebSocket. Based on your request to synchronize the `Processor` and `Invokers`, let’s work through how these components can be integrated effectively. The intent here will be to ensure these components interact with one another seamlessly and that all aspects of your program are cohesive.

### Overview of Synchronizing `Processor` and `Invokers`

#### Key Points of Synchronization

1. **Use Cases**: 
   - Identify which operations in `Processor` need to call methods in `Invokers`. For instance, when executing a swap or transferring tokens, the `Processor` methods could utilize the corresponding functions defined in the `Invokers` for executing those actions on-chain.

2. **Error Handling**: 
   - Ensure that error handling in both files propagates errors up to the main processing method. The handling of specific `AmmError` types should be done in the `Processor`, while the actual network calls are managed in `Invokers`.

3. **Function Calls**: 
   - Wherever a logic function in `Processor` requires an action that will invoke a Solana instruction (e.g., placing an order, transferring tokens), utilize the functions from `Invokers`. This keeps your commands to the Solana blockchain organized and modular.

4. **Data Structures**: 
   - Ensure that the requirements of the data structures (like `AmmInfo`, `OpenOrders`, etc.) being passed between these components are correctly aligned. If you introduce changes to one, the other should also reflect those changes accordingly.

### Suggested Changes and Integrations

Here's an outline of how you can update your `Processor` and `Invokers` files to work together effectively:

#### Example Integration Steps

1. **Update `Processor.rs`**: When you need to execute a swap or some other blockchain instruction, use the `Invokers` in the corresponding method. Here is some pseudocode (you will replace with actual logic):

   ```rust
   pub fn process_swap_base_in(...parameters...) -> ProgramResult {
       // ... your existing logic ...

       // Example use case of Invokers
       let swap_ix = Invokers::token_transfer(
           token_program_info.clone(),
           source_account_info.clone(),
           destination_account_info.clone(),
           owner_account_info.clone(),
           amount
       )?;

       // Sending the transaction
       invoke(&swap_ix, &[...])?;
   }
   ```

2. **Error Handling**: Wherever you handle errors in `Processor`, ensure consistent usage of `AmmError`:

   ```rust
   if some_condition {
       return Err(AmmError::InsufficientFunds.into());
   }
   ```

3. **Update `Invokers.rs`**: Ensure that all function signatures accept the appropriate parameters from your `Processor`. Use consistent logging in the `Invokers` for debugging.

   ```rust
   pub fn invoke_dex_new_order(...params...) -> Result<(), ProgramError> {
       // Build spark
       // Potential print statements or logging here
       let ix = ...; // your logic here
   }
   ```

4. **Shared Utilities**: If more complex logic must be shared, consider creating a `utils.rs` library file to house common utilities shared between `Processor` and `Invokers`.

### Finalizing Synchronization

***Sharing More Files***: If you feel additional files such as utility functions or specific business logic files need to be seen, please provide those as well. For now, based on what you've shared, these guided steps will help synchronize `Processor` and `Invokers`.

***Use of the WebSocket Listener***: Depending on the use case of your `HeliusWebSocketListener`, consider how websocket messages (for example, real-time price updates) can be integrated back into your transaction logic in the `main.rs` or `Processor`. You might call `fetch_token_price` from within the listener context.

If you'd like to proceed with implementing any of these steps or have more files to review, feel free to share! I'm here to assist further!

turns-00035.parquet:48332

35494a150d039f38986501d0
turn 7/26gpt-4o-mini-2024-07-18EnglishUnited States715 words
degenerate_repetitionAbsentFinal dense release
USER
i am using helius rpc and api / websocket the upgraded subscription 
ASSISTANT
Great! Utilizing the Helius RPC API along with WebSocket subscriptions can enhance the interactivity and responsiveness of your application with real-time updates about market data, token prices, and more. To ensure efficient integration of Helius RPC and WebSocket functionality within your existing project, here’s a structured approach focusing on both the RPC client and the WebSocket listener.

### Integration Strategy for Helius RPC and WebSocket

#### 1. Helius RPC Client

You've likely already set up the HTTP RPC client with functions for fetching token prices. Make sure to implement additional methods to interact with various aspects of the Helius API. Here’s how you can enhance it:

```rust
impl HeliusRpcClient {
    // Existing constructor
    pub fn new(base_url: &str) -> Self {
        Self {
            client: Client::new(),
            base_url: base_url.to_string(),
        }
    }

    // Fetch token price
    pub async fn fetch_token_price(&self, token_address: &str) -> Result<f64> {
        let url = format!("{}/v0/token-price", self.base_url);
        // Implementing HTTP POST request
        let response: Value = self.client.post(&url)
            .json(&serde_json::json!({ "tokenAddress": token_address }))
            .send()
            .await?
            .json()
            .await?;

        Ok(response["price"]
            .as_f64()
            .ok_or_else(|| anyhow::anyhow!("No price found"))?)
    }

    // Additional method to get market data
    pub async fn fetch_market_data(&self, market_id: &str) -> Result<Value> {
        let url = format!("{}/v0/market/{}", self.base_url, market_id);
        let response: Value = self.client.get(&url)
            .send()
            .await?
            .json()
            .await?;
        
        Ok(response)
    }
}
```

#### 2. Helius WebSocket Listener

Ensure that your WebSocket listener can subscribe to events that you’re interested in, such as price updates, trades, or any other relevant market activity. You may want to define how you listen for specific messages and handle them effectively.

Here’s how you might structure the `HeliusWebSocketListener` to incorporate a message processing mechanism:

```rust
impl HeliusWebSocketListener {
    pub async fn start(&self) -> Result<()> {
        let (ws_stream, _) = connect_async(Url::parse(&self.url)?).await?;

        let mut ws_stream = ws_stream;
        while let Some(msg) = ws_stream.next().await {
            match msg {
                Ok(m) => {
                    // Process the incoming message
                    self.process_message(&m).await?;
                },
                Err(e) => {
                    println!("Error receiving message: {:?}", e);
                    break;
                }
            }
        }
        Ok(())
    }

    async fn process_message(&self, message: &tungstenite::Message) -> Result<()> {
        match message {
            tungstenite::Message::Text(text) => {
                // Parse and handle incoming text messages
                let parsed_msg: Value = serde_json::from_str(text)?;
                println!("Received message: {:?}", parsed_msg);

                // Process based on the type of message
                match parsed_msg.get("type") {
                    Some(Value::String(ref mtype)) if mtype == "price_update" => {
                        // Get the updated price and perform actions based on it
                        let token_price = parsed_msg["data"]["price"].as_f64().unwrap();
                        println!("Updated token price: {}", token_price);
                        // Potentially call the relevant method to handle the price update
                    },
                    _ => {
                        // Handle other message types or unknown formats
                    },
                }
            },
            _ => {
                // Handle binary messages or other types if necessary
                println!("Received non-text message: {:?}", message);
            }
        }
        Ok(())
    }
}
```

### Connecting RPC and WebSocket

Here’s how RPC and WebSocket integrations can connect:

1. **Use WebSocket for Real-time Updates**: When token prices or market data update via WebSocket, you can dynamically update the user interface or process trades without waiting for the next RPC call.

2. **Use RPC for Initial Data Fetching**: The HTTP RPC client can be used to fetch initial market conditions, prices, and other setup data when the application starts. This ensures that you have a baseline state before real-time events start coming in.

3. **React to Changes**: If certain thresholds are reached (e.g., a price crosses a target), the application can perform trades or notify users based on incoming WebSocket messages.

### Example Workflow

Here’s a high-level workflow incorporating both components:
1. **Initialize the Helius RPC Client** at the start of your application.
2. **Fetch initial market data** using the HTTP client.
3. **Start the WebSocket listener** to listen for price updates or other relevant events.
4. **React to updates**, updating your local model, performing trades, or responding to user actions as necessary.

### Additional Recommendations
- **Testing**: Implement unit tests for both the RPC and WebSocket integrations. Make sure to simulate API responses, especially for error cases.
- **Documentation**: Clearly document how these integrations work, including how to set up the WebSocket and any expected message structures.
- **Graceful Shutdown**: Ensure your WebSocket listener can shut down gracefully, saving state as needed, especially during termination of the application.

If you have additional files or specific functions related to the Helius API that you want to share or integrate further, please let me know!

turns-00035.parquet:48333

7b0eb23b3a457ab0b120e3c6
turn 8/26gpt-4o-mini-2024-07-18EnglishUnited States497 words
degenerate_repetitionAbsentFinal dense release
USER
so hang on everything else is setup properly now?? all  my files are synched up ???
ASSISTANT
Based on the files you've shared and the discussions we've had, your setup appears to be well-structured, covering the key components needed for implementing your Solana AMM (Automated Market Maker) integrated with the Helius RPC and WebSocket APIs. Here’s a brief overview of what you have and any key actions to ensure everything is synchronized:

### Summary of Key Components

1. **Error Handling (`error.rs`)**:
   - Comprehensive error definitions that help in gracefully handling various issues in your application.

2. **Processor (`processor.rs`)**:
   - Contains the core business logic for handling different instructions like swaps, deposits, and withdrawals. This should use the defined error types from `error.rs` and call functions from `invokers.rs` for performing on-chain operations.

3. **Invokers (`invokers.rs`)**:
   - Manages the execution of instructions against the Solana blockchain. It should be tightly integrated with the logic defined in `processor.rs`.

4. **Main Functionality (`main.rs`)**:
   - Entry point for executing client-side operations, including handling swaps using the Helius APIs. It uses the `HeliusRpcClient` and the `HeliusWebSocketListener`.

5. **Helius RPC Client and WebSocket Listener**:
   - Encapsulates API interactions to fetch market data and receive real-time updates, enhancing user experience and responsiveness.

### Synchronization Status

- **File Structures**: The structure of the files is logical, with separation of concerns, meaning different functionalities are grouped logically (e.g., RPC calls, error handling, processing transactions).
  
- **Error Handling**: You should ensure that all parts of your application are using the error types defined in `error.rs`. Consistently catching these errors and either logging or relaying them appropriately will help maintain the robustness of the system.

- **Integration Points**: Make sure that:
  - The `Processor` calls methods from `Invokers` to perform the necessary transactions.
  - The `HeliusRpcClient` and `HeliusWebSocketListener` are effectively working together—fetching initial data with the RPC client and updating in real time with the WebSocket.

### Final Steps for Verification

1. **Run Tests**: Ensure you have sufficient unit and integration tests covering all functionalities, including edge cases, to verify handling of various error states.

2. **Client Initialization**: Make sure that the client setups for WebSocket and RPC are clearly defined, especially how they are initialized and started in your `main.rs`.

3. **End-to-End Testing**: If possible, perform end-to-end tests within a local or testnet environment to simulate actual usage and confirm that all components are interacting as expected.

4. **Documentation**: Documenting the structure, dependencies, and usage of each component could be helpful for future reference or collaboration. Comments in lines of code also help clarify intended logic and usage context.

5. **Dependency Management**: Ensure all necessary dependencies are correctly included in your `Cargo.toml` file.

If you feel confident about the structure and functionality based on the current files and implementations you’ve shared, it seems like you are on the right track! If there are any remaining files you're unsure about or specific functionalities you want to verify for synchronization, please share those, and I would be happy to assist further!

turns-00035.parquet:48334

3faee76c5d6bc4b58159045d
turn 9/26gpt-4o-mini-2024-07-18EnglishUnited States324 words
degenerate_repetitionAbsentFinal dense release
USER
so check ill try to run it and show u error messages prolly the best solution here
ASSISTANT
That sounds like a great plan! Running the code and capturing any error messages or unexpected behavior is an effective way to identify issues in your project. Here are a few steps you can follow when running your application:

### Steps to Run Your Application

1. **Build the Project**: Make sure your project compiles without errors. You can build it using the command:

   ```bash
   cargo build
   ```

   or for running:

   ```bash
   cargo run
   ```

2. **Run the Tests**: If you have automated tests set up, run them to verify that the individual components behave as expected:

   ```bash
   cargo test
   ```

3. **Monitor Outputs**: Pay attention to the console output while your application runs. Any `println!` statements or error messages should provide insight into what happens during execution. 

4. **Capture Errors**: If you encounter any errors, make a note of:
   - The exact error message.
   - The line number or context in which the error occurred.
   - Any additional debugging output you've added, which might give context to the error.

### Error Reporting

When you encounter errors, you can share the error messages with me here. Include details such as:
- The error message.
- Any relevant code snippets if they provide context for the error.
- Your expected outcome vs. what is actually happening.

### Common Errors to Look Out For
- **Compilation Errors**: Usually indicate issues with syntax or type mismatches.
- **Runtime Errors**: These can include panics, states that are invalid, or unhandled results.
- **Network Errors**: If your application is making network requests (like HTTP or WebSocket), ensure your endpoints are correct, and validate that your environment is set up to allow those connections.

Once you have the error messages, share them here, and I'll do my best to help you troubleshoot and resolve the issues! Good luck, and I'm here when you're ready!

turns-00035.parquet:48335

50cb1f7cf93b88e76150a496
turn 10/26gpt-4o-mini-2024-07-18EnglishUnited States490 words
degenerate_repetitionAbsentFinal dense release
USER
 Compiling anchor-lang v0.30.0
error[E0599]: no variant or associated item named `ArithmeticOverflow` found for enum `ProgramError` in the current scope
   --> C:\Users\Orsen\.cargo\registry\src\index.crates.io-6f17d22bba15001f\anchor-lang-0.30.0\src\lib.rs:204:34
    |
204 |             .ok_or(ProgramError::ArithmeticOverflow)?;
    |                                  ^^^^^^^^^^^^^^^^^^ variant or associated item not found in `ProgramError`

error[E0599]: no variant or associated item named `ArithmeticOverflow` found for enum `ProgramError` in the current scope
   --> C:\Users\Orsen\.cargo\registry\src\index.crates.io-6f17d22bba15001f\anchor-lang-0.30.0\src\lib.rs:225:34
    |
225 |             .ok_or(ProgramError::ArithmeticOverflow)?;
    |                                  ^^^^^^^^^^^^^^^^^^ variant or associated item not found in `ProgramError`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `anchor-lang` (lib) due to 2 previous errors
ASSISTANT
The error messages you’re encountering indicate that the `ProgramError` enum in the `anchor-lang` crate does not contain a variant called `ArithmeticOverflow`. This could be due to a few reasons, such as a version mismatch, deprecations in the library, or changes in the API.

### Possible Solutions

1. **Version Check**:
   - Ensure that you are using a compatible version of `anchor-lang` and any other related dependencies. If you’re following a tutorial or a specific chart, check that the versions in your `Cargo.toml` align with those examples.

2. **Update Dependencies**:
   - If you are not locked into a specific version of `anchor-lang`, try updating it to the latest version. You can do this by modifying your `Cargo.toml` file and running:

   ```bash
   cargo update
   ```

3. **Check Documentation**:
   - Review the [anchor documentation](https://docs.rs/anchor-lang/latest/anchor_lang/) or the [repository on GitHub](https://github.com/coral-xyz/anchor) to verify if `ArithmeticOverflow` has been replaced or renamed. 

4. **Use Alternative Error Types**:
   - If `ArithmeticOverflow` has been deprecated or moved to a different location, you should look for the current error type handling overflow scenarios in the library.

   - For example, sometimes arithmetic errors are handled generically with:
     ```rust
     .ok_or(ProgramError::Custom(ArithmeticError as u32))?;
     ```

   - The `anchor-lang` library might have error handling replaced or updated to a more generic error type to accommodate different overflow scenarios.

5. **Downgrade Anchor**:
   - If you recently updated your `anchor-lang` and it broke your code due to API changes, consider downgrading back to the version that was previously working.

6. **Cargo.lock File**:
   - Check your `Cargo.lock` file to confirm the version of `anchor-lang` that is in use. If other dependencies depend on a specific version of `anchor-lang`, it might have been resolved to an incompatible version due to that dependency constraint.

### Example of Updating `Cargo.toml`

Here's how you may want to structure your dependency section in the `Cargo.toml`:

```toml
[dependencies]
anchor-lang = "0.30" # Make sure this is the version you want
# other dependencies...
```

### Next Steps

1. **Make these changes and run `cargo build` again to see if the issue is resolved**.
2. **If you continue running into issues**, share the `Cargo.toml` contents, and I can provide more targeted assistance.
3. **If changes in the API appear consistently**, try to look for existing issues on the official GitHub repository or community support forums for more insights on the usage of the latest dependencies.

Feel free to ask if you have additional questions or need more help!