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

# Tài khoản CPMM

> Mỗi PDA và tài khoản chung mà một pool CPMM sử dụng: PoolState, AmmConfig, authority, LP mint, token vaults, và ring buffer quan sát.

<Info>
  **Trang này được dịch tự động bằng AI. Phiên bản tiếng Anh là bản chính thức.**

  [Xem bản tiếng Anh →](/products/cpmm/accounts)
</Info>

<Info>
  Program ID và PDA seeds cho CPMM được liệt kê chính thức trong [`reference/program-addresses`](/vi/reference/program-addresses). Trang này tập trung vào **mục đích của mỗi tài khoản và các bất biến nó duy trì**, chứ không phải các địa chỉ được hardcode.
</Info>

## Sáu tài khoản của một pool CPMM

Mỗi pool CPMM được mô tả đầy đủ bởi sáu program-derived addresses (PDAs) dưới chương trình CPMM, cộng với một tài khoản `AmmConfig` chung mà nó tham chiếu. Khi bạn có hai mint, bạn có thể suy ra tất cả mọi thứ một cách xác định mà không cần chạm vào mạng.

| Tài khoản     | Seed(s)                                                                                                       | Chủ sở hữu             | Mục đích                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `authority`   | `"vault_and_lp_mint_auth_seed"`                                                                               | CPMM                   | Người ký cho mọi phép di chuyển vault và mọi LP mint/burn. Được chia sẻ trên **tất cả** các pool CPMM.                                                                                                                                                                                                                                                                                                       |
| `poolState`   | `"pool"`, `ammConfig`, `token0Mint`, `token1Mint` **hoặc** bất kỳ keypair ngẫu nhiên nào do người ký cung cấp | CPMM                   | Struct trạng thái của pool — cặp mint, số dư vault, cấp cung LP, tích lũy phí, con trỏ quan sát. Lệnh `Initialize` của CPMM chấp nhận PDA chính tắc được suy ra từ bốn seed **hoặc** một keypair tùy ý được ký bởi người tạo. Đường dẫn keypair ngẫu nhiên tồn tại để đánh bại một cuộc tấn công front-running nơi kẻ thù quan sát mempool và chạy nước rút để chiếm PDA chính tắc trước người tạo hợp pháp. |
| `lpMint`      | `"pool_lp_mint"`, `poolState`                                                                                 | SPL Token              | LP token của pool. Supply = tổng LP đang lưu hành. Mint authority = CPMM authority PDA.                                                                                                                                                                                                                                                                                                                      |
| `vault0`      | `"pool_vault"`, `poolState`, `token0Mint`                                                                     | SPL Token / Token-2022 | Giữ số dư token0 của pool. Được sở hữu bởi authority PDA.                                                                                                                                                                                                                                                                                                                                                    |
| `vault1`      | `"pool_vault"`, `poolState`, `token1Mint`                                                                     | SPL Token / Token-2022 | Giữ số dư token1 của pool. Được sở hữu bởi authority PDA.                                                                                                                                                                                                                                                                                                                                                    |
| `observation` | `"observation"`, `poolState`                                                                                  | CPMM                   | Ring buffer các mẫu giá được sử dụng cho TWAP. Được viết trên mỗi swap.                                                                                                                                                                                                                                                                                                                                      |

Và cấu hình chung:

| Tài khoản   | Seed(s)                      | Chủ sở hữu | Mục đích                                                                                                                                                                      |
| ----------- | ---------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ammConfig` | `"amm_config"`, `index: u16` | CPMM       | Lưu trữ các tỷ lệ phí giao dịch/giao thức/quỹ/người tạo và các khóa quản trị. Một cho mỗi "tầng phí". Poolstate liên kết với một tại thời điểm tạo và không thể thay đổi sau. |

## Suy ra một pool từ không có gì ngoài hai mint

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

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

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

// token0 < token1 by byte order. Getting this wrong yields a valid PDA
// that points at a nonexistent pool.
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>
  **Luôn sắp xếp mint trước khi suy ra pool PDA.** Seed sử dụng hash hai mint theo thứ tự byte, không theo thứ tự người dùng. Hai pool với `(A, B)` và `(B, A)` sẽ va chạm trên chuỗi — sắp xếp là cách chương trình làm cho ánh xạ chính tắc.
</Tip>

<Note>
  **Pool ID không phải lúc nào cũng là PDA chính tắc.** `Initialize` chấp nhận một keypair signer tùy ý làm `pool_state` ngoài PDA ở trên. Nếu tài khoản được truyền không khớp với PDA chính tắc, chương trình yêu cầu nó phải là một signer — tức là, người tạo truyền một keypair mới mà họ ký cùng với. Đây là phòng chống front-run: bất kỳ bên thứ ba nào đua để nắm lấy PDA chính tắc có thể được tránh bởi người tạo hợp pháp sử dụng một keypair ngẫu nhiên thay thế. Các PDA hạ lưu (`lpMint`, `vault0`, `vault1`, `observation`) vẫn được suy ra từ `poolState.key()`, vì vậy chúng vẫn duy nhất cho bất kỳ địa chỉ nào được sử dụng. Khi bạn lập chỉ mục các pool, luôn khám phá pool ID từ trạng thái trên chuỗi (ví dụ: các tài khoản `PoolState` dưới chương trình CPMM), chứ không phải bằng cách suy ra PDA chính tắc — cách sau sẽ bỏ lỡ các pool keypair ngẫu nhiên.
</Note>

## Bố cục tài khoản

Các định nghĩa Rust đầy đủ nằm trong nguồn [`raydium-cp-swap`](https://github.com/raydium-io/raydium-cp-swap). Các trường dưới đây là những trường bạn sẽ đọc từ một tích hợp.

### `PoolState`

```rust theme={null}
// programs/cp-swap/src/states/pool.rs
pub struct PoolState {
    pub amm_config: Pubkey,               // binds this pool to an AmmConfig
    pub pool_creator: Pubkey,             // who ran 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 or Token-2022 program
    pub token_1_program: Pubkey,

    pub observation_key: Pubkey,          // == observation PDA
    pub auth_bump: u8,
    pub status: u8,                       // bitmask: deposit | withdraw | swap
    pub lp_mint_decimals: u8,
    pub mint_0_decimals: u8,
    pub mint_1_decimals: u8,

    pub lp_supply: u64,                   // mirrors lp_mint supply
    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; swaps rejected before this
    pub recent_epoch: u64,

