Skip to main content
이 페이지는 AI 자동 번역입니다. 모든 내용은 영문판을 기준으로 합니다.영문판 보기 →
버전 배너. 모든 TypeScript 데모는 @raydium-io/raydium-sdk-v2@0.2.64-alpha를 대상으로 하며, 0.2.42-alpha (2026-04)에 대해 마지막으로 실행되었고 2026-09-09에 Solana mainnet-beta에 대해 0.2.64-alpha 소스에 대해 호출 서명을 다시 확인했습니다. 끝의 Rust CPI 스켈레톤은 raydium-clmm의 master를 대상으로 하며, Anchor =0.32.1을 고정합니다 — CPMM 페이지에서 사용하는 1.0.2가 아닙니다; 둘은 하나의 크레이트에 함께 있을 수 없습니다. 프로그램 ID는 SDK를 통해 reference/program-addresses에서 가져옵니다.

설정

이 페이지의 모든 데모는 raydium-sdk-V2-demo/src/clmm의 파일을 미러링합니다. GitHub 링크는 각 섹션 옆에 있습니다. 부트스트랩은 데모 리포지토리의 config.ts.template (소스)를 따릅니다 — disableFeatureCheck: true는 모든 비자명한 통합에 권장되는 설정입니다:

CLMM 풀 생성

소스: src/clmm/createPool.ts
SDK는:
  • 파생 전에 바이트 순서로 mint1/mint2를 정렬합니다.
  • sqrt_price_x64 = floor(sqrt(initialPrice × 10^(dB−dA)) × 2^64)를 계산합니다.
  • observation 및 tick_array_bitmap_extension 계정을 생성합니다.
  • ammConfig에 정의된 풀 생성 수수료를 지불합니다.

선택한 범위에서 포지션 열기

소스: src/clmm/createPosition.ts
SDK는 범위가 터치하는 틱 배열을 계산하고 이를 계정으로 전달합니다. init 명령을 번들로 묶을 필요가 없습니다 — init-tick-array 명령이 없습니다; OpenPosition*은 누락된 틱 배열을 자체적으로 할당하며, 지불자의 비용으로 합니다.

기존 포지션의 유동성 증가

소스: src/clmm/increaseLiquidity.ts

유동성 감소 (동시에 수수료 수집)

소스: src/clmm/decreaseLiquidity.ts 및 src/clmm/closePosition.ts
수수료 및 보상만 수집하려면, liquidity = new BN(0)으로 decreaseLiquidity를 호출합니다. 명령의 부작용은 token_fees_owed_{0,1} 및 reward_amount_owed를 정산하고 이를 전송하는 것입니다 — 이것이 둘 중 하나를 수집하는 유일한 방법입니다. 유동성과 수수료를 0으로 만든 후 포지션을 완전히 닫으려면, 최종 decreaseLiquidity 호출에서 ownerInfo: { closePosition: true }를 전달합니다. SDK는 ClosePosition을 추가하고 NFT를 소각합니다.
제한된 발행자 포지션에는 호환되는 close 빌더가 필요합니다. 이러한 포지션에는 동결된 NFT 토큰 계정이 있습니다. ClosePosition은 CLMM이 소각 전에 계정을 해제할 수 있도록 포지션의 풀 ID를 첫 번째 남은 계정으로 추가해야 합니다. 프로그램 소스 분기에는 SDK 변경이 포함되지 않습니다. 제한된 발행자 포지션 생성을 활성화하기 전에 SDK 릴리스가 명시적으로 동결된 close 경로를 지원하는지 확인하세요.
직접 Anchor 클라이언트의 경우, 선언된 계정을 변경하지 않고 풀을 추가합니다:
모든 close에서 poolId를 전달할 수 있습니다. CLMM은 positionNftAccount가 동결되었을 때만 읽으며, 이는 하나의 클라이언트 경로를 이전 및 새 포지션과 호환되게 유지합니다.

보상 수집

소스: src/clmm/harvestAllRewards.ts
harvestAllRewards는 전달된 모든 풀의 모든 포지션을 순회하고, 수수료 및 보상을 정산하는 0-유동성 DecreaseLiquidity 호출 (및 모든 UpdateRewardInfos)을 배치하고, 필요한 경우 트랜잭션 전체에 분할합니다.

스왑

소스: src/clmm/swap.ts
시뮬레이션은 온체인 프로그램과 동일한 로직으로 틱 맵을 오프체인에서 순회하고 출력 금액 (amountCalculated)과 스왑이 터치할 정확한 계정 목록 (accounts)을 반환합니다. 항상 시뮬레이션이 반환하는 remainingAccounts를 전달합니다: 너무 적으면 스왑이 NotEnoughTickArrayAccount로 중간에 되돌아갑니다; 오래된 것들은 계산을 낭비합니다.
PoolUtils.computeAmountOutFormat은 여전히 존재하지만, ComputeClmmPoolInfo (API 풀 객체가 아닌 getPoolInfoFromRpc의 computePoolInfo)와 두 개의 추가 필수 인수 — tickarrayBitmapExtension 및 blockTimestamp — 가 필요하며, raydium.clmm.fetchTickArrays 메서드가 없습니다 (fetchTickArrays는 자유 함수이며; 모듈 수준 헬퍼는 PoolUtils.fetchMultiplePoolTickArrays이고 getPoolInfoFromRpc에서 반환된 tickData / tickArrays입니다).

사용자 정의 가능한 CLMM 풀 생성

