# CPMM feesの設定方法

各 CPMM プールには、作成時に手数料設定が割り当てられます。手数料設定は on-chain 上で次のものとして保存されます `AmmConfig` accounts — 各 account は、どのプールでも使用できる手数料ティアを定義します。

### 手数料設定

すべてのプールは単一の `AmmConfig` を参照し、各スワップで手数料がどのように徴収・分配されるかを決定します。利用可能なすべての設定を表示するには、以下のスクリプトを使用して on-chain accounts を照会してください。公開 config ID はこのエンドポイント（<https://api-v3.raydium.io/main/cpmm-config>）でも確認できます。なお、このエンドポイントには一部のカスタム config ID は含まれていません。

{% hint style="info" %}
カスタム手数料設定をリクエストするには、Raydium チームにお問い合わせください。
{% endhint %}

### 出力例

```
--- Config index: 0 ---
  Address:           D4FPEruKEHrG5TenZ2mpDGEfu1iUvTiqBxvpU8HLBvC2
  Trade Fee Rate:    2500 (0.25%)
  Protocol Fee Rate: 120000 (取引手数料の 12%)
  Fund Fee Rate:     40000 (取引手数料の 4%)
  Creator Fee Rate:  500 (0.05%)
  Create Pool Fee:   150000000 lamports (0.15 SOL)
```

### 手数料の分配方法

Config 0 を例として、 **1,000,000 トークン** のスワップにおける手数料の流れは次のとおりです。

#### 1. 手数料は input から差し引かれます

この `trade_fee_rate` および `creator_fee_rate` は、それぞれスワップの input amount に適用されます:

```
trade_fee   = 1,000,000 × 0.25%  = 2,500 トークン
creator_fee = 1,000,000 × 0.05%  =   500 トークン
                                    ─────
差し引かれる合計                    = 3,000 トークン
```

残りの **997,000 トークン** が constant-product swap に入り、output が決定されます。

#### 2. 取引手数料は protocol、treasury、LPs の間で分割されます

`protocol_fee_rate` および `fund_fee_rate` は、 **取引手数料**の割合であり、スワップ量ではありません:

```
protocol_fee = 2,500 × 12% = 300 トークン
fund_fee     = 2,500 ×  4% = 100 トークン
lp_fee       = 2,500 - 300 - 100 = 2,100 トークン
```

LP fee はプールの vaults に残り、LP ポジションの価値を増加させます。

#### 3. 要約

| 受取者              | 数量            | 計算                           |
| ---------------- | ------------- | ---------------------------- |
| LPs              | 2,100 トークン    | trade\_fee - protocol - fund |
| Raydium protocol | 300トークン       | 取引手数料の12%                    |
| Raydium treasury | 100トークン       | 取引手数料の4%                     |
| Pool creator     | 500トークン       | swap inputの0.05%             |
| **合計手数料**        | **3,000トークン** | **swap inputの0.30%**         |

プロトコル手数料と treasury 手数料は pool vault に蓄積され、 `protocol_owner` および `fund_owner` アドレスによって別々に収集されます。

### AmmConfig フィールド

| フィールド                 | 説明                                                                      |
| --------------------- | ----------------------------------------------------------------------- |
| `trade_fee_rate`      | swap input から差し引かれる手数料。単位は 1e-6。 `2500` = 0.25%.                        |
| `protocol_fee_rate`   | 取引手数料のうち Raydium protocol に送られる割合。単位は 1e-6。 `120000` = 12%.             |
| `fund_fee_rate`       | 取引手数料のうち Raydium treasury に送られる割合。単位は 1e-6。 `40000` = 4%.               |
| `creator_fee_rate`    | swap input から差し引かれ、pool creator に送られる追加手数料。 `500` = 0.05%.              |
| `create_pool_fee`     | この config で pool を作成するための lamports による一回限りの手数料。 `150000000` = 0.15 SOL。 |
| `disable_create_pool` | true のとき `true`、この config では新しい pool の作成は無効になります。既存の pool には影響しません。     |
| `protocol_owner`      | 蓄積された protocol 手数料を収集するアドレス。                                            |
| `fund_owner`          | 蓄積された treasury 手数料を収集するアドレス。                                            |

すべての rate フィールドは `1,000,000`を分母として使用します。例えば、 `2500` の値は 2500/1000000 = 0.25% を意味します。

### すべての configurations を取得

以下のスクリプトは mainnet からすべての `AmmConfig` accounts を照会します:

```typescript
import { Connection, PublicKey } from "@solana/web3.js";
import { createHash } from "crypto";

const RPC_URL = "<YOUR_RPC_URL>";
const PROGRAM_ID = new PublicKey("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C");

// Anchor discriminator: sha256("account:AmmConfig") の先頭 8 バイト
const discriminator = createHash("sha256")
  .update("account:AmmConfig")
  .digest()
  .subarray(0, 8);

interface AmmConfig {
  bump: number;
  disableCreatePool: boolean;
  index: number;
  tradeFeeRate: bigint;
  protocolFeeRate: bigint;
  fundFeeRate: bigint;
  createPoolFee: bigint;
  protocolOwner: PublicKey;
  fundOwner: PublicKey;
  creatorFeeRate: bigint;
}

function parseAmmConfig(data: Buffer): AmmConfig {
  let offset = 8; // Anchor discriminator をスキップ

  const bump = data.readUInt8(offset);             offset += 1;
  const disableCreatePool = data.readUInt8(offset) !== 0; offset += 1;
  const index = data.readUInt16LE(offset);         offset += 2;
  const tradeFeeRate = data.readBigUInt64LE(offset);    offset += 8;
  const protocolFeeRate = data.readBigUInt64LE(offset); offset += 8;
  const fundFeeRate = data.readBigUInt64LE(offset);     offset += 8;
  const createPoolFee = data.readBigUInt64LE(offset);   offset += 8;
  const protocolOwner = new PublicKey(data.subarray(offset, offset + 32)); offset += 32;
  const fundOwner = new PublicKey(data.subarray(offset, offset + 32));     offset += 32;
  const creatorFeeRate = data.readBigUInt64LE(offset);

  return {
    bump, disableCreatePool, index, tradeFeeRate,
    protocolFeeRate, fundFeeRate, createPoolFee,
    protocolOwner, fundOwner, creatorFeeRate,
  };
}

async function main() {
  const connection = new Connection(RPC_URL, "confirmed");
  console.log("すべての AmmConfig アカウントを取得しています...\n");

  const accounts = await connection.getProgramAccounts(PROGRAM_ID, {
    filters: [{
      memcmp: {
        offset: 0,
        bytes: discriminator.toString("base64"),
        encoding: "base64",
      },
    }],
  });

  console.log(`AmmConfig アカウントを ${accounts.length} 件見つけました\n`);

  for (const { pubkey, account } of accounts) {
    const config = parseAmmConfig(account.data as Buffer);
    console.log(`--- Config index: ${config.index} ---`);
    console.log(`  Address:           ${pubkey.toBase58()}`);
    console.log(`  Trade Fee Rate:    ${config.tradeFeeRate} (${Number(config.tradeFeeRate) / 10000}%)`);
    console.log(`  Protocol Fee Rate: ${config.protocolFeeRate} (${Number(config.protocolFeeRate) / 10000}% of trade fee)`);
    console.log(`  Fund Fee Rate:     ${config.fundFeeRate} (${Number(config.fundFeeRate) / 10000}% of trade fee)`);
    console.log(`  Creator Fee Rate:  ${config.creatorFeeRate} (${Number(config.creatorFeeRate) / 10000}%)`);
    console.log(`  Create Pool Fee:   ${config.createPoolFee} lamports (${Number(config.createPoolFee) / 1e9} SOL)`);
    console.log(`  Disable Pool:      ${config.disableCreatePool}`);
    console.log(`  Protocol Owner:    ${config.protocolOwner.toBase58()}`);
    console.log(`  Fund Owner:        ${config.fundOwner.toBase58()}`);
    console.log();
  }
}

main().catch(console.error);
```
