Эта страница переведена с помощью ИИ. За эталон принимается английская версия.Открыть английскую версию →
Инвариант
CPMM поддерживает классический инвариант постоянного произведения на двух хранилищах: гдеx — баланс хранилища 0 после любых комиссий передачи Token-2022 при получении, аналогично для y. Каждый своп должен оставить k' ≥ k после учёта торговых комиссий, зачисленных LP (бакеты протокола, фонда и создателя не учитываются в k — они находятся в хранилище, но исключены из представления кривой, см. Комиссии на кривой ниже). Таким образом, k монотонно растёт со временем по мере накопления комиссий LP.
Цена LP-токенов определяется резервами пула, а не k:
Сжигание ΔLP LP-токенов возвращает ровно ΔLP × x / lpSupply token0 и ΔLP × y / lpSupply token1. Ни кривая, ни k не изменяются при депозите или снятии — только своп меняет цену.
Модель комиссий на пути своп
CPMM применяет две независимо оцениваемые комиссии на каждый своп:- Торговая комиссия берётся на входной стороне по ставке
AmmConfig.trade_fee_rate. Затем она разделяется на доли LP, протокола и фонда (доля LP остаётся в хранилище и увеличиваетk; доли протокола и фонда извлекаются из учёта хранилища). - Комиссия создателя (активна только при
enable_creator_fee == true) берётся по ставкеAmmConfig.creator_fee_rate. Она берётся на входной или выходной стороне в зависимости отPoolState.creator_fee_onи направления своп (см.products/cpmm/fees). Это отдельный бакет — никогда не часть торговой комиссии.
Доля протокола в комиссии создателя не появляется нигде на этой странице, и это намеренно. Она применяется, когда
CollectCreatorFee перемещает накопленные комиссии между двумя счётчиками, которые кривая уже исключает — не во время своп. Математика своп, котировки и k идентичны с ней и без неё. См. products/cpmm/fees.FEE_RATE_DENOMINATOR = 1_000_000trade_fee_rate— изAmmConfig, например2500= 0,25% от соответствующей стороны объёмаcreator_fee_rate— изAmmConfig, например1000= 0,10% от соответствующей стороны объёмаprotocol_fee_rate,fund_fee_rate— выражены в единицах1/FEE_RATE_DENOMINATORторговой комиссии, а не объёма
protocol_fee + fund_fee + creator_fee хранится в хранилищах, но отслеживается отдельно в состоянии пула (protocol_fees_token*, fund_fees_token*, creator_fees_token*). Когда проверка инварианта постоянного произведения проверяет k' ≥ k, она использует балансы хранилища минус все три накопленные, но не собранные комиссии — таким образом, LP получают только lp_fee.
См. products/cpmm/fees для инструкций сбора и отработанных числовых примеров.
SwapBaseInput (точный вход)
«Пользователь даёт нам ровноamount_in входного токена и получает как минимум minimum_amount_out выходного токена.»
Игнорируя Token-2022 на момент:
Δx_net = amount_in_after_trade_fee.
Затем программа обновляет учёт хранилища так, чтобы часть trade_fee, причитающаяся протоколу/фонду/создателю, находилась в бакетах «накопленных» (не включённых в следующий x кривой), в то время как доля LP присоединяется к x для следующего своп.
Token-2022 на входной стороне
Если входной токен имеет расширение комиссии передачи, токен вычитает свою комиссию при передаче от пользователя → хранилище. Таким образом, хранилище фактически получаетamount_in − transfer_fee_in(amount_in). Программа CPMM поэтому вычисляет:
amount_in_after_trade_fee. Это важно, потому что цена кривой вычисляется от чистой суммы, которая попала в хранилище, а не от объявленной суммы пользователя.
Token-2022 на выходной стороне
Если выходной токен имеет комиссию передачи, пул отправляетamount_out из своего хранилища пользователю. Токен затем снимет свою комиссию на выходе, поэтому пользователь получает amount_out − transfer_fee_out(amount_out). Программа вычисляет amount_out из кривой как обычно, но интегратор должен преобразовать число «отправка из хранилища» пула в число «получение пользователем» при отображении котировок.
Проверка проскальзывания
После вычисленияamount_out:
minimum_amount_out, чтобы константа проскальзывания была выражена в том, что пользователь фактически получит, а не в том, что отправит хранилище.
SwapBaseOutput (точный выход)
«Пользователь получит ровноamount_out выходного токена и готов заплатить до maximum_amount_in входного токена.»
Инвертируя кривую для Δx_net:
Потолок важен — он гарантирует k' ≥ k после целочисленного усечения. Затем:
gross_needed.
Проверка проскальзывания
Отработанный пример
Состояние пула, игнорируя Token-2022:x = 1_000_000_000_000(1 000 000,000000 token0, 6 десятичных)y = 2_000_000_000_000(2 000 000,000000 token1, 6 десятичных)AmmConfig:trade_fee_rate = 2500,protocol_fee_rate = 120_000,fund_fee_rate = 40_000,creator_fee_rate = 0
SwapBaseInput с amount_in = 1_000_000_000 (1 000,000000 token0). Комиссия создателя отключена (enable_creator_fee = false).
enable_creator_fee = true с creator_fee_rate = 1000 (0,10%) на входной стороне, программа взяла бы total_input_fee = ceil(1_000_000_000 * 3500 / 1_000_000) = 3_500_000, затем разделила бы её как creator_fee = 1_000_000 и trade_fee = 2_500_000. Арифметика протокола/фонда/LP на trade_fee не изменяется из примера выше — комиссия создателя — это отдельный бакет, накопленный в creator_fees_token0 и исключённый из curve_x вместе с бакетами протокола и фонда.
Если входной токен имеет комиссию передачи Token-2022 в 1%, хранилище получит 990_000_000 токенов вместо 1_000_000_000, и все последующие вычисления используют эту чистую сумму.
Правило обновления наблюдения
На каждом своп программа оценивает, нужно ли добавить новое наблюдение в кольцевой буфер:- Кумулятивная цена, не спотовая цена. Одно наблюдение — это не цена. Чтобы получить TWAP от времени
t0кt1, прочитайте наблюдения, ближайшие к каждому концу, и вычислите(cumulative(t1) − cumulative(t0)) / (t1 − t0). - Образцы ограничены по скорости. Последовательные своп в одном слоте могут использовать одно наблюдение. Чтение наблюдения сразу после своп может поэтому выглядеть устаревшим на один слот — это нормально.
products/clmm/accounts.
Комиссии на кривой
Это тонкая часть и стоит её выделить. Арифметика кривой работает против чистых балансов хранилища — то есть сырого баланса SPL минус накопленные комиссии протокола, фонда и создателя (все три — независимые бакеты — см.products/cpmm/fees). Конкретная картина:
- Не котируйте от сырых балансов. Сначала вычтите поля накопленных комиссий, или вызовите
SwapBaseInputкак симуляцию и возьмите его результат. CollectProtocolFeeперемещает токены из хранилища. После сбораraw_vault_balanceпадает, ноcurve_balanceне изменяется; цена пула не движется. Это намеренно.
Точность и переполнение
- Вся арифметика кривой использует промежуточные значения
u128для предотвращения переполнения наx * y. - Деление округляется к нулю, кроме
Δx_netвSwapBaseOutput, который округляется вверх, и вычисления комиссии, которое округляется вверх наtrade_feeи вниз на подразделения. Эти направления округления выбраны так, чтобы инвариант никогда не уменьшался из-за целочисленного усечения. - Пулы с экстремальными соотношениями хранилищ (миллиарды : 1) могут столкнуться с полами точности на небольших сделках; программа возвращает
ZeroTradingTokensв этом случае. См.reference/error-codes.
Куда дальше
products/cpmm/fees— полная семантика уровня комиссий и сбора.products/cpmm/instructions— инструкции, которые вызывают эту математику.algorithms/constant-product— вывод и граничные случаиx · y = k, общие для AMM v4 и CPMM.
raydium-io/raydium-cp-swap— математика своп вstates/curve.rs- Отчёты аудита Raydium, ссылки на которые находятся в
security/audits

