메인 콘텐츠로 건너뛰기
이 페이지는 AI 자동 번역입니다. 모든 내용은 영문판을 기준으로 합니다.영문판 보기 →
버전 정보
  • SDK: @raydium-io/raydium-sdk-v2@0.2.42-alpha
  • 클러스터: Solana mainnet-beta
  • Stable AMM 프로그램 ID: 5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h (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에 있습니다.

설정

npm install @raydium-io/raydium-sdk-v2 @solana/web3.js @solana/spl-token
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의 두 가지 동등한 신호:
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할지 알려줍니다. 하드코딩할 필요가 없습니다.

민트 쌍으로 풀 찾기

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 프로그램에 전달하고 최적의 경로를 선택하도록 합니다.

Stable 풀을 통한 스왑

LiquidityModule.swap 흐름은 v4 풀과 동일한 형태입니다. v5 풀 객체를 전달하기만 하면 됩니다:
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 인수가 필요하지 않습니다.

유동성 추가 및 제거

addLiquidityremoveLiquidity는 v4 및 v5 풀에서 동일하게 작동합니다:
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 수학을 노출합니다:
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 없음, 서명 없음. 온체인 ModelDataInfoinitLayout()에 의해 한 번 가져와지고 raydium.liquidity.stableLayout에 캐시됩니다. 현재 준비금 (x, y)을 전달하면 헬퍼는 조회 테이블을 이진 검색하고 두 개의 인접한 DataElement 행 사이를 선형 보간하여 계산합니다. 기본 알고리즘은 products/stable/math를 참조하세요.

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

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

다음 단계

  • Math — 조회 테이블 보간이 어떻게 작동하는지.
  • Instructions — 전체 명령어 참조.
  • AMM Routing — AMM v4, CPMM, CLMM, Stable 전체의 멀티풀 라우팅.
소스: