> ## 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.

# CLMM 명령어

> 완전한 CLMM 명령어 참조: 풀 생명주기, 포지션 관리, 스왑, 보상 스트림.

<Info>
  **이 페이지는 AI 자동 번역입니다. 모든 내용은 영문판을 기준으로 합니다.**

  [영문판 보기 →](/products/clmm/instructions)
</Info>

<Info>
  이 페이지는 [`products/clmm/accounts`](/ko/products/clmm/accounts) (계정이 무엇인지)와 [`products/clmm/math`](/ko/products/clmm/math) (수학이 무엇인지)와 함께 사용됩니다. 인자와 계정 순서에 대해 권위 있는 참조이며, 특정 바이트 레이아웃은 IDL에서 나옵니다.

  2026-09 프로그램 업그레이드는 CLMM을 Anchor 1.0.2 / Solana 3.1.10으로 재구축했으며, 관리자 명령어 `CollectExcessLamports`를 추가했고, `CreateAmmConfig`가 `owner` / `fund_owner`에 쓰는 내용을 변경했습니다. 사용자 대면 명령어는 계정, 인자 또는 수학을 변경하지 않았습니다. [2026-09-30 변경 로그 항목](/ko/reference/changelog/2026-09-30-clmm-anchor-1-and-excess-lamports)을 참조하세요.
</Info>

## 명령어 목록

| 그룹 | 명령어 | 설명 |
| - | - | - |
| 관리자 | `CreateAmmConfig` | 새로운 수수료 계층을 정의합니다. |
| 관리자 | `UpdateAmmConfig` | 기존 계층의 요금을 변경합니다. |
| 관리자 | `UpdatePoolStatus` | 풀의 작업을 일시 중지/재개합니다. |
| 관리자 | `CreateSupportMintAssociated` | CLMM 풀에서 사용할 Token-2022 민트 확장 구성을 허용 목록에 추가합니다. |
| 관리자 | `CreateOperationAccount` | 프로그램 수준 작업 계정을 초기화합니다 (일회성). |
| 관리자 | `UpdateOperationAccount` | 작업 계정 허용 목록을 수정합니다. |
| 관리자 | `CreateDynamicFeeConfig` | u16 인덱스 아래에 재사용 가능한 동적 수수료 매개변수 집합을 만듭니다. |
| 관리자 | `UpdateDynamicFeeConfig` | 기존 `DynamicFeeConfig`를 수정합니다. 이미 스냅샷한 풀은 영향을 받지 않습니다. |
| 관리자 | `CreatePermissionPda` | `CreatePermissionedPool`을 호출할 권리를 권한에 부여하여 `Permission` PDA를 초기화합니다. |
| 관리자 | `ClosePermissionPda` | 이전에 부여된 권한을 취소하여 `Permission` PDA를 닫습니다. 임차료는 호출자에게 환불됩니다. |
| 풀 | `CreatePool` | `AmmConfig`에 바인딩된 CLMM 풀을 초기화합니다. 표준, FromInput 수수료 경로. `CreateCustomizablePool`과 공존합니다. |
| 풀 | `CreateCustomizablePool` | 새 풀에 권장됩니다. `CreatePool`과 동일한 형태에 `collect_fee_on`과 선택적 `enable_dynamic_fee` 플래그를 추가합니다. |
| 풀 | `CreatePermissionedPool` | 이미 풀이 있는 쌍에 대해 **추가** 풀을 만들고, 클라이언트가 제공한 `seed_index`를 풀 PDA에 포함합니다. 지불자는 `Permission` PDA를 보유해야 합니다. |
| 포지션 | `OpenPosition` / `OpenPositionV2` / `OpenPositionWithToken22Nft` | 포지션 NFT를 발행합니다. `OpenPositionV2`는 V1을 대체합니다. `OpenPositionWithToken22Nft`는 NFT를 Token-2022로 발행합니다. V2 경로는 기본 볼트 민트의 동결 권한이 제한된 발행자 목록과 일치할 때만 NFT 계정을 동결합니다. 그렇지 않으면 계정은 동결되지 않은 상태로 유지됩니다. |
| 포지션 | `IncreaseLiquidity` / `IncreaseLiquidityV2` | 기존 포지션에 유동성을 추가합니다. |
| 포지션 | `DecreaseLiquidity` / `DecreaseLiquidityV2` | 유동성을 제거하고 미지급 수수료를 수집합니다. |
| 포지션 | `ClosePosition` | NFT를 소각하고 `PersonalPositionState`를 닫습니다. NFT 계정이 동결되어 있으면 먼저 남은 계정으로 제공된 풀 PDA로 해동합니다. |
| 포지션 | `CloseProtocolPosition` | 레거시 `ProtocolPositionState` PDA에 대한 관리자 전용 스윕입니다. 현재 프로그램은 더 이상 `ProtocolPositionState`를 생성하거나 읽지 않습니다. 이 명령어는 오래된 프로그램 버전에서 생성된 계정의 임차료를 회수하기 위해서만 존재합니다. |
| 스왑 | `Swap` / `SwapV2` | 상수 유동성 스왑입니다. 두 변형 모두 동적 수수료, 단측 수수료 라우팅 및 지정가 주문 매칭을 적용합니다. 유일한 차이점은 `SwapV2`가 Token-2022 민트를 허용한다는 것입니다 (V1 변형은 두 볼트 모두 클래식 SPL 토큰이어야 함). |
| 스왑 | `SwapRouterBaseIn` | 라우터에서 사용하는 다중 홉입니다. |
| 지정가 주문 | `OpenLimitOrder` | 틱에서 판매 주문을 배치합니다. 미체결 토큰은 틱에 앉아 있으며, 가격이 지나갈 때 매칭 엔진이 채웁니다. |
| 지정가 주문 | `IncreaseLimitOrder` | 기존 공개 주문에 추가합니다. |
| 지정가 주문 | `DecreaseLimitOrder` | 공개 주문을 줄이거나 취소합니다. 미체결 나머지와 이미 정산된 출력을 지급합니다. |
| 지정가 주문 | `SettleLimitOrder` | 채워진 출력 토큰을 주문 소유자에게 푸시합니다. 소유자 또는 운영 키퍼가 호출할 수 있습니다. |
| 지정가 주문 | `CloseLimitOrder` | 완전히 소비된 주문 계정을 닫습니다. 임차료는 항상 주문의 `owner`에게 반환됩니다. 소유자 또는 키퍼가 호출할 수 있습니다. |
| 수수료 | `CollectProtocolFee` | 프로토콜 수수료의 관리자 스윕입니다. |
| 수수료 | `CollectFundFee` | 펀드 수수료의 관리자 스윕입니다. |
| 보상 | `InitializeReward` | 풀에 새로운 보상 스트림을 연결합니다. |
| 보상 | `SetRewardParams` | 기존 보상의 방출 속도/종료를 변경합니다. |
| 보상 | `UpdateRewardInfos` | 보상 성장을 현재로 정산합니다 (모든 스왑 / 포지션 변경으로 호출됨). |
| 보상 | `TransferRewardOwner` | 보상 스트림을 설정하거나 충전할 수 있는 권한을 이전합니다. |
| 보상 | `CollectRemainingRewards` | 보상 스트림의 `end_time` 이후, 어떤 포지션에도 할당되지 않은 토큰을 펀더에게 스윕합니다. |
| 관리자 | `CloseSupportMintAssociated` | 민트의 `SupportMintAssociated` 등록을 제거하고 임차료를 환불합니다. |
| 관리자 | `CollectExcessLamports` | 한 풀의 `PoolState` 및 볼트에서 임차료 면제 최소값 이상의 램포트를 스윕하고, 전달된 CLMM 소유 PDA도 스윕합니다. `admin` 또는 전용 수집 램포트 지갑입니다. 2026-09에 추가됨. |

