Skip to main content

raydium_sdk_V2/raydium/clmm/utils/
tick_array_bitmap.rs

1use std::ops::{Neg, Shl, Shr};
2use carbon_raydium_clmm_decoder::accounts::tick_array_bitmap_extension::TickArrayBitmapExtension;
3use rug::Complete;
4use crate::raydium::clmm::utils::constants::{MAX_TICK, MIN_TICK};
5use crate::raydium::clmm::utils::tick::{TICK_ARRAY_BITMAP_SIZE, TICK_ARRAY_SIZE, TickUtils};
6use crate::raydium::clmm::utils::tick_query::TickQuery;
7use crate::raydium::clmm::utils::util::{is_zero, leading_zeros, least_significant_bit, most_significant_bit, trailing_zeros};
8
9pub struct TickArrayBitmap {}
10
11pub struct TickArrayBitmapExtensionUtils {}
12
13pub struct NextTickArray {
14    pub is_init: bool,
15    pub tick_index: i32,
16}
17
18pub struct GetBitmapTickBoundary {
19    min: i32,
20    max: i32,
21}
22
23impl TickArrayBitmap {
24    pub fn get_bitmap_tick_boundary(tick_array_start_index: i32, tick_spacing: u16) -> GetBitmapTickBoundary {
25        let ticks_in_one_bitmap = Self::max_tick_in_tick_array_bitmap(tick_spacing) as i32;
26        let tick_array_start_index_abs = tick_array_start_index.abs();
27        let mut m = (tick_array_start_index_abs as f32 / ticks_in_one_bitmap as f32).floor() as i32;
28        if tick_array_start_index < 0 && tick_array_start_index_abs % ticks_in_one_bitmap != 0 {
29            m += 1;
30        }
31        let min = ticks_in_one_bitmap * m;
32        if tick_array_start_index < 0 {
33            let min_neg = min.neg();
34            GetBitmapTickBoundary { min: min_neg, max: min_neg + ticks_in_one_bitmap }
35        } else {
36            GetBitmapTickBoundary { min, max: min + ticks_in_one_bitmap }
37        }
38    }
39    pub fn max_tick_in_tick_array_bitmap(tick_spacing: u16) -> u32 {
40        tick_spacing as u32 * TICK_ARRAY_SIZE as u32 * TICK_ARRAY_BITMAP_SIZE as u32
41    }
42    pub fn next_initialized_tick_array_start_index(bitmap: &rug::Integer, last_tick_array_start_index: i32, tick_spacing: u16, zero_for_one: bool) -> Result<NextTickArray, String> {
43        if !TickQuery::check_is_valid_start_index(last_tick_array_start_index, tick_spacing) {
44            return Err("nextInitializedTickArrayStartIndex check error".to_string());
45        }
46        let tick_boundary = Self::max_tick_in_tick_array_bitmap(tick_spacing) as i32;
47        let next_tick_array_start_index = if zero_for_one {
48            last_tick_array_start_index - TickQuery::tick_count(tick_spacing) as i32
49        } else {
50            last_tick_array_start_index + TickQuery::tick_count(tick_spacing) as i32
51        };
52        if next_tick_array_start_index < -tick_boundary || next_tick_array_start_index >= tick_boundary {
53            return Ok(NextTickArray { is_init: false, tick_index: last_tick_array_start_index });
54        }
55        let multiplier = (tick_spacing as usize * TICK_ARRAY_SIZE) as i32;
56        let mut compressed = next_tick_array_start_index / multiplier + 512;
57        if next_tick_array_start_index < 0 && next_tick_array_start_index % multiplier != 0 {
58            compressed -= 1;
59        }
60        let bit_pos = compressed.abs();
61        return if zero_for_one {
62            let offset_bitmap = bitmap.shl(1024 - bit_pos - 1).complete();
63            match most_significant_bit(1024, &offset_bitmap) {
64                None => {
65                    Ok(NextTickArray { is_init: false, tick_index: -tick_boundary })
66                }
67                Some(next_bit) => {
68                    let next_array_start_index = (bit_pos - next_bit as i32 - 512) * multiplier;
69                    Ok(NextTickArray { is_init: true, tick_index: next_array_start_index })
70                }
71            }
72        } else {
73            let offset_bitmap = bitmap.shr(bit_pos).complete();
74            match least_significant_bit(1024, &offset_bitmap) {
75                None => {
76                    Ok(NextTickArray { is_init: false, tick_index: tick_boundary - TickQuery::tick_count(tick_spacing) as i32 })
77                }
78                Some(next_bit) => {
79                    let next_array_start_index = (bit_pos + next_bit as i32 - 512) * multiplier;
80                    Ok(NextTickArray { is_init: true, tick_index: next_array_start_index })
81                }
82            }
83        }
84    }
85}
86
87pub struct CheckTickArrayIsInitResult {
88    pub is_initialized: bool,
89    pub start_index: i32,
90}
91
92pub struct GetBitmapResult {
93    offset: u32,
94    tick_array_bitmap: [u64; 8],
95}
96
97pub struct ExtensionTickBoundaryResult {
98    positive_tick_boundary: i32,
99    negative_tick_boundary: i32,
100}
101
102impl TickArrayBitmapExtensionUtils {
103    pub fn next_initialized_tick_array_in_bitmap(
104        tick_array_bitmap: [u64; 8],
105        next_tick_array_start_index: i32,
106        tick_spacing: u16,
107        zero_for_one: bool,
108    ) -> NextTickArray {
109        let bitmap_tick_boundary = TickArrayBitmap::get_bitmap_tick_boundary(next_tick_array_start_index, tick_spacing);
110        let tick_array_offset_in_bitmap = Self::tick_array_offset_in_bitmap(next_tick_array_start_index, tick_spacing);
111        if zero_for_one {
112            let offset_bitmap = TickUtils::merge_tick_array_bitmap(tick_array_bitmap.as_slice()).shl(TICK_ARRAY_BITMAP_SIZE as u32 - 1 - tick_array_offset_in_bitmap);
113            return if is_zero(512, &offset_bitmap) {
114                NextTickArray { is_init: false, tick_index: bitmap_tick_boundary.min }
115            } else {
116                let next_bit = leading_zeros(512, &offset_bitmap);
117                let next_array_start_index = next_tick_array_start_index - next_bit as i32 * TickQuery::tick_count(tick_spacing) as i32;
118                NextTickArray { is_init: true, tick_index: next_array_start_index }
119            };
120        } else {
121            let offset_bitmap = TickUtils::merge_tick_array_bitmap(tick_array_bitmap.as_slice()).shr(tick_array_offset_in_bitmap);
122            return if is_zero(512, &offset_bitmap) {
123                NextTickArray { is_init: false, tick_index: bitmap_tick_boundary.max - TickQuery::tick_count(tick_spacing) as i32 }
124            } else {
125                let next_bit = trailing_zeros(512, &offset_bitmap);
126                let next_array_start_index = next_tick_array_start_index + next_bit as i32 * TickQuery::tick_count(tick_spacing) as i32;
127                NextTickArray { is_init: true, tick_index: next_array_start_index }
128            };
129        }
130    }
131    pub fn next_initialized_tick_array_from_one_bitmap(
132        last_tick_array_start_index: i32,
133        tick_spacing: u16,
134        zero_for_one: bool,
135        tick_array_bitmap_extension: &carbon_raydium_clmm_decoder::accounts::tick_array_bitmap_extension::TickArrayBitmapExtension,
136    ) -> Result<NextTickArray, String> {
137        let multiplier = TickQuery::tick_count(tick_spacing) as i32;
138        let next_tick_array_start_index = if zero_for_one {
139            last_tick_array_start_index - multiplier as i32
140        } else {
141            last_tick_array_start_index + multiplier
142        };
143        let tick_array_bitmap = Self::get_bitmap(next_tick_array_start_index, tick_spacing, tick_array_bitmap_extension)?;
144        Ok(Self::next_initialized_tick_array_in_bitmap(tick_array_bitmap.tick_array_bitmap, next_tick_array_start_index, tick_spacing, zero_for_one))
145    }
146    pub fn extension_tick_boundary(tick_spacing: u16) -> Result<ExtensionTickBoundaryResult, String> {
147        let positive_tick_boundary = TickArrayBitmap::max_tick_in_tick_array_bitmap(tick_spacing) as i32;
148        let negative_tick_boundary = positive_tick_boundary.neg();
149        if MAX_TICK <= positive_tick_boundary {
150            return Err(format!("extensionTickBoundary check error: {MAX_TICK}, {positive_tick_boundary}"));
151        }
152        if negative_tick_boundary <= MIN_TICK {
153            return Err(format!("extensionTickBoundary check error: {negative_tick_boundary}, {MIN_TICK}"));
154        }
155        Ok(ExtensionTickBoundaryResult { positive_tick_boundary, negative_tick_boundary })
156    }
157    pub fn check_extension_boundary(tick_index: i32, tick_spacing: u16) -> Result<(), String> {
158        let extension_tick_boundary = Self::extension_tick_boundary(tick_spacing)?;
159        if tick_index >= extension_tick_boundary.negative_tick_boundary && tick_index < extension_tick_boundary.positive_tick_boundary {
160            return Err("checkExtensionBoundary -> InvalidTickArrayBoundary".to_string());
161        }
162        Ok(())
163    }
164    pub fn get_bitmap_offset(tick_index: i32, tick_spacing: u16) -> Result<u32, String> {
165        if !TickQuery::check_is_valid_start_index(tick_index, tick_spacing) {
166            return Err("No enough initialized tickArray".to_string());
167        }
168        Self::check_extension_boundary(tick_index, tick_spacing)?;
169
170        let ticks_in_one_bitmap = TickArrayBitmap::max_tick_in_tick_array_bitmap(tick_spacing);
171        let tick_index_abs = tick_index.abs() as u32;
172        // floor
173        let mut offset = (tick_index_abs / ticks_in_one_bitmap) - 1;
174        if tick_index < 0 && tick_index_abs % ticks_in_one_bitmap == 0 {
175            offset -= 1;
176        }
177        Ok(offset)
178    }
179    pub fn get_bitmap(tick_index: i32, tick_spacing: u16, tick_array_bitmap_extension: &TickArrayBitmapExtension) -> Result<GetBitmapResult, String> {
180        let offset = Self::get_bitmap_offset(tick_index, tick_spacing)?;
181        let tick_array_bitmap = if tick_index < 0 {
182            tick_array_bitmap_extension.negative_tick_array_bitmap[offset as usize]
183        } else {
184            tick_array_bitmap_extension.positive_tick_array_bitmap[offset as usize]
185        };
186        Ok(GetBitmapResult { offset, tick_array_bitmap })
187    }
188    pub fn check_tick_array_is_init(tick_array_start_index: i32, tick_spacing: u16, tick_array_bitmap_extension: &TickArrayBitmapExtension) -> Result<CheckTickArrayIsInitResult, String> {
189        let tick_array_bitmap = Self::get_bitmap(tick_array_start_index, tick_spacing, tick_array_bitmap_extension)?;
190        let tick_array_offset_in_bitmap = Self::tick_array_offset_in_bitmap(tick_array_start_index, tick_spacing);
191        let is_initialized = TickUtils::merge_tick_array_bitmap(&tick_array_bitmap.tick_array_bitmap).get_bit(tick_array_offset_in_bitmap);
192        Ok(CheckTickArrayIsInitResult { is_initialized, start_index: tick_array_start_index })
193    }
194    pub fn tick_array_offset_in_bitmap(tick_array_start_index: i32, tick_spacing: u16) -> u32 {
195        let m = tick_array_start_index.abs() as u32 % TickArrayBitmap::max_tick_in_tick_array_bitmap(tick_spacing);
196        // floor
197        let mut tick_array_offset_in_bitmap = m / TickQuery::tick_count(tick_spacing);
198        if tick_array_start_index < 0 && m != 0 {
199            tick_array_offset_in_bitmap = TICK_ARRAY_BITMAP_SIZE as u32 - tick_array_offset_in_bitmap;
200        }
201        tick_array_offset_in_bitmap
202    }
203}