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

# Stable AMM 程式碼示例

> 如何從 API 識別 Stable AMM 池、透過流動性模組進行交換（原生支援 version=5 池），以及使用鏈下穩定曲線輔助函數。

<Info>
  **本頁內容由 AI 自動翻譯，所有內容以英文版本為準。**

  [查看英文版 →](/products/stable/code-demos)
</Info>

<Info>
  **版本資訊。**

  * SDK：`@raydium-io/raydium-sdk-v2@0.2.42-alpha`
  * 叢集：Solana `mainnet-beta`
  * Stable AMM 程式 ID：`5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h`（見 [`reference/program-addresses`](/zh-Hant/reference/program-addresses)）
  * 最後驗證：2026-04

  SDK 的 `liquidity` 模組原生支援 Stable AMM 池。Stable 池在 `ApiV3PoolInfoStandardItem` 上顯示為 **`version: 5`**（或 `pooltype: "StablePool"`）；相同的 `addLiquidity` / `removeLiquidity` / 交換輔助函數適用於它們，就像 AMM v4（`version: 4`）常數乘積池一樣——SDK 會偵測變體並自動發出正確的指令。鏈下穩定曲線數學位於 `src/raydium/liquidity/stable.ts`。
</Info>

## 設定

```bash theme={null}
npm install @raydium-io/raydium-sdk-v2 @solana/web3.js @solana/spl-token
```

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

const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!));

const raydium = await Raydium.load({
  connection,
  owner,
  cluster: "mainnet",
  // 選用：如果你打算直接呼叫 `liquidity/stable.ts` 中的報價
  // 輔助函數，請載入穩定曲線模型配置。池級別的交換 / 新增 / 移除
  // 會為你懶加載，所以大多數呼叫者可以跳過此步驟。
});

// 一次性：預先取得鏈上模型資料配置，供鏈下
// 穩定曲線輔助函數使用。只有在你直接呼叫 getStablePrice / getDxByDyBaseIn /
// getDyByDxBaseIn 時才需要。addLiquidity / removeLiquidity / swap 不需要。
await raydium.liquidity.initLayout();
```

## 識別 Stable 池

`ApiV3PoolInfoStandardItem` 上的兩個等效信號：

```ts theme={null}
const isStable =
  pool.version === 5 ||
  pool.pooltype.includes("StablePool"); // SDK 內部使用此字串檢查

// 或者，按程式 ID：
const STABLE_AMM_PROGRAM_ID = "5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h";
const isStableByProgram = pool.programId === STABLE_AMM_PROGRAM_ID;
```

AMM v4（`version: 4`，常數乘積）和 Stable AMM（`version: 5`）都透過 SDK 上相同的 `LiquidityModule` API 流動。在內部，模組分派到：

* `InstructionType.AmmV4AddLiquidity` / `AmmV4RemoveLiquidity` 用於 v4 池
* `InstructionType.AmmV5AddLiquidity` / `AmmV5RemoveLiquidity` 用於 v5（Stable）池

池的 `programId`（與池金鑰一起返回）告訴 SDK 要 CPI 進入哪個程式；你不需要硬編碼它。

## 按代幣對查找池

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

// 兩個常見的代幣作為範例
const mintA = new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); // USDT
const mintB = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC

const pools = await raydium.api.fetchPoolByMintPair({
  mint1: mintA.toBase58(),
  mint2: mintB.toBase58(),
});

const stablePool = pools.find(
  (p) => p.version === 5 || p.pooltype.includes("StablePool"),
);

if (!stablePool) {
  throw new Error("此代幣對不存在 Stable 池");
}

console.log("Stable 池 id：", stablePool.id);
console.log("Stable 池 programId：", stablePool.programId);
console.log("TVL：", stablePool.tvl);
```

如果代幣對同時有 v4（常數乘積）池和 v5（穩定）池，回應包括兩者——選擇你的流程需要的那個，或將它們交給 [AMM 路由](/zh-Hant/products/routing) 程式，讓它選擇最佳路由。

## 透過 Stable 池交換

`LiquidityModule.swap` 流程的形狀與 v4 池相同——只需將 v5 池物件交給它：

```ts theme={null}
import { Percent, TokenAmount, toToken } from "@raydium-io/raydium-sdk-v2";

const inputAmount = new TokenAmount(toToken(stablePool.mintA), 1_000_000); // 1 USDT
const slippage = new Percent(50, 10_000); // 0.5%

// 使用 SDK 的穩定曲線輔助函數在內部計算預期輸出。
const { amountOut, minAmountOut } = raydium.liquidity.computeAmountOut({
  poolInfo: stablePool,
  amountIn: inputAmount,
  mintIn:  stablePool.mintA.address,
  mintOut: stablePool.mintB.address,
  slippage,
});

console.log("預期輸出：", amountOut.toSignificant());
console.log("最小輸出：", minAmountOut.toSignificant());

// 建立並簽署交換交易。
const { transaction, execute } = await raydium.liquidity.swap({
  poolInfo: stablePool,
  amountIn:    inputAmount.raw,
  amountOut:   minAmountOut.raw,
  fixedSide:   "in",
  txVersion:   TxVersion.V0,
});

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

SDK 從池金鑰讀取池的 `programId` 並分派到 Stable AMM 程式。不需要特殊的 `programId` 引數。

## 新增和移除流動性

`addLiquidity` 和 `removeLiquidity` 在 v4 和 v5 池上的工作方式相同：

```ts theme={null}
import { Percent, TokenAmount, toToken } from "@raydium-io/raydium-sdk-v2";

const amountInA = new TokenAmount(toToken(stablePool.mintA), 100_000_000); // 100 USDT
const slippage  = new Percent(50, 10_000); // 0.5%