createCustomizablePool은 풀 생성 시 동적 수수료 및 단측 수수료 토글을 노출하는 진입점입니다. createPool의 형태에 두 가지 추가 사항을 취합니다:
enableDynamicFee 플래그가 없고 dynamicFeeConfigId 매개변수가 없으며, startTime도 없습니다. dynamicFeeConfig를 제공하는 것이 동적 수수료를 활성화합니다 — 이를 생략하면 정적 수수료 풀을 얻으며, 오류가 없습니다. 또한 SDK 열거형 멤버는 TokenOnlyA / TokenOnlyB이지만, 온체인 Rust 열거형은 Token0Only / Token1Only로 표기합니다; 숫자 값은 일치합니다 (FromInput = 0).
createPool은 기본 수수료, 동적 수수료 없음 경로에 계속 작동합니다. 두 노브 중 하나가 필요할 때마다 createCustomizablePool을 사용합니다. 온체인 계정 목록은 products/clmm/instructions를 참조하세요.

리미트 오더

리미트 오더는 사용자 입력을 단일 틱에 주차하고 스왑이 해당 틱을 교차할 때 FIFO로 채워집니다. 출력은 정산 시 소유자의 ATA로 푸시됩니다; 소유자는 채워지기 위해 온라인에 있을 필요가 없습니다.

리미트 오더 열기

SDK는 (owner, nonce PDA, order nonce)에서 LimitOrderState PDA를 파생하고, 지갑별 LimitOrderNonce를 범프하고, 오더를 해당 틱의 FIFO 코호트에 삽입합니다.

열린 오더 증가 / 감소

decreaseLimitOrder는 오더의 채워지지 않은 부분에서만 제거할 수 있습니다; 채워진 부분은 정산까지 잠깁니다. 오더가 이미 완전히 채워졌으면 두 명령 모두 InvalidOrderPhase로 되돌아갑니다.

채워진 오더 정산

settleLimitOrder는 오더의 unfilled_ratio_x64를 코호트 추적기에 대해 읽고, 채워진 출력을 계산하고, 소유자의 ATA로 전송합니다. 소유자는 이를 직접 호출할 수 있습니다; limit_order_admin (오프체인 운영 키퍼)도 소유자를 대신하여 호출할 수 있습니다 — 출력은 여전히 소유자에게 갑니다. 완전히 정산된 오더를 닫아 임차료를 복구하려면 closeLimitOrder (단일) 또는 closeAllLimitOrder (배치)를 사용합니다. 한 번에 많은 것을 정산하려면 settleAllLimitOrder는 v0 tx에 맞는 만큼 많은 SettleLimitOrder 호출을 패킹합니다.

지갑의 주차된 오더 나열 (오프체인)

활성 오더 엔드포인트는 채워지지 않은 오더와 부분적으로 채워진 오더를 하나의 페이로드로 반환합니다 (totalAmount / filledAmount / pendingSettle은 단계를 구분합니다). 닫힌 오더 기록의 경우 /limit-order/history/order/list-by-user?wallet=… (지갑별, nextPageId로 페이지 매김)을 사용합니다; 특정 오더의 전체 이벤트 로그의 경우 /limit-order/history/event/list-by-pda?pda=…를 사용합니다.

Rust CPI 스켈레톤

SwapV2의 남은 계정 순서:
스왑이 확장을 필요로 하지 않으면 생략합니다; 그렇지 않으면 첫 번째 남은 계정입니다.

일반적인 함정

  • 간격을 벗어난 틱 끝점 → TickAndSpacingNotMatch. 항상 TickUtil.getPriceAndTick (단수 TickUtil)을 통해 스냅합니다.
  • SwapV2에 제공된 틱 배열이 충분하지 않음 → NotEnoughTickArrayAccount. swapInternal(...).accounts에서 목록을 가져옵니다.
  • 비트맵 확장 없는 전체 범위 포지션 → 확장 PDA는 쓰기 가능해야 합니다; SDK가 자동으로 처리합니다.
  • sqrt_price_x64를 price로 착각 → 여기서 2배 혼동은 특히 고통스럽습니다. 의심할 때는 SDK가 인간이 읽을 수 있는 가격에서 계산하도록 하세요.
  • 너무 열심히 보상 수집 → 각 수집은 0-유동성 DecreaseLiquidity이고 하나의 트랜잭션이 소요됩니다. harvestAllRewards를 통해 많은 포지션에 걸쳐 배치하고, 그 execute가 { sequentially: true }를 필요로 함을 기억하세요.
  • NFT 계정을 직접 닫기 → ClosePosition은 NFT를 소각하고 그 ATA를 닫습니다. 또한 Token-2022 NFT 민트를 닫습니다; 클래식 SPL Token 민트는 공급이 0으로 유지됩니다. 왜냐하면 그 프로그램은 민트를 닫을 수 없기 때문입니다. 지원되는 계정을 별도로 닫지 마세요. 그렇지 않으면 명령이 되돌아갑니다.
  • 간격이 아닌 틱에서 리미트 오더 열기 → TickAndSpacingNotMatch. 항상 내보낸 getOrderTick 헬퍼를 통해 양자화합니다.
  • 완전히 채워진 오더에서 decreaseLimitOrder 호출 → InvalidOrderPhase. 대신 settleLimitOrder를 사용한 후 closeLimitOrder를 사용합니다.
  • enableDynamicFee 플래그 기대 → 없습니다. dynamicFeeConfig를 생략하면 정적 수수료 풀을 조용히 생성하며, 오류가 없습니다. 동적 수수료를 원했다면 /main/clmm-dynamic-config에서 선택한 구성 계정의 PublicKey를 전달합니다.

다음으로 갈 곳

소스: