Raydium AMM V3 中的 Tick 系统详解

  • zero
  • 发布于 2025-08-02 23:46
  • 阅读 369

Raydium 的交易量近期一度超过了 Uniswap 在 23 条链上的总和,成为市场关注的焦点。本文将深入解析其 AMM V3 中的 Tick 系统,带你看懂这个关键机制是如何重新定义流动性管理效率的

概述

Raydium AMM V3 采用了类似 Uniswap V3 的集中流动性模型,其核心是一个精密的 Tick 系统。这个系统通过多层数据结构来高效管理价格区间和流动性分布,主要包含四个核心概念:Tick、TickArray、TickMap 和 TickMapExtension

1. Tick - 价格点的基本单位

1.1 Tick 的定义

tick_array.rs中定义的 TickState 是价格系统的基本单位:

pub struct TickState {
    pub tick: i32,
    /// 当从左到右(右到左)跨越此tick时加上(减去)的净流动性量
    pub liquidity_net: i128,
    /// 此tick的总流动性
    pub liquidity_gross: u128,
    /// 此tick另一侧的每单位流动性费用增长
    pub fee_growth_outside_0_x64: u128,
    pub fee_growth_outside_1_x64: u128,
    /// 奖励增长数组
    pub reward_growths_outside_x64: [u128; REWARD_NUM],
    /// 合约升级预留字段
    pub padding: [u32; 13],
}

1.2 Tick 的核心特性

  • 价格映射:每个 tick 对应一个特定的价格点,价格 = 1.0001^tick
  • 流动性管理:记录该价格点的流动性信息
  • 费用跟踪:追踪费用和奖励的累积
  • 边界检查:必须在有效的价格范围内

    1.3 Tick 的关键方法

  • initialize(): 初始化 tick,确保 tick 值符合 tick_spacing 要求
    pub fn initialize(&mut self, tick: i32, tick_spacing: u16) -> Result<()> {
        //检查tick 
        if TickState::check_is_out_of_boundary(tick) {
            return err!(ErrorCode::InvaildTickIndex);
        }
        require!(
            tick % i32::from(tick_spacing) == 0,
            ErrorCode::TickAndSpacingNotMatch
        );
        self.tick = tick;
        Ok(())
    }

-update(): 更新流动性,返回是否发生了初始化状态翻转

/// Updates a tick and returns true if the tick was flipped from initialized to uninitialized
    pub fn update(
        &mut self,
        tick_current: i32,
        liquidity_delta: i128,
        fee_growth_global_0_x64: u128,
        fee_growth_global_1_x64: u128,
        upper: bool,
        reward_infos: &[RewardInfo; REWARD_NUM],
    ) -> Result<bool> {
        let liquidity_gross_before = self.liquidity_gross;
        let liquidity_gross_after =
            liquidity_math::add_delta(liquidity_gross_before, liquidity_delta)?;

        // Either liquidity_gross_after becomes 0 (uninitialized) XOR liquidity_gross_before
        // was zero (initialized)
        let flipped = (liquidity_gross_after == 0) != (liquidity_gross_before == 0);
        if liquidity_gross_before == 0 {
            // by convention, we assume that all growth before a tick was initialized happened _below_ the tick
            if self.tick <= tick_current {
                self.fee_growth_outside_0_x64 = fee_growth_global_0_x64;
                self.fee_growth_outside_1_x64 = fee_growth_global_1_x64;
                self.reward_growths_outside_x64 = RewardInfo::get_reward_growths(reward_infos);
            }
        }

        self.liquidity_gross = liquidity_gross_after;

        // when the lower (upper) tick is crossed left to right (right to left),
        // liquidity must be added (removed)
        self.liquidity_net = if upper {
            self.liquidity_net.checked_sub(liquidity_delta)
        } else {
            self.liquidity_net.checked_add(liquidity_delta)
        }
        .unwrap();
        Ok(flipped)
    }
  • cross(): 处理价格跨越该 tick 时的状态变化
