Langsung ke konten utama

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.

Halaman ini diterjemahkan secara otomatis oleh AI. Versi bahasa Inggris adalah acuan resmi.Lihat versi bahasa Inggris →
ID program dan seed PDA untuk CPMM tercantum secara kanonik di reference/program-addresses. Halaman ini fokus pada apa kegunaan setiap akun dan invarian yang dipertahankannya, bukan alamat yang dikodekan.

Enam akun dari pool CPMM

Setiap pool CPMM sepenuhnya dijelaskan oleh enam program-derived addresses (PDA) di bawah program CPMM, ditambah satu akun AmmConfig bersama yang dirujuknya. Setelah Anda memiliki dua mint, Anda dapat menurunkan semuanya secara deterministik tanpa menyentuh jaringan.
AkunSeed(s)PemilikTujuan
authority"vault_and_lp_mint_auth_seed"CPMMPenanda tangan untuk setiap pergerakan vault dan setiap LP mint/burn. Dibagikan di semua pool CPMM.
poolState"pool", ammConfig, token0Mint, token1Mint atau keypair acak yang disediakan penanda tanganCPMMStruct state pool — pasangan mint, saldo vault, pasokan LP, akrual biaya, pointer observasi. Instruksi CPMM Initialize menerima baik PDA kanonik yang diturunkan dari empat seed atau keypair arbitrer yang ditandatangani oleh pembuat. Jalur keypair acak ada untuk mengalahkan serangan front-running di mana penyerang mengawasi mempool dan berlomba menempati PDA kanonik sebelum pembuat sah.
lpMint"pool_lp_mint", poolStateSPL TokenToken LP pool. Pasokan = total LP yang beredar. Mint authority = CPMM authority PDA.
vault0"pool_vault", poolState, token0MintSPL Token / Token-2022Menyimpan saldo token0 pool. Dimiliki oleh authority PDA.
vault1"pool_vault", poolState, token1MintSPL Token / Token-2022Menyimpan saldo token1 pool. Dimiliki oleh authority PDA.
observation"observation", poolStateCPMMRing buffer sampel harga yang digunakan untuk TWAP. Ditulis pada setiap swap.
Dan konfigurasi bersama:
AkunSeed(s)PemilikTujuan
ammConfig"amm_config", index: u16CPMMMenyimpan tingkat biaya trade/protocol/fund/creator dan kunci admin. Satu per “fee tier”. Poolstate terikat pada satu saat pembuatan dan tidak dapat berubah kemudian.
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";

const CPMM_PROGRAM_ID = new PublicKey(
  "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"
); // mainnet — see reference/program-addresses

function u16ToBytes(n: number): Buffer {
  const b = Buffer.alloc(2);
  b.writeUInt16BE(n);
  return b;
}

// token0 < token1 by byte order. Getting this wrong yields a valid PDA
// that points at a nonexistent pool.
function sortMints(a: PublicKey, b: PublicKey): [PublicKey, PublicKey] {
  return Buffer.compare(a.toBuffer(), b.toBuffer()) < 0 ? [a, b] : [b, a];
}

export function deriveCpmmAccounts(
  mintA: PublicKey,
  mintB: PublicKey,
  ammConfigIndex = 0,
) {
  const [token0Mint, token1Mint] = sortMints(mintA, mintB);

  const [ammConfig] = PublicKey.findProgramAddressSync(
    [Buffer.from("amm_config"), u16ToBytes(ammConfigIndex)],
    CPMM_PROGRAM_ID,
  );
  const [authority] = PublicKey.findProgramAddressSync(
    [Buffer.from("vault_and_lp_mint_auth_seed")],
    CPMM_PROGRAM_ID,
  );
  const [poolState] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("pool"),
      ammConfig.toBuffer(),
      token0Mint.toBuffer(),
      token1Mint.toBuffer(),
    ],
    CPMM_PROGRAM_ID,
  );
  const [lpMint] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_lp_mint"), poolState.toBuffer()],
    CPMM_PROGRAM_ID,
  );
  const [vault0] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), token0Mint.toBuffer()],
    CPMM_PROGRAM_ID,
  );
  const [vault1] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), token1Mint.toBuffer()],
    CPMM_PROGRAM_ID,
  );
  const [observation] = PublicKey.findProgramAddressSync(
    [Buffer.from("observation"), poolState.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  return {
    ammConfig,
    authority,
    poolState,
    lpMint,
    token0Mint,
    token1Mint,
    vault0,
    vault1,
    observation,
  };
}
Selalu urutkan mint sebelum menurunkan PDA pool. Seed melakukan hash dua mint dalam urutan byte, bukan urutan pengguna. Dua pool dengan (A, B) dan (B, A) akan bertabrakan on-chain — pengurutan adalah cara program membuat pemetaan kanonik.
ID pool tidak selalu PDA kanonik. Initialize menerima keypair penanda tangan arbitrer sebagai pool_state selain PDA di atas. Jika akun yang dilewatkan tidak cocok dengan PDA kanonik, program memerlukan itu menjadi penanda tangan — yaitu, pembuat melewatkan keypair segar yang mereka tandatangani. Ini adalah pertahanan front-run: pihak ketiga apa pun yang berlomba merebut PDA kanonik dapat dihindari oleh pembuat sah menggunakan keypair acak sebagai gantinya. PDA hilir (lpMint, vault0, vault1, observation) masih diturunkan dari poolState.key(), sehingga mereka tetap unik untuk alamat mana pun yang digunakan. Ketika Anda mengindeks pool, selalu temukan ID pool dari state on-chain (mis., akun PoolState di bawah program CPMM), bukan dengan menurunkan PDA kanonik — yang terakhir akan melewatkan pool keypair acak.

Tata letak akun

Definisi Rust lengkap ada di sumber raydium-cp-swap. Bidang di bawah adalah yang akan Anda baca dari integrasi.

PoolState

// programs/cp-swap/src/states/pool.rs
pub struct PoolState {
    pub amm_config: Pubkey,               // binds this pool to an AmmConfig
    pub pool_creator: Pubkey,             // who ran initialize
    pub token_0_vault: Pubkey,            // == vault0 PDA
    pub token_1_vault: Pubkey,            // == vault1 PDA

    pub lp_mint: Pubkey,
    pub token_0_mint: Pubkey,
    pub token_1_mint: Pubkey,

    pub token_0_program: Pubkey,          // SPL Token or Token-2022 program
    pub token_1_program: Pubkey,

    pub observation_key: Pubkey,          // == observation PDA
    pub auth_bump: u8,
    pub status: u8,                       // bitmask: deposit | withdraw | swap
    pub lp_mint_decimals: u8,
    pub mint_0_decimals: u8,
    pub mint_1_decimals: u8,

    pub lp_supply: u64,                   // mirrors lp_mint supply
    pub protocol_fees_token_0: u64,
    pub protocol_fees_token_1: u64,
    pub fund_fees_token_0: u64,
    pub fund_fees_token_1: u64,

    pub open_time: u64,                   // unix; swaps rejected before this
    pub recent_epoch: u64,

    // Creator-fee state (added after the original layout):
    pub creator_fee_on: u8,               // 0=BothToken, 1=OnlyToken0, 2=OnlyToken1
    pub enable_creator_fee: bool,
    pub padding1: [u8; 6],
    pub creator_fees_token_0: u64,
    pub creator_fees_token_1: u64,

    pub padding: [u64; 28],
}
Apa yang sebenarnya harus dibaca:
  • lp_supply — cerminan internal pool dari pasokan total LP mint. Gunakan untuk matematis LP-share; nilainya harus cocok dengan pasokan on-chain mint, tetapi membacanya dari PoolState menghindari pengambilan akun tambahan.
  • protocol_fees_token{0,1}, fund_fees_token{0,1} — biaya yang terakrual yang belum disapu. Ini tidak mempengaruhi pricing swap; mereka duduk di vault hingga CollectProtocolFee / CollectFundFee dipanggil.
  • status — bitmask yang mengontrol apakah Swap, Deposit, Withdraw diizinkan. Diperbarui oleh admin melalui UpdatePoolStatus. SDK memeriksa ini sebelum membangun transaksi; jika Anda langsung CPI-ing, periksa sendiri.
  • token0_program / token1_program — program token untuk CPI ke dalam untuk setiap vault. Satu dapat berupa SPL Token klasik dan yang lain Token-2022; mereka independen.
  • open_time — timestamp Unix. Swap sebelum waktu ini gagal. Deposit diizinkan sebelum open_time jadi pool dapat disemai.
  • creator_fee_on / enable_creator_fee — bersama-sama mengontrol apakah biaya creator opsional aktif untuk pool ini dan sisi swap mana ia dikumpulkan. enable_creator_fee == false mengosongkan jalur biaya creator sepenuhnya. Ketika diaktifkan, creator_fee_on memilih: 0 = ambil biaya dari token mana pun yang merupakan input swap (BothToken); 1 = ambil biaya dari token_0 saja (lewati di swap token_1 → token_0); 2 = ambil biaya dari token_1 saja. Ditetapkan saat pembuatan pool melalui InitializeWithPermission; tidak dapat berubah kemudian.
  • creator_fees_token_{0,1} — biaya creator yang terakrual, disapu oleh CollectCreatorFee.

AmmConfig

pub struct AmmConfig {
    pub bump: u8,
    pub disable_create_pool: bool,
    pub index: u16,                       // matches the seed
    pub trade_fee_rate: u64,              // e.g., 2500 = 0.25%
    pub protocol_fee_rate: u64,           // fraction of trade fee to protocol
    pub fund_fee_rate: u64,               // fraction of trade fee to fund
    pub create_pool_fee: u64,             // paid once at init (in SOL or token)
    pub protocol_owner: Pubkey,           // can call CollectProtocolFee
    pub fund_owner: Pubkey,               // can call CollectFundFee
    pub creator_fee_rate: u64,            // optional pool-creator fee rate (1/1_000_000 of volume)
    pub padding: [u64; 15],
}
Tiga hal yang perlu diperhatikan:
  1. trade_fee_rate dan creator_fee_rate adalah fraksi dari volume, keduanya dinyatakan dalam unit 1/1_000_000. 2500 berarti 0,25% dari volume trade. protocol_fee_rate dan fund_fee_rate adalah fraksi dari trade fee (bukan dari volume), dalam denominator 1/1_000_000 yang sama. Biaya creator bukan fraksi dari trade fee — ini adalah tingkat independen sendiri. Aritmetika lengkap ada di products/cpmm/fees.
  2. index adalah u16, jadi seed hash menggunakan 2 byte big-endian. Kesalahan urutan byte adalah bug integrasi umum.
  3. AmmConfig tidak dapat diubah di tingkat pool. Pool menunjuk satu AmmConfig saat pembuatan dan tidak pernah berpindah. Perubahan biaya menyebar karena pool membaca konfigurasi setiap swap — tetapi pool tidak dapat dipindahkan di antara fee tier.
Catatan tentang biaya creator: tingkat sendiri (creator_fee_rate) ada di AmmConfig dan dibagikan di seluruh fee tier. Apakah pool tertentu benar-benar mengenakan biayanya (enable_creator_fee) dan sisi swap mana yang dikenakannya (creator_fee_on) ada di PoolState. Biaya creator independen dari trade fee — ini adalah tingkat sendiri, terakrual ke penghitung sendiri (creator_fees_token_{0,1}), dan tidak pernah mengurangi bagian LP / protocol / fund dari trade fee. Penyapuan melalui CollectCreatorFee. Lihat products/cpmm/fees untuk mekanika lengkap.

Permission

Akun kontrol akses kecil yang digunakan oleh InitializeWithPermission. Program CPMM mendukung jalur pembuatan pool berupa izin sehingga program lain (mis. LaunchLab saat meluluskan token ke CPMM) dapat membuktikan mereka berhak membuat pool terhadap AmmConfig yang diberikan.
pub struct Permission {
    pub authority: Pubkey,    // who is allowed to call InitializeWithPermission
    pub padding: [u64; 8],
}
PDA Permission dibuat oleh admin CPMM melalui CreatePermissionPda dan dibatalkan melalui ClosePermissionPda. Pengguna akhir tidak berinteraksi dengan akun ini secara langsung — ini adalah pipa untuk alur lintas program.

Vault dan Token-2022

vault0 dan vault1 dimiliki oleh CPMM authority PDA, dan pemilik token-program mereka (token_program) adalah baik SPL Token atau Token-2022, ditentukan saat pembuatan pool oleh program mint. Pool menangani dua kasus secara transparan — Anda melewatkan token-program ID yang benar untuk setiap sisi di akun instruksi Swap / Deposit / Withdraw. CPMM menerapkan daftar whitelist ekstensi yang ketat saat pembuatan pool (is_supported_mint di utils/token.rs). Mint Token-2022 dapat digunakan dalam pool CPMM hanya jika setiap ekstensi yang dibawanya ada di daftar ini:
  • TransferFeeConfig. Diterapkan oleh mint di setiap transfer. Pool berada di sisi penerima untuk deposit SwapBaseInput dan sisi pengirim untuk penarikan. Program menghitung jumlah bersih yang masuk ke vault dan mengatur kurva sesuai. Lihat algorithms/token-2022-transfer-fees.
  • MetadataPointer dan TokenMetadata. Metadata on-mint standar. Tidak ada pengaruh pada swap math.
  • InterestBearingConfig. Jumlah UI mint terakrual bunga. Vault menyimpan jumlah mentah; kurva beroperasi hanya pada jumlah mentah. UI yang menampilkan APR harus memanggil pembantu Token-2022 untuk merender jumlah UI.
  • ScaledUiAmount. Ekstensi penskalaan tampilan UI. Perlakuan yang sama dengan InterestBearingConfig — kurva menggunakan jumlah mentah.
Ekstensi lain apa pun — PermanentDelegate, TransferHook, DefaultAccountState, NonTransferable, ConfidentialTransfer, Group/GroupMember, MintCloseAuthority, dll. — menyebabkan Initialize ditolak dengan NotSupportMint. Pengecualiannya adalah whitelist mint hard-coded kecil di program (beberapa pubkey spesifik) yang melewati pemeriksaan ekstensi; ini digunakan untuk mengonboardkan mint spesifik kasus per kasus. Daftar ekstensi terverifikasi dan whitelist mint ada di sumber CP-Swap di programs/cp-swap/src/utils/token.rs dan dapat berubah dengan upgrade program di masa depan.

Observation

Akun observasi adalah ring buffer dari entri ObservationState, masing-masing menyimpan block_timestamp dan harga kumulatif. Pada setiap swap, program menambahkan observasi baru jika waktu yang cukup telah berlalu sejak yang terakhir. TWAP dihitung dengan membaca dua observasi dan membagi Δcumulative / Δtime.
// OBSERVATION_NUM is hardcoded in the program to 100.
pub const OBSERVATION_NUM: usize = 100;

pub struct Observation {
    pub block_timestamp:              u64,
    pub cumulative_token_0_price_x32: u128,   // Q32.32, top 64 bits left for overflow
    pub cumulative_token_1_price_x32: u128,
}

pub struct ObservationState {
    pub initialized:           bool,
    pub observation_index:     u16,                            // circular index
    pub pool_id:               Pubkey,
    pub observations:          [Observation; OBSERVATION_NUM], // 100 entries
    pub last_update_timestamp: u64,                            // timestamp of the most recent append
    pub padding:               [u64; 3],
}
Ring buffer diukur untuk 100 observasi. Setiap observasi 40 byte, jadi array saja 4.000 byte; PDA ObservationState lengkap sekitar 4.100 byte setelah bidang sekitarnya dan discriminator. Dua aturan konsumen:
  • Jangan gunakan observasi tunggal sebagai harga. Itu adalah kumulatif, bukan harga spot. Gunakan dua di antaranya untuk menghitung TWAP.
  • Pilih observasi setidaknya satu blok terpisah. Swap dalam blok yang sama mungkin tidak menghasilkan observasi baru; membaca back-to-back dapat mengembalikan record yang sama.
Lebih banyak math di products/clmm/accounts.

Daur hidup akun

AcaraAkun dibuatAkun dihancurkan
InitializepoolState, lpMint, vault0, vault1, observation
Deposit— (mungkin membuat LP ATA pengguna)
Withdraw
Swap— (mungkin membuat ATA tujuan pengguna)
CollectProtocolFee
CollectFundFee
UpdatePoolStatus
Pool CPMM dan PDA mereka tidak pernah ditutup. Bahkan dengan likuiditas nol poolState tetap ada. Ini disengaja: menabur ulang pool yang sama nanti mempertahankan buffer observasi historikal dan derivasi PDA-nya tetap stabil.

Apa yang harus dibaca di mana

Sumber: