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

# Các ví dụ mã AMM v4

> Các ví dụ TypeScript để swap và cung cấp thanh khoản cho một pool AMM v4 hiện có. Tạo pool mới nằm ngoài phạm vi — hãy sử dụng CPMM thay thế.

<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/amm-v4/code-demos)
</Info>

<Info>
  **Phiên bản banner.** Tất cả các ví dụ nhắm tới `@raydium-io/raydium-sdk-v2@0.2.42-alpha` trên Solana mainnet-beta, được xác minh vào tháng 04 năm 2026. Program ID: `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` (xem [`reference/program-addresses`](/vi/reference/program-addresses)).
</Info>

<Info>
  **Tạo pool mới không được hiển thị ở đây.** Giao diện Raydium không còn cung cấp tính năng tạo pool AMM v4 — các cặp mới mặc định sử dụng [CPMM](/vi/products/cpmm/code-demos). Chính chương trình AMM v4 vẫn chấp nhận `Initialize2` trên chuỗi; nó chỉ không phải là con đường được khuyến nghị. Các ví dụ dưới đây bao gồm các hoạt động pool trực tiếp mà mọi nhà tích hợp vẫn cần: swap, deposit, withdraw.
</Info>

## Thiết lập

```ts theme={null}
import { Connection, Keypair, clusterApiUrl } from "@solana/web3.js";
import { Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import fs from "node:fs";

const connection = new Connection(process.env.RPC_URL ?? clusterApiUrl("mainnet-beta"));
const owner = Keypair.fromSecretKey(
  new Uint8Array(JSON.parse(fs.readFileSync(process.env.KEYPAIR!, "utf8"))),
);
const raydium = await Raydium.load({ owner, connection, cluster: "mainnet" });
```

## Lấy pool theo ID

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

const poolId = new PublicKey("<AMM_V4_POOL_ID>");

// Lấy đối tượng pool được chuẩn hóa bởi SDK. Đối với AMM v4, điều này bao gồm các tài khoản OpenBook
// mà các trình xây dựng hướng dẫn sẽ cần.
const data = await raydium.liquidity.getPoolInfoFromRpc({ poolId });
const { poolInfo, poolKeys, poolRpcData } = data;

console.log("Pair:", poolInfo.mintA.symbol, "/", poolInfo.mintB.symbol);
console.log("Version:", poolInfo.version);       // 4 for AMM v4
console.log("Market:", poolKeys.marketId.toBase58());
```

`poolKeys` là struct mà các trình xây dựng hướng dẫn sử dụng. Nó chứa mọi tài khoản AMM v4 và OpenBook theo thứ tự mà chương trình mong đợi.

## Swap (base-in)

```ts theme={null}
import BN from "bn.js";

const amountIn = new BN(1_000_000);            // 1 USDC (6-decimal quote)
const inputMint = new PublicKey(poolInfo.mintB.address);  // USDC
const slippage  = 0.005;

const computed = raydium.liquidity.computeAmountOut({
  poolInfo,
  amountIn,
  mintIn: inputMint,
  mintOut: new PublicKey(poolInfo.mintA.address),
  slippage,
});