/// Transitions to the current tick as needed by price movement, returning the amount of liquidity
    /// added (subtracted) when tick is crossed from left to right (right to left)
    pub fn cross(
        &mut self,
        fee_growth_global_0_x64: u128,
        fee_growth_global_1_x64: u128,
        reward_infos: &[RewardInfo; REWARD_NUM],
    ) -> i128 {
        self.fee_growth_outside_0_x64 = fee_growth_global_0_x64
            .checked_sub(self.fee_growth_outside_0_x64)
            .unwrap();
        self.fee_growth_outside_1_x64 = fee_growth_global_1_x64
            .checked_sub(self.fee_growth_outside_1_x64)
            .unwrap();

        for i in 0..REWARD_NUM {
            if !reward_infos[i].initialized() {
                continue;
            }

            self.reward_growths_outside_x64[i] = reward_infos[i]
                .reward_growth_global_x64
                .checked_sub(self.reward_growths_outside_x64[i])
                .unwrap();
        }

        self.liquidity_net
    }

-clear(): 清除流动性

    pub fn clear(&mut self) {
        self.liquidity_net = 0;
        self.liquidity_gross = 0;
        self.fee_growth_outside_0_x64 = 0;
        self.fee_growth_outside_1_x64 = 0;
        self.reward_growths_outside_x64 = [0; REWARD_NUM];
    }
  • is_initialized():该tick是否初始化
   pub fn is_initialized(self) -> bool {
       //判断该tick的总流动性(liquidity_gross)是否为0
        self.liquidity_gross != 0
    }

-check_is_out_of_boundary():该tick是否符合边界要求

    /// Common checks for a valid tick input.
    /// A tick is valid if it lies within tick boundaries
    pub fn check_is_out_of_boundary(tick: i32) -> bool {
        tick < tick_math::MIN_TICK || tick > tick_math::MAX_TICK
    }

2. TickArray - Tick 的容器

2.1 TickArray 的结构

TickArrayState 是 Tick 的容器,每个 TickArray 包含 60 个连续的 Tick

pub struct TickArrayState {
    pub pool_id: Pubkey,
    pub start_tick_index: i32,
    pub ticks: [TickState; TICK_ARRAY_SIZE_USIZE], // 60个tick
    pub initialized_tick_count: u8,
    pub recent_epoch: u64,
    pub padding: [u8; 107],
}

2.2 TickArray 的设计原理

  • 批量管理:将 60 个连续的 tick 组织在一起,减少账户数量
  • 内存优化:使用 zero_copy 避免序列化开销
  • 范围定位:通过 start_tick_index 快速定位 tick 范围
  • 初始化跟踪:记录已初始化的 tick 数量

    2.3 TickArray 的关键算法

    /// Get next initialized tick in tick array, `current_tick_index` can be any tick index, in other words, `current_tick_index` not exactly a point in the tickarray,
    /// and current_tick_index % tick_spacing maybe not equal zero.
    /// If price move to left tick <= current_tick_index, or to right tick > current_tick_index
    pub fn next_initialized_tick(
        &mut self,
        current_tick_index: i32,
        tick_spacing: u16,
        zero_for_one: bool,
    ) -> Result<Option<&mut TickState>> {
        //计算出当前的tick array上的start tick index
        let current_tick_array_start_index =
            TickArrayState::get_array_start_index(current_tick_index, tick_spacing);
        //传入的current_tick_index 不在当前tick array
        if current_tick_array_start_index != self.start_tick_index {
            return Ok(None);
        }
        //在当前tick array里的位置
        let mut offset_in_array =
            (current_tick_index - self.start_tick_index) / i32::from(tick_spacing);
        //从当前位置offset_in_array开始找
        if zero_for_one {
            //token a swap token b
            //token a的价格降低 tick 从右往左
            //从后往前找
            while offset_in_array >= 0 {
                if self.ticks[offset_in_array as usize].is_initialized() {
                    return Ok(self.ticks.get_mut(offset_in_array as usize));
                }
                offset_in_array = offset_in_array - 1;
            }
        } else {
            offset_in_array = offset_in_array + 1;
            while offset_in_array < TICK_ARRAY_SIZE {
                if self.ticks[offset_in_array as usize].is_initialized() {
                    return Ok(self.ticks.get_mut(offset_in_array as usize));
                }
                offset_in_array = offset_in_array + 1;
            }
        }
        Ok(None)
    }

// 计算 tick 在 TickArray 中的偏移量
fn get_tick_offset_in_array(self, tick_index: i32, tick_spacing: u16) -> Result<usize> {
    let start_tick_index = TickArrayState::get_array_start_index(tick_index, tick_spacing);
    let offset_in_array = ((tick_index - self.start_tick_index) / i32::from(tick_spacing)) as usize;
    Ok(offset_in_array)
}

// 获取数组的起始索引
pub fn get_array_start_index(tick_index: i32, tick_spacing: u16) -> i32 {
    let tick_spacing_i32 = i32::from(tick_spacing);
    let array_tick_count = tick_spacing_i32 * TICK_ARRAY_SIZE;

    if tick_index < 0 {
        (tick_index + 1) / array_tick_count - 1
    } else {
        tick_index / array_tick_count
    } * array_tick_count
}

/// Input an arbitrary tick_index, output the start_index of the tick_array it sits on
/// 该函数的作用是根据tick_index和tick_spacing来计算出它所在的TickArray的起始tick index
 pub fn get_array_start_index(tick_index: i32, tick_spacing: u16) -> i32 {
     //一个tick array有多少元素
     let ticks_in_array = TickArrayState::tick_count(tick_spacing);
     //判断是第几个tick array
     let mut start = tick_index / ticks_in_array;
     //遵循左开右闭[-100,0)  [0,100) 如果为负数,要减1
     //假如为-50 ticks_in_array为100
     //-50/100=0,如果不减1的话会落在0,减1落在-1
     //如果是-100 ticks_in_array为100
     //-100/100=-1  -100%100=0 还是落在-1
     if tick_index < 0 && tick_index % ticks_in_array != 0 {
          start = start - 1
      }
      start * ticks_in_array
}

//根据tick_sapcing来判断他是否是TickArray的起始值
pub fn check_is_valid_start_index(tick_index: i32, tick_spacing: u16) -> bool {
     // 检查tick是否合法
     if TickState::check_is_out_of_boundary(tick_index) {
        //如果超过边界,大于最大值 判定为不合法
        if tick_index > tick_math::MAX_TICK {
            return false;
         }
         //如果刚好等于最小值,则判定为合法
          let min_start_index =
               TickArrayState::get_array_start_index(tick_math::MIN_TICK, tick_spacing);
            return tick_index == min_start_index;
      }
      //根据tick_sapcing来判断他是否是TickArray的起始值
      tick_index % TickArrayState::tick_count(tick_spacing) == 0
}
//用于计算tickArray中覆盖了多少tick
//tick_sapcing 是tick之间的步长
//tick 为1 该tick_count 60*1=60
//tick 为5 该tick_count 60*5=300
//[150,155,160,165,170,175,180,185,190]
pub fn tick_count(tick_spacing: u16) -> i32 {
    TICK_ARRAY_SIZE * i32::from(tick_spacing)
}

3. TickMap (位图) - 快速索引系统

3.1 位图的基本概念

位图是一个高效的数据结构,用于快速查找已初始化的 TickArray,在tick_array_bit_map.rs 中定义

pub const TICK_ARRAY_BITMAP_SIZE: i32 = 512;
pub type TickArryBitmap = [u64; 8]; // 512位的位图

// 计算位图能覆盖的最大tick范围
pub fn max_tick_in_tickarray_bitmap(tick_spacing: u16) -> i32 {
    i32::from(tick_spacing) * TICK_ARRAY_SIZE * TICK_ARRAY_BITMAP_SIZE
}

pub fn get_bitmap_tick_boundary(tick_array_start_index: i32, tick_spacing: u16) -> (i32, i32) {
    //一个bitmap有管理多少tick
    let ticks_in_one_bitmap: i32 = max_tick_in_tickarray_bitmap(tick_spacing);
    //该tick在第几个bitmap(后续可扩展tick map extension)
    let mut m = tick_array_start_index.abs() / ticks_in_one_bitmap;

    if tick_array_start_index < 0 && tick_array_start_index.abs() % ticks_in_one_bitmap != 0 {
        //处理边界问题处理
        //负数 [-512,0)
        //正数 [0,512)
        m += 1;
    }

    let min_value: i32 = ticks_in_one_bitmap * m;
    if tick_array_start_index < 0 {
        (-min_value, -min_value + ticks_in_one_bitmap)
    } else {
        //(start,end)
        //start 这个bitmap覆盖的最小的tick值
        //end 是这个bitmap覆盖的最大的tick值
        (min_value, min_value + ticks_in_one_bitmap)
    }
}
/// Given a tick, calculate whether the tickarray it belongs to has been initialized.
pub fn check_current_tick_array_is_initialized(
    bit_map: U1024,
    tick_current: i32,
    tick_spacing: u16,
) -> Result<(bool, i32)> {
    if TickState::check_is_out_of_boundary(tick_current) {
        return err!(ErrorCode::InvaildTickIndex);
    }
    let multiplier = i32::from(tick_spacing) * TICK_ARRAY_SIZE;
    // +512 把索引从[-512,511] 映射到[0,1023]
    // 最多有两个 bitmap 1024个tickArray
    let mut compressed = tick_current / multiplier + 512;
    if tick_current < 0 && tick_current % multiplier != 0 {
        // round towards negative infinity
        // 处理负数
        compressed -= 1;
    }
    let bit_pos = compressed.abs();
    // set current bit
    //在第 bit_pos 位置上有一个1,其他位都是0的掩码
    let mask = U1024::one() << bit_pos.try_into().unwrap();
    // 检查第 bit_pos 位是否为1
    // - 对 bit_map 和 mask 执行按位与操作
    // - 如果 bit_map 在第 bit_pos 位为1,有流动性 结果为 mask
    // - 如果 bit_map 在第 bit_pos 位为0,没有流动性 结果为0
    let masked = bit_map & mask;
    // check the current bit whether initialized
    let initialized = masked != U1024::default();
    if initialized {
        // (有流动性,计算当前tick array的起始索引) 
        return Ok((true, (compressed - 512) * multiplier));
    }
    // the current bit is not initialized
    return Ok((false, (compressed - 512) * multiplier));
}

3.2 位图的工作原理

  • 位映射:每一位代表一个 TickArray 的初始化状态
  • 快速查找:使用位操作(leading_zeros, trailing_zeros)快速定位
  • 范围覆盖:每个位图覆盖 512 个 TickArray
  • 双向搜索:支持向上和向下搜索下一个已初始化的 TickArray

    3.3 位图搜索算法

/// 找到下一个初始化的tick array的起始索引
pub fn next_initialized_tick_array_start_index(
    bit_map: U1024,
    last_tick_array_start_index: i32,
    tick_spacing: u16,
    zero_for_one: bool,
) -> (bool, i32) {
    assert!(TickArrayState::check_is_valid_start_index(
        last_tick_array_start_index,
        tick_spacing
    ));

    let tick_boundary = max_tick_in_tickarray_bitmap(tick_spacing);
    /// 计算下一个tick array的起始索引
    let next_tick_array_start_index = if zero_for_one {
        // 从下往上找
        last_tick_array_start_index - TickArrayState::tick_count(tick_spacing)
    } else {
        // 从上往下找
        last_tick_array_start_index + TickArrayState::tick_count(tick_spacing)
    };

    // 检查下一个tick array的起始索引是否在边界内
    if next_tick_array_start_index < -tick_boundary || next_tick_array_start_index >= tick_boundary
    {
        return (false, last_tick_array_start_index);
    }

    let multiplier = i32::from(tick_spacing) * TICK_ARRAY_SIZE;
    //
    let mut compressed = next_tick_array_start_index / multiplier + 512;
    if next_tick_array_start_index < 0 && next_tick_array_start_index % multiplier != 0 {
        // round towards negative infinity
        compressed -= 1;
    }
    let bit_pos = compressed.abs();

    if zero_for_one {
        // tick from upper to lower
        // find from highter bits to lower bits
        /// << 左移运算符 左边的位置丢弃,右边加0
        /// 把bit_map 左移 1024 - bit_pos - 1 位 (把bit_pos位置,移到首位)
        /// 找到最左边的1
        let offset_bit_map = bit_map << (1024 - bit_pos - 1).try_into().unwrap();
        let next_bit = most_significant_bit(offset_bit_map);
        /// 找到下一个tick array的起始索引
        if next_bit.is_some() {
            let next_array_start_index =
                (bit_pos - i32::from(next_bit.unwrap()) - 512) * multiplier;
            (true, next_array_start_index)
        } else {
            // not found til to the end
            (false, -tick_boundary)
        }
    } else {
        // tick from lower to upper
        // find from lower bits to highter bits
        /// >> 右移运算符 右边的位置丢弃,左边加0
        /// 把bit_map 右移 bit_pos 位 (把bit_pos位置,移到末位)
        /// 找到最右边的1
        let offset_bit_map = bit_map >> (bit_pos).try_into().unwrap();
        let next_bit = least_significant_bit(offset_bit_map);
        if next_bit.is_some() {
            let next_array_start_index =
                (bit_pos + i32::from(next_bit.unwrap()) - 512) * multiplier;
            (true, next_array_start_index)
        } else {
            // not found til to the end
            (
                false,
                tick_boundary - TickArrayState::tick_count(tick_spacing),
            )
        }
    }
}

4. TickMapExtension - 扩展位图系统

4.1 扩展系统的必要性

由于单个位图只能覆盖有限的价格范围,TickArrayBitmapExtension提供了扩展支持:

const EXTENSION_TICKARRAY_BITMAP_SIZE: usize = 14;
#[account(zero_copy(unsafe))]
#[repr(C, packed)]
#[derive(Debug)]
pub struct TickArrayBitmapExtension {
    pub pool_id: Pubkey,
    /// Packed initialized tick array state for start_tick_index is positive
    /// 每个bitmap 用8个 u64 来表示,每个 tick array 用 1 位来表示
    pub positive_tick_array_bitmap: [[u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE],
    /// Packed initialized tick array state for start_tick_index is negitive
    pub negative_tick_array_bitmap: [[u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE],
}

4.2 扩展系统的设计

  • 分层管理:核心位图处理常用价格范围,扩展位图处理极端价格
  • 正负分离:分别管理正数和负数 tick 范围
  • 多位图阵列:每个方向有 14 个位图,大大扩展了覆盖范围
  • 边界检查:确保 tick 在扩展范围内

    4.3 扩展位图的核心算法

fn get_bitmap_offset(tick_index: i32, tick_spacing: u16) -> Result<usize> {
     require!(
        TickArrayState::check_is_valid_start_index(tick_index, tick_spacing),
        ErrorCode::InvaildTickIndex
    );
    Self::check_extension_boundary(tick_index, tick_spacing)?;
    let ticks_in_one_bitmap = max_tick_in_tickarray_bitmap(tick_spacing);
    //tick_index.abs() / ticks_in_one_bitmap 计算出这是第几个bitmap
    //ick_index.abs() / ticks_in_one_bitmap - 1
    // - -1 = 因为扩展位图从第 1 个位图开始(第 0 个是基础位图)
    // 先使用bitmap ,bitmap不够再使用bitmap extension    
    let mut offset = tick_index.abs() / ticks_in_one_bitmap - 1;
    //[) 左闭右开,处理边界 所以需要-1
     if tick_index < 0 && tick_index.abs() % ticks_in_one_bitmap == 0 {
            offset -= 1;
     }
    Ok(offset as usize)
}
// 获取对应的位图
fn get_bitmap(&self, tick_index: i32, tick_spacing: u16) -> Result<(usize, TickArryBitmap)> {
    let offset = Self::get_bitmap_offset(tick_index, tick_spacing)?;
    if tick_index < 0 {
        Ok((offset, self.negative_tick_array_bitmap[offset]))
    } else {
        Ok((offset, self.positive_tick_array_bitmap[offset]))
    }
}

 /// Check if the tick in tick array bitmap extension
 pub fn check_extension_boundary(tick_index: i32, tick_spacing: u16) -> Result<()> {
     let positive_tick_boundary = max_tick_in_tickarray_bitmap(tick_spacing);
     let negative_tick_boundary = -positive_tick_boundary;
     require_gt!(tick_math::MAX_TICK, positive_tick_boundary);
     require_gt!(negative_tick_boundary, tick_math::MIN_TICK);
     if tick_index >= negative_tick_boundary && tick_index < positive_tick_boundary {
        return err!(ErrorCode::InvalidTickArrayBoundary);
     }
     Ok(())
 }
 /// Check if the tick array is initialized
 pub fn check_tick_array_is_initialized(
     &self,
     tick_array_start_index: i32,
     tick_spacing: u16,
) -> Result<(bool, i32)> {
   let (_, tickarray_bitmap) = self.get_bitmap(tick_array_start_index, tick_spacing)?;

   let tick_array_offset_in_bitmap =
         Self::tick_array_offset_in_bitmap(tick_array_start_index, tick_spacing);

   if U512(tickarray_bitmap).bit(tick_array_offset_in_bitmap as usize) {
          return Ok((true, tick_array_start_index));
    }
    Ok((false, tick_array_start_index))
 }

 /// Flip the value of tick in the bitmap.
 pub fn flip_tick_array_bit(
        &mut self,
        tick_array_start_index: i32,
        tick_spacing: u16,
 ) -> Result<()> {
        let (offset, tick_array_bitmap) = self.get_bitmap(tick_array_start_index, tick_spacing)?;
        //计算中该tick在bitmap(512个tick array)中第几个位置
        let tick_array_offset_in_bitmap =
            Self::tick_array_offset_in_bitmap(tick_array_start_index, tick_spacing);
        let tick_array_bitmap = U512(tick_array_bitmap);
        //如果该tick在第5个,那么需要将第5个位置的bitmap
        //mask 就是0000000000100000
        let mask = U512::one() << tick_array_offset_in_bitmap;
        //
        if tick_array_start_index < 0 {
            self.negative_tick_array_bitmap[offset as usize] = tick_array_bitmap.bitxor(mask).0;
        } else {
            self.positive_tick_array_bitmap[offset as usize] = tick_array_bitmap.bitxor(mask).0;
        }
        Ok(())
    }
     //计算出该tick第几个TickArray上
    pub fn tick_array_offset_in_bitmap(tick_array_start_index: i32, tick_spacing: u16) -> i32 {
        // 计算出该tick在bitmap里的位置
        let m = tick_array_start_index.abs() % max_tick_in_tickarray_bitmap(tick_spacing);
        // 计算出该tick第几个TickArray上
        let mut tick_array_offset_in_bitmap = m / TickArrayState::tick_count(tick_spacing);

        if tick_array_start_index < 0 && m != 0 {
            // 一个bitmap是有512个TickArray组成的,
            // 如果为负数 左开右闭[)
            // 用512减去 tick_array_offset_in_bitmap
            // [[],[],[],[],[]]
            // 所以为了方便理解,我们可以认为
            // 第0个TickArray在bitmap的第511位
            // 第1个TickArray在bitmap的第510位
            // 第2个TickArray在bitmap的第509位
            // 第511个TickArray在bitmap的第0位
            tick_array_offset_in_bitmap = TICK_ARRAY_BITMAP_SIZE - tick_array_offset_in_bitmap;
        }
        tick_array_offset_in_bitmap
    }

5. 系统协同工作流程

5.1 查找下一个已初始化的 TickArray

next_initialized_tick_array_from_one_bitmap 展示了完整的查找流程:

pub fn next_initialized_tick_array_from_one_bitmap(
    &self,
    last_tick_array_start_index: i32,
    tick_spacing: u16,
    zero_for_one: bool,
) -> Result<(bool, i32)> {
    // 1. 计算下一个搜索位置
    let multiplier = TickArrayState::tick_count(tick_spacing);
    let next_tick_array_start_index = if zero_for_one {
        last_tick_array_start_index - multiplier
    } else {
        last_tick_array_start_index + multiplier
    };

    // 2. 边界检查
    if next_tick_array_start_index < min_tick_array_start_index
        || next_tick_array_start_index > max_tick_array_start_index {
        return Ok((false, next_tick_array_start_index));
    }

    // 3. 获取对应位图
    let (_, tickarray_bitmap) = self.get_bitmap(next_tick_array_start_index, tick_spacing)?;

    // 4. 在位图中搜索
    Ok(Self::next_initialized_tick_array_in_bitmap(
        tickarray_bitmap,
        next_tick_array_start_index,
        tick_spacing,
        zero_for_one,
    ))
}

5.2 位图内部搜索算法

next_initialized_tick_array_in_bitmap实现了高效的位操作搜索:

    pub fn next_initialized_tick_array_in_bitmap(
        tickarray_bitmap: TickArryBitmap,
        next_tick_array_start_index: i32,
        tick_spacing: u16,
        zero_for_one: bool,
    ) -> (bool, i32) {
        let (bitmap_min_tick_boundary, bitmap_max_tick_boundary) =
            get_bitmap_tick_boundary(next_tick_array_start_index, tick_spacing);

        let tick_array_offset_in_bitmap =
            Self::tick_array_offset_in_bitmap(next_tick_array_start_index, tick_spacing);
        if zero_for_one {
            // tick from upper to lower
            // find from highter bits to lower bits
            let offset_bit_map = U512(tickarray_bitmap)
                << (TICK_ARRAY_BITMAP_SIZE - 1 - tick_array_offset_in_bitmap);

            let next_bit = if offset_bit_map.is_zero() {
                None
            } else {
                Some(u16::try_from(offset_bit_map.leading_zeros()).unwrap())
            };

            //原始位图: 1 1 0 1 0 1 1 0
            //位置:    7 6 5 4 3 2 1 0
            //左移一位  1 0 1 0 1 1 0 0
            //左移2位   0 1 0 1 1 0 0 0
            //左移3位   1 0 1 1 0 0 0 0
            //左移4位   0 1 1 0 0 0 0 0

            if next_bit.is_some() {
                let next_array_start_index = next_tick_array_start_index
                    - i32::from(next_bit.unwrap()) * TickArrayState::tick_count(tick_spacing);
                return (true, next_array_start_index);
            } else {
                // not found til to the end
                return (false, bitmap_min_tick_boundary);
            }
        } else {
            // tick from lower to upper
            // find from lower bits to highter bits
            // 把低位给抹除掉,如果为0(第0位为1)
            let offset_bit_map = U512(tickarray_bitmap) >> tick_array_offset_in_bitmap;

            //原始位图: 1 1 0 1 0 1 1 0
            // 位置:    7 6 5 4 3 2 1 0

            // 右移1位:  0 1 1 0 1 0 1 1
            // 位置:    7 6 5 4 3 2 1 0

            // 右移2位:  0 0 1 1 0 1 0 1
            // 位置:    7 6 5 4 3 2 1 0

            // 右移3位:  0 0 0 1 1 0 1 0
            // 位置:    7 6 5 4 3 2 1 0

            let next_bit = if offset_bit_map.is_zero() {
                None
            } else {
                Some(u16::try_from(offset_bit_map.trailing_zeros()).unwrap())
            };
            if next_bit.is_some() {
                let next_array_start_index = next_tick_array_start_index
                    + i32::from(next_bit.unwrap()) * TickArrayState::tick_count(tick_spacing);
                return (true, next_array_start_index);
            } else {
                // not found til to the end
                //没有找到,返回当前bitmap中最后一个TickArray的起始位置
                return (
                    false,
                    bitmap_max_tick_boundary - TickArrayState::tick_count(tick_spacing),
                );
            }
        }
    }

6. Tick、TickArray、TickMap、TickMapExtension 关系

6.1 查找流动性

    pub fn get_first_initialized_tick_array(
        &self,
        tickarray_bitmap_extension: &Option<TickArrayBitmapExtension>,
        zero_for_one: bool,
    ) -> Result<(bool, i32)> {
    ///
        let (is_initialized, start_index) =
            ///判断是否在基础bitmap中,如果不在就到extension中查询
            if self.is_overflow_default_tickarray_bitmap(vec![self.tick_current]) {
                tickarray_bitmap_extension
                    .unwrap()
                    .check_tick_array_is_initialized(
                        TickArrayState::get_array_start_index(self.tick_current, self.tick_spacing),
                        self.tick_spacing,
                    )?
            } else {
                //在基础bitmap中
                check_current_tick_array_is_initialized(
                    U1024(self.tick_array_bitmap),
                    self.tick_current,
                    self.tick_spacing.into(),
                )?
            };
        if is_initialized {
            return Ok((true, start_index));
        }
        //
        let next_start_index = self.next_initialized_tick_array_start_index(
            tickarray_bitmap_extension,
            TickArrayState::get_array_start_index(self.tick_current, self.tick_spacing),
            zero_for_one,
        )?;
        require!(
            next_start_index.is_some(),
            ErrorCode::InsufficientLiquidityForDirection
        );
        return Ok((false, next_start_index.unwrap()));
    }

pub fn next_initialized_tick_array_start_index(
        &self,
        tickarray_bitmap_extension: &Option<TickArrayBitmapExtension>,
        mut last_tick_array_start_index: i32,
        zero_for_one: bool,
    ) -> Result<Option<i32>> {
        last_tick_array_start_index =
            TickArrayState::get_array_start_index(last_tick_array_start_index, self.tick_spacing);

        loop {
            //现在基础bitmap中查询
            let (is_found, start_index) =
                tick_array_bit_map::next_initialized_tick_array_start_index(
                    U1024(self.tick_array_bitmap),
                    last_tick_array_start_index,
                    self.tick_spacing,
                    zero_for_one,
                );
            if is_found {
                return Ok(Some(start_index));
            }
            last_tick_array_start_index = start_index;

            if tickarray_bitmap_extension.is_none() {
                return err!(ErrorCode::MissingTickArrayBitmapExtensionAccount);
            }
            //没找到就去extension中查询
            let (is_found, start_index) = tickarray_bitmap_extension
                .unwrap()
                .next_initialized_tick_array_from_one_bitmap(
                    last_tick_array_start_index,
                    self.tick_spacing,
                    zero_for_one,
                )?;
            if is_found {
                return Ok(Some(start_index));
            }
            last_tick_array_start_index = start_index;

            if last_tick_array_start_index < tick_math::MIN_TICK
                || last_tick_array_start_index > tick_math::MAX_TICK
            {
                return Ok(None);
            }
        }
    }

6.2 关系图

image.png

7. 性能优化策略

7.1 位操作优化

  • leading_zeros/trailing_zero:使用 CPU 原生指令快速查找
  • 位移操作:通过左移和右移快速定位搜索范围
  • 掩码操作:使用 XOR 操作高效翻转位状态

    7.2 内存布局优化

  • zero_copy:避免序列化/反序列化开销
  • packed 结构:减少内存占用
  • 批量操作:一次处理多个 tick

    7.3 算法复杂度

  • 查找复杂度:O(1) - 通过位操作实现常数时间查找
  • 更新复杂度:O(1) - 直接位操作更新
  • 空间复杂度:O(n) - 线性空间存储

    8. 实际应用场景

    8.1 交易执行

    在执行 swap 操作时,系统需要:

    1. 根据当前价格找到对应的 TickArray
    2. 在 TickArray 中查找下一个有流动性的 tick
    3. 如果当前 TickArray 没有足够流动性,通过位图查找下一个 TickArray
    4. 重复此过程直到完成交易或达到价格限制

      8.2 流动性管理

      添加或移除流动性时:

    5. 确定流动性范围对应的 tick 边界
    6. 初始化相关的 TickArray(如果需要)
    7. 更新位图标记 TickArray 状态
    8. 更新 tick 的流动性信息

      8.3 价格发现

      系统通过 tick 系统实现精确的价格发现:

  • 每个 tick 对应精确的价格点
  • 流动性集中在特定价格区间
  • 通过位图快速跳跃到有流动性的价格区间

    9. 总结

    Raydium AMM V3 的 Tick 系统是一个精心设计的多层架构:

  • Tick:最基本的价格和流动性单位
  • TickArray:批量管理 tick,减少账户数量
  • TickMap:位图索引,实现快速查找TickArray
  • TickMapExtension:扩展位图,支持更大价格范围 这个系统通过巧妙的数据结构设计和位操作优化,实现了:
  • 高效查找:O(1) 时间复杂度的 TickArray 定位
  • 内存优化:紧凑的数据布局和批量管理
  • 可扩展性:支持极大的价格范围
  • 精确控制:精确的流动性分布和价格发现 这种设计使得 Raydium AMM V3 能够在保持高性能的同时,为用户提供精确的流动性控制和高效的交易执行。
点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zero
zero
0xc803...057e
江湖只有他的大名,没有他的介绍。