> ## 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 풀을 식별하고, Liquidity 모듈을 통해 스왑하며(version=5 풀을 기본 지원), 오프체인 stable-curve 헬퍼를 사용하는 방법입니다.

<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`](/ko/reference/program-addresses) 참조)
  * 마지막 검증: 2026-04

  SDK의 `liquidity` 모듈은 Stable AMM 풀을 기본적으로 지원합니다. Stable 풀은 `ApiV3PoolInfoStandardItem`에서 **`version: 5`** (또는 `pooltype: "StablePool"`)로 표시되며, AMM v4 (`version: 4`) 상수곱 풀과 동일한 `addLiquidity` / `removeLiquidity` / 스왑 헬퍼를 사용합니다. SDK가 변형을 감지하고 올바른 명령어를 자동으로 생성합니다. 오프체인 stable-curve 수학은 `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`의 견적 헬퍼를 직접 호출하려면
  // stable curve 모델 레이아웃을 로드합니다. 풀 수준의 스왑 / 추가 / 제거는
  // 이를 지연 로드하므로 대부분의 호출자는 이 단계를 건너뛸 수 있습니다.
});

// 일회성: 오프체인 stable-curve 헬퍼에서 사용하는 온체인 모델 데이터 레이아웃을 미리 가져옵니다.
// 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를 통해 흐릅니다. 내부적으로 모듈은 다음으로 디스패치합니다:

* v4 풀의 경우 `InstructionType.AmmV4AddLiquidity` / `AmmV4RemoveLiquidity`
* v5 (Stable) 풀의 경우 `InstructionType.AmmV5AddLiquidity` / `AmmV5RemoveLiquidity`

풀의 `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 (stable) 풀이 모두 있으면 응답에 둘 다 포함됩니다. 흐름에 필요한 것을 선택하거나 [AMM Routing](/ko/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의 stable-curve 헬퍼를 내부적으로 사용하여 예상 출력을 계산합니다.
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는 `pooltype.includes("StablePool")`이 참이므로 `InstructionType.AmmV5AddLiquidity`를 생성합니다. 해당하는 `removeLiquidity` 흐름은 대칭입니다. `lpAmount`와 각 측면에서 수락할 최소 금액을 입력합니다.

## 오프체인 견적 헬퍼 (stable.ts)

서버 측 견적 또는 백테스팅의 경우 SDK는 기본 stable-curve 수학을 노출합니다:

```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`](/ko/products/stable/math)를 참조하세요.

## AMM Routing을 통한 라우팅 (멀티홉 / 최적 가격)

직접 거래소를 선택하지 않으려면 [AMM Routing](/ko/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`](/ko/products/routing) 라우팅 흐름을 선호합니다. Stable을 포함한 모든 Raydium 풀 유형을 처리합니다.
2. **풀별 작업의 경우** (알려진 Stable 풀에서 LP 추가 / 제거), `LiquidityModule`을 직접 사용합니다. v5 풀을 자동으로 감지합니다.
3. **오프체인 견적 / 분석의 경우** `initLayout()` 후 `getStablePrice` / `getDyByDxBaseIn` / `getDxByDyBaseIn`을 호출합니다. 모델 데이터가 캐시된 후 견적당 RPC 트래픽이 없습니다.
4. **원시 `SwapBaseIn` 명령어를 직접 인코딩하지 마세요.** [2026-06-22 업그레이드](/ko/reference/changelog)는 죽은 OpenBook 계정을 제거했으므로 새로운 스왑 레이아웃은 **9개 계정**을 사용합니다 (이전 18개 계정 레이아웃은 하위 호환성을 위해 여전히 파싱됨). `Deposit`은 이제 12개 계정입니다 (이전 14개 호환), `Withdraw` 12개 (이전 21/22개 호환), `WithdrawPnl` 10개이며 **호환성 경로가 없습니다**. SDK의 사전 구성된 헬퍼는 올바른 레이아웃과 순서를 선택합니다. 직접 구현하는 것은 오류가 발생하기 쉽습니다. 전체 계정 테이블은 [`products/stable/instructions`](/ko/products/stable/instructions)를 참조하세요.

## 다음 단계

* [Math](/ko/products/stable/math) — 조회 테이블 보간이 어떻게 작동하는지.
* [Instructions](/ko/products/stable/instructions) — 전체 명령어 참조.
* [AMM Routing](/ko/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`.
