Skip to main content
Эта страница переведена с помощью ИИ. За эталон принимается английская версия.Открыть английскую версию →
На этой странице описаны структура и назначение каждого аккаунта. Семена (seeds) канонические и перечислены в reference/program-addresses. Пул CLMM требует больше аккаунтов, чем пул CPMM, потому что ликвидность хранится разреженно по диапазону тиков; понимание этой разреженности — основная часть этой страницы.

Инвентарь аккаунтов

Активный пул CLMM описывается следующими семействами аккаунтов. Все принадлежат программе CLMM, кроме двух минтов и их хранилищ.

PoolState

Текущее состояние пула, читается при каждом своппе и каждом изменении позиции.
Поля, с которыми вы будете работать:
  • sqrt_price_x64 и tick_current — состояние цены пула. Обновляются вместе при каждом своппе. tick_current — это пол от log_{1.0001}(price).
  • liquidity — это активная ликвидность — сумма значений L для всех позиций, диапазон которых содержит tick_current. Изменяется каждый раз, когда своп пересекает тик, и каждый раз, когда позиция открывается/закрывается/изменяется.
  • fee_growth_global_{0,1}_x64 — кумулятивные комиссии, заработанные на единицу ликвидности по всей истории пула. Позиции читают это, чтобы вычислить, что им причитается.
  • tick_spacing привязан к AmmConfig при инициализации и никогда не меняется. Он определяет, какие индексы тиков могут быть конечными точками позиции.
  • tick_array_bitmap — это встроенная битовая карта, охватывающая часто используемый диапазон тиков вокруг спотовой цены. Для пулов, позиции которых простираются далеко, отслеживание переполнения находится в отдельном TickArrayBitmapExtension.
  • fee_on фиксируется при создании пула. 0 (FromInput) воспроизводит классическое поведение Uniswap-V3. 1 и 2 направляют комиссию своппа на одну сторону книги — см. products/clmm/fees для анализа компромиссов.
  • seed_index — это [0, 0] для каждого пула, созданного через CreatePool / CreateCustomizablePool (один канонический пул на пару). Ненулевое значение означает, что пул был создан через CreateCustomizablePoolV2 (до 2026-10-22 назывался CreatePermissionedPool) и индекс является частью семян PDA пула, позволяя нескольким пулам сосуществовать для одной и той же (config, mint0, mint1). Чтобы повторно вывести адрес такого пула, вы должны знать его seed_index.
  • dynamic_fee_info содержит состояние волатильности для надбавки динамической комиссии. Когда включено, каждый своп пересчитывает dynamic_fee_component поверх AmmConfig.trade_fee_rate. Структура документирована под DynamicFeeInfo ниже; пулы без динамической комиссии оставляют всю структуру нулевой.

AmmConfig