// 計算曲線對此 A 大小所需的匹配 B 金額。
const { anotherAmount, minAnotherAmount } = raydium.liquidity.computePairAmount({
  poolInfo: stablePool,
  amount:   amountInA.toSignificant(),
  baseIn:   true,
  slippage,
});

const { execute } = await raydium.liquidity.addLiquidity({
  poolInfo: stablePool,
  amountInA,
  amountInB:      anotherAmount,
  otherAmountMin: minAnotherAmount,
  fixedSide:      "a",
  txVersion:      TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("新增流動性 tx：", txId);
```

在內部，SDK 發出 `InstructionType.AmmV5AddLiquidity`，因為 `pooltype.includes("StablePool")` 為真。對應的 `removeLiquidity` 流程是對稱的——輸入 `lpAmount` 和你將接受的每一側的最小金額。

## 鏈下報價輔助函數（stable.ts）

對於伺服器端報價或回測，SDK 公開了底層穩定曲線數學：

```ts theme={null}
import {
  getStablePrice,
  getDxByDyBaseIn,
  getDyByDxBaseIn,
} from "@raydium-io/raydium-sdk-v2";

// 在使用這些之前，你必須呼叫一次 initLayout()（將鏈上
// `ModelDataInfo` PDA 載入到 SDK 的 StableLayout 快取中）。
await raydium.liquidity.initLayout();

const modelData = raydium.liquidity.stableLayout;

// 池目前儲備量處的現貨價格。
const price = getStablePrice(modelData, /* x */, /* y */, /* withFee */);
console.log("現貨價格：", price);

// 報價：給定 dx 輸入，輸出多少 dy（此處不應用費用）？
const dyOut = getDyByDxBaseIn(modelData, /* x */, /* y */, /* dx */);

// 報價：給定 dy 輸出目標，需要多少 dx 輸入？
const dxIn  = getDxByDyBaseIn(modelData, /* x */, /* y */, /* dy */);
```

這些是純函數——無 RPC，無簽署。鏈上 `ModelDataInfo` 由 `initLayout()` 取得一次並快取在 `raydium.liquidity.stableLayout` 中。傳遞目前儲備量（`x`、`y`），輔助函數透過二進位搜尋查詢表並在兩個相鄰 `DataElement` 列之間線性插值來計算。見 [`products/stable/math`](/zh-Hant/products/stable/math) 了解底層演算法。

## 透過 AMM 路由進行路由（多跳 / 最佳價格）

如果你不想自己選擇場所，[AMM 路由](/zh-Hant/products/routing) 程式將考慮每個 Raydium AMM（v4 / CPMM / CLMM / Stable）並透過最佳組合進行路由：

```ts theme={null}
const route = await raydium.tradeV2.fetchRoutes({
  inputMint:  mintA,
  outputMint: mintB,
  amount:     new BN(1_000_000),
  slippage,
});

// route.routes[0].poolType 告訴你最佳路由使用哪些程式；
// 每當 Stable 池是最優路徑的一部分時，「Stable」就會出現在這裡。
console.log(route.routes[0]);

const { execute } = await raydium.tradeV2.swap({
  inputMint:    mintA,
  outputMint:   mintB,
  inputAmount:  new BN(1_000_000),
  swapResult:   route.routes[0],
  slippage,
  txVersion:    TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
```

這是生產交換器和聚合器的推薦路徑——你永遠不需要手動決定 Stable 池是否存在或它是否是今天更好的場所。

## 建議

1. **對於終端使用者交換**，優先使用 [`tradeV2`](/zh-Hant/products/routing) 路由流程。它處理每個 Raydium 池類型，包括 Stable。
2. **對於池特定操作**（已知 Stable 池上的 LP 新增 / 移除），直接使用 `LiquidityModule`——它自動偵測 v5 池。
3. **對於鏈下報價 / 分析**，在 `initLayout()` 後呼叫 `getStablePrice` / `getDyByDxBaseIn` / `getDxByDyBaseIn`。模型資料快取後，每個報價無 RPC 流量。
4. **不要手動編碼原始 `SwapBaseIn` 指令。** [2026-06-22 升級](/zh-Hant/reference/changelog) 移除了已棄用的 OpenBook 帳戶，所以新的交換配置採用 **9 個帳戶**（舊的 18 帳戶配置仍可解析以實現向後相容性）。`Deposit` 現在是 12 個帳戶（舊的 14 個相容），`Withdraw` 12 個（舊的 21/22 個相容），`WithdrawPnl` 10 個，**沒有**相容性路徑。SDK 的預建輔助函數為你選擇正確的配置和順序；自己編寫容易出錯。見 [`products/stable/instructions`](/zh-Hant/products/stable/instructions) 了解完整帳戶表。

## 接下來去哪裡

* [數學](/zh-Hant/products/stable/math)——查詢表插值的工作原理。
* [指令](/zh-Hant/products/stable/instructions)——完整指令參考。
* [AMM 路由](/zh-Hant/products/routing)——跨 AMM v4、CPMM、CLMM、Stable 的多池路由。

來源：

* [`raydium-sdk-V2/src/raydium/liquidity/liquidity.ts`](https://github.com/raydium-io/raydium-sdk-V2/blob/master/src/raydium/liquidity/liquidity.ts)——模組入口點；v4 / v5 分派。
* [`raydium-sdk-V2/src/raydium/liquidity/stable.ts`](https://github.com/raydium-io/raydium-sdk-V2/blob/master/src/raydium/liquidity/stable.ts)——`StableLayout`、`getStablePrice`、`getDxByDyBaseIn`、`getDyByDxBaseIn`。
