Эта страница переведена с помощью ИИ. За эталон принимается английская версия.Открыть английскую версию →
Эта страница дополняет
products/clmm/accounts (что такое аккаунты) и products/clmm/math (что такая математика). Она является авторитетным источником для аргументов и порядка аккаунтов; конкретные макеты байтов берутся из IDL.Обновление программы 2026-09 перестроило CLMM на Anchor 1.0.2 / Solana 3.1.10, добавило административную инструкцию CollectExcessLamports и изменило то, что CreateAmmConfig записывает в owner / fund_owner. Ни одна пользовательская инструкция не изменила свои аккаунты, аргументы или математику. См. запись журнала изменений от 2026-09-30.Инвентарь инструкций
Нет инструкции инициализации массива тиков, и она не требуется. Массив тиков создаётся внутри
OpenPosition* / IncreaseLiquidity* через TickArrayState::get_or_create_tick_array, оплачивается
payer. Передайте (возможно, ещё неинициализированный, принадлежащий системе) PDA массива тиков как
tick_array_lower / tick_array_upper и программа выделит его, если он отсутствует.
OpenLimitOrder делает то же самое для своего единственного tick_array.CreateAmmConfig, UpdateAmmConfig, UpdatePoolStatus, CreateOperationAccount, UpdateOperationAccount, CloseProtocolPosition) защищены жёстко закодированным публичным ключом admin программы. CreatePermissionPda / ClosePermissionPda принимают либо публичный ключ admin либо выделенный ключ permission_pda_admin; CreateSupportMintAssociated / CloseSupportMintAssociated принимают либо публичный ключ admin либо выделенный ключ владельца support-mint; CollectExcessLamports принимает либо публичный ключ admin либо выделенный кошелёк для сбора lamports. Административные инструкции потока вознаграждения (TransferRewardOwner, CollectRemainingRewards) защищены финансистом вознаграждения, а не администратором программы.
Суффикс V2 означает “поддерживает Token-2022 на хранилищах / NFT, требует слота расширения bitmap”. SDK выбирает V2 по умолчанию для новых пулов.
CreatePool
Аргументы
Предусловия
token_mint_0 < token_mint_1по порядку байтов.amm_config.disable_create_pool == false.- Mints не отклоняются списком разрешений расширения Token-2022.
pool_state.sqrt_price_x64 = sqrt_price_x64,tick_current = floor(log_{1.0001}(price)).pool_state.liquidity = 0(нет позиций).pool_state.fee_on = FromInput(устаревший по умолчанию).pool_state.dynamic_fee_infoобнулен (динамическая комиссия отключена).
CreateCustomizablePool
Рекомендуется для новых пулов. Тот же эффект, что и CreatePool, плюс режим сбора комиссии для каждого пула и опциональное включение динамической комиссии.
Аргументы
CreatePool. dynamic_fee_config — не объявленный аккаунт.
Предусловия — те же, что и CreatePool. Если enable_dynamic_fee = false, remaining_account не требуется и любые переданные сканируются только на предмет записей support-mint.
Постусловия
pool_state.fee_onустановлен на выбранный вариантCollectFeeOn.- Если динамическая комиссия была включена:
pool_state.dynamic_fee_infoинициализирован из предоставленногоDynamicFeeConfig(пять параметров калибровки скопированы; поля состояния обнулены). - В противном случае:
pool_state.dynamic_fee_infoобнулен (= динамическая комиссия неактивна навсегда для этого пула).
fee_on и бит включения динамической комиссии устанавливаются только при создании пула. Нет встроенного обновления — пулы, созданные через устаревший CreatePool, не могут ретроактивно получить динамическую комиссию или одностороннюю комиссию. Новые развёртывания должны по умолчанию использовать эту инструкцию.
CreatePermissionedPool
Как CreatePool, так и CreateCustomizablePool выводят PDA пула из ["pool", amm_config, token_mint_0, token_mint_1], поэтому существует ровно один канонический адрес пула на тройку (config, mint0, mint1) — второй init с теми же семенами не удаётся. CreatePermissionedPool снимает это ограничение, встраивая предоставленный клиентом seed_index: u16 в семена PDA пула, позволяя несколько пулов для одной пары и уровня комиссии — каждый по своему адресу. Поскольку произвольный адрес пула — это привилегированная возможность, плательщик должен иметь PDA Permission, который его авторизует.
Всё остальное о пуле идентично CreateCustomizablePool: он принимает те же CreateCustomizableParams и поддерживает одностороннюю комиссию и опциональное включение динамической комиссии.
Аргументы
CreateCustomizablePool, плюс в начале:
PDA
pool_state выводится из ["pool", amm_config, token_mint_0, token_mint_1, seed_index.to_le_bytes()].
Предусловия
seed_index != 0.seed_indexиз0зарезервирован для устаревших пулов и отклоняется здесь; компонент семени[0, 0]— это то, что заставляет адрес устаревшего пула свернуться в классическую четырёхсеменную форму.- PDA
permissionдляpayerсуществует (создана администратором черезCreatePermissionPda). - Те же правила mint / allow-list, что и
CreatePool.
- Новый
pool_stateсуществует по адресу, выведенному изseed_index, сpool_state.seed_index = seed_index. - Все остальные постусловия совпадают с
CreateCustomizablePool(режим комиссии, опциональная динамическая комиссия).
Эта инструкция не расширяет общий доступ к созданию пула — бесправное создание продолжается через
CreatePool / CreateCustomizablePool, которые остаются одним пулом на пару. CreatePermissionedPool существует для конкретного случая, когда внесённый в белый список оператор нуждается в нескольких пулах для одной пары (например, различающихся начальными ценами или когортами запуска) и имеет PDA Permission, предоставленный администратором.OpenPositionV2 / OpenPositionWithToken22Nft
Создать новую позицию внутри существующего пула.
Аргументы
OpenPositionV2 объявляет 22; OpenPositionWithToken22Nft объявляет 20.
OpenPositionV2, по порядку:
OpenPositionWithToken22Nft — это тот же список с удалёнными metadata_account (5) и metadata_program (19) — он записывает метаданные позиции через расширение метаданных Token-2022 на mint NFT вместо этого — давая 20 аккаунтов. Его position_nft_mint — это голый Signer и position_nft_account — UncheckedAccount.
Математика — см. products/clmm/math. Учитывая base_flag, программа разрешает либо liquidity, либо (amount_0_max, amount_1_max) в фактическое L и фактические потреблённые суммы токенов.
Предусловия
tick_lower < tick_upper, оба кратныpool.tick_spacing, в пределах[MIN_TICK, MAX_TICK].- Два PDA массива тиков переданы. Они не должны уже существовать —
get_or_create_tick_arrayвыделяет отсутствующий за счётpayerвнутри этой инструкции. Нет отдельной инструкции инициализации массива тиков. - Пользователь имеет по крайней мере
amount_0_maxиamount_1_maxв исходных ATA.
personal_positionсуществует,liquidityустановлена,fee_growth_inside_lastснята.- Записи массива тиков в
tick_lowerиtick_upperобновлены (liquidity_gross += L,liquidity_net ± L, снимки роста комиссии поддерживаются). pool_state.liquidity += L, если позиция в диапазоне (tick_lower ≤ tick_current < tick_upper).- Mint NFT позиции записывает
pool_stateкак полномочие заморозки. Полномочие mint удаляется после выпуска единственного NFT. Запись полномочия заморозки не изменяет состояние аккаунта токена NFT. - Аккаунт токена NFT остаётся разморозенным, если инструкция — это
OpenPositionV2илиOpenPositionWithToken22Nftи полномочие заморозки любого mint хранилища совпадает со списком ограниченных издателей CLMM. Только этот совпадающий путь V2 замораживает аккаунт.OpenPositionV1 не замораживает.
TickInvalidOrder (tick_lower >= tick_upper), TickAndSpacingNotMatch (конечная точка не кратна tick_spacing), InvalidTickIndex (вне [MIN_TICK, MAX_TICK]), MissingTickArrayBitmapExtensionAccount (диапазон вне встроенного bitmap и расширение не добавлено), NotApproved (pool_state.status блокирует открытие), ZeroAmountSpecified.
Замораживание позиции не добавляет объявленные аккаунты инструкции или аргументы. Клиенты могут открывать эти позиции с существующими макетами V2. Поведение выбирается в цепи из
vault_0_mint и vault_1_mint.IncreaseLiquidityV2
Добавить ликвидность к уже открытой позиции.
Аргументы
OpenPosition: нет rent, нет system_program, нет associated_token_program и нет аккаунта метаданных.
Добавьте PDA
TickArrayBitmapExtension как remaining_accounts[0], когда диапазон позиции находится вне встроенного bitmap.
Эффект
- Передаёт
amount_0_actual/amount_1_actualиз пользователя → хранилища. - Увеличивает
personal_position.liquidityиpool_state.liquidity(если в диапазоне), и соответственноliquidity_gross/liquidity_netконечного тика. - Собирает комиссии и вознаграждения, причитающиеся с последнего касания, и кредитует их на
token_fees_owed_{0,1}/reward_amount_owed. Они выплачиваются только наDecreaseLiquidity/DecreaseLiquidityV2, а не на увеличение — нет отдельной инструкции сбора.
DecreaseLiquidityV2
Удалить ликвидность из позиции.
Аргументы
IncreaseLiquidityV2: personal_position и pool_state поменяны местами, хранилища идут перед массивами тиков, аккаунты на стороне пользователя названы recipient_token_account_*, и есть дополнительный memo_program.
Оставшиеся аккаунты — три на активное собираемое вознаграждение, в порядке
reward_token_vault(W), recipient_token_account(W), reward_vault_mint. Добавьте PDA TickArrayBitmapExtension в начало, когда диапазон позиции находится вне встроенного bitmap.
Это также единственный способ собрать комиссии и вознаграждения. Чтобы собрать без изменения
позиции, вызовите с
liquidity = 0, amount_0_min = 0, amount_1_min = 0.- Вычисляет
(amount_0, amount_1)для удалённогоLс учётом текущегоsqrt_price_x64. - Урегулирует комиссии/вознаграждения, накопленные с последнего касания, то же, что и
IncreaseLiquidity. - Передаёт
amount_0 + fees_owed_0иamount_1 + fees_owed_1из хранилищ пользователю. - Уменьшает счётчики ликвидности; если новое
personal_position.liquidity == 0, позиция имеет право наClosePosition.
amount_0_min и amount_1_min — это минимумы, которые пользователь принимает за вычетом комиссий передачи Token-2022 на выходной стороне.
ClosePosition
Сжечь NFT позиции и закрыть PersonalPositionState.
Объявленные аккаунты
Оставшиеся аккаунты
- Разморозенный NFT: ничего не требуется; дополнительный аккаунт пула безвреден, потому что обработчик его не читает.
- Замороженный NFT: добавьте
personal_position.pool_idкак первый оставшийся аккаунт. Программа загружает его какPoolStateи использует его семена PDA для подписания разморозки.
personal_position.liquidity == 0.tokens_fees_owed_{0,1} == 0.- Все счётчики вознаграждения
reward_amount_owed == 0.
- Если аккаунт токена NFT заморожен, проверяет, что первый оставшийся аккаунт равен
personal_position.pool_id, затем размораживает его с помощью PDA пула. - Сжигает NFT.
- Закрывает аккаунт токена NFT и
personal_position, возвращая арендуnft_owner. Если NFT позиции использует Token-2022, он также закрывает mint NFT; классические mint SPL Token не могут быть закрыты и остаются с нулевым предложением.
AccountLack при закрытии замороженной позиции. Передача пула для каждого закрытия — это самая простая совместимая стратегия.
SwapV2
Пройти по кривой ликвидности; точный вход или точный выход в зависимости от is_base_input.
Аргументы
Вызывающие передают ранжированный список массивов тиков, охватывающих ожидаемый проход своп; программа использует столько, сколько ей нужно. SDK вычисляет этот список через
PoolUtils.computeAmountOutFormat или конечную точку котировки API.
Предусловия
pool_state.statusпозволяет своп.now >= open_time.sqrt_price_limit_x64находится на правильной сторонеsqrt_price_x64для направления.
TooLittleOutputReceived (точный вход проскальзывание), TooMuchInputPaid (точный выход проскальзывание), SqrtPriceLimitOverflow, NotEnoughTickArrayAccount, InvalidFirstTickArrayAccount, MissingTickArrayBitmapExtensionAccount, LiquidityInsufficient, NotApproved (бит своп установлен на pool_state.status). CLMM не имеет варианта ExceededSlippage — это имя CPMM — и нет TickArrayNotFound.
Что SwapV2 делает внутри, что вызывающие должны знать (после выпуска 2025):
- Надбавка динамической комиссии — если
pool.dynamic_fee_infoненулевой, программа обновляет накопитель волатильности, используя расстояние тика, пройденное с последнего своп (с правилами фильтра/затухания изproducts/clmm/fees), и добавляетdynamic_fee_componentповерхAmmConfig.trade_fee_rate. Общая комиссия ограничена 10% (MAX_FEE_RATE_NUMERATOR / 1_000_000). - Сопоставление лимитных ордеров — когда проход цены пересекает тик, содержащий открытые лимитные ордеры, программа сначала заполняет доступную ликвидность лимитного ордера на этом тике (FIFO по
order_phase), затем продолжает вдоль кривой ликвидности LP. Заполненные суммы обновляютtick.unfilled_ratio_x64иtick.part_filled_orders_remainingдля последующего урегулирования; сами ордеры остаются неиспользованными до тех пор, пока их владелец не вызоветSettleLimitOrder. - Маршрутизация комиссии в одну сторону — когда
pool.fee_on = Token0OnlyилиToken1Only, шаг своп всё ещё вычисляет тот же вход-выход торговли; комиссия затем маршрутизируется на настроенную сторону. Для направлений, где настроенная сторона комиссии — это выход, комиссия вычитается из выхода своп (пользователь получаетout − fee); для направлений, где это вход, поведение совпадает сFromInput. См.is_fee_on_input(zero_for_one)иis_fee_on_token0(zero_for_one)наPoolState.
Swap (V1) реализует ту же динамическую комиссию, маршрутизацию комиссии в одну сторону и сопоставление лимитных ордеров, что и SwapV2; единственная функция, которой ему не хватает, — это поддержка Token-2022 — оба хранилища должны быть классическим SPL Token. Пулы с любым mint Token-2022 должны быть обменены через SwapV2. Агрегатор и SDK уже предпочитают V2 для каждого участка CLMM, поэтому вызывающим не нужно ветвиться по типу mint.
OpenLimitOrder
Разместить ордер на продажу на конкретном тике. Ордер сидит в когорте FIFO для каждого тика и заполняется при пересечении цены.
Аргументы
Оставшиеся аккаунты —
[0] tick_array_bitmap_extension, требуется только при инициализации массива тиков, чей начальный индекс выходит за пределы встроенного bitmap пула. Передайте ничего в противном случае.
Изменение списка аккаунтов (выпуск 2026-07).
OpenLimitOrder теперь также принимает аккаунты выходной стороны — output_token_account, output_vault и output_vault_mint — в дополнение к входной стороне. Они используются только для валидации: программа отклоняет ордер, если входной или выходной аккаунт токена владельца заморожен. Это гарантирует, что заполнение может быть фактически урегулировано на выходной ATA владельца, что важно для allow-list / по умолчанию замороженных mint Token-2022 (например, разрешённые токены), где аккаунт может быть ещё не разморожен. Клиенты, построенные на основе старого одностороннего списка аккаунтов, должны добавить три выходных аккаунта.- Ни
input_token_account, ниoutput_token_accountне заморожены (иначеNotApproved). pool_state.statusпозволяет как своп (бит 4), так и операции лимитного ордера (бит 5) (иначеNotApproved).tick_index % pool.tick_spacing == 0и в пределах[MIN_TICK, MAX_TICK].tick_indexнаходится на правильной сторонеpool.tick_currentдля выбранного направления (продажа token0 → тик должен быть выше текущего, и наоборот). Продажа на уже пересечённом тике будет сопоставлена немедленно и отклонена.
limit_orderсуществует, снимаяtick.order_phaseиtick.unfilled_ratio_x64во время открытия.tick.orders_amount += amount(в текущей когорте).limit_order_nonce.order_nonce += 1.- Испущено
OpenLimitOrderEvent.
NotApproved (входной или выходной аккаунт токена заморожен, или пул имеет отключённый своп / лимитный ордер), ZeroAmountSpecified (amount == 0 после комиссии передачи входной стороны), InvalidLimitOrderAmount (сумма произведёт выход ниже 1 базовой единицы на этом тике, или переполнение u64), InvalidTickIndex (вне [MIN_TICK, MAX_TICK], или на неправильной стороне tick_current для выбранного направления), TickAndSpacingNotMatch (tick_index % pool.tick_spacing != 0), OrderPhaseSaturated.
IncreaseLimitOrder
Добавить к существующему открытому ордеру. Может быть вызвано только владельцем ордера.
Аргументы
system_program.
Предусловия
limit_order.owner == signer.- Ордер всё ещё находится в той же когорте (
tick.order_phase == limit_order.order_phase). Если когорта уже начала заполняться, ордер частично урегулирован — вызывающему следует сначала вызватьDecreaseLimitOrderилиSettleLimitOrderдля продвижения вперёд.
- Передаёт
amountиз ATA владельца вinput_vault. limit_order.total_amount += amount;tick.orders_amount += amount.
DecreaseLimitOrder
Уменьшить или полностью отменить открытый ордер. Выплачивает неисполненный остаток владельцу, плюс любой выход, уже урегулированный прошлыми частичными заполнениями.
Аргументы
Эффект
- Пересчитывает заполненную сумму ордера из
unfilled_ratio_x64когорты с момента открытия. - Отправляет заполненный выход на
output_token_account. - Отправляет
amountнеисполненного входа обратно наinput_token_account. - Обновляет
limit_orderсоответственно. Если новый неисполненный остаток равен нулю, программа закрывает аккаунт и возвращает арендуowner.
SettleLimitOrder
Отправить заполненные выходные токены владельцу без изменения неисполненного остатка ордера. Полезно, когда хранители auto_withdraw хотят капельно выплачивать долгосрочные частичные заполнения.
Вызывающий — либо владелец ордера, либо limit_order_admin программы (автономный оперативный горячий кошелёк, который запускает автоматизированный цикл хранителя). Хранитель не имеет других полномочий — он не может перемещать пользовательские средства вне отправки заполненного выхода на ATA владельца ордера.
Аккаунты
Эффект
- Вычисляет кумулятивный выход, причитающийся, используя
(limit_order.unfilled_ratio_x64, tick.unfilled_ratio_x64). - Передаёт дельту на
output_token_account. - Обновляет
limit_order.settled_output. - Не закрывает ордер; он всё ещё открыт против любого оставшегося входа.
CloseLimitOrder
Закрыть полностью исполненный аккаунт ордера. Аренда всегда возвращается limit_order.owner независимо от того, кто подписывает.
Вызывающий — либо owner, либо limit_order_admin.
Предусловия
- Ордер имеет нулевой неисполненный остаток (либо
amount == total_amountбыл заполнен и урегулирован, либо владелец ранее уменьшил ордер до нуля и забыл закрыть).
- Закрывает
limit_order; аренда отправляетсяlimit_order.owner.
CreateDynamicFeeConfig (администратор)
Создать переиспользуемый набор параметров под индексом u16.
Аргументы
Распространённые ошибки —
InvalidDynamicFeeConfigParams, если decay_period <= filter_period или любое поле с нулевым значением выходит за пределы.
UpdateDynamicFeeConfig (администратор)
Изменить существующий DynamicFeeConfig. Пулы, которые уже сделали снимок конфигурации во время создания, не обновляются ретроактивно; только вновь созданные пулы, которые ссылаются на эту конфигурацию, подберут новые значения.
Аргументы — те же пять полей калибровки, что и CreateDynamicFeeConfig (filter_period, decay_period, reduction_factor, dynamic_fee_control, max_volatility_accumulator); index зафиксирован при создании и не переходит здесь.
CollectProtocolFee / CollectFundFee
Очистить накопленные комиссии протокола/фонда из хранилищ пула получателю, обнулив соответствующие поля PoolState.protocol_fees_* / fund_fees_*. Это не макет CPMM — CLMM не имеет аккаунта authority, и поля получателя названы recipient_token_account_{0,1} вместо recipient_token_{0,1}_account.
Аргументы — amount_0_requested: u64, amount_1_requested: u64.

