Documentation Index
Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
Use this file to discover all available pages before exploring further.
Эта страница переведена с помощью ИИ. За эталон принимается английская версия.Открыть английскую версию →
Зачем нужны тики
Ликвидность в CLMM сосредоточена в диапазонах цен. Чтобы работать с диапазонами на цепи, цены квантуются в целые числа — тики, где каждый тик представляет постоянный множитель от предыдущего: Один тик соответствует движению цены на 0,01%, или примерно 1 базисный пункт. Соответствие выглядит так:Индекс тика i | Множитель цены |
|---|---|
0 | 1.0000 |
100 | 1.0100 (≈ +1.00%) |
-100 | 0.9900 (≈ −0.99%) |
10000 | 2.7181 (≈ e) |
MAX_TICK = 443636 | ≈ 1.84e19 |
MIN_TICK = -443636 | ≈ 5.42e-20 |
MIN_TICK и MAX_TICK выбраны так, чтобы sqrt_price_x64 уместился в u128 на обоих концах. Каждый пул требует, чтобы tick_lower >= MIN_TICK и tick_upper <= MAX_TICK. На практике веб-интерфейс ограничивает диапазон намного сильнее, чтобы предотвратить блокировку ликвидности в недостижимых тиках.
Интервал тиков
AmmConfig пула устанавливает интервал тиков — единственные тики, которые позиция может использовать в качестве граничных точек. Если tick_spacing = 60, допустимы только тики …, −120, −60, 0, 60, 120, …. Попытка открыть позицию с граничной точкой 31 откатывается с ошибкой InvalidTickIndex.
Типичные публичные интервалы:
| Уровень комиссии | trade_fee_rate | Интервал тиков | Минимальный шаг цены на позицию |
|---|---|---|---|
| 0.01% | 100 | 1 | 0.01% |
| 0.05% | 500 | 10 | 0.10% |
| 0.25% | 2500 | 60 | 0.60% |
| 1.00% | 10000 | 120 | 1.21% |
Массивы тиков
Пул не хранит состояние каждого тика в отдельных аккаунтах. Вместо этогоTICK_ARRAY_SIZE смежных тиков (60 в текущей Raydium CLMM) упакованы в один TickArrayState. Первый тик массива — это его start_tick_index, и он охватывает ровно TICK_ARRAY_SIZE * tick_spacing целых единиц тика.
Для tick_spacing = 60 и TICK_ARRAY_SIZE = 60:
- Каждый массив тиков охватывает
60 × 60 = 3600целых тиков. start_tick_index— это кратное 3600:…, -7200, -3600, 0, 3600, 7200, ….
t = 2040 при tick_spacing = 60 находится в массиве тиков с start_tick_index = 0. Граничная точка позиции t = 4200 находится в массиве с start_tick_index = 3600.
Когда создаётся массив
Массив тиков создаётся лениво: первая позиция, ссылающаяся на какой-либо тик внутри него, инициализирует массив и платит за хранение. Свопы не инициализируют массивы тиков — они пропускают неинициализированные массивы, используя битовую карту. Поток открытия позиции в SDK проверяет выбранный диапазон, вычисляет список массивов тиков, которые он затрагивает, и добавляет инструкцииinit_tick_array в ту же транзакцию что и OpenPosition, если какие-то из них отсутствуют.
Массивы тиков не закрываются
После инициализации массив тиков сохраняется на весь срок существования пула. Программа не предоставляет способ закрыть массив тиков, даже когдаinitialized_tick_count вернётся к нулю. Плата за хранение массивов тиков не возвращается; плата, уплаченная первой позицией, которая коснулась массива, остаётся заблокирована в этом аккаунте навсегда. Это намеренный компромисс: повторное использование существующего массива тиков бесплатно для каждой последующей позиции, поэтому активно торгуемый пул платит за хранение только один раз за слот (pool, start_tick_index), независимо от текучести.
Битовая карта
Поиск “следующего инициализированного тика слева/справа от текущего тика” должен быть быстрым — один свап может пересечь много тиков. Пул хранит 1-бит-на-массив-тиков битовую карту прямо вPoolState для диапазона ±1,024 массивов вокруг тика 0. За пределами этого диапазона (полнодиапазонные позиции, экзотические конфигурации) TickArrayBitmapExtension предоставляет переполнение.
Свопу прогулять битовую карту: lowest_set_bit_above(tick_current_array_index) даёт следующий массив с инициализированным тиком на стороне, в которую свопу движется. Внутри этого массива похожее сканирование битов находит следующий инициализированный тик.
liquidity_gross и liquidity_net
Каждый инициализированный тик хранит две величины ликвидности:
liquidity_gross— суммаLнад всеми позициями, ссылающимися на этот тик как граничную точку. Когдаliquidity_grossдостигает нуля, тик становится неинициализированным и может быть удалён из битовой карты.liquidity_net— знаковое изменение уровня пулаliquidityпри пересечении этого тика в направлении вверх (слева направо в пространстве тиков). Если этот тик — нижняя граница позиции размеромL, он добавляет+L; если это верхняя граница этой позиции, он добавляет−L.
- Позиция A:
tick_lower = -120,tick_upper = 0, ликвидностьL_A = 100. - Позиция B:
tick_lower = -60,tick_upper = 60, ликвидностьL_B = 50.
| Тик | Затронут | liquidity_gross | liquidity_net |
|---|---|---|---|
-120 | A lower | 100 | +100 |
-60 | B lower | 50 | +50 |
0 | A upper | 100 | −100 |
60 | B upper | 50 | −50 |
liquidity при разных значениях tick_current:
tick_current = -180:liquidity = 0(до любой позиции)tick_current = -90:liquidity = 100(только внутри A)tick_current = -30:liquidity = 150(внутри A и B)tick_current = 30:liquidity = 50(только внутри B)tick_current = 90:liquidity = 0(после обеих)
liquidity_net (возможно, отрицательное значение) к PoolState.liquidity. Это точно механизм Uniswap v3.
Позиции как NFT
Позиция Raydium CLMM — это NFT. Открытие позиции создаёт новый минт с предложением 1 в кошельке вызывающего, и авторитет минта — программа CLMM. Программа привязывает владение позицией к тому, кто держит баланс в ATA этого минта во время CPI. Следствия:- Позиции передаваются. Кошелёк может продать или раздать позицию, передав NFT. Новый владелец может вызвать
CollectRewards,IncreaseLiquidityи т. д. - Позиции доступны вне CLMM. Маркетплейсы и кошельки отображают позиции как другие NFT. SDK устанавливает разумные
name/symbolв метаданные минта. - PDA позиции выводится из минта NFT. Вы можете найти
PersonalPositionStateбез знания текущего владельца.
Позиции Token-2022
Новые пулы CLMM могут создавать позиции в Token-2022 вместо классического SPL Token. Программа предоставляет две параллельные инструкции открытия —OpenPosition и OpenPositionWithToken22Nft — с идентичной семантикой, кроме того, какой программа токена владеет минтом NFT. Совместимость кошельков и маркетплейсов различается; интерфейс Raydium отслеживает оба варианта.
Правила допустимого диапазона
ПриOpenPosition программа проверяет:
tick_lower < tick_upper.tick_lower % tick_spacing == 0иtick_upper % tick_spacing == 0.MIN_TICK <= tick_lowerиtick_upper <= MAX_TICK.- Вызывающий предоставил массивы тиков, содержащие
tick_lowerиtick_upper— либо уже инициализированные, либо черезinit_tick_arrayв той же транзакции. - Аккаунт расширения битовой карты, если эта позиция выходит в диапазон расширения.
InvalidTickIndex, NotApproved или InsufficientLiquidity в зависимости от нарушенного ограничения. См. reference/error-codes.
”В диапазоне” против “вне диапазона”
Позиция находится в диапазоне, когдаtick_lower <= tick_current < tick_upper. Только позиции в диапазоне способствуют PoolState.liquidity и, следовательно, только они получают комиссии за свопы.
Позиция вне диапазона:
- Держит 100% одного токена (того, мимо которого цена уже прошла). Конкретно, если
tick_current < tick_lower, позиция держит только token1 (она уже была “продана” движением цены); еслиtick_current >= tick_upper, она держит только token0. - Не получает комиссии за свопы.
- Продолжает получать вознаграждения, если потоки вознаграждения пула выделяют средства на ликвидность вне диапазона — но поведение Raydium по умолчанию “выделять только в диапазон”, соответствуя соглашению Uniswap v3. См.
products/clmm/fees.
Типичные ошибки интеграции
- Граничные точки не кратные интервалу. Код, который вычисляет тик из целевой цены, должен округлить его к кратному
tick_spacingперед передачей вOpenPosition. Вспомогательные функции SDK (TickUtils.getTickWithPriceAndTickspacing) это делают; самодельная математика часто не делает. - Отсутствующие массивы тиков. Открытие широкой позиции может потребовать инициализации нескольких массивов тиков; забывчивость передать их как записываемые аккаунты откатывает инструкцию. SDK-метод
openPositionFromBaseвозвращает список для вас. - Устаревший тик после свопа.
tick_currentможет пересечь много тиков в одном свопе. Если ваш интерфейс показывает “текущий тик” из одного вызова RPC, а потом открывает позицию в более позднем, относительное положение относительно активной цены может отличаться на десятки тиков. Переполучите данные перед подписанием. - NFT позиций с дополнительными метаданными. Если вы создаёте кошелёк, распознающий позиции Raydium, определяйте их по авторитету минта (= PDA программы CLMM), а не по жёстко закодированному полю метаданных.
Что дальше
- Math — пошаговое объяснение свопа и вывод прироста комиссии, в котором участвуют граница тиков.
- Accounts — макеты
TickArrayStateиPositionState. - Fees and rewards — как нахождение в диапазоне контролирует накопление комиссий.
algorithms/clmm-math— общий вывод формул концентрированной ликвидности.
raydium-io/raydium-clmm— модулиtick_array,tick,position- “Uniswap v3 Core” whitepaper, §6 (ticks), §7 (fee growth)