<Note>
  **초기화 틱 배열 명령어는 없으며 필요하지 않습니다.** 틱 배열은 `OpenPosition*` / `IncreaseLiquidity*` 내에서 `TickArrayState::get_or_create_tick_array`에 의해 생성되며, `payer`가 비용을 지불합니다. (아직 초기화되지 않았을 수 있는 시스템 소유) 틱 배열 PDA를 `tick_array_lower` / `tick_array_upper`로 전달하면 프로그램이 누락된 경우 할당합니다. `OpenLimitOrder`는 단일 `tick_array`에 대해 동일하게 수행합니다.
</Note>

대부분의 관리자 전용 명령어 (`CreateAmmConfig`, `UpdateAmmConfig`, `UpdatePoolStatus`, `CreateOperationAccount`, `UpdateOperationAccount`, `CloseProtocolPosition`)는 프로그램의 하드코딩된 `admin` 공개 키로 제어됩니다. `CreatePermissionPda` / `ClosePermissionPda`는 `admin` 공개 키 **또는** 전용 `permission_pda_admin` 키를 허용합니다. `CreateSupportMintAssociated` / `CloseSupportMintAssociated`는 `admin` 공개 키 **또는** 전용 지원 민트 소유자 키를 허용합니다. `CollectExcessLamports`는 `admin` 공개 키 **또는** 전용 수집 램포트 지갑을 허용합니다. 보상 스트림 관리자 명령어 (`TransferRewardOwner`, `CollectRemainingRewards`)는 프로그램 관리자가 아닌 보상 펀더로 제어됩니다.

V2 접미사는 "볼트 / NFT에서 Token-2022를 지원하며, 비트맵 확장 슬롯이 필요함"을 의미합니다. SDK는 새 풀에 대해 기본적으로 V2를 선택합니다.

## `CreatePool`

**인자**

```
sqrt_price_x64: u128           // 초기 가격
open_time:      u64            // 이 시간 이전의 스왑은 거부됨
```

**계정 (축약)**

| # | 이름 | W | S | 설명 |
| - | - | - | - | - |
| 1 | `pool_creator` | W | S | |
| 2 | `amm_config` | | | 선택된 수수료 계층. |
| 3 | `pool_state` | W | | 여기서 `init`. |
| 4 | `token_mint_0` | | | 정렬됨. |
| 5 | `token_mint_1` | | | |
| 6 | `token_vault_0` | W | | 여기서 `init`, 풀 권한 PDA가 소유. |
| 7 | `token_vault_1` | W | | |
| 8 | `observation_state` | W | | 여기서 `init`. |
| 9 | `tick_array_bitmap` | W | | `TickArrayBitmapExtension` PDA. 여기서 `init`. |
| 10 | `token_program_0` | | | `token_mint_0`을 소유한 프로그램 — SPL Token **또는** Token-2022. |
| 11 | `token_program_1` | | | `token_mint_1`을 소유한 프로그램. |
| 12 | `system_program` | | | |
| 13 | `rent` | | | |

