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

# カスタムプログラムからの CPI

> オンチェーンプログラムが Raydium を呼び出す構成パターン — エスクロー、アグリゲーター プロキシ、自動複利運用ヴォルト — アカウント構築、PDA 署名者、残りのアカウント、コンピュート予算の完全な実装例を含みます。

<Info>
  **このページは AI による自動翻訳です。すべての内容は英語版を正とします。**

  [英語版を表示 →](/integration-guides/cpi-integration)
</Info>

<Info>
  [`sdk-api/rust-cpi`](/ja/sdk-api/rust-cpi) では、各 Raydium プログラムを呼び出すための低レベルの仕組みについて説明しています。このページはその上位レベルの補足です。Raydium をカスタムプログラムに組み込む *理由*、ユースケースに適した *パターン*、そして端から端までの完全な実装方法について説明します。
</Info>

## CPI が適切なツールである場合

カスタムプログラムは、トレードがあなたのプログラムのみが実行できる他のオンチェーン状態変更と原子的に発生する必要がある場合に有効です。一般的なケースは以下の通りです。

* **エスクロー・リミットオーダープログラム** — ユーザーがミントをエスクロー内に預け、プログラムが価格条件を監視し、トリガーされたときにプログラムが原子的に Raydium を通じてスワップし、ユーザーのアカウントにクレジットします。
* **アグリゲーター プロキシ** — Raydium と 1 つ以上の他の DEX を通じてスワップをルーティングする単一の命令で、すべてのホップがプログラムが所有する単一のスリッページチェック下にあります。
* **自動複利運用ヴォルト** — LP またはファーム ステークをヴォルトに預け、ヴォルトがスケジュールに従ってリワードを収穫し、流動性を再供給し、シェアトークンを発行します。
* **ストラテジー ヴォルト** — CLMM を通じてスワップして再バランスするレバレッジ LP ポジション、ポジションをクローズして 1 つのトランザクションで担保をスワップするリクイデーター。
* **カスタム ベスティング付きトークン起動プラットフォーム** — プログラムがベスティング トークンを保有し、スケジュールに従って Raydium プールにリリースします。

オフチェーン コードからスワップを送信したいだけの場合、CPI は過剰です — SDK を使用してください。CPI が複雑さに見合う価値があるのは、独自の状態との原子性が要件である場合のみです。

## 構成パターン

### パターン 1: シン プロキシ

プログラムは単一の命令を公開し、いくつかのポリシー（例：ホワイトリストに登録されたミント ペア、検証済みユーザーの手数料割引）を検証してから Raydium に転送します。

```
┌──────────────┐   user tx    ┌────────────────┐  CPI  ┌──────────┐
│ user         │─────────────▶│ your program   │──────▶│ Raydium  │
└──────────────┘              │  (validate)    │       │  (CPMM)  │
                              └────────────────┘       └──────────┘
```

状態はユーザーの ATA に存在します。プログラムはトークンを所有しません。最小限の信頼フットプリント。

### パターン 2: エスクロー

プログラムはユーザーの入力ミントを保有する PDA を所有しています。トリガー時に、PDA は Raydium へのスワップの CPI に署名します。

```
           deposit                   trigger
   user ───────────▶  PDA vault  ───────────────▶  Raydium swap
                     (your prog)                    (signed by PDA)
                                                            │
                                                            ▼
                                                    PDA vault (output mint)
                                                            │
                                                     withdraw ▼
                                                         user
```

