Skip to main content
이 페이지는 AI 자동 번역입니다. 모든 내용은 영문판을 기준으로 합니다.영문판 보기 →
이 페이지는 각 계정의 레이아웃과 역할을 설명합니다. 시드(seed)는 정규 형식이며 reference/program-addresses에 나열되어 있습니다. CLMM 풀은 CPMM 풀보다 계정이 많은데, 이는 유동성이 틱 범위에 걸쳐 희소하게 저장되기 때문입니다. 이 희소성을 이해하는 것이 이 페이지의 대부분을 차지합니다.

계정 목록

실행 중인 CLMM 풀은 다음 계정 그룹으로 설명됩니다. 모두 CLMM 프로그램이 소유하며, 두 개의 민트와 그 볼트는 제외됩니다.

PoolState

풀의 실시간 상태로, 모든 스왑과 포지션 변경 시 읽힙니다.
실제로 사용할 필드:
  • **sqrt_price_x64**와 **tick_current**는 풀의 가격 상태입니다. 모든 스왑에서 함께 업데이트됩니다. tick_current는 log_{1.0001}(price)의 바닥값입니다.
  • **liquidity**는 활성 유동성입니다. 범위가 tick_current를 포함하는 모든 포지션의 L 값의 합입니다. 스왑이 틱을 넘을 때마다, 그리고 포지션이 열리거나 닫히거나 크기가 조정될 때마다 변경됩니다.
  • **fee_growth_global_{0,1}_x64**는 전체 풀 이력에 걸쳐 유동성 단위당 누적된 수수료입니다. 포지션은 이를 읽어 자신에게 얼마나 많은 수수료가 지급되는지 계산합니다.
  • **tick_spacing**은 초기화 시 AmmConfig에 고정되며 절대 변경되지 않습니다. 포지션 끝점으로 허용되는 틱 인덱스를 결정합니다.
  • **tick_array_bitmap**은 현물 가격 주변의 일반적으로 사용되는 틱 범위를 다루는 인라인 비트맵입니다. 포지션이 멀리 도달하는 풀의 경우, 오버플로우 추적은 별도의 TickArrayBitmapExtension에 있습니다.
  • **fee_on**은 풀 생성 시 고정됩니다. 0 (FromInput)은 클래식 Uniswap-V3 동작을 재현합니다. 1과 2는 스왑 수수료를 책의 한쪽으로 라우팅합니다. products/clmm/fees에서 트레이드오프를 참조하세요.
  • **seed_index**는 CreatePool / CreateCustomizablePool을 통해 생성된 모든 풀에 대해 [0, 0]입니다 (쌍당 하나의 정규 풀). 0이 아닌 값은 풀이 CreateCustomizablePoolV2 (2026-10-22 이전에는 CreatePermissionedPool이라고 명명됨)를 통해 생성되었으며 인덱스가 풀의 PDA 시드의 일부임을 의미하므로 동일한 (config, mint0, mint1)에 대해 여러 풀이 공존할 수 있습니다. 이러한 풀의 주소를 다시 파생시키려면 seed_index를 알아야 합니다.
  • **dynamic_fee_info**는 동적 수수료 할증에 대한 변동성 상태를 전달합니다. 활성화되면 모든 스왑은 AmmConfig.trade_fee_rate 위에 dynamic_fee_component를 재계산합니다. 레이아웃은 아래 DynamicFeeInfo에 설명되어 있습니다. 동적 수수료가 없는 풀은 전체 구조를 0으로 남깁니다.

AmmConfig