const { execute } = await raydium.liquidity.swap({
  poolInfo,
  poolKeys,
  amountIn,
  amountOut: computed.minAmountOut,
  fixedSide: "in",
  inputMint,
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Swap tx:", txId);
```

SDK định tuyến các swap AMM v4 thông qua các điểm vào V2, không chứa các tài khoản OpenBook. (Sau bản nâng cấp 2026-07, các tài khoản thị trường không còn được xác thực ngay cả trên con đường v1 cũ.)

## Swap (base-out)

```ts theme={null}
const amountOut = new BN(1_000_000_000);       // 1 SOL (9-decimal base)
const slippage  = 0.005;

const computed = raydium.liquidity.computeAmountIn({
  poolInfo,
  amountOut,
  mintOut: new PublicKey(poolInfo.mintA.address),
  mintIn: new PublicKey(poolInfo.mintB.address),
  slippage,
});

const { execute } = await raydium.liquidity.swap({
  poolInfo,
  poolKeys,
  amountIn: computed.maxAmountIn,
  amountOut,
  fixedSide: "out",
  inputMint: new PublicKey(poolInfo.mintB.address),
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

## Thêm thanh khoản

```ts theme={null}
const amountA = new BN(100_000_000);           // 0.1 SOL

const { anotherAmount, maxAnotherAmount } = raydium.liquidity.computePairAmount({
  poolInfo,
  amount: amountA,
  baseIn: true,
  slippage: 0.01,
});

const { execute } = await raydium.liquidity.addLiquidity({
  poolInfo,
  poolKeys,
  amountInA: amountA,
  amountInB: maxAnotherAmount,
  fixedSide: "a",
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

`fixedSide: "a"` cho SDK biết bạn đã cung cấp `amountInA` chính xác và `amountInB` nên tối đa là `maxAnotherAmount`. Thanh khoản trên sách của pool được giải quyết trước phép toán tỷ lệ để tỷ lệ gửi khớp với dự trữ mới nhất.

## Loại bỏ thanh khoản

```ts theme={null}
const lpAmount = new BN(50_000);               // LP to burn

const { execute } = await raydium.liquidity.removeLiquidity({
  poolInfo,
  poolKeys,
  lpAmount,
  baseAmountMin: new BN(0),
  quoteAmountMin: new BN(0),
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

Các mức tối thiểu slippage bảo vệ chống lại trạng thái pool thay đổi giữa thời gian trước báo giá và thời gian hạ cánh.

## Điều chỉnh compute-unit / priority fee

Các swap AMM v4 nặng về compute vì mọi hướng dẫn xác thực toàn bộ trạng thái OpenBook. Một swap điển hình sử dụng 180k–250k CU tùy thuộc vào có bao nhiêu đơn hàng mở cần giải quyết trên đường đi. Luôn truyền giới hạn compute-unit:

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

const { execute, innerTransactions } = await raydium.liquidity.swap({
  /* ...params... */
  computeBudgetConfig: {
    units: 400_000,
    microLamports: 50_000,       // priority fee
  },
});
```

Nếu bạn bỏ qua `computeBudgetConfig`, SDK vẫn có thể sử dụng giá trị mặc định của riêng nó; kiểm tra `innerTransactions` để xác nhận. Xem [`integration-guides/priority-fee-tuning`](/vi/integration-guides/priority-fee-tuning).

## CPI Rust trực tiếp

Nếu bạn phải CPI vào AMM v4 từ chương trình Anchor của riêng bạn, bạn sẽ cần mô hình hóa danh sách tài khoản của `SwapBaseIn` một cách chính xác. Một bản phác thảo tối thiểu:

```rust theme={null}
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program::invoke_signed;
use anchor_lang::solana_program::instruction::Instruction;

const AMM_V4_PROGRAM_ID: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");

#[derive(Accounts)]
pub struct ProxyAmmV4Swap<'info> {
    /// CHECK:
    pub token_program: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm:          UncheckedAccount<'info>,
    /// CHECK:
    pub amm_authority: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm_open_orders: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm_target_orders: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub pool_coin_token_account: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub pool_pc_token_account: UncheckedAccount<'info>,
    /// CHECK:
    pub market_program: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_bids: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_asks: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_event_queue: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_coin_vault: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_pc_vault: UncheckedAccount<'info>,
    /// CHECK:
    pub market_vault_signer: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub user_source: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub user_dest: UncheckedAccount<'info>,
    pub user_owner: Signer<'info>,
}

pub fn proxy_swap(
    ctx: Context<ProxyAmmV4Swap>,
    amount_in: u64,
    minimum_amount_out: u64,
) -> Result<()> {
    // Instruction discriminator for SwapBaseIn is 9 on AMM v4.
    let mut data = vec![9u8];
    data.extend_from_slice(&amount_in.to_le_bytes());
    data.extend_from_slice(&minimum_amount_out.to_le_bytes());

    let ix = Instruction {
        program_id: AMM_V4_PROGRAM_ID,
        accounts: vec![
            AccountMeta::new_readonly(ctx.accounts.token_program.key(), false),
            AccountMeta::new(ctx.accounts.amm.key(), false),
            AccountMeta::new_readonly(ctx.accounts.amm_authority.key(), false),
            AccountMeta::new(ctx.accounts.amm_open_orders.key(), false),
            AccountMeta::new(ctx.accounts.amm_target_orders.key(), false),
            AccountMeta::new(ctx.accounts.pool_coin_token_account.key(), false),
            AccountMeta::new(ctx.accounts.pool_pc_token_account.key(), false),
            AccountMeta::new_readonly(ctx.accounts.market_program.key(), false),
            AccountMeta::new(ctx.accounts.market.key(), false),
            AccountMeta::new(ctx.accounts.market_bids.key(), false),
            AccountMeta::new(ctx.accounts.market_asks.key(), false),
            AccountMeta::new(ctx.accounts.market_event_queue.key(), false),
            AccountMeta::new(ctx.accounts.market_coin_vault.key(), false),
            AccountMeta::new(ctx.accounts.market_pc_vault.key(), false),
            AccountMeta::new_readonly(ctx.accounts.market_vault_signer.key(), false),
            AccountMeta::new(ctx.accounts.user_source.key(), false),
            AccountMeta::new(ctx.accounts.user_dest.key(), false),
            AccountMeta::new_readonly(ctx.accounts.user_owner.key(), true),
        ],
        data,
    };
    invoke_signed(&ix, &ctx.accounts.to_account_infos(), &[])?;
    Ok(())
}
```

AMM v4 không cung cấp một crate Anchor cho CPI. Bản phác thảo trên sử dụng một `Instruction` được xây dựng thủ công.

<Note>
  Bản phác thảo CPI trên sử dụng **bố cục `SwapBaseIn` v1 cũ** (tag 9, 17 tài khoản) để đọc/tái tạo các giao dịch hiện có. Kể từ bản nâng cấp 2026-07, các tài khoản thị trường được chấp nhận nhưng bị bỏ qua. **Đối với mã mới, hãy ưu tiên `SwapBaseInV2` / `SwapBaseOutV2`** (tags 16 / 17), loại bỏ các tài khoản thị trường (và `amm_open_orders`) hoàn toàn — chỉ truyền 8 tài khoản: `token_program`, `amm`, `amm_authority`, hai vault pool, hai tài khoản token người dùng, và `user_owner`.
</Note>

## Những cạm bẫy

* **Số lượng tài khoản sai trên các swap v1.** `SwapBaseIn` / `SwapBaseOut` cũ vẫn yêu cầu danh sách đầy đủ 17 tài khoản (hoặc 18 tài khoản) — một *số lượng* không khớp sẽ hoàn nguyên với `WrongAccountsNumber`. *Nội dung* của các tài khoản thị trường không còn được xác thực, nhưng bạn vẫn phải chiếm các vị trí của chúng. Ưu tiên các điểm vào V2 để tránh điều này.
* **Đọc số dư vault thô.** Dự trữ hiện là vault-only; trừ đi PnL tích lũy (`need_take_pnl_*`). SDK quote hoặc `api-v3.raydium.io/pools/info/ids` xử lý điều này cho bạn.
* **Gọi các hướng dẫn đã bị xóa.** `MonitorStep`, `MigrateToOpenBook`, `WithdrawSrm`, `SimulateInfo`, `AdminCancelOrders`, và `Initialize` / `PreInitialize` cũ hiện hoàn nguyên. Sử dụng `Initialize2` để tạo pool; không có crank để gọi.
* **Token-2022 mints.** Không được hỗ trợ. Không thể tạo pool AMM v4 chống lại một mint Token-2022; bất kỳ cặp Token-2022 nào cũng nên ở trên CPMM hoặc CLMM.

## Tiếp theo

* [`products/amm-v4/instructions`](/vi/products/amm-v4/instructions) — bề mặt cấp hướng dẫn đằng sau các ví dụ này.
* [`user-flows/migrate-amm-v4-to-cpmm`](/vi/user-flows/migrate-amm-v4-to-cpmm) — nếu bạn là LP đang xem xét di chuyển.
* [`integration-guides/priority-fee-tuning`](/vi/integration-guides/priority-fee-tuning) — kích thước priority-fee cho các swap AMM v4 nặng.

Nguồn:

* [Raydium SDK v2](https://github.com/raydium-io/raydium-sdk-V2)
* [Raydium AMM program](https://github.com/raydium-io/raydium-amm)