重要な詳細：PDA は `CpiContext::new_with_signer` を通じて署名します。[PDA 署名者シード](#pda-署名者シード)を参照してください。

### パターン 3: 構成マルチホップ

プログラムは 1 つの命令で複数の CPI を発行し、すべてのホップ全体で単一のスリッページ境界を強制します。Raydium スワップ命令はそれぞれ独自の `minimum_amount_out` を持ちますが、それらを 0（または非常に緩い下限）に設定し、最後のホップの後に厳密な最終最小値を自分で強制します。

```
instruction:
  CPI swap: tokenA → tokenB   (raydium, loose min)
  CPI swap: tokenB → tokenC   (raydium / third-party, loose min)
  CPI swap: tokenC → tokenD   (raydium, loose min)
  require(user.tokenD_ata.amount - pre_balance >= user_min_out)
```

これにより、ルート全体に対して単一のリバート ゲートが得られます。すべてのホップがスリッページセーフであると信頼する場合にのみこのパターンを使用してください。そうでない場合は、各ホップに独自の最小値を強制させてください。

### パターン 4: ヴォルト・ストラテジー

プログラムは PDA に LP トークンまたはファーム ステークを保有しています。キーパー（またはユーザー）が `compound()` を呼び出すと、以下が実行されます。

1. ファームからリワードを収穫します。
2. リワードをプール トークンにスワップします（CPMM または CLMM への CPI）。
3. 収益を LP に戻します（別の CPI）。
4. 新しい LP をステークします（別の CPI）。

すべて 1 つのトランザクションで実行されるため、ヴォルトの NAV は原子的に移動します。コンピュート予算は通常 600k～1M CU です。アドレス ルックアップ テーブルは必須です。

## アカウント リスト構築

呼び出し元プログラムの `Accounts` 構造体は Raydium プログラムのアカウント順序を反映しますが、ほとんどの Raydium 側のアカウントは `UncheckedAccount` です。Raydium がそれ自体で検証するためです。*あなた*が所有するアカウントにのみ制約を追加します。

```rust theme={null}
use anchor_lang::prelude::*;
use anchor_spl::token::{Token, TokenAccount};

#[derive(Accounts)]
pub struct EscrowSwap<'info> {
    /// The escrow PDA; holds input mint and signs the CPI.
    #[account(
        mut,
        seeds = [b"escrow", user.key().as_ref()],
        bump = escrow.bump,
    )]
    pub escrow: Account<'info, Escrow>,

    #[account(mut)]
    pub user: Signer<'info>,

    // ----- Raydium-side accounts, mostly unchecked -----

    /// CHECK: validated by CPMM
    #[account(mut)] pub pool_state: UncheckedAccount<'info>,
    /// CHECK: validated by CPMM
    pub amm_config: UncheckedAccount<'info>,
    /// CHECK: validated by CPMM
    pub pool_authority: UncheckedAccount<'info>,
    #[account(mut)] pub input_vault:  Account<'info, TokenAccount>,
    #[account(mut)] pub output_vault: Account<'info, TokenAccount>,
    /// CHECK: validated by CPMM
    #[account(mut)] pub observation_state: UncheckedAccount<'info>,

    /// Escrow's input ATA — owned by the escrow PDA.
    #[account(
        mut,
        associated_token::mint = input_mint,
        associated_token::authority = escrow,
    )]
    pub escrow_input_ata: Account<'info, TokenAccount>,

    /// Escrow's output ATA.
    #[account(
        mut,
        associated_token::mint = output_mint,
        associated_token::authority = escrow,
    )]
    pub escrow_output_ata: Account<'info, TokenAccount>,

    pub input_mint:  Account<'info, anchor_spl::token::Mint>,
    pub output_mint: Account<'info, anchor_spl::token::Mint>,

    pub cpmm_program:       Program<'info, raydium_cp_swap::program::RaydiumCpSwap>,
    pub token_program:      Program<'info, Token>,
    pub token_program_2022: Program<'info, anchor_spl::token_2022::Token2022>,
}
```

非対称性 — あなたのアカウントに対する厳密な検証、Raydium のアカウントに対する `UncheckedAccount` — は怠惰ではありません。受信者は独自に検証します。呼び出し元で二重検証するだけで CU が消費され、Raydium が新しい構造体レイアウト フィールドを出荷するときに同期がずれるリスクがあります。

## CPI 呼び出し自体

```rust theme={null}
use raydium_cp_swap::cpi::{self, accounts::Swap as CpmmSwap};

pub fn escrow_swap(
    ctx: Context<EscrowSwap>,
    amount_in: u64,
    minimum_amount_out: u64,
) -> Result<()> {
    let user_key = ctx.accounts.user.key();
    let bump     = ctx.accounts.escrow.bump;
    let seeds: &[&[u8]] = &[b"escrow", user_key.as_ref(), &[bump]];
    let signer: &[&[&[u8]]] = &[seeds];

    let cpi_accounts = CpmmSwap {
        // `payer` is the account whose signature must cover the input ATA. That ATA is
        // owned by the escrow PDA, so the escrow — signing via new_with_signer below —
        // goes here, NOT `user`.
        payer:                ctx.accounts.escrow.to_account_info(),
        // `authority` is CPMM's own vault PDA at [b"vault_and_lp_mint_auth_seed"];
        // CPMM signs the vault leg with it itself. Passing your escrow here fails the
        // seeds constraint before any transfer runs.
        authority:            ctx.accounts.pool_authority.to_account_info(),
        amm_config:           ctx.accounts.amm_config.to_account_info(),
        pool_state:           ctx.accounts.pool_state.to_account_info(),
        input_token_account:  ctx.accounts.escrow_input_ata.to_account_info(),
        output_token_account: ctx.accounts.escrow_output_ata.to_account_info(),
        input_vault:          ctx.accounts.input_vault.to_account_info(),
        output_vault:         ctx.accounts.output_vault.to_account_info(),
        input_token_program:  ctx.accounts.token_program.to_account_info(),
        output_token_program: ctx.accounts.token_program.to_account_info(),
        input_token_mint:     ctx.accounts.input_mint.to_account_info(),
        output_token_mint:    ctx.accounts.output_mint.to_account_info(),
        observation_state:    ctx.accounts.observation_state.to_account_info(),
    };

    // Anchor 1.0 takes the program's Pubkey here, not its AccountInfo.
    let cpi_ctx = CpiContext::new_with_signer(
        *ctx.accounts.cpmm_program.key,
        cpi_accounts,
        signer,
    );

    cpi::swap_base_input(cpi_ctx, amount_in, minimum_amount_out)?;
    Ok(())
}
```

### PDA 署名者シード

CPI は、`authority` として渡された PDA が呼び出し元が主張する導出と一致する場合にのみ成功します。2 つは以下に同意する必要があります。

1. シード バイト シーケンス（ここでは `[b"escrow", user.key().as_ref()]`）。
2. バンプ。
3. 呼び出し元プログラム ID（Raydium ではなく、あなたのプログラム）。

PDA が何と一致しなければならないかに注意してください。CPMM の `authority` スロットは **CPMM 自身の** ボルト PDA です — プログラム全体で固定されたアカウントで、CPMM がそれを導出し自ら署名します。あなたのプログラムはこれを制御も差し替えもできません。あなたの PDA シードが一致しなければならないアカウントは **`payer`** です：チェックは CPMM 自身の `transfer_from_user_to_pool_vault` ヘルパー内で行われ、`payer` として渡されたアカウントが `input_token_account` の所有者であることを要求します。

一般的なバグ：`escrow_input_ata` がエスクロー PDA によって所有されているのに、`user` を `payer` として渡すことです。SPL Token プログラムは `owner mismatch` で拒否します。常に `payer` を ATA の所有者にしてください — そしてその所有者が PDA の場合は `new_with_signer` で署名してください。

## 残りのアカウント

いくつかの Raydium 命令は、固定されたアカウントの後に追加された可変長のアカウント リストを取ります — **残りのアカウント**。

* **CLMM `SwapV2`**: スワップが走査する可能性のあるティック配列の 1～8 個の `TickArrayState` アカウント（スワップ方向）。
* **Farm v6 `Deposit` / `Harvest` / `Withdraw`**: `(reward_vault, user_reward_ata)` ペア、ライブ リワード スロットごとに 1 ペア。
* **Token-2022 転送フック ミント**: 転送フック プログラムと、フックが必要とするアカウント。

Anchor CPI ヘルパーは残りのアカウントを型チェックしません。それらを通す：

```rust theme={null}
let cpi_ctx = CpiContext::new_with_signer(program_id, accounts, signer)
    .with_remaining_accounts(ctx.remaining_accounts.to_vec());
```

**順序が重要です。** CLMM の場合：

```
remaining = [
    tick_array_in_direction_0,    // first one crossed
    tick_array_in_direction_1,
    ...,
]
```

Farm v6 ハーベストの場合：

```
remaining = [
    reward_vault_0, user_reward_ata_0,
    reward_vault_1, user_reward_ata_1,
    // omit any slot whose reward_state is Uninitialized
]
```

呼び出し元プログラムは、クライアントから受け取った残りのアカウントを変更されずに通す必要があります。フィルタリングまたは並べ替えを試みないでください。

## 構成呼び出しのコンピュート予算

CPI は呼び出しフレーム自体に約 1,500 CU のコストがかかります。被呼び出し側の独自の CU 使用量がその上に積み重なります。

以下の被呼び出し側の数値は、**2026-09-09** に高ボリュームのプールで実行されたライブのメインネット トランザクションから、Raydium プログラム自身の呼び出しに対する `Program <id> consumed N of M compute units` ログ行を読み取って測定したものです（したがって内部のトークン プログラム CPI も含まれます）：

| 呼び出される命令 | 測定 CU | サンプル |
| - | - | - |
| CPMM `swap_base_input` | \~23,000 | 22,721–23,052 (n=8) |
| AMM v4 swap | \~26,000 | 25,215–26,002 |
| CLMM `swap`（V1） | \~41,000 | 37,148–44,721 |
| CLMM `swap_v2` | \~48,000 | 43,838–52,887 (n=6) |
| CLMM `increase_liquidity_v2` | \~36,000 | 36,058 |

各 CPI フレームにつき約 1,500 と、あなた自身のプログラムのオーバーヘッドをその上に追加してください。CLMM のスワップ コストはティック交差の回数に応じて増えるため、その数値は下限として扱ってください。Token-2022 ミントは転送自体の拡張機能処理コストを追加します。一律の乗数を当てるのではなく、自分のミントで測定してください。

<Warning>
  このページの以前の版には 5～7 倍高い見積もり（CPMM スワップ約 150,000 CU、CLMM 約 180,000）が
  記載されていました。それらは一度も測定されたものではありません。ドキュメント化された数値ではなく、
  自分自身の `computeUnitsConsumed` の読み取り値から予算を組んでください — また、ATA 作成、wSOL の
  ラッピング、compute-budget 命令を数えると、トランザクション全体は Raydium 命令単体よりも高くつく
  ことに注意してください。
</Warning>

常に明示的な `ComputeBudgetProgram::set_compute_unit_limit` を設定してください。

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

const tx = new Transaction().add(
  ComputeBudgetProgram.setComputeUnitLimit({ units: 900_000 }),
  ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFeeMicroLamports }),
  yourInstruction,
);
```

デフォルトの 200k CU 上限は、構成呼び出しが完了する前に静かに枯渇します。

## エラー伝播

Raydium のプログラムは安定したエラー コードを持つ Anchor エラーを返します。呼び出し元プログラムはそれらを `Err(ProgramError::Custom(code))` として見ます。デフォルトでバブルアップします。

```rust theme={null}
cpi::swap_base_input(cpi_ctx, amount_in, minimum_amount_out)?;
```

または特定のコードをインターセプトします。

```rust theme={null}
use anchor_lang::error::{Error, ERROR_CODE_OFFSET};
use raydium_cp_swap::error::ErrorCode as CpmmErr;