    // Creator-fee state (added after the original layout):
    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],
}
```

Những gì thực sự cần đọc:

* **`lp_supply`** — bản sao nội bộ của pool về tổng cung LP mint. Sử dụng nó để tính toán phần LP; giá trị phải khớp với cung trên chuỗi của mint, nhưng đọc từ `PoolState` tránh được một lệnh gọi tài khoản bổ sung.
* **`protocol_fees_token{0,1}`**, **`fund_fees_token{0,1}`** — phí *tích lũy* chưa được quét. Chúng không ảnh hưởng đến giá swap; chúng nằm trong vault cho đến khi `CollectProtocolFee` / `CollectFundFee` được gọi.
* **`status`** — một bitmask kiểm soát xem `Swap`, `Deposit`, `Withdraw` có được phép hay không. Được cập nhật bởi quản trị viên qua `UpdatePoolStatus`. SDK kiểm tra điều này trước khi xây dựng giao dịch; nếu bạn đang CPI trực tiếp, hãy kiểm tra nó tự mình.
* **`token0_program` / `token1_program`** — chương trình token để CPI vào cho mỗi vault. Một có thể là SPL Token cổ điển và cái kia là Token-2022; chúng độc lập.
* **`open_time`** — một dấu thời gian Unix. Swaps trước thời gian này sẽ thất bại. Deposits được phép trước `open_time` để pool có thể được seed.
* **`creator_fee_on` / `enable_creator_fee`** — cùng nhau kiểm soát xem phí nhà tạo tùy chọn có hoạt động cho pool này hay không và phía nào của swap nó được thu. `enable_creator_fee == false` loại bỏ hoàn toàn con đường phí nhà tạo. Khi được bật, `creator_fee_on` chọn: `0` = lấy phí từ bất kỳ token nào là đầu vào swap (`BothToken`); `1` = chỉ lấy phí từ `token_0` (bỏ qua swaps `token_1 → token_0`); `2` = chỉ lấy phí từ `token_1`. Được đặt tại lúc tạo pool qua `InitializeWithPermission`; không thể thay đổi sau.
* **`creator_fees_token_{0,1}`** — phí nhà tạo tích lũy, được quét bởi `CollectCreatorFee`.

### `AmmConfig`

```rust theme={null}
pub struct AmmConfig {
    pub bump: u8,
    pub disable_create_pool: bool,
    pub index: u16,                       // matches the seed
    pub trade_fee_rate: u64,              // e.g., 2500 = 0.25%
    pub protocol_fee_rate: u64,           // fraction of trade fee to protocol
    pub fund_fee_rate: u64,               // fraction of trade fee to fund
    pub create_pool_fee: u64,             // paid once at init (in SOL or token)
    pub protocol_owner: Pubkey,           // can call CollectProtocolFee
    pub fund_owner: Pubkey,               // can call CollectFundFee
    pub creator_fee_rate: u64,            // optional pool-creator fee rate (1/1_000_000 of volume)
    pub padding: [u64; 15],
}
```

Ba điều cần cẩn thận:

1. **`trade_fee_rate` và `creator_fee_rate` là các phân số của khối lượng**, cả hai được biểu thị trong đơn vị `1/1_000_000`. `2500` có nghĩa là 0.25% của khối lượng giao dịch. **`protocol_fee_rate` và `fund_fee_rate` là các phân số của *phí giao dịch*** (không phải của khối lượng), trong cùng mẫu số `1/1_000_000`. Phí nhà tạo **không phải** là một phân số của phí giao dịch — nó là tỷ lệ độc lập của riêng nó. Số học đầy đủ nằm trong [`products/cpmm/fees`](/vi/products/cpmm/fees).
2. **`index` là một `u16`**, vì vậy seed hash sử dụng 2 byte big-endian. Sai lệch một trên thứ tự byte là một lỗi tích hợp phổ biến.
3. **`AmmConfig` không thay đổi được ở mức pool.** Một pool trỏ đến một `AmmConfig` tại thời điểm tạo và không bao giờ chuyển đổi. Thay đổi phí được truyền bá vì pool đọc cấu hình mỗi swap — nhưng pool không thể được di chuyển giữa các tầng phí.

Một lưu ý về phí nhà tạo: **tỷ lệ** chính nó (`creator_fee_rate`) nằm trên `AmmConfig` và được chia sẻ trên tầng phí. Liệu một pool cụ thể có thực sự tính nó (`enable_creator_fee`) và phía nào của swap nó hạ cánh (`creator_fee_on`) nằm trên `PoolState`. Phí nhà tạo **độc lập** với phí giao dịch — nó là tỷ lệ của riêng nó, tích lũy vào các bộ đếm của riêng nó (`creator_fees_token_{0,1}`), và không bao giờ giảm các phần LP / giao thức / quỹ của phí giao dịch. Quét được thực hiện qua `CollectCreatorFee`. Xem [`products/cpmm/fees`](/vi/products/cpmm/fees) để biết toàn bộ cơ chế.

### `Permission`

Một tài khoản kiểm soát truy cập nhỏ được sử dụng bởi `InitializeWithPermission`. Chương trình CPMM hỗ trợ một con đường tạo pool được phép để các chương trình khác (ví dụ: LaunchLab khi nâng cấp token lên CPMM) có thể chứng minh rằng họ được phép tạo một pool so với một `AmmConfig` nhất định.

```rust theme={null}
pub struct Permission {
    pub authority: Pubkey,    // who is allowed to call InitializeWithPermission
    pub padding: [u64; 8],
}
```

Permission PDA được tạo bởi quản trị viên CPMM qua `CreatePermissionPda` và được thu hồi qua `ClosePermissionPda`. Người dùng cuối không tương tác trực tiếp với tài khoản này — đó là plumbing cho các luồng chéo chương trình.

### Vaults và Token-2022

`vault0` và `vault1` được sở hữu bởi CPMM authority PDA, và chủ sở hữu token-program của chúng (`token_program`) là SPL Token hoặc Token-2022, được xác định tại lúc tạo pool bởi chương trình của mint. Pool xử lý hai trường hợp một cách minh bạch — bạn truyền ID token-program chính xác cho mỗi phía trong các tài khoản lệnh `Swap` / `Deposit` / `Withdraw`.

CPMM thực thi một **danh sách cho phép extension** chặt chẽ tại lúc tạo pool (`is_supported_mint` trong `utils/token.rs`). Một mint Token-2022 có thể được sử dụng trong một pool CPMM chỉ nếu **mỗi** extension mà nó mang theo đều nằm trong danh sách này:

* **`TransferFeeConfig`.** Được áp dụng bởi mint trên mỗi lần chuyển. Pool nằm ở phía nhận cho các deposit `SwapBaseInput` và phía gửi cho các rút. Chương trình tính toán **số tiền ròng** hạ cánh trong vault và đặt đường cong tương ứng. Xem [`algorithms/token-2022-transfer-fees`](/vi/algorithms/token-2022-transfer-fees).
* **`MetadataPointer`** và **`TokenMetadata`.** Siêu dữ liệu chuẩn trên mint. Không ảnh hưởng đến toán học swap.
* **`InterestBearingConfig`.** Số tiền UI của mint tích lũy lãi. Vault lưu trữ các số tiền thô; đường cong hoạt động chỉ trên các số tiền thô. UIs hiển thị APR nên gọi các trình trợ giúp Token-2022 để kết xuất số tiền UI.
* **`ScaledUiAmount`.** Extension chia tỷ lệ hiển thị UI. Xử lý tương tự như `InterestBearingConfig` — đường cong sử dụng các số tiền thô.

Bất kỳ extension khác — `PermanentDelegate`, `TransferHook`, `DefaultAccountState`, `NonTransferable`, `ConfidentialTransfer`, `Group`/`GroupMember`, `MintCloseAuthority`, v.v. — khiến `Initialize` bị từ chối với `NotSupportMint`. Ngoại lệ là một **danh sách mint được hardcode nhỏ** trong chương trình (một số pubkey cụ thể) bỏ qua kiểm tra extension; nó được sử dụng để đưa các mint cụ thể theo từng trường hợp một.

Danh sách extension được kiểm duyệt và danh sách mint whitelist nằm trong nguồn CP-Swap dưới `programs/cp-swap/src/utils/token.rs` và có thể thay đổi với các nâng cấp chương trình trong tương lai.

### Observation

Tài khoản observation là một ring buffer của các mục `ObservationState`, mỗi mục lưu trữ một `block_timestamp` và một **cumulative price** (giá tích lũy). Trên mỗi swap, chương trình nối thêm một observation mới nếu đã trôi qua đủ thời gian kể từ lần cuối cùng. TWAPs được tính bằng cách đọc hai observation và chia `Δcumulative / Δtime`.

```rust theme={null}
// OBSERVATION_NUM is hardcoded in the program to 100.
pub const OBSERVATION_NUM: usize = 100;

