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

# AMM v4 코드 예제

> 기존 AMM v4 풀에 대한 스왑 및 유동성 공급을 위한 TypeScript 예제입니다. 새 풀 생성은 범위 밖이므로 CPMM을 사용하세요.

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

  [영문판 보기 →](/products/amm-v4/code-demos)
</Info>

<Info>
  **버전 배너.** 모든 예제는 Solana mainnet-beta에 대해 `@raydium-io/raydium-sdk-v2@0.2.42-alpha`를 대상으로 하며, 2026년 4월에 검증되었습니다. 프로그램 ID: `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` ([`reference/program-addresses`](/ko/reference/program-addresses) 참조).
</Info>

<Info>
  **새 풀 생성은 여기에 표시되지 않습니다.** Raydium UI는 더 이상 AMM v4 풀 생성을 제공하지 않으며, 새 페어는 기본적으로 [CPMM](/ko/products/cpmm/code-demos)으로 설정됩니다. AMM v4 프로그램 자체는 여전히 온체인에서 `Initialize2`를 수락하지만, 권장되는 경로가 아닙니다. 아래 예제는 모든 통합자가 여전히 필요로 하는 라이브 풀 작업을 다룹니다: 스왑, 입금, 출금.
</Info>

## 설정

```ts theme={null}
import { Connection, Keypair, clusterApiUrl } from "@solana/web3.js";
import { Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import fs from "node:fs";

const connection = new Connection(process.env.RPC_URL ?? clusterApiUrl("mainnet-beta"));
const owner = Keypair.fromSecretKey(
  new Uint8Array(JSON.parse(fs.readFileSync(process.env.KEYPAIR!, "utf8"))),
);
const raydium = await Raydium.load({ owner, connection, cluster: "mainnet" });
```

## ID로 풀 가져오기

```ts theme={null}
import { PublicKey } from "@solana/web3.js";

const poolId = new PublicKey("<AMM_V4_POOL_ID>");

// SDK 정규화된 풀 객체를 가져옵니다. AMM v4의 경우 명령어 빌더가 필요로 하는
// OpenBook 계정을 포함합니다.
const data = await raydium.liquidity.getPoolInfoFromRpc({ poolId });
const { poolInfo, poolKeys, poolRpcData } = data;

console.log("Pair:", poolInfo.mintA.symbol, "/", poolInfo.mintB.symbol);
console.log("Version:", poolInfo.version);       // AMM v4의 경우 4
console.log("Market:", poolKeys.marketId.toBase58());
```

`poolKeys`는 명령어 빌더가 사용하는 구조체입니다. 프로그램이 예상하는 순서대로 모든 AMM v4 및 OpenBook 계정을 포함합니다.

## 스왑 (기본 입력)

```ts theme={null}
import BN from "bn.js";

const amountIn = new BN(1_000_000);            // 1 USDC (6자리 쿼트)
const inputMint = new PublicKey(poolInfo.mintB.address);  // USDC
const slippage  = 0.005;

const computed = raydium.liquidity.computeAmountOut({
  poolInfo,
  amountIn,
  mintIn: inputMint,
  mintOut: new PublicKey(poolInfo.mintA.address),
  slippage,
});

const { execute } = await raydium.liquidity.swap({
  poolInfo,
  poolKeys,
  amountIn,
  amountOut: computed.minAmountOut,
  fixedSide: "in",
  inputMint,
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Swap tx:", txId);
```

SDK는 AMM v4 스왑을 V2 진입점을 통해 라우팅하며, 이는 OpenBook 계정을 포함하지 않습니다. (2026년 7월 업그레이드 후 마켓 계정은 레거시 v1 경로에서도 더 이상 검증되지 않습니다.)

## 스왑 (기본 출력)

```ts theme={null}
const amountOut = new BN(1_000_000_000);       // 1 SOL (9자리 기본)
const slippage  = 0.005;

const computed = raydium.liquidity.computeAmountIn({
  poolInfo,
  amountOut,
  mintOut: new PublicKey(poolInfo.mintA.address),
  mintIn: new PublicKey(poolInfo.mintB.address),
  slippage,
});

const { execute } = await raydium.liquidity.swap({
  poolInfo,
  poolKeys,
  amountIn: computed.maxAmountIn,
  amountOut,
  fixedSide: "out",
  inputMint: new PublicKey(poolInfo.mintB.address),
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

## 유동성 추가

```ts theme={null}
const amountA = new BN(100_000_000);           // 0.1 SOL