match cpi::swap_base_input(cpi_ctx, amount_in, minimum_amount_out) {
    Ok(_) => {}
    Err(Error::ProgramError(pe))
        if pe.program_error
            == ProgramError::Custom(CpmmErr::ExceededSlippage as u32 + ERROR_CODE_OFFSET) =>
    {
        // Your program might want to retry at a larger slippage, or unwind state.
        return err!(YourErr::PoolTooVolatile);
    }
    Err(err) => return Err(err),
}
```

`ERROR_CODE_OFFSET` という項に注意してください：`#[error_code]` のバリアントは 6000 から始まる番号で発行されるため、素の enum 判別子と比較しても一致することはありません。（`anchor-lang` にも `raydium_cp_swap` にも `is_err` ヘルパーは存在しません — このページの以前のリビジョンでは、存在しないヘルパーを使用していました。）

エラー コードから意味へのマッピングは IDL ポリシーごとに安定しています（[`sdk-api/anchor-idl`](/ja/sdk-api/anchor-idl)を参照）。新しいコードは最後に追加され、既存のコードの意味は変わりません。

## 完全な実装例：リミットオーダー エスクロー

フロー：

1. **`open_order`** — ユーザーが `input_mint` の `amount_in` をエスクロー PDA に預け、ターゲット `min_amount_out` と有効期限を記録します。
2. **`execute_order`** — 誰でも（キーパー）現在のプール アカウントで呼び出すことができます。プログラムは現在の見積もりが `min_amount_out` 以上であることを確認してから、Raydium スワップに CPI し、出力をエスクロー内に保持します。
3. **`claim`** — ユーザーはエスクロー内から出力ミントを引き出します。

