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.
이 페이지는 AI 자동 번역입니다. 모든 내용은 영문판을 기준으로 합니다.영문판 보기 →
틱이 존재하는 이유
CLMM의 유동성은 가격 범위에 집중되어 있습니다. 범위를 체인에서 다루기 쉽게 만들기 위해 가격은 정수 틱으로 양자화되며, 각 틱은 이전 틱의 상수배입니다: 틱은 0.01% 가격 변동, 즉 약 1 베이시스 포인트에 해당합니다. 매핑은 다음과 같습니다:틱 인덱스 i | 가격 승수 |
|---|---|
0 | 1.0000 |
100 | 1.0100 (≈ +1.00%) |
-100 | 0.9900 (≈ −0.99%) |
10000 | 2.7181 (≈ e) |
MAX_TICK = 443636 | ≈ 1.84e19 |
MIN_TICK = -443636 | ≈ 5.42e-20 |
MIN_TICK과 MAX_TICK은 양쪽 끝에서 sqrt_price_x64가 u128에 맞도록 선택됩니다. 모든 풀은 tick_lower >= MIN_TICK과 tick_upper <= MAX_TICK을 강제합니다. 실제로 웹 UI는 사용자가 도달 불가능한 틱에 유동성을 잠그는 것을 방지하기 위해 범위를 훨씬 더 좁게 제한합니다.
틱 간격
풀의AmmConfig는 틱 간격을 정합니다. 이는 포지션이 끝점으로 사용할 수 있는 유일한 틱입니다. tick_spacing = 60이면 …, −120, −60, 0, 60, 120, … 틱만 유효합니다. 끝점 31로 포지션을 열려는 시도는 InvalidTickIndex로 되돌려집니다.
일반적인 공개 간격:
| 수수료 등급 | trade_fee_rate | 틱 간격 | 틱 포지션당 가장 거친 가격 스텝 |
|---|---|---|---|
| 0.01% | 100 | 1 | 0.01% |
| 0.05% | 500 | 10 | 0.10% |
| 0.25% | 2500 | 60 | 0.60% |
| 1.00% | 10000 | 120 | 1.21% |
틱 배열
풀은 틱 단위 상태를 별도 계정에 저장하지 않습니다. 대신TICK_ARRAY_SIZE 인접 틱(현재 Raydium CLMM에서는 60)이 단일 TickArrayState으로 압축됩니다. 배열의 첫 틱은 start_tick_index이고, 정확히 TICK_ARRAY_SIZE * tick_spacing 정수-틱 단위를 다룹니다.
tick_spacing = 60과 TICK_ARRAY_SIZE = 60의 경우:
- 각 틱 배열은
60 × 60 = 3600정수 틱을 포함합니다. start_tick_index는 3600의 배수입니다:…, -7200, -3600, 0, 3600, 7200, ….
tick_spacing = 60에서 포지션 끝점 t = 2040은 start_tick_index = 0인 틱 배열에 있습니다. 포지션 끝점 t = 4200은 start_tick_index = 3600인 배열에 있습니다.
배열이 생성될 때
틱 배열은 지연 로딩됩니다: 내부의 틱을 참조하는 첫 번째 포지션이 배열을 초기화하고 렌트를 지불합니다. 스왑은 틱 배열을 초기화하지 않습니다. 비트맵을 사용하여 초기화되지 않은 배열을 건너뜁니다. SDK의 포지션 열기 플로우는 선택한 범위를 검사하고 건드리는 틱 배열 목록을 계산하며 누락된 배열이 있으면OpenPosition과 같은 트랜잭션에 init_tick_array 명령어를 추가합니다.
틱 배열은 닫혀지지 않습니다
틱 배열이 초기화되면 풀의 수명 동안 유지됩니다. 프로그램은initialized_tick_count가 0으로 돌아온 후에도 틱 배열을 닫을 방법을 제공하지 않습니다. 틱 배열에 대한 렌트 복구가 없습니다. 배열에 접근한 첫 번째 포지션이 지불한 렌트는 그 계정에 영구적으로 잠깁니다. 이는 의도적 트레이드오프입니다: 기존 틱 배열을 재사용하는 것은 모든 후속 포지션에 무료이므로, 거래량이 많은 풀은 변동이 많아도 (pool, start_tick_index) 슬롯당 한 번만 렌트 비용을 지불합니다.
비트맵
“현재 틱의 좌측/우측 다음 초기화된 틱 찾기”는 빨라야 합니다. 스왑이 많은 틱을 지날 수 있기 때문입니다. 풀은 틱 0 주변 ±1,024 배열 범위에 대해PoolState에 인라인 1비트-틱-배열 비트맵을 저장합니다. 그 범위 밖(전체 범위 포지션, 특이한 설정)에서는 TickArrayBitmapExtension이 오버플로우를 제공합니다.
스왑은 비트맵을 따라갑니다: lowest_set_bit_above(tick_current_array_index)는 스왑이 향하는 방향의 초기화된 틱이 있는 다음 배열을 제공합니다. 그 배열 내에서 유사한 비트 스캔이 다음 초기화된 틱을 찾습니다.
liquidity_gross와 liquidity_net
모든 초기화된 틱은 두 개의 유동성 값을 저장합니다:
liquidity_gross— 이 틱을 끝점으로 참조하는 모든 포지션의L합.liquidity_gross가 0에 도달하면 틱은 초기화되지 않고 비트맵에서 제거될 수 있습니다.liquidity_net— 가격이 이 틱을 상향 이동(틱 공간에서 좌측에서 우측)할 때 풀 수준liquidity로의 부호있는 변화. 이 틱이 크기L인 포지션의 하한이면+L에 기여하고, 상한이면−L에 기여합니다.
- 포지션 A:
tick_lower = -120,tick_upper = 0, 유동성L_A = 100. - 포지션 B:
tick_lower = -60,tick_upper = 60, 유동성L_B = 50.
| 틱 | 적용 대상 | liquidity_gross | liquidity_net |
|---|---|---|---|
-120 | A 하한 | 100 | +100 |
-60 | B 하한 | 50 | +50 |
0 | A 상한 | 100 | −100 |
60 | B 상한 | 50 | −50 |
tick_current 값에 대한 풀 수준 liquidity:
tick_current = -180:liquidity = 0(어떤 포지션 전)tick_current = -90:liquidity = 100(A만 내부)tick_current = -30:liquidity = 150(A와 B 모두 내부)tick_current = 30:liquidity = 50(B만 내부)tick_current = 90:liquidity = 0(둘 다 초과)
liquidity_net(음수일 수 있음)을 PoolState.liquidity에 추가합니다. 이는 정확한 Uniswap-v3 메커니즘입니다.
NFT로서의 포지션
Raydium CLMM 포지션은 NFT입니다. 포지션을 열면 공급량 1인 완전히 새로운 민트를 호출자의 지갑에 민팅하고, 민트의 권한은 CLMM 프로그램입니다. 프로그램은 포지션 소유권을 CPI 시점에서 그 민트의 ATA에 잔액을 보유한 모든 사람에게 키합니다. 결과:- 포지션은 양도 가능합니다. 지갑은 NFT를 이전하여 포지션을 판매하거나 에어드롭할 수 있습니다. 새 보유자는
CollectRewards,IncreaseLiquidity등을 호출할 수 있습니다. - 포지션은 CLMM 외부에서 주소 지정 가능합니다. 마켓플레이스와 지갑은 다른 NFT처럼 포지션을 표시합니다. SDK는 민트 메타데이터에 합리적인
name/symbol을 설정합니다. - 포지션의 PDA는 NFT 민트에서 파생됩니다. 현재 보유자를 모르고도
PersonalPositionState를 찾을 수 있습니다.
Token-2022 포지션
최신 CLMM 풀은 클래식 SPL Token 대신 Token-2022에서 포지션을 민팅할 수 있습니다. 프로그램은 두 개의 병렬 열기 명령어를 노출합니다.OpenPosition과 OpenPositionWithToken22Nft는 NFT 민트를 소유한 토큰 프로그램을 제외하고 동일한 의미를 가집니다. 지갑 및 마켓플레이스 호환성은 다릅니다. Raydium의 UI는 둘 다 추적합니다.
허용된 범위 규칙
OpenPosition 시점에서 프로그램은 다음을 강제합니다:
tick_lower < tick_upper.tick_lower % tick_spacing == 0과tick_upper % tick_spacing == 0.MIN_TICK <= tick_lower과tick_upper <= MAX_TICK.- 호출자가
tick_lower와tick_upper를 포함하는 틱 배열을 제공했습니다. 이미 초기화되어 있거나 같은 트랜잭션의init_tick_array를 통해. - 이 포지션이 확장 범위로 확장되면 비트맵 확장 계정.
InvalidTickIndex, NotApproved 또는 InsufficientLiquidity로 되돌려집니다. reference/error-codes를 참조하세요.
”범위 내” vs “범위 외”
포지션은tick_lower <= tick_current < tick_upper일 때 범위 내입니다. 범위 내 포지션만 PoolState.liquidity에 기여하므로 스왑 수수료를 얻습니다.
범위 외 포지션:
- 범위가 지나간 한 가지 토큰의 100%를 보유합니다. 구체적으로,
tick_current < tick_lower이면 포지션은 토큰1만 보유합니다(가격이 멀어지면서 이미 “판매”되었음).tick_current >= tick_upper이면 토큰0만 보유합니다. - 스왑 수수료를 얻지 않습니다.
- 풀의 보상 스트림이 범위 외 유동성에 방출하면 계속해서 보상을 적립합니다. 하지만 Raydium의 기본 동작은 “범위 내에만 방출”로, Uniswap v3 규칙과 일치합니다.
products/clmm/fees를 참조하세요.
일반적인 통합 함정
- 간격을 벗어난 끝점. 목표 가격에서 틱을 계산하는 코드는
OpenPosition에 전달하기 전에tick_spacing의 배수로 스냅해야 합니다. SDK 헬퍼(TickUtils.getTickWithPriceAndTickspacing)는 이를 수행합니다. 직접 작성한 수학은 종종 그렇지 않습니다. - 누락된 틱 배열. 넓은 포지션을 열려면 여러 틱 배열을 초기화해야 할 수 있습니다. 쓰기 가능한 계정으로 전달하는 것을 잊으면 되돌려집니다. SDK의
openPositionFromBase는 목록을 반환합니다. - 스왑 후 오래된 틱.
tick_current는 한 번의 스왑에서 많은 틱을 지날 수 있습니다. UX가 한 RPC 호출에서 “현재 틱”을 표시하고 나중에 포지션을 열면, 라이브 가격에 대한 상대 포지션이 수십 틱 떨어져 있을 수 있습니다. 서명하기 직전에 다시 가져오세요. - 추가 메타데이터가 있는 포지션 NFT. Raydium 포지션을 인식하는 지갑을 구축하면 하드코딩된 메타데이터 필드가 아닌 민트 권한(= CLMM 프로그램의 PDA)으로 감지하세요.
다음으로 이동
- 수학 — 틱 경계가 참여하는 스왑 단계 통과 및 수수료 성장 도출.
- 계정 —
TickArrayState와PositionState레이아웃. - 수수료와 보상 — 범위 내 여부가 수수료 적립을 제어하는 방법.
algorithms/clmm-math— 집중 유동성 공식의 공유 도출.
raydium-io/raydium-clmm—tick_array,tick,position모듈- “Uniswap v3 Core” 백서, §6 (틱), §7 (수수료 성장)