<Warning>
  슬롯 10과 11은 **민트별**이며, "SPL Token 다음 Token-2022"가 아닙니다. 각각은 일치하는 민트의 `mint::token_program`으로 제약되므로, `token_mint_0`이 Token-2022이고 `token_mint_1`이 클래식 SPL인 풀의 경우 슬롯 10에 Token-2022를 전달하고 슬롯 11에 SPL Token을 전달해야 합니다. `CreateCustomizablePool`과 `CreatePermissionedPool`은 동일한 두 슬롯을 사용합니다.
</Warning>

**전제 조건**

* `token_mint_0 < token_mint_1` (바이트 순서).
* `amm_config.disable_create_pool == false`.
* 민트는 Token-2022 확장 허용 목록에 의해 거부되지 않습니다.

**사후 조건**

* `pool_state.sqrt_price_x64 = sqrt_price_x64`, `tick_current = floor(log_{1.0001}(price))`.
* `pool_state.liquidity = 0` (아직 포지션 없음).
* `pool_state.fee_on = FromInput` (레거시 기본값).
* `pool_state.dynamic_fee_info`는 0으로 설정됨 (동적 수수료 비활성화).

## `CreateCustomizablePool`

새 풀에 권장됩니다. `CreatePool`과 동일한 효과에 풀별 수수료 수집 모드와 선택적 동적 수수료 옵트인을 추가합니다.

**인자**

```rust theme={null}
pub struct CreateCustomizableParams {
    pub sqrt_price_x64:    u128,
    pub collect_fee_on:    CollectFeeOn,   // FromInput | Token0Only | Token1Only
    pub enable_dynamic_fee: bool,
}
```

**계정** — `CreatePool`의 정확히 13개 선언된 계정. `dynamic_fee_config`는 선언된 계정이 **아닙니다**.

<Warning>
  `enable_dynamic_fee = true`일 때, 스냅샷할 `DynamicFeeConfig`는 **마지막** `remaining_account`로 제공되어야 합니다. 핸들러는 `ctx.remaining_accounts.last()`를 읽습니다. 모든 `SupportMintAssociated` 바이패스 PDA는 따라서 그 앞에 와야 하며, 그렇지 않으면 프로그램이 잘못된 계정을 스냅샷하고 역직렬화에 실패합니다. 플래그가 설정되어 있는데 생략하면 `AccountLack`으로 실패합니다.
</Warning>

**전제 조건** — `CreatePool`과 동일합니다. `enable_dynamic_fee = false`이면 `remaining_account`가 필요하지 않으며 전달된 모든 것은 지원 민트 레코드만 스캔됩니다.

**사후 조건**

* `pool_state.fee_on`이 선택된 `CollectFeeOn` 변형으로 설정됨.
* 동적 수수료가 활성화된 경우: `pool_state.dynamic_fee_info`는 제공된 `DynamicFeeConfig`에서 초기화됨 (5개 보정 매개변수 복사됨, 상태 필드는 0으로 설정됨).
* 그렇지 않으면: `pool_state.dynamic_fee_info`는 0으로 설정됨 (= 이 풀에 대해 영구적으로 동적 수수료 비활성화).

`fee_on`과 동적 수수료 활성화 비트는 **풀 생성 시에만** 설정됩니다. 제자리 업그레이드는 없습니다. 레거시 `CreatePool`을 통해 생성된 풀은 동적 수수료나 단측 수수료를 소급하여 얻을 수 없습니다. 새 배포는 이 명령어를 기본값으로 사용해야 합니다.

## `CreatePermissionedPool`

`CreatePool`과 `CreateCustomizablePool` 모두 `["pool", amm_config, token_mint_0, token_mint_1]`에서 풀 PDA를 파생하므로, `(config, mint0, mint1)` 트리플당 정확히 **하나의** 정규 풀 주소가 있습니다. 동일한 시드에서 두 번째 `init`은 실패합니다. `CreatePermissionedPool`은 클라이언트가 제공한 `seed_index: u16`을 풀 PDA 시드에 포함하여 이 제한을 해제하므로, **동일한 쌍 및 수수료 계층에 대해 여러 풀**을 만들 수 있습니다. 각각 자신의 주소에서. 임의의 풀 주소는 특권 기능이므로, 지불자는 이를 승인하는 `Permission` PDA를 보유해야 합니다.

풀에 대한 다른 모든 것은 `CreateCustomizablePool`과 동일합니다. 동일한 `CreateCustomizableParams`를 사용하고 단측 수수료 및 동적 수수료 옵트인을 지원합니다.

**인자**

```rust theme={null}
customizable_params: CreateCustomizableParams   // CreateCustomizablePool과 동일한 구조
seed_index:          u16                          // 0이 아니어야 함. 풀 PDA 시드에 포함됨
```

**계정 (축약)** — `CreateCustomizablePool`과 동일하며, 앞에 추가:

| # | 이름 | W | S | 설명 |
| - | - | - | - | - |
| 1 | `payer` | W | S | 임차료를 지불합니다. 아래 `permission` PDA의 권한이어야 합니다. |
| 2 | `pool_creator` | | | `pool_state.owner`로 기록됩니다. `payer`와 다를 수 있습니다. |
| 3 | `permission` | | | PDA `["permission", payer]`. Anchor는 존재하지 않거나 프로그램이 소유하지 않으면 호출을 거부합니다. |
| … | (`amm_config`, `pool_state`, 민트, 볼트, `observation_state`, `tick_array_bitmap`, 토큰/시스템 프로그램) | | | `CreateCustomizablePool`과 같음. |

`pool_state` PDA는 `["pool", amm_config, token_mint_0, token_mint_1, seed_index.to_le_bytes()]`에서 파생됩니다.

**전제 조건**

* `seed_index != 0`. `seed_index`가 `0`이면 레거시 풀용으로 예약되어 있으며 여기서 거부됩니다. `[0, 0]` 시드 구성요소는 레거시 풀 주소를 클래식 4시드 형태로 축소하는 것입니다.
* `payer`에 대한 `permission` PDA가 존재합니다 (관리자가 `CreatePermissionPda`를 통해 생성).
* `CreatePool`과 동일한 민트 / 허용 목록 규칙.

**사후 조건**

* 새로운 `pool_state`가 `seed_index` 파생 주소에 존재하며, `pool_state.seed_index = seed_index`.
* 다른 모든 사후 상태는 `CreateCustomizablePool`과 일치합니다 (수수료 모드, 선택적 동적 수수료).

<Note>
  이 명령어는 일반 풀 생성 액세스를 확대하지 **않습니다**. 권한 없는 생성은 `CreatePool` / `CreateCustomizablePool`을 통해 계속되며, 이는 쌍당 하나의 풀로 유지됩니다. `CreatePermissionedPool`은 화이트리스트된 운영자가 동일한 쌍에 대해 여러 풀이 필요한 경우 (예: 다른 초기 가격 또는 출시 코호트)와 관리자가 부여한 `Permission` PDA를 보유하는 특정 경우에 존재합니다.
</Note>

## `OpenPositionV2` / `OpenPositionWithToken22Nft`

기존 풀 내에 새로운 포지션을 만듭니다.

**인자**

```
tick_lower_index: i32
tick_upper_index: i32
tick_array_lower_start_index: i32
tick_array_upper_start_index: i32
liquidity:  u128              // 원하는 L (또는 아래 금액을 사용하려면 0)
amount_0_max: u64
amount_1_max: u64
with_metadata: bool           // NFT 메타데이터 작성 (Metaplex)
base_flag: Option<bool>       // true = amount0에 맞춤; false = amount1에 맞춤
```

**계정** — 두 변형은 *다른* 계정 목록을 가집니다. `OpenPositionV2`는 22개를 선언합니다. `OpenPositionWithToken22Nft`는 20개를 선언합니다.

`OpenPositionV2`, 순서대로:

| # | 이름 | W | S | 설명 |
| - | - | - | - | - |
| 1 | `payer` | W | S | |
| 2 | `position_nft_owner` | | | |
| 3 | `position_nft_mint` | W | S | 신규 키페어. |
| 4 | `position_nft_account` | W | | 소유자의 NFT용 ATA. |
| 5 | `metadata_account` | W | | Metaplex 메타데이터 PDA. `with_metadata = false`일 때도 **무조건** 선언됨. |
| 6 | `pool_state` | W | | |
| 7 | `protocol_position` | | | 흔적; 읽기 전용 `UncheckedAccount`. |
| 8 | `tick_array_lower` | W | | 초기화되지 않으면 여기서 생성됨. |
| 9 | `tick_array_upper` | W | | 동일. |
| 10 | `personal_position` | W | | 여기서 생성됨. |
| 11 | `token_account_0` | W | | 사용자 소스 ATA. |
| 12 | `token_account_1` | W | | |
| 13 | `token_vault_0` | W | | |
| 14 | `token_vault_1` | W | | |
| 15 | `rent` | | | |
| 16 | `system_program` | | | |
| 17 | `token_program` | | | |
| 18 | `associated_token_program` | | | |
| 19 | `metadata_program` | | | Metaplex 토큰 메타데이터. |
| 20 | `token_program_2022` | | | |
| 21 | `vault_0_mint` | | | |
| 22 | `vault_1_mint` | | | |

`OpenPositionWithToken22Nft`는 `metadata_account` (5)와 `metadata_program` (19)이 **제거된** 동일한 목록입니다. 대신 NFT 민트의 Token-2022 메타데이터 확장을 통해 포지션의 메타데이터를 작성하므로 20개 계정을 제공합니다. 그 `position_nft_mint`는 베어 `Signer`이고 `position_nft_account`는 `UncheckedAccount`입니다.

<Warning>
  **`tick_array_bitmap_extension`은 두 변형 모두에서 선언된 계정이 아닙니다.** 포지션의 범위가 풀의 인라인 `tick_array_bitmap` (±512 틱 배열) 외부에 있으면, `TickArrayBitmapExtension` PDA를 시드 `["pool_tick_array_bitmap_extension", pool_state]`에서 `remaining_accounts[0]`으로 추가합니다.
</Warning>

**수학** — [`products/clmm/math`](/ko/products/clmm/math)를 참조하세요. `base_flag`가 주어지면, 프로그램은 `liquidity` 또는 `(amount_0_max, amount_1_max)` 중 하나를 실제 `L`과 소비된 실제 토큰 금액으로 해결합니다.

**전제 조건**

* `tick_lower < tick_upper`, 모두 `pool.tick_spacing`의 배수, `[MIN_TICK, MAX_TICK]` 내.
* 두 개의 틱 배열 PDA가 전달됩니다. 이미 존재할 필요는 **없습니다**. `get_or_create_tick_array`는 이 명령어 내에서 `payer`의 비용으로 누락된 것을 할당합니다. 별도의 초기화 틱 배열 명령어는 없습니다.
* 사용자는 소스 ATA에 최소 `amount_0_max`와 `amount_1_max`를 가지고 있습니다.