Начиная с обновления 2026-09, CreateAmmConfig записывает жестко закодированный protocol_fee_owner::ID программы в owner и fund_fee_owner::ID в fund_owner. До этого он копировал ключ подписавшего администратора в оба. Существующие конфиги сохраняют то, на что они были созданы или ротированы, поэтому читайте оба поля с аккаунта. Параметры UpdateAmmConfig 3 / 4 по-прежнему их ротируют. Адреса находятся в reference/program-addresses. Типичный опубликованный набор уровней комиссий CLMM (подтвердите против GET https://api-v3.raydium.io/main/clmm-config): protocol_fee_rate и fund_fee_rate — это доли торговой комиссии; то же соглашение, что и в CPMM. См. products/clmm/fees.

TickArrayState

CLMM не хранит отдельную запись для каждого тика. Это потребовало бы миллиарды аккаунтов. Вместо этого программа группирует TICK_ARRAY_SIZE соседних инициализированных или неинициализированных тиков (обычно 60 или 88 в зависимости от версии программы) в TickArrayState, который создаётся лениво при первом использовании.
Четыре поля лимитных ордеров равны нулю для любого тика, который никогда не использовался для лимитного ордера. Когда ордера открываются на тике, программа отслеживает их как последовательность когорт:
  • order_phase — это идентификатор когорты. Он увеличивается каждый раз, когда когорта переходит из состояния «полностью неисполненная» в «частично исполненная».
  • orders_amount — это общее количество входного токена текущей (новейшей) когорты.
  • part_filled_orders_remaining отслеживает предыдущую когорту, которая в настоящий момент исполняется текущими свопами.
  • unfilled_ratio_x64 — это множитель Q64.64, привязанный к когорте: когда своп исполняет X% когорты, коэффициент умножается на (1 − X). Каждый открытый ордер хранит свой снимок (order_phase, unfilled_ratio_x64) в момент открытия, поэтому математика расчёта сводится к сравнению снимков.
Правила:
  • Конечный тик позиции t должен удовлетворять условию t % tick_spacing == 0. Программа отклоняет позиции с неправильным интервалом.
  • Массив тика расположен по адресу floor(t / (TICK_ARRAY_SIZE * tick_spacing)) * (TICK_ARRAY_SIZE * tick_spacing).
  • Массив тиков инициализируется лениво: первая позиция или своп, которые касаются неинициализированного массива, создают его и оплачивают ренту.
  • Массив тиков никогда не закрывается программой. После выделения он сохраняется на протяжении всего времени существования пула, даже после того, как каждый тик внутри него вернётся к liquidity_gross == 0. Последующие позиции и свопы повторно используют существующий аккаунт без дополнительной ренты. Нет пути очистки массивов тиков, управляемого ClosePosition.

TickArrayBitmapExtension

PoolState.tick_array_bitmap (встроенный) охватывает диапазон «близко к спот» — ±512 массива тиков (TICK_ARRAY_BITMAP_SIZE = 512; поле [u64; 16] содержит 1024 бита, поровну разделённые между отрицательными и положительными начальными индексами, поэтому индексы массива −512 … +511). За пределами этого диапазона (для экстремальных значений тиков) программа поддерживает аккаунт расширения:
Если диапазон вашей позиции «нормальный», вы никогда не думаете об аккаунте расширения. Позиции полного диапазона (например, (MIN_TICK, MAX_TICK)) требуют его; SDK разрешает это за вас.

Позиции

Позиция CLMM — это набор из трёх аккаунтов плюс минт:

Минт NFT позиции

Минт SPL Token или Token-2022 с предложением 1. NFT позиции в кошельке владельца — это ATA, содержащий этот единственный токен. Программа привязывает авторизацию к текущему держателю баланса ATA NFT, а не к Pubkey, хранящемуся в состоянии. Новые минты NFT позиции устанавливают pool_state как свой freeze authority перед минтингом единственного токена и удалением mint authority. Установка freeze authority не замораживает сам аккаунт NFT. Аккаунт остаётся разморозенным и передаваемым, если только не выполнены оба условия: вызывающий использует OpenPositionV2 или OpenPositionWithToken22Nft, и freeze authority хотя бы одного базового минта хранилища появляется в списке ограниченных издателей CLMM. Только тогда CLMM замораживает аккаунт NFT после минтинга. Это не изменяет ни один байт PersonalPositionState или PoolState.

PersonalPositionState

Один на открытую позицию. Привязан к минту NFT.

ProtocolPositionState (устарело)

Более старые версии CLMM хранили агрегированную бухгалтерию по (pool, tick_lower, tick_upper) в PDA ProtocolPositionState. Новые версии больше не создают и не читают этот аккаунт. Слот по-прежнему появляется в списках аккаунтов OpenPosition / IncreaseLiquidity / DecreaseLiquidity как UncheckedAccount для совместимости ABI, но программа не записывает в него. Существующие аккаунты в цепи — это рудименты; администратор может вызвать CloseProtocolPosition для возврата ренты.Агрегированная бухгалтерия диапазона теперь выводится непосредственно из двух конечных тиков (liquidity_gross, liquidity_net и полей fee_growth_outside_* / reward_growths_outside_x64 для каждого тика) в TickArrayState. Формула fee-growth-inside fee_growth_inside = global − outside_lower − outside_upper продолжает работать без аккаунта агрегированной позиции.

Observation

Буфер наблюдений CLMM хранит кумулятивный тик, а не кумулятивную цену. Внешние потребители вычисляют геометрическую среднюю цену за интервал из (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0), а затем price = 1.0001 ** tick. См. algorithms/clmm-math.

DynamicFeeConfig и DynamicFeeInfo

Параметры динамической комиссии находятся в двух местах. Переиспользуемый шаблон — DynamicFeeConfig — управляется администратором и используется пулами, которые согласились. Состояние выполнения для каждого пула — DynamicFeeInfo — встроено в PoolState и обновляется каждым свопом.

DynamicFeeConfig

Seed PDA: ["dynamic_fee_config", index.to_be_bytes()]. Создаётся через create_dynamic_fee_config (с ограничением администратора) и изменяется через update_dynamic_fee_config. Пул, созданный с enable_dynamic_fee = true, снимает пять параметров калибровки конфига (filter_period, decay_period, reduction_factor, dynamic_fee_control, max_volatility_accumulator) в свой собственный DynamicFeeInfo при создании; более поздние изменения DynamicFeeConfig не влияют ретроактивно на существующие пулы.

DynamicFeeInfo (встроено в PoolState)

Нижние четыре поля — это состояние; верхние пять — это калибровка, скопированная из DynamicFeeConfig. Математика комиссии и правила затухания задокументированы в products/clmm/math и products/clmm/fees. Константы, используемые формулой:

LimitOrderState

Один аккаунт на открытый лимитный ордер.
Жизненный цикл:
  1. Open — пользователь вызывает open_limit_order, депонирует total_amount входного токена, ордер привязывается к когорте TickState.
  2. (опционально) Increase / Decrease — increase_limit_order добавляет к total_amount; decrease_limit_order возвращает неисполненные токены (и любой исполненный выход до этого момента).
  3. Settle — когда когорта полностью или частично исполнена, владелец или оперативный хранитель вызывает settle_limit_order для отправки выходных токенов на ATA владельца.
  4. Close — как только unfilled_amount == 0, аккаунт можно закрыть. Рента всегда возвращается owner.
Seed PDA: [owner.as_ref(), limit_order_nonce.key().as_ref(), limit_order_nonce.order_nonce.to_be_bytes().as_ref()]. Таким образом, PDA ордера уникален для каждого (owner, nonce_index, order_nonce).

LimitOrderNonce

Счётчик для каждой пары (wallet, nonce_index), позволяющий одному пользователю запускать несколько параллельных конвейеров лимитных ордеров без коллизий на PDA.
Seed PDA: [user_wallet.as_ref(), &[nonce_index]]. Большинство клиентов используют nonce_index = 0 и позволяют order_nonce нести кардинальность.

Permission (снято с производства)

Аккаунт Permission был удалён в обновлении от 2026-10-22. Раньше он ограничивал доступ к CreatePermissionedPool, которая теперь является бесправовой CreateCustomizablePoolV2. Программа больше не читает и не закрывает эти аккаунты, а инструкции CreatePermissionPda / ClosePermissionPda удалены. PDA, созданные до обновления (["permission", authority], 280 байт), остаются в цепи как инертные данные; игнорируйте их при сканировании аккаунтов, принадлежащих CLMM.

Получение ключевых аккаунтов

Точные строки seed всегда следует проверять по IDL в цепи и reference/program-addresses.

Краткий справочник жизненного цикла

Аккаунты TickArrayState никогда не закрываются программой — они сохраняются на протяжении всей жизни пула. После инициализации tick array он остаётся в цепи даже когда каждый tick внутри него возвращается к liquidity_gross == 0. Повторное использование существующего tick array бесплатно; только первая позиция, касающаяся никогда не инициализированного массива, платит его rent.

Что читать где

Источники: