> ## 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.

# Constant-product AMM

> Инвариант x·y=k, ценообразование на основе резервов, вывод проскальзывания и варианты обработки комиссий, используемые в CPMM и AMM v4 от Raydium. Это справочная страница по математике, на которую ссылаются все x·y=k продукты Raydium.

<Info>
  **Эта страница переведена с помощью ИИ. За эталон принимается английская версия.**

  [Открыть английскую версию →](/algorithms/constant-product)
</Info>

## Инвариант

Маркет-мейкер с постоянным произведением (CPMM) держит два резерва `x` и `y` и обеспечивает:

```
x · y ≥ k       (после каждой сделки)
```

где `k` — произведение резервов до сделки. Для рынка без комиссий `x · y = k` точно. С комиссиями `k` строго растёт (доля LP от комиссии остаётся в резервах).

Инвариант специально сделан геометрическим: он гарантирует, что независимо от того, насколько малым становится один резерв, другой растёт неограниченно, чтобы уравновесить его — то есть пул никогда не может быть полностью истощен с какой-либо стороны.

## Ценообразование

### Спот-цена

Маржинальная цена `y` в терминах `x` в любой момент — это касательная к кривой:

```
p = y / x
```

(вывод: неявное дифференцирование `x · y = k` даёт `dy/dx = −y/x`; игнорируя знак, `|dy/dx| = y/x`).

Это цена, которую пул предлагает для бесконечно малой сделки. Для любой конечной сделки реализованная цена хуже из-за проскальзывания вдоль кривой.

### Своп с точным входом (даёте `Δx`, получаете `Δy`)

С комиссиями, пусть `f` — ставка комиссии (например, `f = 0.0025` для 25 bps). Применяем комиссию к входу, затем используем инвариант для решения выхода:

```
Δx_after_fee = Δx · (1 − f)
Δy           = y · Δx_after_fee / (x + Δx_after_fee)
```

Резервы после сделки:

```
x' = x + Δx
y' = y − Δy
```