**사후 조건**

* `personal_position`이 존재하고, `liquidity`가 설정되고, `fee_growth_inside_last`가 스냅샷됨.
* `tick_lower`와 `tick_upper`의 틱 배열 항목이 업데이트됨 (`liquidity_gross += L`, `liquidity_net ± L`, 수수료 성장 스냅샷 유지).
* 포지션이 범위 내에 있으면 `pool_state.liquidity += L` (`tick_lower ≤ tick_current < tick_upper`).
* 포지션 NFT 민트는 `pool_state`를 동결 권한으로 기록합니다. 민트 권한은 단일 NFT가 발행된 후 제거됩니다. 동결 권한을 기록하는 것은 NFT 토큰 계정의 상태를 변경하지 않습니다.
* NFT 토큰 계정은 명령어가 `OpenPositionV2` 또는 `OpenPositionWithToken22Nft` **이고** 볼트 민트의 동결 권한이 CLMM의 제한된 발행자 목록과 일치하는 경우를 제외하고는 동결되지 않은 상태로 유지됩니다. 일치하는 V2 경로만 계정을 동결합니다. `OpenPosition` V1은 동결하지 않습니다.

**일반적인 오류** — `TickInvalidOrder` (`tick_lower >= tick_upper`), `TickAndSpacingNotMatch` (끝점이 `tick_spacing`의 배수가 아님), `InvalidTickIndex` (`[MIN_TICK, MAX_TICK]` 외부), `MissingTickArrayBitmapExtensionAccount` (범위가 인라인 비트맵 외부이고 확장이 추가되지 않음), `NotApproved` (`pool_state.status`가 열기를 차단), `ZeroAmountSpecified`.

<Note>
  포지션 동결은 선언된 명령어 계정이나 인자를 추가하지 않습니다. 클라이언트는 기존 V2 레이아웃으로 이러한 포지션을 열 수 있습니다. 동작은 `vault_0_mint`와 `vault_1_mint`에서 온체인으로 선택됩니다.
</Note>

## `IncreaseLiquidityV2`

이미 열려 있는 포지션에 유동성을 추가합니다.

**인자**

```
liquidity: u128
amount_0_max: u64
amount_1_max: u64
base_flag: Option<bool>
```

**계정** — 15개이며, `OpenPosition`에서 파생 **불가능**합니다. `rent`, `system_program`, `associated_token_program` 및 메타데이터 계정이 없습니다.

| # | 이름 | W | S |
| - | - | - | - |
| 1 | `nft_owner` | | S |
| 2 | `nft_account` | | |
| 3 | `pool_state` | W | |
| 4 | `protocol_position` | | |
| 5 | `personal_position` | W | |
| 6 | `tick_array_lower` | W | |
| 7 | `tick_array_upper` | W | |
| 8 | `token_account_0` | W | |
| 9 | `token_account_1` | W | |
| 10 | `token_vault_0` | W | |
| 11 | `token_vault_1` | W | |
| 12 | `token_program` | | |
| 13 | `token_program_2022` | | |
| 14 | `vault_0_mint` | | |
| 15 | `vault_1_mint` | | |

포지션의 범위가 인라인 비트맵 외부에 있으면 `TickArrayBitmapExtension` PDA를 `remaining_accounts[0]`으로 앞에 추가합니다.

**효과**

* 사용자 → 볼트에서 `amount_0_actual` / `amount_1_actual`을 전송합니다.
* `personal_position.liquidity`와 `pool_state.liquidity` (범위 내인 경우)를 증가시키고, 끝점 틱 `liquidity_gross` / `liquidity_net`을 그에 따라 증가시킵니다.
* **마지막 터치 이후 미지급 수수료 및 보상을 수집**하고 `token_fees_owed_{0,1}` / `reward_amount_owed`에 입금합니다. 이들은 `DecreaseLiquidity` / `DecreaseLiquidityV2`에서만 지급되며, 증가에서는 지급되지 않습니다. 독립형 수집 명령어는 없습니다.

## `DecreaseLiquidityV2`

포지션에서 유동성을 제거합니다.

**인자**

```
liquidity: u128
amount_0_min: u64
amount_1_min: u64
```

**계정** — 16개이며, `IncreaseLiquidityV2`와 **동일한 형태나 순서가 아닙니다**. `personal_position`과 `pool_state`가 교환되고, 볼트가 틱 배열 앞에 오고, 사용자 측 계정의 이름이 `recipient_token_account_*`이며, 추가 `memo_program`이 있습니다.

| # | 이름 | W | S |
| - | - | - | - |
| 1 | `nft_owner` | | S |
| 2 | `nft_account` | | |
| 3 | `personal_position` | W | |
| 4 | `pool_state` | W | |
| 5 | `protocol_position` | | |
| 6 | `token_vault_0` | W | |
| 7 | `token_vault_1` | W | |
| 8 | `tick_array_lower` | W | |
| 9 | `tick_array_upper` | W | |
| 10 | `recipient_token_account_0` | W | |
| 11 | `recipient_token_account_1` | W | |
| 12 | `token_program` | | |
| 13 | `token_program_2022` | | |
| 14 | `memo_program` | | |
| 15 | `vault_0_mint` | | |
| 16 | `vault_1_mint` | | |