```rust theme={null}
#[account]
pub struct LimitOrder {
    pub user:          Pubkey,
    pub input_mint:    Pubkey,
    pub output_mint:   Pubkey,
    pub amount_in:     u64,
    pub min_out:       u64,
    pub expiry_unix:   i64,
    pub state:         u8,    // 0 open, 1 filled, 2 cancelled, 3 expired
    pub bump:          u8,
}

#[program]
pub mod limit_orders {
    use super::*;

    pub fn execute_order(
        ctx: Context<ExecuteOrder>,
    ) -> Result<()> {
        let order = &ctx.accounts.order;
        require!(order.state == 0, OrderErr::NotOpen);
        require!(Clock::get()?.unix_timestamp < order.expiry_unix, OrderErr::Expired);

        let user_key = order.user;
        let seeds: &[&[u8]] = &[b"order", user_key.as_ref(), &[order.bump]];
        let signer: &[&[&[u8]]] = &[seeds];

        let pre_out_balance = ctx.accounts.escrow_output_ata.amount;

        let cpi_accounts = CpmmSwap {
            // The escrow's input ATA is owned by the `order` PDA, so `order` must be
            // `payer` — it signs via new_with_signer below. The keeper only pays the
            // transaction fee; it is not the ATA owner.
            payer:                ctx.accounts.order.to_account_info(),
            // CPMM's own vault PDA at [b"vault_and_lp_mint_auth_seed"].
            authority:            ctx.accounts.pool_authority.to_account_info(),
            amm_config:           ctx.accounts.amm_config.to_account_info(),
            pool_state:           ctx.accounts.pool_state.to_account_info(),
            input_token_account:  ctx.accounts.escrow_input_ata.to_account_info(),
            output_token_account: ctx.accounts.escrow_output_ata.to_account_info(),
            input_vault:          ctx.accounts.input_vault.to_account_info(),
            output_vault:         ctx.accounts.output_vault.to_account_info(),
            input_token_program:  ctx.accounts.token_program.to_account_info(),
            output_token_program: ctx.accounts.token_program.to_account_info(),
            input_token_mint:     ctx.accounts.input_mint.to_account_info(),
            output_token_mint:    ctx.accounts.output_mint.to_account_info(),
            observation_state:    ctx.accounts.observation_state.to_account_info(),
        };

        let cpi_ctx = CpiContext::new_with_signer(
            *ctx.accounts.cpmm_program.key,
            cpi_accounts,
            signer,
        );

        // Let the escrow enforce the minimum — we trust Raydium's slippage, but we
        // also re-check our own post-swap delta in case a future change ever relaxes it.
        cpi::swap_base_input(cpi_ctx, order.amount_in, order.min_out)?;

        ctx.accounts.escrow_output_ata.reload()?;
        let delta = ctx.accounts.escrow_output_ata.amount
            .checked_sub(pre_out_balance)
            .ok_or(error!(OrderErr::AccountingError))?;
        require!(delta >= order.min_out, OrderErr::InsufficientOutput);

        let order = &mut ctx.accounts.order;
        order.state = 1;
        Ok(())
    }
}
```

