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

# Аккаунты CPMM

> Все PDA и общие аккаунты, которые трогает пул CPMM: PoolState, AmmConfig, authority, LP mint, хранилища токенов и кольцевой буфер наблюдений.

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

  [Открыть английскую версию →](/products/cpmm/accounts)
</Info>

<Info>
  Идентификаторы программ и seeds PDA для CPMM перечислены в [`reference/program-addresses`](/ru/reference/program-addresses). На этой странице рассматривается **назначение каждого аккаунта и инварианты, которые он поддерживает**, а не жёстко заданные адреса.
</Info>

## Шесть аккаунтов пула CPMM

Каждый пул CPMM полностью описывается шестью адресами, производными программы (PDA) под программой CPMM, плюс один общий аккаунт `AmmConfig`, на который он ссылается. Имея два mint'а, вы можете детерминировано вычислить всё остальное без обращения к сети.

| Аккаунт       | Seed(s)                                                                                             | Владелец               | Назначение                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------- | --------------------------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `authority`   | `"vault_and_lp_mint_auth_seed"`                                                                     | CPMM                   | Подписант для каждого перемещения средств из хранилища и каждой операции mint/burn LP. Общий для **всех** пулов CPMM.                                                                                                                                                                                                                                                                                                                                               |
| `poolState`   | `"pool"`, `ammConfig`, `token0Mint`, `token1Mint` **или** любая пара ключей, подписанная создателем | CPMM                   | Структура состояния пула — пара mint'ов, балансы хранилищ, предложение LP, начисленные комиссии, указатель наблюдения. Инструкция `Initialize` CPMM принимает либо каноническую PDA, полученную из четырёх seeds, **либо** произвольную пару ключей, подписанную создателем. Путь со случайной парой ключей существует для предотвращения front-running атаки, где злоумышленник отслеживает mempool и спешит занять каноническую PDA раньше легитимного создателя. |
| `lpMint`      | `"pool_lp_mint"`, `poolState`                                                                       | SPL Token              | LP токен пула. Предложение = общее выданное LP. Полномочия на mint = PDA полномочий CPMM.                                                                                                                                                                                                                                                                                                                                                                           |
| `vault0`      | `"pool_vault"`, `poolState`, `token0Mint`                                                           | SPL Token / Token-2022 | Хранит баланс пула token0. Принадлежит PDA полномочий.                                                                                                                                                                                                                                                                                                                                                                                                              |
| `vault1`      | `"pool_vault"`, `poolState`, `token1Mint`                                                           | SPL Token / Token-2022 | Хранит баланс пула token1. Принадлежит PDA полномочий.                                                                                                                                                                                                                                                                                                                                                                                                              |
| `observation` | `"observation"`, `poolState`                                                                        | CPMM                   | Кольцевой буфер образцов цены для TWAP. Записывается при каждом обмене.                                                                                                                                                                                                                                                                                                                                                                                             |

И общая конфигурация:

| Аккаунт     | Seed(s)                      | Владелец | Назначение                                                                                                                                                                                                 |
| ----------- | ---------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ammConfig` | `"amm_config"`, `index: u16` | CPMM     | Содержит ставки торговой комиссии, комиссии протокола, фонда, создателя и ключи администратора. Один на каждый «ярус комиссий». Poolstate привязывается к одному при создании и не может измениться позже. |

## Вычисление пула, имея только два mint'а

```ts theme={null}
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";

const CPMM_PROGRAM_ID = new PublicKey(
  "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"
); // mainnet — см. reference/program-addresses

function u16ToBytes(n: number): Buffer {
  const b = Buffer.alloc(2);
  b.writeUInt16BE(n);
  return b;
}

// token0 < token1 по порядку байтов. Ошибка здесь приводит к валидной PDA,
// которая указывает на несуществующий пул.
function sortMints(a: PublicKey, b: PublicKey): [PublicKey, PublicKey] {
  return Buffer.compare(a.toBuffer(), b.toBuffer()) < 0 ? [a, b] : [b, a];
}