pub struct Observation {
    pub block_timestamp:              u64,
    pub cumulative_token_0_price_x32: u128,   // Q32.32, top 64 bits left for overflow
    pub cumulative_token_1_price_x32: u128,
}

pub struct ObservationState {
    pub initialized:           bool,
    pub observation_index:     u16,                            // circular index
    pub pool_id:               Pubkey,
    pub observations:          [Observation; OBSERVATION_NUM], // 100 entries
    pub last_update_timestamp: u64,                            // timestamp of the most recent append
    pub padding:               [u64; 3],
}
```

Ring buffer có kích thước cho **100 observations**. Mỗi observation là 40 bytes, vì vậy mảng một mình là 4,000 bytes; `ObservationState` PDA đầy đủ là khoảng 4,100 bytes sau các trường xung quanh và discriminator.

Hai quy tắc người tiêu dùng:

* **Đừng sử dụng một observation duy nhất làm giá.** Nó là một *cumulative*, không phải là spot price. Sử dụng hai trong số chúng để tính toán một TWAP.
* **Chọn observations ít nhất một block riêng biệt.** Swaps trong cùng một block có thể không tạo ra một observation mới; đọc lại có thể trả về cùng một bản ghi.

Thêm toán học trong [`products/clmm/accounts`](/vi/products/clmm/accounts).

## Vòng đời tài khoản

| Sự kiện              | Tài khoản được tạo                                       | Tài khoản bị phá hủy |
| -------------------- | -------------------------------------------------------- | -------------------- |
| `Initialize`         | `poolState`, `lpMint`, `vault0`, `vault1`, `observation` | —                    |
| `Deposit`            | — (có thể tạo user LP ATA)                               | —                    |
| `Withdraw`           | —                                                        | —                    |
| `Swap`               | — (có thể tạo user destination ATA)                      | —                    |
| `CollectProtocolFee` | —                                                        | —                    |
| `CollectFundFee`     | —                                                        | —                    |
| `UpdatePoolStatus`   | —                                                        | —                    |

Các pool CPMM và PDAs của chúng **không bao giờ được đóng**. Ngay cả ở mức thanaticity bằng không, `poolState` vẫn tồn tại. Điều này là cố ý: re-seeding cùng một pool sau đó bảo toàn buffer observation lịch sử của nó và suy ra PDA của nó vẫn ổn định.

## Đọc gì ở đâu

* **Danh sách tài khoản lệnh** (tài khoản nào trong số các tài khoản ở trên có thể ghi/signer cho mỗi lệnh): [`products/cpmm/instructions`](/vi/products/cpmm/instructions).
* **Ngữ nghĩa tích lũy phí**: [`products/cpmm/fees`](/vi/products/cpmm/fees).
* **Toán học swap / quy tắc cập nhật observation**: [`products/cpmm/math`](/vi/products/cpmm/math).
* **Seeds chính tắc / program IDs**: [`reference/program-addresses`](/vi/reference/program-addresses).

Nguồn:

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