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

# Phí chuyển Token-2022 trong swap

> Cách extension phí chuyển của Token-2022 thay đổi việc tính toán số lượng trước/sau swap, sản phẩm Raydium nào hỗ trợ nó, và các trường hợp đặc biệt mà người tích hợp cần xử lý (phí không đối xứng, cập nhật phí, giới hạn phí tối đa, các extension không chuyển được).

<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 →](/algorithms/token-2022-transfer-fees)
</Info>

<Info>
  Ma trận hỗ trợ: **CPMM** hỗ trợ Token-2022 đầy đủ, bao gồm mint có phí chuyển. **CLMM** hỗ trợ Token-2022 với phí chuyển thông qua account `SwapV2` rõ ràng. **AMM v4** không hỗ trợ Token-2022 hoàn toàn. **LaunchLab** không hỗ trợ Token-2022 cho mint cơ sở (nó tạo mint SPL cổ điển). **Farm v6** hỗ trợ Token-2022 trên cả mint đặt cọc và mint phần thưởng.
</Info>

## Phí chuyển là gì

Token-2022 là chương trình Token SPL thứ hai (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` → `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`). Trong số các extension của nó, extension **transfer-fee** khiến mọi lệnh `TransferChecked` trên mint token đều trừ một khoản phí từ số tiền được chuyển. Phí được định tuyến tới người nhận được chỉ định bởi mint authority và có thể được cập nhật bởi authority (trong các giới hạn).

Một mint có phí chuyển có hai tham số liên quan:

* **`transfer_fee_basis_points`** — tỷ lệ (ví dụ 100 = 1%).
* **`maximum_fee`** — giới hạn tuyệt đối cho mỗi lần chuyển (để những giao dịch khổng lồ không phải trả phí vô hạn).

Một mint có thể có **hai** cấu hình phí chuyển hoạt động cùng lúc: cái "mới hơn" (có hiệu lực ngay bây giờ) và cái "cũ hơn" (đang được lên lịch loại bỏ). Đây là thiết kế "chuyển đổi epoch" — những thay đổi về phí chuyển có hiệu lực trên ranh giới epoch để tránh làm giật ngạc nhiên các giao dịch đang diễn ra.

## Tại sao điều này quan trọng đối với swap

Vault của pool nắm giữ các số dư thực tế. Khi bạn gọi swap Raydium:

1. Bạn gửi `amount_in` tới vault pool. Nếu **in-mint** có phí chuyển, vault nhận được `amount_in − fee_in`, không phải `amount_in`.
2. Toán học swap hoạt động trên số tiền vault nhận được.
3. Pool gửi `amount_out` tới ATA của bạn. Nếu **out-mint** có phí chuyển, bạn nhận được `amount_out − fee_out`, không phải `amount_out`.

Nếu chương trình swap không chính xác và sử dụng tham số `amount_in` thô, kiểm tra bất biến sẽ thất bại vì vault nhận được ít hơn những gì chương trình nghĩ. Ngược lại, nếu nó tính `amount_out` mà không trừ đi phí chuyển outbound, bạn sẽ thấy thiếu hụt và đổ lỗi cho chương trình.

Raydium CPMM và CLMM (thông qua `SwapV2`) xử lý điều này bằng cách:

* **Pre-swap**: tính toán `in_after_fee = amount_in − transfer_fee_on(amount_in, in_mint)`, và sử dụng `in_after_fee` trong toán học đường cong.
* **Post-swap**: tính toán `out_gross = amount_out_from_curve`, gửi `out_gross` tới bạn qua `TransferChecked` mà chương trình Token-2022 sẽ tự động giảm bớt bằng phí chuyển.

`minAmountOut` về slippage của bạn được kiểm tra so với `out_gross` (những gì pool gửi), **không phải** so với những gì bạn nhận được. Đây là cách mọi DEX Solana chính hãng xử lý Token-2022, và nó quan trọng vì:

* Nếu pool kiểm tra post-fee, một cập nhật phí giữa quote và thực thi sẽ khiến giao dịch hoàn lại.
* Kiểm tra pre-fee ghim lỗi vào chất lượng của quote, không phải vào những thay đổi phí ngoài dòng của bạn.

UI nên trừ phí chuyển Token-2022 dự kiến khi hiển thị "Bạn nhận được" cho bạn.

## Tính phí Token-2022

Chương trình SPL Token-2022 cung cấp trợ giúp xác định được. Trong Rust:

```rust theme={null}
use spl_token_2022::extension::transfer_fee::TransferFeeConfig;
use spl_token_2022::extension::StateWithExtensions;

let mint_data = ...;
let state = StateWithExtensions::<Mint>::unpack(&mint_data)?;
let config = state.get_extension::<TransferFeeConfig>()?;

let epoch = Clock::get()?.epoch;
let fee_bp = config.get_epoch_fee(epoch).transfer_fee_basis_points;
let max_fee = u64::from(config.get_epoch_fee(epoch).maximum_fee);

let fee = (amount as u128 * fee_bp as u128 / 10_000).min(max_fee as u128) as u64;
```

Trong TypeScript (thông qua `@solana/spl-token`):

```ts theme={null}
import { getTransferFeeConfig, getTransferFeeAmount } from "@solana/spl-token";

const config = getTransferFeeConfig(mintAccount);
const currentEpochFee = config.olderTransferFee.epoch <= currentEpoch
  ? config.newerTransferFee
  : config.olderTransferFee;

const rate   = currentEpochFee.transferFeeBasisPoints;
const maxFee = currentEpochFee.maximumFee;
const fee    = Math.min(Math.floor(amount * rate / 10_000), Number(maxFee));
```

## Công thức swap điều chỉnh (CPMM, exact-input)

Gọi `f_pool` là tỷ lệ phí pool, `f_in` là tỷ lệ phí chuyển in-mint, `max_in` là giới hạn tối đa của nó, `f_out` là tỷ lệ phí chuyển out-mint, `max_out` là giới hạn tối đa của nó.

```
transfer_fee_in  = min(amount_in · f_in / 10_000, max_in)
vault_received   = amount_in − transfer_fee_in

pool_fee         = ceil(vault_received · f_pool / 1_000_000)
amount_after     = vault_received − pool_fee
amount_out_gross = y · amount_after / (x + amount_after)

transfer_fee_out = min(amount_out_gross · f_out / 10_000, max_out)
user_receives    = amount_out_gross − transfer_fee_out
```

Kiểm tra slippage: `amount_out_gross ≥ min_amount_out` (không phải `user_receives ≥ min_amount_out`). `minAmountOut` của bạn được đặt bởi SDK thành `expected_gross · (1 − slippage)` — giữ giới hạn ở phía "gửi", không phải phía "nhận".

## Công thức điều chỉnh (CPMM, exact-output)

SDK lặp lại để tìm `amount_in` sao cho `user_receives = amount_out_exact`:

```
# bạn muốn nhận amount_out_exact sau phí chuyển
amount_out_gross = amount_out_exact + transfer_fee_out_for(amount_out_exact)

# sau đó giải CPMM exact-output cho amount_after:
amount_after     = ceil(x · amount_out_gross / (y − amount_out_gross))

# sau đó thêm phí pool:
vault_received   = ceil(amount_after · 1_000_000 / (1_000_000 − f_pool))

# sau đó thêm phí chuyển in:
amount_in = ceil(vault_received · 10_000 / (10_000 − f_in))
# (hoặc lặp lại — xem trường hợp cạnh cap phí bên dưới)
```

Các cap `max_in` / `max_out` làm cho phép tính trở nên phi tuyến tính vì khi đạt được cap, phí dừng tăng. `computeAmountIn` / `computeAmountOut` của SDK xử lý điều này bằng cách lặp lại nếu công thức ngây thơ sẽ đẩy quá cap.

## Trường hợp đặc biệt

### Phí không đối xứng (một bên có phí, bên kia không)

Phổ biến trong thực tế. Các công thức trên đã xử lý điều này — nếu một bên có `f_in = 0`, các điều khoản liên quan sẽ sụp đổ. Không có trường hợp đặc biệt trong chương trình.

### Cập nhật phí giữa swap

Nếu phí chuyển của mint thay đổi giữa thời gian quote và thời gian thực thi, swap sẽ hoặc hạ cánh với kinh tế hơi tồi tệ hơn (bạn chịu sự khác biệt trong dung sai slippage) hoặc hoàn lại (đầu ra gross giảm dưới `minAmountOut`). Giới hạn slippage hấp thụ điều này; không cần bảo vệ bổ sung.

### Giới hạn phí tối đa

Khi giao dịch đủ lớn để đạt `maximum_fee`, phí bão hòa và mức tăng thêm là không. Điều này làm cho tỷ lệ hiệu quả tiệm cận đến không cho những giao dịch rất lớn, có thể gây ra những đường cong định giá kỳ lạ trên những thị trường vô cùng không có tính thanh khoản. `computeAmountOut` của SDK tính đến điều này.

### Extension không thể chuyển

Một số mint Token-2022 sử dụng extension `NonTransferable`, từ chối tất cả các lệnh `Transfer` trừ tới và từ mint authority. Những mint như vậy **không thể** được sử dụng trong pool Raydium hoàn toàn. `CreatePool` từ chối chúng khi khởi tạo.

### Mint có lãi suất

Token-2022 cũng hỗ trợ extension `InterestBearingConfig` khiến số dư dường như tăng theo thời gian. Pool của Raydium đọc số dư vault thô (bỏ qua lãi suất accrual), vì vậy trên pool có mint có lãi suất, LP nắm bắt lãi suất đã tích lũy như một món quà thuần túy bất cứ khi nào họ hoàn lại (số dư vault tăng nhanh hơn biểu diễn cung cấp LP). Người tích hợp nên coi đây là vấn đề không cần quan tâm nhưng nên ghi lại cho phía LP.

### Transfer hooks

Extension `TransferHook` của Token-2022 cho phép CPI tùy ý trên mọi lần chuyển. Raydium CPMM hỗ trợ những cái này — hướng dẫn swap chuyển tiếp các account hook — nhưng nó thêm chi phí CU và yêu cầu hook có hành vi tốt. CLMM `SwapV2` cũng hỗ trợ hook. AMM v4 không hỗ trợ Token-2022 hoàn toàn, vì vậy câu hỏi không phát sinh.

## Ví dụ thực tế

Pool CPMM, `x = 1_000_000 USDY, y = 1_000_000 USDC`, phí pool 0.25%.

* USDY có phí chuyển 1%, `max_fee = 10_000` (0.01 USDY với 6 chữ số thập phân).
* USDC không có phí chuyển.

Bạn swap `amount_in = 1_000 USDY` cho USDC (exact-input).

```
transfer_fee_in = min(1_000 · 100 / 10_000, 10_000)  = 10     // 1%, well under cap
vault_received  = 1_000 − 10 = 990

pool_fee        = ceil(990 · 2_500 / 1_000_000)  = 3    // 0.25%
amount_after    = 990 − 3 = 987

amount_out_gross = 1_000_000 · 987 / (1_000_000 + 987) = 986_027 / ...  ≈ 985.97
```

≈ 985.97 USDC. Không có phí chuyển outbound, vì vậy bạn nhận được 985.97 USDC.

## Con trỏ

* [`products/cpmm/overview`](/vi/products/cpmm/overview) — hỗ trợ CPMM Token-2022.
* [`products/clmm/instructions`](/vi/products/clmm/instructions) — `SwapV2` so với `Swap` để định tuyến Token-2022.
* [`solana-fundamentals/spl-token-and-token-2022`](/vi/solana-fundamentals/spl-token-and-token-2022) — mô hình extension Token-2022 chung.

Nguồn:

* [Tài liệu extension phí chuyển SPL Token-2022](https://spl.solana.com/token-2022/extensions#transfer-fees)
* Mã nguồn chương trình CPMM Raydium (xử lý Token-2022 của `SwapBaseInput` / `SwapBaseOutput`).
* Mã nguồn chương trình CLMM Raydium (`SwapV2`).