Полная сумма `Δx` входит в резервы. Доля LP от комиссии остаётся в `x'`; доля протокола исключается из кривой через отдельный учёт (см. [Варианты учёта комиссий](#варианты-учёта-комиссий) ниже).

### Своп с точным выходом (получаете `Δy`, платите минимальный `Δx`)

```
Δx_after_fee = x · Δy / (y − Δy)
Δx           = Δx_after_fee / (1 − f)
```

`Δx` округляется в большую сторону, чтобы пул не недозарядил.

## Проскальзывание и ценовое воздействие

**Ценовое воздействие** измеряет, насколько спот-цена пула изменяется в результате сделки:

```
p_before = y / x
p_after  = y' / x' = (y − Δy) / (x + Δx)
impact   = (p_before − p_after) / p_before
```

Для малого `Δx / x` разложение в ряд первого порядка даёт:

```
impact ≈ 2 · Δx / x      (игнорируя комиссии)
```

Интуиция: своп на 1% вызывает примерно 2% ценовое воздействие. Этот коэффициент 2 — причина того, что CPMM пулы, котирующие средние сделки, выглядят «тонкими» по сравнению с рынками с биржевым стаканом — вы не просто покупаете по текущему лучшему биду, вы идёте вверх по собственной маржинальной цене.

**Эффективная цена**, которую платит трейдер:

```
effective = Δx / Δy
```

Спрэд между `p_before` и `effective` — это **проскальзывание**. Проскальзывание в UI обычно выражается как `(effective − p_before) / p_before`; `computeAmountOut` SDK возвращает оба значения `amountOut` и `priceImpact` по этой причине.

## Проверка инварианта в коде

После своп протоколы перепроверяют:

```
k' = x' · y'  ≥  k  =  x · y
```

Любое нарушение — это ошибка программы или переполнение при арифметических операциях. Инструкции своп Raydium делают эту проверку явной как постусловие:

```rust theme={null}
let k_before = coin_reserve_before as u128 * pc_reserve_before as u128;
let k_after  = coin_reserve_after  as u128 * pc_reserve_after  as u128;
require!(k_after >= k_before, ErrorCode::InvariantViolation);
```

## Варианты учёта комиссий

Проверка инварианта предполагает, что комиссия LP остаётся в резервах. Разные продукты Raydium по-разному обрабатывают компоненты протокола, фонда и автора:

### Соглашение CPMM

Комиссии — это `u64` значения, похожие на базисные пункты, с делителем `1_000_000`. Торговая комиссия делится на `trade_fee_rate` (всего) и затем подразделяется через `protocol_fee_rate`, `fund_fee_rate`, `creator_fee_rate`. На каждом своп:

```
trade_fee     = ceil(Δx · trade_fee_rate / 1_000_000)
protocol_fee  = trade_fee · protocol_fee_rate / 1_000_000
fund_fee      = trade_fee · fund_fee_rate     / 1_000_000
creator_fee   = trade_fee · creator_fee_rate  / 1_000_000
lp_fee        = trade_fee − protocol_fee − fund_fee − creator_fee
```

Три доли, не относящиеся к LP, накапливаются в отдельных счётчиках (`protocol_fees_*`, `fund_fees_*`, `creator_fees_*`), которые **исключены** из резервов, используемых в инварианте. Вот как комиссии можно снять без перемещения кривой. См. [`products/cpmm/fees`](/ru/products/cpmm/fees).

### Соглашение AMM v4

Комиссии — это соотношения `numerator / denominator` с делителем `10_000`. Разделение фиксировано при создании пула и сохраняется в `AmmInfo.fees`:

```
swap_fee  = ceil(Δx · swap_fee_numerator / swap_fee_denominator)    // напр. 0.25%
pnl_share = swap_fee · pnl_numerator / swap_fee_numerator            // напр. 0.03 / 0.25 = 12%
lp_share  = swap_fee − pnl_share                                     // 0.22% от объёма
```

`pnl_share` накапливается в `state_data.need_take_pnl_*` и исключается из резервов; `lp_share` остаётся в хранилище. См. [`products/amm-v4/fees`](/ru/products/amm-v4/fees).

Оба соглашения сохраняют инвариант одинаково — различие чисто косметическое (делитель + количество подкатегорий).

## Правила округления

* **Расчёт комиссии округляется в большую сторону.** Гарантирует, что пул никогда не заряжает комиссию меньше.
* **Выходное количество округляется в меньшую сторону.** Гарантирует, что инвариант строго выполняется (`k' > k` даже до добавления комиссии).
* **Входное количество для точного выхода округляется в большую сторону.** Гарантирует, что пользователь не недоплачивает.

Вся арифметика использует `u128` для промежуточных произведений `x · Δx`, чтобы избежать переполнения при больших резервах. Конечные результаты приводятся обратно к `u64` с проверкой насыщения.

## Граничные случаи

### Пустой пул

До первого `Deposit`, `x = y = 0`. Инструкции своп отклоняют попытки до депозита.

### Нулевой выход

Если `Δx` настолько мало, что округленный вниз `Δy` равен 0, инструкция прерывается с `ZeroTradingTokens`. Это предотвращает извлечение стоимости без платежа; также означает, что пыльные свопы на сильно несбалансированных пулах не удаются.

### Пыльные LP

Первый `Deposit` имеет специальную обработку: он вычисляет начальное предложение LP как `sqrt(x · y)` и сжигает небольшое количество (обычно 100 LP-единиц), чтобы предотвратить «атаку инфляции первого депозитора» (когда злоумышленник делает пожертвование в хранилище и завышает стоимость LP-токена). Последующие депозиты используют математику пропорционального распределения.

## Связь с арбитражем

Цена CPMM пула изменяется только через:

1. Сделки через сам пул (пользователи идут по кривой).
2. Пожертвования (отправка токенов в хранилище без своп).

Поскольку сделки детерминировано перемещают цену вдоль кривой, любой пул, цена которого отличается от цены на более широком рынке, создаёт возможность арбитража. Арбитражёры приводят цену пула обратно к рыночной цене в ожидании. Вот почему говорят, что CPMM пулы «котируют цену без оракула»: рынок находит цену через арбитраж, а не когда пул читает её снаружи.

Обратная сторона: сам пул является контрагентом арбитражёра, поэтому любая прибыль от арбитража — это безвозмездный убыток LP (минус комиссия, захватываемая LP).

## Практические примеры

### Пример 1 — маленькая сделка, пренебрежимое проскальзывание

Пул: `x = 1_000_000, y = 2_000_000, k = 2·10^12`. Комиссия `f = 0.0025`.

Сделка `Δx = 1_000`:

```
Δx_after_fee = 1000 · 0.9975  = 997.5
Δy           = 2_000_000 · 997.5 / (1_000_000 + 997.5)
             = 1_995_000_000 / 1_000_997.5
             ≈ 1_993.01
```

Эффективная цена: `1000 / 1993.01 ≈ 0.5018`. Спот до: `0.5`. Воздействие: \~0.36%.

### Пример 2 — средняя сделка, заметное проскальзывание

Тот же пул, `Δx = 100_000` (10% от `x`):

```
Δx_after_fee = 100_000 · 0.9975 = 99_750
Δy           = 2_000_000 · 99_750 / (1_000_000 + 99_750)
             = 199_500_000_000 / 1_099_750
             ≈ 181_405
```

Эффективная: `100_000 / 181_405 ≈ 0.5513`. Воздействие: \~10.3% — примерно половина от правила `2 · 10% = 20%` (это правило является верхним пределом для пула с постоянным произведением без комиссий; торговая комиссия плюс инверсия в формуле снижают её).

## Ссылки

* [`products/cpmm/math`](/ru/products/cpmm/math) — конкретные выборы округления и делителя комиссии CPMM.
* [`products/amm-v4/math`](/ru/products/amm-v4/math) — как резервы AMM v4, интегрированные с OpenBook, расширяют эту модель.
* [`algorithms/slippage-and-price-impact`](/ru/algorithms/slippage-and-price-impact) — отдельная страница про масштабирование допуска проскальзывания для UI.

Источники:

* Uniswap v2 whitepaper — каноническое описание `x · y = k`.
* Исходный код программы Raydium CPMM.
* Исходный код программы Raydium AMM v4.