const { anotherAmount, maxAnotherAmount } = raydium.liquidity.computePairAmount({
  poolInfo,
  amount: amountA,
  baseIn: true,
  slippage: 0.01,
});

const { execute } = await raydium.liquidity.addLiquidity({
  poolInfo,
  poolKeys,
  amountInA: amountA,
  amountInB: maxAnotherAmount,
  fixedSide: "a",
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

`fixedSide: "a"`는 정확한 `amountInA`를 제공했으며 `amountInB`는 최대 `maxAnotherAmount`여야 함을 SDK에 알립니다. 풀의 온체인 유동성은 비례 수학 전에 정산되므로 입금 비율이 최신 준비금과 일치합니다.

## 유동성 제거

```ts theme={null}
const lpAmount = new BN(50_000);               // 소각할 LP

const { execute } = await raydium.liquidity.removeLiquidity({
  poolInfo,
  poolKeys,
  lpAmount,
  baseAmountMin: new BN(0),
  quoteAmountMin: new BN(0),
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

슬리피지 최소값은 사전 견적과 착륙 시간 사이에 풀의 상태가 변하는 것을 방지합니다.

## 컴퓨트 유닛 / 우선순위 수수료 조정

AMM v4 스왑은 모든 명령어가 전체 OpenBook 상태를 검증하기 때문에 컴퓨트 집약적입니다. 일반적인 스왑은 경로를 통해 정산해야 하는 미결 주문 수에 따라 180k\~250k CU를 사용합니다. 항상 컴퓨트 유닛 제한을 전달하세요:

```ts theme={null}
import { ComputeBudgetProgram } from "@solana/web3.js";

const { execute, innerTransactions } = await raydium.liquidity.swap({
  /* ...params... */
  computeBudgetConfig: {
    units: 400_000,
    microLamports: 50_000,       // 우선순위 수수료
  },
});
```

`computeBudgetConfig`를 생략하면 SDK가 여전히 자체 기본값을 사용할 수 있습니다. `innerTransactions`를 검사하여 확인하세요. [`integration-guides/priority-fee-tuning`](/ko/integration-guides/priority-fee-tuning)을 참조하세요.

## 직접 Rust CPI

자신의 Anchor 프로그램에서 AMM v4로 CPI해야 하는 경우, `SwapBaseIn`의 계정 목록을 정확히 모델링해야 합니다. 최소 스케치:

```rust theme={null}
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program::invoke_signed;
use anchor_lang::solana_program::instruction::Instruction;

const AMM_V4_PROGRAM_ID: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");

#[derive(Accounts)]
pub struct ProxyAmmV4Swap<'info> {
    /// CHECK:
    pub token_program: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm:          UncheckedAccount<'info>,
    /// CHECK:
    pub amm_authority: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm_open_orders: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm_target_orders: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub pool_coin_token_account: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub pool_pc_token_account: UncheckedAccount<'info>,
    /// CHECK:
    pub market_program: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_bids: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_asks: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_event_queue: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_coin_vault: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_pc_vault: UncheckedAccount<'info>,
    /// CHECK:
    pub market_vault_signer: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub user_source: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub user_dest: UncheckedAccount<'info>,
    pub user_owner: Signer<'info>,
}

pub fn proxy_swap(
    ctx: Context<ProxyAmmV4Swap>,
    amount_in: u64,
    minimum_amount_out: u64,
) -> Result<()> {
    // SwapBaseIn의 명령어 판별자는 AMM v4에서 9입니다.
    let mut data = vec![9u8];
    data.extend_from_slice(&amount_in.to_le_bytes());
    data.extend_from_slice(&minimum_amount_out.to_le_bytes());

    let ix = Instruction {
        program_id: AMM_V4_PROGRAM_ID,
        accounts: vec![
            AccountMeta::new_readonly(ctx.accounts.token_program.key(), false),
            AccountMeta::new(ctx.accounts.amm.key(), false),
            AccountMeta::new_readonly(ctx.accounts.amm_authority.key(), false),
            AccountMeta::new(ctx.accounts.amm_open_orders.key(), false),
            AccountMeta::new(ctx.accounts.amm_target_orders.key(), false),
            AccountMeta::new(ctx.accounts.pool_coin_token_account.key(), false),
            AccountMeta::new(ctx.accounts.pool_pc_token_account.key(), false),
            AccountMeta::new_readonly(ctx.accounts.market_program.key(), false),
            AccountMeta::new(ctx.accounts.market.key(), false),
            AccountMeta::new(ctx.accounts.market_bids.key(), false),
            AccountMeta::new(ctx.accounts.market_asks.key(), false),
            AccountMeta::new(ctx.accounts.market_event_queue.key(), false),
            AccountMeta::new(ctx.accounts.market_coin_vault.key(), false),
            AccountMeta::new(ctx.accounts.market_pc_vault.key(), false),
            AccountMeta::new_readonly(ctx.accounts.market_vault_signer.key(), false),
            AccountMeta::new(ctx.accounts.user_source.key(), false),
            AccountMeta::new(ctx.accounts.user_dest.key(), false),
            AccountMeta::new_readonly(ctx.accounts.user_owner.key(), true),
        ],
        data,
    };
    invoke_signed(&ix, &ctx.accounts.to_account_infos(), &[])?;
    Ok(())
}
```

AMM v4는 CPI용 Anchor 크레이트를 제공하지 않습니다. 위의 스케치는 수동으로 구성된 `Instruction`을 사용합니다.

<Note>
  위의 CPI 스케치는 기존 트랜잭션을 읽거나 재현하기 위해 **레거시 v1 `SwapBaseIn` 레이아웃**(태그 9, 17개 계정)을 사용합니다. 2026년 7월 업그레이드 이후 마켓 계정은 수락되지만 무시됩니다. **새 코드의 경우 `SwapBaseInV2` / `SwapBaseOutV2`** (태그 16 / 17)를 선호하세요. 이는 마켓 계정(및 `amm_open_orders`)을 완전히 제거하고 8개 계정만 전달합니다: `token_program`, `amm`, `amm_authority`, 두 개의 풀 금고, 두 개의 사용자 토큰 계정, 및 `user_owner`.
</Note>

## 함정

* **v1 스왑의 잘못된 계정 수.** 레거시 `SwapBaseIn` / `SwapBaseOut`은 여전히 전체 17개 계정(또는 18개 계정) 목록이 필요합니다. 일치하지 않는 *수*는 `WrongAccountsNumber`로 되돌립니다. 마켓 계정의 *내용*은 더 이상 검증되지 않지만 여전히 해당 슬롯을 차지해야 합니다. 이를 피하려면 V2 진입점을 선호하세요.
* **원본 금고 잔액 읽기.** 준비금은 이제 금고 전용입니다. 누적된 PnL(`need_take_pnl_*`)을 빼세요. SDK 견적 또는 `api-v3.raydium.io/pools/info/ids`가 이를 처리합니다.
* **제거된 명령어 호출.** `MonitorStep`, `MigrateToOpenBook`, `WithdrawSrm`, `SimulateInfo`, `AdminCancelOrders`, 및 레거시 `Initialize` / `PreInitialize`는 이제 되돌립니다. 풀 생성을 위해 `Initialize2`를 사용하세요. 호출할 크랭크가 없습니다.
* **Token-2022 민트.** 지원되지 않습니다. AMM v4 풀은 Token-2022 민트에 대해 생성될 수 없습니다. 모든 Token-2022 페어는 CPMM 또는 CLMM에 있어야 합니다.

## 다음 단계

* [`products/amm-v4/instructions`](/ko/products/amm-v4/instructions) — 이 예제 뒤의 명령어 수준 표면.
* [`user-flows/migrate-amm-v4-to-cpmm`](/ko/user-flows/migrate-amm-v4-to-cpmm) — 마이그레이션을 고려 중인 LP의 경우.
* [`integration-guides/priority-fee-tuning`](/ko/integration-guides/priority-fee-tuning) — 무거운 AMM v4 스왑을 위한 우선순위 수수료 크기 조정.

출처:

* [Raydium SDK v2](https://github.com/raydium-io/raydium-sdk-V2)
* [Raydium AMM program](https://github.com/raydium-io/raydium-amm)