**남은 계정** — 수집 중인 활성 보상당 3개씩, 순서대로 `reward_token_vault(W)`, `recipient_token_account(W)`, `reward_vault_mint`. 포지션의 범위가 인라인 비트맵 외부에 있으면 `TickArrayBitmapExtension` PDA를 앞에 추가합니다.

<Note>
  이것은 또한 수수료와 보상을 수집하는 **유일한** 방법입니다. 포지션을 변경하지 않고 수집하려면 `liquidity = 0`, `amount_0_min = 0`, `amount_1_min = 0`으로 호출합니다.
</Note>

**효과**

* 현재 `sqrt_price_x64`가 주어진 제거된 `L`에 대해 `(amount_0, amount_1)`을 계산합니다.
* 마지막 터치 이후 발생한 수수료/보상을 정산합니다. `IncreaseLiquidity`와 동일합니다.
* `amount_0 + fees_owed_0`과 `amount_1 + fees_owed_1`을 볼트에서 사용자에게 전송합니다.
* 유동성 카운터를 감소시킵니다. 새로운 `personal_position.liquidity == 0`이면 포지션은 `ClosePosition`에 적합합니다.

**슬리피지** — `amount_0_min`과 `amount_1_min`은 출력 측의 Token-2022 전송 수수료를 차감한 사용자가 수락하는 최소값입니다.

## `ClosePosition`

포지션 NFT를 소각하고 `PersonalPositionState`를 닫습니다.

**선언된 계정**

| # | 이름 | W | S | 설명 |
| - | - | - | - | - |
| 1 | `nft_owner` | W | S | 금액 `1`을 보유하는 NFT 토큰 계정을 소유해야 합니다. |
| 2 | `position_nft_mint` | W | | `personal_position.nft_mint`와 일치해야 합니다. |
| 3 | `position_nft_account` | W | | NFT 소유자의 계정. 클래식 SPL Token 또는 Token-2022일 수 있습니다. |
| 4 | `personal_position` | W | | `nft_owner`로 닫힘. |
| 5 | `system_program` | | | 포지션 상태를 닫습니다. |
| 6 | `token_program` | | | NFT 민트와 토큰 계정을 소유해야 합니다. |

**남은 계정**

* 동결되지 않은 NFT: 필요 없음. 추가 풀 계정은 핸들러가 읽지 않으므로 무해합니다.
* 동결된 NFT: `personal_position.pool_id`를 첫 번째 남은 계정으로 추가합니다. 프로그램은 이를 `PoolState`로 로드하고 해동에 서명하기 위해 PDA 시드를 사용합니다.

**전제 조건**

* `personal_position.liquidity == 0`.
* `tokens_fees_owed_{0,1} == 0`.
* 모든 보상 카운터 `reward_amount_owed == 0`.

(즉, 먼저 모든 것을 수집하고 0으로 감소시킵니다.)

**효과**

* NFT 토큰 계정이 동결되어 있으면, 첫 번째 남은 계정이 `personal_position.pool_id`와 같은지 확인한 다음 풀 PDA로 해동합니다.
* NFT를 소각합니다.
* NFT 토큰 계정과 `personal_position`을 닫고 임차료를 `nft_owner`에게 환불합니다. 포지션 NFT가 Token-2022를 사용하면 NFT 민트도 닫습니다. 클래식 SPL Token 민트는 닫을 수 없으며 공급량 0으로 유지됩니다.

해동, 소각 및 닫기는 원자적입니다. NFT는 이 단계 사이에 양도 가능해질 수 없습니다.

**조건부 클라이언트 중단** — 선언된 IDL 레이아웃은 변경되지 않으므로 레거시 클라이언트는 기존 및 동결되지 않은 포지션을 계속 닫습니다. 풀 남은 계정을 생략하는 레거시 빌더는 동결된 포지션을 닫을 때 `AccountLack`으로 실패합니다. 모든 닫기에 대해 풀을 전달하는 것이 가장 간단한 호환 전략입니다.

## `SwapV2`

유동성 곡선을 따라갑니다. `is_base_input`에 따라 정확한 입력 **또는** 정확한 출력.

**인자**

```
amount: u64                  // is_base_input=true이면 입력, 그렇지 않으면 출력
other_amount_threshold: u64  // 최소 출력 또는 최대 입력
sqrt_price_limit_x64: u128   // 하드 바운드. 0 ⇒ 무제한
is_base_input: bool
```

**계정 (축약)**

| # | 이름 | W | S | 설명 |
| - | - | - | - | - |
| 1 | `payer` | | S | |
| 2 | `amm_config` | | | |
| 3 | `pool_state` | W | | |
| 4 | `input_token_account` | W | | |
| 5 | `output_token_account` | W | | |
| 6 | `input_vault` | W | | |
| 7 | `output_vault` | W | | |
| 8 | `observation_state` | W | | |
| 9 | `token_program` | | | |
| 10 | `token_program_2022` | | | V2. |
| 11 | `memo_program` | | | V2 (일부 Token-2022 경로에 필요). |
| 12 | `input_vault_mint`, `output_vault_mint` | | | V2. |
| 13 | `tick_array_bitmap_extension` (선택사항) | W | | 스왑이 확장으로 이동하는 경우. |
| 14+ | `tick_array` (남은 것) | W | | 예상 범위를 포함할 충분한 배열. |

호출자는 예상 스왑 이동을 포함하는 순위가 지정된 틱 배열 목록을 전달합니다. 프로그램은 필요한 만큼 사용합니다. SDK는 `PoolUtils.computeAmountOutFormat` 또는 API의 견적 끝점을 통해 이 목록을 계산합니다.

**전제 조건**

* `pool_state.status`가 스왑을 허용합니다.
* `now >= open_time`.
* `sqrt_price_limit_x64`는 방향에 대해 `sqrt_price_x64`의 올바른 쪽에 있습니다.

**일반적인 오류** — `TooLittleOutputReceived` (정확한 입력 슬리피지), `TooMuchInputPaid` (정확한 출력 슬리피지), `SqrtPriceLimitOverflow`, `NotEnoughTickArrayAccount`, `InvalidFirstTickArrayAccount`, `MissingTickArrayBitmapExtensionAccount`, `LiquidityInsufficient`, `NotApproved` (스왑 비트가 `pool_state.status`에 설정됨). CLMM에는 `ExceededSlippage` 변형이 없습니다. 그것은 CPMM의 이름이며, `TickArrayNotFound`도 없습니다.

**`SwapV2`가 호출자가 알아야 할 내부적으로 수행하는 것** (2025년 이후 릴리스):

1. **동적 수수료 할증** — `pool.dynamic_fee_info`가 0이 아니면, 프로그램은 마지막 스왑 이후 순회한 틱 거리를 사용하여 변동성 누적기를 업데이트하고 (필터/감소 규칙 포함 [`products/clmm/fees`](/ko/products/clmm/fees)) `AmmConfig.trade_fee_rate` 위에 `dynamic_fee_component`를 추가합니다. 총 수수료는 10% (`MAX_FEE_RATE_NUMERATOR / 1_000_000`)로 제한됩니다.
2. **지정가 주문 매칭** — 가격 이동이 공개 지정가 주문을 보유한 틱을 지날 때, 프로그램은 먼저 해당 틱에서 사용 가능한 지정가 주문 유동성을 채웁니다 (FIFO by `order_phase`), 그 다음 LP 유동성 곡선을 따라갑니다. 채워진 금액은 나중의 정산을 위해 `tick.unfilled_ratio_x64`와 `tick.part_filled_orders_remaining`을 업데이트합니다. 주문 자체는 소유자가 `SettleLimitOrder`를 호출할 때까지 미지급 상태로 유지됩니다.
3. **단측 수수료 라우팅** — `pool.fee_on = Token0Only` 또는 `Token1Only`일 때, 스왑 단계는 여전히 동일한 입력 출력 거래를 계산합니다. 수수료는 구성된 쪽으로 라우팅됩니다. 구성된 수수료 쪽이 출력인 방향의 경우, 수수료는 스왑 출력에서 공제됩니다 (사용자는 `out − fee`를 받음). 입력인 방향의 경우, 동작은 `FromInput`과 일치합니다. `PoolState`의 `is_fee_on_input(zero_for_one)`과 `is_fee_on_token0(zero_for_one)`을 참조하세요.

`Swap` (V1)은 `SwapV2`와 동일한 동적 수수료, 단측 수수료 라우팅 및 지정가 주문 매칭을 구현합니다. 유일하게 부족한 기능은 Token-2022 지원입니다. 두 볼트 모두 클래식 SPL Token이어야 합니다. Token-2022 민트가 있는 풀은 `SwapV2`를 통해 스왑해야 합니다. 애그리게이터와 SDK는 이미 모든 CLMM 레그에 대해 V2를 선호하므로 호출자는 민트 유형에 따라 분기할 필요가 없습니다.

## `OpenLimitOrder`

특정 틱에서 판매 주문을 배치합니다. 주문은 틱별 FIFO 코호트에 앉아 있으며 가격이 지나갈 때 채워집니다.

**인자**

```
nonce_index:    u8       // 사용자가 선택한 논스 계정 인덱스 (지갑당 0..255)
zero_for_one:   bool     // true: 토큰0을 토큰1로 판매; false: 토큰1을 토큰0으로 판매
tick_index:     i32      // pool.tick_spacing의 배수여야 함
amount:         u64      // 입력 토큰 금액
```

**계정 (축약)**

| # | 이름 | W | S | 설명 |
| - | - | - | - | - |
| 1 | `payer` | W | S | 주문 소유자. 임차료를 지불합니다. |
| 2 | `pool_state` | W | | |
| 3 | `tick_array` | W | | `tick_index`를 포함하는 틱 배열. |
| 4 | `limit_order_nonce` | W | | PDA. `init_if_needed` — 이 `nonce_index` 아래에서 사용자의 첫 주문에서 생성됨. |
| 5 | `limit_order` | W | | PDA. 여기서 `init`. |
| 6 | `input_token_account` | W | | 사용자의 입력 ATA. `frozen`에 대해 확인됨. |
| 7 | `output_token_account` | W | | 사용자의 **출력** ATA. 열기에서 차감되지 않음. 나중에 채우기를 전달할 수 있도록 `frozen`이 아닌지 확인하기 위해서만 필요합니다. |
| 8 | `input_vault` | W | | 풀 입력 볼트. |
| 9 | `output_vault` | W | | 풀 출력 볼트. 출력 측의 민트를 식별하여 `output_token_account` / `output_vault_mint` 확인을 위해 사용됩니다. |
| 10 | `input_vault_mint` | | | Token-2022 수수료 처리. |
| 11 | `output_vault_mint` | | | 출력 측의 민트 (동결 확인 쌍). |
| 12 | `input_token_program` | | | SPL 또는 Token-2022. |
| 13 | `system_program` | | | `limit_order`, `limit_order_nonce` 및 필요한 경우 `tick_array` 할당에 자금을 지원합니다. |

**남은 계정** — `[0]` `tick_array_bitmap_extension`, 시작 인덱스가 풀의 인라인 비트맵 외부에 있는 틱 배열을 초기화할 때만 필요합니다. 그렇지 않으면 아무것도 전달하지 마세요.

<Warning>
  **`rent` 계정이 없습니다**. 구조는 `system_program`에서 끝나며 13개의 선언된 계정입니다. 위치 14의 잘못된 `rent`는 선택적 비트맵 확장이 읽히는 정확한 위치에 착지하므로 주문은 인라인 비트맵 외부의 틱 배열을 처음 초기화해야 할 때까지 작동하는 것처럼 보입니다. 그 다음 실패합니다.
</Warning>

<Note>
  **계정 목록 변경 (2026-07 릴리스).** `OpenLimitOrder`는 이제 입력 측에 추가로 **출력 측** 계정 — `output_token_account`, `output_vault`, `output_vault_mint` —도 사용합니다. 이들은 검증에만 사용됩니다. 프로그램은 소유자의 입력 **또는** 출력 토큰 계정이 동결되어 있으면 주문을 거부합니다. 이는 채우기가 실제로 소유자의 출력 ATA로 정산될 수 있음을 보장하며, 이는 허용 목록 / 기본 동결 Token-2022 민트 (예: 권한 있는 토큰)에 중요합니다. 여기서 계정이 아직 해동되지 않았을 수 있습니다. 이전 단측 계정 목록에 대해 구축된 클라이언트는 3개의 출력 계정을 추가해야 합니다.
</Note>

**전제 조건**

* `input_token_account`와 `output_token_account` 모두 동결되지 않음 (그렇지 않으면 `NotApproved`).
* `pool_state.status`가 스왑 (비트 4)과 지정가 주문 (비트 5) 작업을 모두 허용함 (그렇지 않으면 `NotApproved`).
* `tick_index % pool.tick_spacing == 0`이고 `[MIN_TICK, MAX_TICK]` 내.
* `tick_index`는 선택된 방향에 대해 `pool.tick_current`의 **올바른 쪽**에 있습니다 (토큰0 판매 → 틱은 현재 위에 있어야 하며, 그 반대도 마찬가지). 이미 지나간 틱에서 판매하면 즉시 매칭되며 거부됩니다.

**사후 조건**

* `limit_order`가 존재하고, 열기 시간에 `tick.order_phase`와 `tick.unfilled_ratio_x64`를 스냅샷합니다.
* `tick.orders_amount += amount` (현재 코호트에서).
* `limit_order_nonce.order_nonce += 1`.
* `OpenLimitOrderEvent` 발행됨.

**일반적인 오류** — `NotApproved` (입력 또는 출력 토큰 계정 동결, 또는 풀이 스왑 / 지정가 주문 비활성화), `ZeroAmountSpecified` (입력 측 전송 수수료 후 `amount == 0`), `InvalidLimitOrderAmount` (금액이 해당 틱에서 1 기본 단위 미만의 출력을 생성하거나 `u64` 오버플로우), `InvalidTickIndex` (`[MIN_TICK, MAX_TICK]` 외부, 또는 선택된 방향에 대해 `tick_current`의 잘못된 쪽), `TickAndSpacingNotMatch` (`tick_index % pool.tick_spacing != 0`), `OrderPhaseSaturated`.

## `IncreaseLimitOrder`

기존 공개 주문에 추가합니다. 주문의 `owner`만 호출할 수 있습니다.

**인자**

```
amount: u64    // 추가 입력 토큰 금액
```

**계정** — 8개, 입력 측만. 논스 계정, 3개의 출력 측 계정 **및** `system_program`을 모두 제거합니다.

| # | 이름 | W | S |
| - | - | - | - |
| 1 | `owner` | | S |
| 2 | `pool_state` | W | |
| 3 | `tick_array` | W | |
| 4 | `limit_order` | W | |
| 5 | `input_token_account` | W | |
| 6 | `input_vault` | W | |
| 7 | `input_vault_mint` | | |
| 8 | `input_token_program` | | |

**전제 조건**

* `limit_order.owner == signer`.
* 주문은 여전히 동일한 코호트에 있습니다 (`tick.order_phase == limit_order.order_phase`). 코호트가 이미 채우기를 시작했으면 주문이 부분적으로 정산됩니다. 호출자는 먼저 `DecreaseLimitOrder` 또는 `SettleLimitOrder`를 호출하여 앞으로 이동해야 합니다.

**효과**

* 소유자 ATA에서 `input_vault`로 `amount`를 전송합니다.
* `limit_order.total_amount += amount`. `tick.orders_amount += amount`.

## `DecreaseLimitOrder`

공개 주문을 줄이거나 완전히 취소합니다. 미체결 나머지를 소유자에게 지급하고, 과거 부분 채우기로 이미 정산된 출력도 지급합니다.

**인자**

```
amount:     u64    // 인출할 입력 토큰 금액 (최대 = 미체결 나머지)
amount_min: u64    // 입력 측 인출에 대한 슬리피지 바닥
```

**계정** — 입력 및 출력 토큰 측:

| # | 이름 | W | S |
| - | - | - | - |
| 1 | `owner` | | S |
| 2 | `pool_state` | W | |
| 3 | `tick_array` | W | |
| 4 | `limit_order` | W | |
| 5 | \`input\_token\_ | | |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.