キーパーはトランザクション手数料を支払います（他の場所でキーパー手数料を取得します — 表示されていません）。`order` PDA はエスクローの入力 ATA を所有しているため、`payer` として CPI に署名します。したがって `ExecuteOrder` には、CPMM 自身のボルト PDA 用に `pool_authority: UncheckedAccount<'info>` フィールドも必要です。Raydium 側のスリッページ チェック *と* エスクロー独自のデルタ チェックの両方が下限を強制します — 二重チェック。

## テスト

Raydium プログラムをローカル バリデーターにプルして統合テストを実行します（`Anchor.toml` から）。

```toml theme={null}
[test.validator]
clone = [
  { address = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C" }, # CPMM
  { address = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK" }, # CLMM
  { address = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" }, # AMM v4
  { address = "FarmqiPv5eAj3j1GMdMCMUGXqPUvmquZtMy86QH6rzhG" }, # Farm v6
]
```

プール状態アカウントもクローンして、テストが実際にスワップを実行できるようにします。`anchor test` はスタートアップ時にメインネットからそれらをフェッチします。[`sdk-api/rust-cpi`](/ja/sdk-api/rust-cpi#testing-a-cpi-flow) を参照してください。

## 構成に固有の落とし穴

### リエントランシー

Solana には真のリエントランシーはありません — CPI は同じ呼び出しで元のプログラムに呼び戻すことはできません。しかし、論理的なリエントランシーに自分自身を構築することはできます。CPI があなたの状態を読み、その後あなたのコードが CPI がそれを変更しなかったと仮定して再度読みます。Raydium の場合、CPI はあなたの状態に触れないため、これはフラッシュ ローン コンテキストなどほど懸念事項ではありません。しかし、Raydium をレンディング プロトコルと構成する場合は注意してください。

### アカウント可変性ドリフト

プログラムがアカウントを `mut` として渡すが、Raydium がそれを読み取り専用として期待する場合（またはその逆）、ランタイムは `InvalidAccountData` で呼び出しを拒否します。常に IDL で Raydium の命令の予期される可変性を確認してください。`raydium_cp_swap::cpi::accounts::Swap` は、CPMM 自身の `Swap` 構造体に付いた `#[account(mut)]` マーカーから、各アカウントの可変性を代わりに設定します — 生成されるフィールドはすべて素の `AccountInfo<'info>` なので、フラグを運ぶのはフィールド型ではなく、導出された `ToAccountMetas` の実装です。

### Token-2022 プログラム フィールド

入力ミントと出力ミントは異なるトークン プログラムの下にある可能性があります — 1 つは SPL Token、1 つは Token-2022。CPI にはこの理由のために個別の `input_token_program` と `output_token_program` フィールドがあります。常に各ミントの `owner` フィールドを確認し、正しいプログラムを各スロットにルーティングしてください。

### バージョン管理されたトランザクション

2 つ以上の Raydium CPI と ATA 作成を実行する構成トランザクションは、レガシー（LUT なしの v0）トランザクションに収まることはめったにありません。V0 とアドレス ルックアップ テーブルを使用してください。`raydium.getRaydiumLutAddresses()` を通じて Raydium のパブリック LUT をプルしてください。

## ポインター

* [`sdk-api/rust-cpi`](/ja/sdk-api/rust-cpi) — 低レベルの CPI 仕組み。
* [`integration-guides/priority-fee-tuning`](/ja/integration-guides/priority-fee-tuning) — コンピュート予算のサイジング。
* [`products/cpmm/code-demos`](/ja/products/cpmm/code-demos)、[`products/clmm/code-demos`](/ja/products/clmm/code-demos)、[`products/farm-staking/code-demos`](/ja/products/farm-staking/code-demos) — 製品ごとの CPI スニペット。

ソース：

* [raydium-cp-swap CPI crate](https://github.com/raydium-io/raydium-cp-swap)
* [raydium-clmm CPI crate](https://github.com/raydium-io/raydium-clmm)
* [Anchor CPI docs](https://www.anchor-lang.com/docs/basics/cpi)


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