2026-09 업그레이드 이후, CreateAmmConfig는 프로그램의 하드코딩된 protocol_fee_owner::ID를 owner에, fund_fee_owner::ID를 fund_owner에 씁니다. 그 전에는 관리자 서명자의 키를 둘 다에 복사했습니다. 기존 구성은 생성되거나 회전된 것을 유지하므로 계정에서 두 필드를 모두 읽으세요. UpdateAmmConfig 매개변수 3 / 4는 여전히 이들을 회전시킵니다. 주소는 reference/program-addresses에 있습니다. 일반적인 공개 CLMM 수수료 계층 집합 (GET https://api-v3.raydium.io/main/clmm-config에 대해 확인): protocol_fee_rate와 fund_fee_rate는 거래 수수료의 분수입니다. CPMM과 동일한 규칙입니다. products/clmm/fees를 참조하세요.

TickArrayState

CLMM은 틱마다 하나의 레코드를 저장하지 않습니다. 그렇게 하면 수십억 개의 계정이 필요합니다. 대신 인접한 초기화된(또는 초기화되지 않은) TICK_ARRAY_SIZE개의 틱(프로그램 버전에 따라 일반적으로 60 또는 88)을 처음 사용할 때 지연 생성되는 TickArrayState로 그룹화합니다.
네 개의 리미트 오더 필드는 리미트 오더에 사용된 적이 없는 모든 틱에서 0입니다. 틱에서 오더가 열리면 프로그램은 이들을 코호트 시퀀스로 추적합니다:
  • order_phase는 코호트 ID입니다. 코호트가 “모두 미체결”에서 “부분 체결”로 전환될 때마다 증가합니다.
  • orders_amount는 현재(최신) 코호트의 입력 토큰 총액입니다.
  • part_filled_orders_remaining은 현재 진행 중인 스왑으로 채워지고 있는 이전 코호트를 추적합니다.
  • unfilled_ratio_x64는 코호트에 전달되는 Q64.64 승수입니다. 스왑이 코호트의 X%를 채우면 비율에 (1 − X)를 곱합니다. 각 오픈 오더는 오픈 시간에 자신의 (order_phase, unfilled_ratio_x64) 스냅샷을 저장하므로 정산 수학은 스냅샷 비교로 축소됩니다.
규칙:
  • 포지션 끝점 틱 t는 t % tick_spacing == 0을 만족해야 합니다. 프로그램은 간격을 벗어난 포지션을 거부합니다.
  • 틱의 배열은 floor(t / (TICK_ARRAY_SIZE * tick_spacing)) * (TICK_ARRAY_SIZE * tick_spacing)에 위치합니다.
  • 틱 배열은 지연 초기화됩니다. 초기화되지 않은 배열을 건드리는 첫 번째 포지션 또는 스왑이 이를 생성하고 렌트를 지불합니다.
  • 틱 배열은 프로그램에 의해 절대 닫히지 않습니다. 할당되면 풀의 수명 동안 지속되며, 내부의 모든 틱이 liquidity_gross == 0으로 돌아간 후에도 유지됩니다. 후속 포지션과 스왑은 추가 렌트 없이 기존 계정을 재사용합니다. 틱 배열에 대한 ClosePosition 기반 정리 경로는 없습니다.

TickArrayBitmapExtension

PoolState.tick_array_bitmap(인라인)은 “현물에 가까운” 범위를 다룹니다 — ±512 틱 배열(TICK_ARRAY_BITMAP_SIZE = 512; [u64; 16] 필드는 1,024비트로 음수 및 양수 시작 인덱스 사이에 균등하게 분할되므로 배열 인덱스 −512 … +511). 그 범위 밖(극단적인 틱 값의 경우) 프로그램은 확장 계정을 유지합니다:
포지션의 범위가 “정상”이면 확장 계정에 대해 생각할 필요가 없습니다. 전체 범위 포지션(예: (MIN_TICK, MAX_TICK))은 이를 필요로 하며, SDK가 이를 해결합니다.

포지션

CLMM 포지션은 세 개의 계정과 민트의 번들입니다:

포지션 NFT 민트

공급량이 1인 SPL Token 또는 Token-2022 민트입니다. 소유자의 지갑에 있는 포지션 NFT는 해당 단일 토큰을 보유하는 ATA입니다. 프로그램은 상태에 저장된 Pubkey가 아니라 NFT의 ATA 잔액의 현재 보유자에게 인증을 키합니다. 새 포지션 NFT 민트는 단일 토큰을 민팅하기 전에 pool_state를 동결 권한으로 설정하고 민트 권한을 제거합니다. 동결 권한을 설정하는 것 자체로는 NFT 계정을 동결하지 않습니다. 계정은 다음 두 조건이 모두 유지되지 않는 한 동결되지 않고 양도 가능합니다. 호출자가 OpenPositionV2 또는 OpenPositionWithToken22Nft를 사용하고, 기본 볼트 민트 중 적어도 하나의 동결 권한이 CLMM의 제한된 발급자 목록에 나타납니다. 그러면 CLMM은 민팅 후 NFT 계정을 동결합니다. 이는 PersonalPositionState 또는 PoolState 바이트를 변경하지 않습니다.

PersonalPositionState

오픈 포지션당 하나입니다. NFT 민트로 키됩니다.

ProtocolPositionState (deprecated)

이전 CLMM 릴리스는 ProtocolPositionState PDA에 풀별 (pool, tick_lower, tick_upper) 집계 기록을 저장했습니다. 최신 릴리스는 더 이상 이 계정을 생성하거나 읽지 않습니다. 슬롯은 여전히 OpenPosition / IncreaseLiquidity / DecreaseLiquidity 계정 목록에 ABI 호환성을 위해 UncheckedAccount로 나타나지만 프로그램은 이에 쓰지 않습니다. 온체인의 기존 계정은 흔적이며, 관리자는 CloseProtocolPosition을 호출하여 이들의 렌트를 회수할 수 있습니다.집계 범위 기록은 이제 TickArrayState의 두 끝점 틱(liquidity_gross, liquidity_net 및 틱별 fee_growth_outside_* / reward_growths_outside_x64)에서 직접 파생됩니다. 수수료 성장 내부 공식 fee_growth_inside = global − outside_lower − outside_upper는 집계 포지션 계정 없이 계속 작동합니다.

관찰

CLMM의 관찰 버퍼는 누적 가격이 아니라 누적 틱을 저장합니다. 외부 소비자는 (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0)에서 간격에 대한 기하 평균 가격을 계산한 다음 price = 1.0001 ** tick을 계산합니다. algorithms/clmm-math를 참조하세요.

DynamicFeeConfig 및 DynamicFeeInfo

동적 수수료 매개변수는 두 곳에 있습니다. 재사용 가능한 템플릿 — DynamicFeeConfig — 은 관리자가 관리하며 옵트인한 풀 간에 공유됩니다. 풀별 런타임 상태 — DynamicFeeInfo — 은 PoolState에 포함되고 모든 스왑으로 업데이트됩니다.

DynamicFeeConfig

PDA 시드: ["dynamic_fee_config", index.to_be_bytes()]. create_dynamic_fee_config(관리자 제한)을 통해 생성되고 update_dynamic_fee_config를 통해 수정됩니다. enable_dynamic_fee = true로 생성된 풀은 생성 시 구성의 다섯 가지 보정 매개변수(filter_period, decay_period, reduction_factor, dynamic_fee_control, max_volatility_accumulator)를 자신의 DynamicFeeInfo로 스냅샷합니다. 나중에 DynamicFeeConfig를 편집해도 기존 풀에는 소급 적용되지 않습니다.

DynamicFeeInfo (PoolState에 포함)

하단의 네 필드는 상태이고, 상단의 다섯 필드는 DynamicFeeConfig에서 복사된 보정입니다. 수수료 수학과 감소 규칙은 products/clmm/math 및 products/clmm/fees에 문서화되어 있습니다. 공식에서 사용되는 상수:

LimitOrderState

오픈 리미트 오더당 하나의 계정입니다.
생명주기:
  1. 오픈 — 사용자가 open_limit_order를 호출하고, 입력 토큰의 total_amount를 예치하며, 오더는 TickState 코호트에 바인딩됩니다.
  2. (선택사항) 증가 / 감소 — increase_limit_order는 total_amount에 추가하고, decrease_limit_order는 미체결 토큰(및 그 시점까지의 정산된 출력)을 반환합니다.
  3. 정산 — 코호트가 완전히 또는 부분적으로 채워지면 소유자 또는 운영 키퍼가 settle_limit_order를 호출하여 출력 토큰을 소유자의 ATA로 푸시합니다.
  4. 종료 — unfilled_amount == 0이 되면 계정을 닫을 수 있습니다. 렌트는 항상 owner로 반환됩니다.
PDA 시드: [owner.as_ref(), limit_order_nonce.key().as_ref(), limit_order_nonce.order_nonce.to_be_bytes().as_ref()]. 오더 PDA는 따라서 (owner, nonce_index, order_nonce) 당 고유합니다.

LimitOrderNonce

사용자 하나가 PDA 충돌 없이 여러 개의 리미트 오더 파이프라인을 병렬로 실행할 수 있도록 하는 (wallet, nonce_index) 별 카운터입니다.
PDA 시드: [user_wallet.as_ref(), &[nonce_index]]. 대부분의 클라이언트는 nonce_index = 0을 사용하고 order_nonce가 카디널리티를 담당하도록 합니다.

Permission (폐기됨)

Permission 계정은 2026-10-22 업그레이드에서 제거되었습니다. 이전에는 CreatePermissionedPool을 제어했으나, 현재는 퍼미션리스 CreateCustomizablePoolV2로 변경되었습니다. 프로그램은 더 이상 이 계정들을 읽거나 닫지 않으며, CreatePermissionPda / ClosePermissionPda 명령어도 제거되었습니다. 업그레이드 이전에 생성된 PDA (["permission", authority], 280바이트)는 체인 상에 비활성 데이터로 남아 있으므로, CLMM 소유 계정을 스캔할 때 무시하면 됩니다.

핵심 계정 유도하기

정확한 시드 문자열은 항상 온체인 IDL과 reference/program-addresses에서 확인해야 합니다.

라이프사이클 빠른 참조

TickArrayState 계정은 프로그램에 의해 절대 닫히지 않습니다 — 풀의 수명 동안 지속됩니다. 틱 배열이 초기화되면 내부의 모든 틱이 liquidity_gross == 0으로 돌아가도 체인 상에 남아 있습니다. 기존 틱 배열을 재사용하는 것은 무료이며, 초기화되지 않은 배열에 처음 접근하는 포지션만 렌트를 지불합니다.

어디서 무엇을 읽을지

출처: