Raydium 的交易量近期一度超过了 Uniswap 在 23 条链上的总和,成为市场关注的焦点。本文将深入解析其 AMM V3 中的 Tick 系统,带你看懂这个关键机制是如何重新定义流动性管理效率的
Raydium AMM V3 采用了类似 Uniswap V3 的集中流动性模型,其核心是一个精密的 Tick 系统。这个系统通过多层数据结构来高效管理价格区间和流动性分布,主要包含四个核心概念:Tick、TickArray、TickMap 和 TickMapExtension
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],
}
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
}
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],
}
/// 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)
}
位图是一个高效的数据结构,用于快速查找已初始化的 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));
}
/// 找到下一个初始化的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),
)
}
}
}
由于单个位图只能覆盖有限的价格范围,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],
}
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
}
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,
))
}
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),
);
}
}
}
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);
}
}
}
在执行 swap 操作时,系统需要:
添加或移除流动性时:
系统通过 tick 系统实现精确的价格发现:
Raydium AMM V3 的 Tick 系统是一个精心设计的多层架构:
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!