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 — это встроенная битовая карта, охватывающая диапазон “близко к спот” — ±1024 массива тиков. За пределами этого диапазона (для экстремальных значений тиков) программа ведет отдельный аккаунт расширения.
  • fee_on фиксируется при создании пула. 0 (FromInput) воспроизводит классическое поведение Uniswap-V3. 1 и 2 маршрутизируют комиссию своп на одну сторону книги — см. products/clmm/fees для компромиссов.
  • seed_index — это [0, 0] для каждого пула, созданного через CreatePool / CreateCustomizablePool (один канонический пул на пару). Ненулевое значение означает, что пул был создан через CreatePermissionedPool, и индекс является частью семян PDA пула, позволяя нескольким пулам сосуществовать для одной и той же (config, mint0, mint1). Чтобы повторно вывести адрес такого пула, вы должны знать его seed_index.
  • dynamic_fee_info содержит состояние волатильности для надбавки динамической комиссии. Когда включено, каждый своп пересчитывает dynamic_fee_component поверх AmmConfig.trade_fee_rate. Структура документирована под DynamicFeeInfo ниже; пулы без динамической комиссии оставляют всю структуру нулевой.

AmmConfig

Типичный опубликованный набор уровней комиссий 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 — это id когорты. Он увеличивается каждый раз, когда когорта переходит из “полностью не заполнена” в “частично заполнена”.
  • 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 (встроенная) охватывает диапазон “близко к спот” — ±1024 массива тиков. За пределами этого диапазона (для экстремальных значений тиков) программа ведет аккаунт расширения:
Если диапазон вашей позиции “нормальный”, вы никогда не думаете об аккаунте расширения. Позиции полного диапазона (например, (MIN_TICK, MAX_TICK)) требуют его; SDK разрешает это для вас.

Позиции

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

Position NFT mint

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

PersonalPositionState

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

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

PDA seed: ["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 / Decreaseincrease_limit_order добавляет к total_amount; decrease_limit_order возвращает незаполненные токены (и любой выполненный вывод до этого момента).
  3. Settle — когда когорта полностью или частично заполнена, владелец или оперативный хранитель вызывает settle_limit_order, чтобы отправить выходные токены на ATA владельца.
  4. Close — как только unfilled_amount == 0, аккаунт закрываем. Рента всегда возвращается owner.
PDA seed: [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.
PDA seed: [user_wallet.as_ref(), &[nonce_index]]. Большинство клиентов используют nonce_index = 0 и позволяют order_nonce нести мощность.

Permission

Аккаунт возможности, чье существование является грантом: если PDA Permission выведена для данного authority, этот authority может вызвать CreatePermissionedPool. Он не хранит ничего, кроме authority, для которого он был создан.
PDA seed: ["permission", authority.as_ref()]. Создается администратором через CreatePermissionPda и удаляется через ClosePermissionPda (рента возвращается вызывающему). Обе администраторские инструкции принимают либо программный admin, либо выделенный ключ permission_pda_admin. Закрытие PDA отзывает грант — authority больше не может создавать дополнительные пулы, но пулы, которые он уже создал, не затронуты.

Вывод ключевых аккаунтов

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

Краткая справка по жизненному циклу

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

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

Источники: