clmm-common 0.1.39

Blockchain, Clmm for Solana
Documentation
use borsh::BorshDeserialize;
use tabled::Tabled;

use crate::contract::state::clmmpools::Clmmpool;
use crate::math::{MAX_TICK, MIN_TICK};

#[derive(BorshDeserialize, Tabled, Copy, Clone, Default)]
pub struct Tick {
    //
    pub is_initialized: bool,

    pub index: i32,

    pub sqrt_price: u128,

    pub liquidity_net: i128,
    // 16
    pub liquidity_gross: u128, // 16

    // Q64.64
    pub fee_growth_outside_a: u128,
    // 16
    // Q64.64
    pub fee_growth_outside_b: u128, // 16

    pub reward_growth_outside: TickRewarder,
}

#[derive(Copy, Clone, BorshDeserialize, Default, Debug, Eq, PartialEq)]
pub struct TickRewarder([u128; 3]);

impl std::fmt::Display for TickRewarder {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut res: Vec<String> = vec![];
        for (i, e) in self.0.iter().enumerate() {
            res.push(format!("{}. growth_outside:{}", i, e));
        }
        let res_str = res.join("\n");
        write!(f, "{}", res_str)
    }
}

impl Tick {
    pub const LEN: usize = 1 + 4 + 16 + 16 + 16 + 16 + 16 + Clmmpool::REWARD_NUM * 16;

    #[inline]
    pub fn min(tick_spacing: u16) -> i32 {
        MIN_TICK + MIN_TICK.abs() % tick_spacing as i32
    }

    #[inline]
    pub fn max(tick_spacing: u16) -> i32 {
        MAX_TICK - MAX_TICK % tick_spacing as i32
    }

    pub fn update(&mut self, tick: &Tick) {
        self.is_initialized = tick.is_initialized;
        self.index = tick.index;
        self.sqrt_price = tick.sqrt_price;
        self.liquidity_net = tick.liquidity_net;
        self.liquidity_gross = tick.liquidity_gross;
        self.fee_growth_outside_a = tick.fee_growth_outside_a;
        self.fee_growth_outside_b = tick.fee_growth_outside_b;
    }

    pub fn get_fee_in_tick_range(
        clmmpool: &Clmmpool,
        tick_lower: Option<&Tick>,
        tick_upper: Option<&Tick>,
        tick_lower_index: i32,
        tick_upper_index: i32,
    ) -> (u128, u128) {
        let (fee_growth_below_a, fee_growth_below_b) = match tick_lower {
            None => (clmmpool.fee_growth_global_a, clmmpool.fee_growth_global_b),
            Some(tick_lower) => match clmmpool.current_tick_index < tick_lower_index {
                true => (
                    clmmpool
                        .fee_growth_global_a
                        .wrapping_sub(tick_lower.fee_growth_outside_a),
                    clmmpool
                        .fee_growth_global_b
                        .wrapping_sub(tick_lower.fee_growth_outside_b),
                ),
                false => (
                    tick_lower.fee_growth_outside_a,
                    tick_lower.fee_growth_outside_b,
                ),
            },
        };
        // By convention, when initializing a tick, no fees have been earned above the tick.
        let (fee_growth_above_a, fee_growth_above_b) = match tick_upper {
            None => (0, 0),
            Some(tick_upper) => match clmmpool.current_tick_index < tick_upper_index {
                true => (
                    tick_upper.fee_growth_outside_a,
                    tick_upper.fee_growth_outside_b,
                ),
                false => (
                    clmmpool
                        .fee_growth_global_a
                        .wrapping_sub(tick_upper.fee_growth_outside_a),
                    clmmpool
                        .fee_growth_global_b
                        .wrapping_sub(tick_upper.fee_growth_outside_b),
                ),
            },
        };
        (
            clmmpool
                .fee_growth_global_a
                .wrapping_sub(fee_growth_below_a)
                .wrapping_sub(fee_growth_above_a),
            clmmpool
                .fee_growth_global_b
                .wrapping_sub(fee_growth_below_b)
                .wrapping_sub(fee_growth_above_b),
        )
    }

    pub fn get_reward_in_tick_range(
        clmmpool: &mut Clmmpool,
        tick_lower: Option<&Tick>,
        tick_upper: Option<&Tick>,
        tick_lower_index: i32,
        tick_upper_index: i32,
    ) -> ([u128; Clmmpool::REWARD_NUM]) {
        let rewarder_infos = clmmpool.reward_infos.0.as_mut_slice();
        let mut rewarder_growths_inside = [0; Clmmpool::REWARD_NUM];

        for i in 0..Clmmpool::REWARD_NUM {
            if !rewarder_infos[i].is_initialized() {
                continue;
            }

            // By convention, assume all prior growth happened below the tick
            let rewarder_growths_below = match tick_lower {
                None => rewarder_infos[i].growth_global,
                Some(tick_lower) => match clmmpool.current_tick_index < tick_lower_index {
                    true => rewarder_infos[i]
                        .growth_global
                        .wrapping_sub(tick_lower.reward_growth_outside.0.as_slice()[i]),
                    false => tick_lower.reward_growth_outside.0.as_slice()[i],
                },
            };

            // By convention, assume all prior growth happened below the tick, not above
            let rewarder_growths_above = match tick_upper {
                None => 0,
                Some(tick_upper) => match clmmpool.current_tick_index < tick_upper_index {
                    true => tick_upper.reward_growth_outside.0.as_slice()[i],
                    false => rewarder_infos[i]
                        .growth_global
                        .wrapping_sub(tick_upper.reward_growth_outside.0.as_slice()[i]),
                },
            };
            rewarder_growths_inside[i] = rewarder_infos[i]
                .growth_global
                .wrapping_sub(rewarder_growths_below)
                .wrapping_sub(rewarder_growths_above);
        }

        rewarder_growths_inside
    }
}