export function deriveCpmmAccounts(
  mintA: PublicKey,
  mintB: PublicKey,
  ammConfigIndex = 0,
) {
  const [token0Mint, token1Mint] = sortMints(mintA, mintB);

  const [ammConfig] = PublicKey.findProgramAddressSync(
    [Buffer.from("amm_config"), u16ToBytes(ammConfigIndex)],
    CPMM_PROGRAM_ID,
  );
  const [authority] = PublicKey.findProgramAddressSync(
    [Buffer.from("vault_and_lp_mint_auth_seed")],
    CPMM_PROGRAM_ID,
  );
  const [poolState] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("pool"),
      ammConfig.toBuffer(),
      token0Mint.toBuffer(),
      token1Mint.toBuffer(),
    ],
    CPMM_PROGRAM_ID,
  );
  const [lpMint] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_lp_mint"), poolState.toBuffer()],
    CPMM_PROGRAM_ID,
  );
  const [vault0] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), token0Mint.toBuffer()],
    CPMM_PROGRAM_ID,
  );
  const [vault1] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), token1Mint.toBuffer()],
    CPMM_PROGRAM_ID,
  );
  const [observation] = PublicKey.findProgramAddressSync(
    [Buffer.from("observation"), poolState.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  return {
    ammConfig,
    authority,
    poolState,
    lpMint,
    token0Mint,
    token1Mint,
    vault0,
    vault1,
    observation,
  };
}
```

<Tip>
  **Всегда сортируйте mint'ы перед вычислением PDA пула.** Seed хеширует два mint'а в порядке байтов, а не в порядке, введённом пользователем. Два пула с `(A, B)` и `(B, A)` столкнулись бы в цепи — сортировка — это способ, которым программа делает отображение каноническим.
</Tip>

<Note>
  **ID пула — это не всегда каноническая PDA.** `Initialize` принимает произвольную пару ключей, подписанную владельцем, как `pool_state` в дополнение к приведённой выше PDA. Если переданный аккаунт не совпадает с канонической PDA, программа требует, чтобы он был подписан — т. е. создатель передаёт свежую пару ключей, которой они подписывают. Это защита от front-run'а: любая третья сторона, спешащая захватить каноническую PDA, может быть обойдена легитимным создателем, использующим вместо этого случайную пару ключей. Нижестоящие PDA (`lpMint`, `vault0`, `vault1`, `observation`) по-прежнему производны из `poolState.key()`, поэтому они остаются уникальными для любого используемого адреса. При индексировании пулов всегда обнаруживайте ID пула из состояния в цепи (например, аккаунты `PoolState` под программой CPMM), а не путём вычисления канонической PDA — последний пропустит пулы со случайными ключами.
</Note>

## Разметка аккаунтов

Полные определения на Rust находятся в исходном коде [`raydium-cp-swap`](https://github.com/raydium-io/raydium-cp-swap). Приведённые ниже поля — это те, которые вы будете читать из интеграции.

### `PoolState`

```rust theme={null}
// programs/cp-swap/src/states/pool.rs
pub struct PoolState {
    pub amm_config: Pubkey,               // привязывает этот пул к AmmConfig
    pub pool_creator: Pubkey,             // кто запустил initialize
    pub token_0_vault: Pubkey,            // == vault0 PDA
    pub token_1_vault: Pubkey,            // == vault1 PDA

    pub lp_mint: Pubkey,
    pub token_0_mint: Pubkey,
    pub token_1_mint: Pubkey,

    pub token_0_program: Pubkey,          // SPL Token или Token-2022 программа
    pub token_1_program: Pubkey,

    pub observation_key: Pubkey,          // == observation PDA
    pub auth_bump: u8,
    pub status: u8,                       // битовая маска: deposit | withdraw | swap
    pub lp_mint_decimals: u8,
    pub mint_0_decimals: u8,
    pub mint_1_decimals: u8,

    pub lp_supply: u64,                   // зеркало предложения lp_mint
    pub protocol_fees_token_0: u64,
    pub protocol_fees_token_1: u64,
    pub fund_fees_token_0: u64,
    pub fund_fees_token_1: u64,

    pub open_time: u64,                   // unix; обмены отклоняются до этого времени
    pub recent_epoch: u64,

    // Состояние комиссии создателя (добавлено после исходной разметки):
    pub creator_fee_on: u8,               // 0=BothToken, 1=OnlyToken0, 2=OnlyToken1
    pub enable_creator_fee: bool,
    pub padding1: [u8; 6],
    pub creator_fees_token_0: u64,
    pub creator_fees_token_1: u64,

    pub padding: [u64; 28],
}
```

Что действительно читать:

* **`lp_supply`** — внутреннее зеркало пула общего предложения LP mint'а. Используйте его для математики доли LP; значение должно совпадать с предложением mint'а в цепи, но его чтение из `PoolState` избегает дополнительной выборки аккаунта.
* **`protocol_fees_token{0,1}`**, **`fund_fees_token{0,1}`** — *начисленные* комиссии, ещё не собранные. Они не влияют на ценообразование обмена; они остаются в хранилищах до вызова `CollectProtocolFee` / `CollectFundFee`.
* **`status`** — битовая маска, контролирующая, разрешены ли `Swap`, `Deposit`, `Withdraw`. Обновляется администратором через `UpdatePoolStatus`. SDK проверяет это перед созданием транзакции; если вы делаете CPI напрямую, проверьте сами.
* **`token0_program` / `token1_program`** — программа токена, в которую нужно делать CPI для каждого хранилища. Один может быть классическим SPL Token, а другой Token-2022; они независимы.
* **`open_time`** — временная метка Unix. Обмены до этого времени не работают. Депозиты разрешены до `open_time`, так что пул может быть инициализирован.
* **`creator_fee_on` / `enable_creator_fee`** — вместе контролируют, активна ли опциональная комиссия создателя для этого пула и с какой стороны обмена она собирается. `enable_creator_fee == false` полностью обнуляет путь комиссии создателя. Когда включено, `creator_fee_on` выбирает: `0` = снять комиссию с любого токена, который является входом обмена (`BothToken`); `1` = снять комиссию только из `token_0` (пропустить на обменах `token_1 → token_0`); `2` = снять комиссию только из `token_1`. Устанавливается при создании пула через `InitializeWithPermission`; не может измениться позже.
* **`creator_fees_token_{0,1}`** — начисленные комиссии создателя, собираемые через `CollectCreatorFee`.

### `AmmConfig`

```rust theme={null}
pub struct AmmConfig {
    pub bump: u8,
    pub disable_create_pool: bool,
    pub index: u16,                       // совпадает с seed
    pub trade_fee_rate: u64,              // напр., 2500 = 0.25%
    pub protocol_fee_rate: u64,           // доля торговой комиссии протоколу
    pub fund_fee_rate: u64,               // доля торговой комиссии фонду
    pub create_pool_fee: u64,             // оплачено один раз при инициализации (в SOL или токене)
    pub protocol_owner: Pubkey,           // может вызвать CollectProtocolFee
    pub fund_owner: Pubkey,               // может вызвать CollectFundFee
    pub creator_fee_rate: u64,            // опциональная ставка комиссии создателя пула (1/1_000_000 объёма)
    pub padding: [u64; 15],
}
```

Три вещи, о которых следует проявить осторожность:

1. **`trade_fee_rate` и `creator_fee_rate` — доли объёма**, обе обозначены в единицах `1/1_000_000`. `2500` означает 0.25% объёма торговли. **`protocol_fee_rate` и `fund_fee_rate` — доли *торговой комиссии*** (не объёма), с тем же знаменателем `1/1_000_000`. Комиссия создателя **не** является долей торговой комиссии — это её собственная независимая ставка. Полная арифметика в [`products/cpmm/fees`](/ru/products/cpmm/fees).
2. **`index` — это `u16`**, поэтому seed hash использует 2 байта big-endian. Ошибка на один байт в порядке байтов — распространённая ошибка интеграции.
3. **`AmmConfig` неизменяем на уровне пула.** Пул указывает на один `AmmConfig` при создании и никогда не переключается. Изменения комиссии распространяются, потому что пул читает конфиг при каждом обмене — но пул не может быть перемещён между ярусами комиссий.

Замечание о комиссиях создателей: сама **ставка** (`creator_fee_rate`) находится на `AmmConfig` и общая для всего яруса комиссий. Начисляет ли конкретный пул её (`enable_creator_fee`) и с какой стороны обмена она попадает (`creator_fee_on`) находится на `PoolState`. Комиссия создателя **независима** от торговой комиссии — это её собственная ставка, начисляемая на её собственные счётчики (`creator_fees_token_{0,1}`), и никогда не уменьшает доли LP / протокола / фонда торговой комиссии. Сбор осуществляется через `CollectCreatorFee`. Полную механику см. в [`products/cpmm/fees`](/ru/products/cpmm/fees).

### `Permission`

Небольшой аккаунт контроля доступа, используемый `InitializeWithPermission`. Программа CPMM поддерживает путь создания пула с разрешением, чтобы другие программы (например, LaunchLab при выводе токена в CPMM) могли доказать, что они имеют право создать пул против данного `AmmConfig`.

```rust theme={null}
pub struct Permission {
    pub authority: Pubkey,    // кто имеет право вызывать InitializeWithPermission
    pub padding: [u64; 8],
}
```

Permission PDA создаётся администратором CPMM через `CreatePermissionPda` и отзывается через `ClosePermissionPda`. Конечные пользователи не взаимодействуют с этим аккаунтом напрямую — это вспомогательная система для cross-program потоков.

### Хранилища и Token-2022

`vault0` и `vault1` принадлежат CPMM authority PDA, а их владелец токен-программы (`token_program`) — либо SPL Token, либо Token-2022, определённый при создании пула по программе mint'а. Пул прозрачно обрабатывает оба случая — вы передаёте правильный ID программы токена для каждой стороны в аккаунты инструкции `Swap` / `Deposit` / `Withdraw`.

CPMM применяет строгий **список разрешённых расширений** при создании пула (`is_supported_mint` в `utils/token.rs`). Mint Token-2022 может быть использован в пуле CPMM, только если **каждое** расширение, которое он содержит, находится в этом списке:

* **`TransferFeeConfig`.** Применяется mint'ом при каждом переводе. Пул находится на стороне получателя для депозитов `SwapBaseInput` и на стороне отправителя для вывода. Программа вычисляет **чистое** количество, попадающее в хранилище, и устанавливает кривую соответственно. См. [`algorithms/token-2022-transfer-fees`](/ru/algorithms/token-2022-transfer-fees).
* **`MetadataPointer`** и **`TokenMetadata`.** Стандартные метаданные on-mint. Не влияет на математику обмена.
* **`InterestBearingConfig`.** UI количество mint'а накапливает проценты. Хранилище хранит сырые количества; кривая работает только с сырыми количествами. UI, отображающие APR, должны вызывать помощников Token-2022 для рендеринга UI количества.
* **`ScaledUiAmount`.** Расширение масштабирования отображения UI. Та же обработка, что и `InterestBearingConfig` — кривая использует сырые количества.

Любое другое расширение — `PermanentDelegate`, `TransferHook`, `DefaultAccountState`, `NonTransferable`, `ConfidentialTransfer`, `Group`/`GroupMember`, `MintCloseAuthority` и т. д. — вызовет отклонение `Initialize` с ошибкой `NotSupportMint`. Исключение — небольшой жёстко закодированный **whitelist mint'ов** в программе (несколько определённых pubkey), которые обходят проверку расширений; он используется для подключения определённых mint'ов в каждом конкретном случае.

Список проверенных расширений и whitelist mint'ов находятся в исходном коде CP-Swap под `programs/cp-swap/src/utils/token.rs` и могут изменяться при будущих обновлениях программы.

### Observation

Аккаунт observation — это кольцевой буфер записей `ObservationState`, каждая из которых хранит `block_timestamp` и **кумулятивную цену**. При каждом обмене программа добавляет новое наблюдение, если прошло достаточно времени с последнего. TWAP вычисляются путём чтения двух наблюдений и деления `Δcumulative / Δtime`.

```rust theme={null}
// OBSERVATION_NUM жёстко закодирована в программе как 100.
pub const OBSERVATION_NUM: usize = 100;

pub struct Observation {
    pub block_timestamp:              u64,
    pub cumulative_token_0_price_x32: u128,   // Q32.32, верхние 64 бита зарезервированы для переполнения
    pub cumulative_token_1_price_x32: u128,
}

pub struct ObservationState {
    pub initialized:           bool,
    pub observation_index:     u16,                            // циклический индекс
    pub pool_id:               Pubkey,
    pub observations:          [Observation; OBSERVATION_NUM], // 100 записей
    pub last_update_timestamp: u64,                            // временная метка самого последнего добавления
    pub padding:               [u64; 3],
}
```

Кольцевой буфер рассчитан на **100 наблюдений**. Каждое наблюдение — 40 байт, поэтому один только массив — 4 000 байт; полный PDA `ObservationState` — около 4 100 байт после окружающих полей и дискриминатора.

Два правила для потребителей:

* **Не используйте одно наблюдение как цену.** Это *кумулятивное* значение, а не спотовая цена. Используйте два из них для вычисления TWAP.
* **Выбирайте наблюдения на расстоянии как минимум одного блока.** Обмены внутри одного блока могут не создать новое наблюдение; чтение подряд может вернуть одну и ту же запись.

Более полная математика в [`products/clmm/accounts`](/ru/products/clmm/accounts).

## Жизненный цикл аккаунтов

| Событие              | Созданные аккаунты                                       | Уничтоженные аккаунты |
| -------------------- | -------------------------------------------------------- | --------------------- |
| `Initialize`         | `poolState`, `lpMint`, `vault0`, `vault1`, `observation` | —                     |
| `Deposit`            | — (может создать пользовательскую LP ATA)                | —                     |
| `Withdraw`           | —                                                        | —                     |
| `Swap`               | — (может создать пользовательскую целевую ATA)           | —                     |
| `CollectProtocolFee` | —                                                        | —                     |
| `CollectFundFee`     | —                                                        | —                     |
| `UpdatePoolStatus`   | —                                                        | —                     |

Пулы CPMM и их PDA **никогда не закрываются**. Даже при нулевой ликвидности `poolState` остаётся. Это намеренно: повторное инициализирование того же пула позже сохраняет его исторический буфер наблюдений, и его вычисление PDA остаётся стабильным.

## Где что читать

* **Списки аккаунтов инструкций** (какие из приведённых выше являются writable/signer для каждой инструкции): [`products/cpmm/instructions`](/ru/products/cpmm/instructions).
* **Семантика начисления комиссий**: [`products/cpmm/fees`](/ru/products/cpmm/fees).
* **Математика обмена / правило обновления наблюдения**: [`products/cpmm/math`](/ru/products/cpmm/math).
* **Канонические seeds / идентификаторы программ**: [`reference/program-addresses`](/ru/reference/program-addresses).

Источники:

* [`raydium-io/raydium-cp-swap` — PoolState, AmmConfig, ObservationState](https://github.com/raydium-io/raydium-cp-swap)
