Ana içeriğe atla

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.

Bu sayfa yapay zekâ tarafından otomatik olarak çevrilmiştir. İngilizce sürüm esas alınır.İngilizce sürümü görüntüle →
CPMM program ID’si ve PDA seed’leri reference/program-addresses sayfasında kanonik olarak listelenir. Bu sayfa her hesabın ne işe yaradığına ve hangi değişmezleri koruduğuna odaklanır, hardcoded adresler değil.

Bir CPMM havuzunun altı hesabı

Her CPMM havuzu, CPMM programı altında altı program türetilmiş adres (PDA) ve başvurduğu bir paylaşılan AmmConfig hesabı tarafından tam olarak tanımlanır. İki mint’e sahip olduğunuzda, ağa dokunmadan her şeyi belirleyici bir şekilde türetebilirsiniz.
HesapSeed(ler)SahibiAmaç
authority"vault_and_lp_mint_auth_seed"CPMMHer vault hareketi ve her LP mint/burn işlemi için imzalayan. Tüm CPMM havuzları arasında paylaşılır.
poolState"pool", ammConfig, token0Mint, token1Mint veya herhangi bir signer tarafından sağlanan rastgele keypairCPMMHavuzun state struct’ı — mint çifti, vault bakiyeleri, LP arzı, ücret birikimi, observation işaretçisi. CPMM Initialize komutu, dört seed’den türetilen kanonik PDA’yı veya oluşturucu tarafından imzalanan keyfi bir keypair’i kabul eder. Rastgele keypair yolu, bir adversary’nin mempool’u gözlemleyip ve meşru oluşturucu kanonik PDA’yı işgal etmesinden önce yarışa katılması durumunda bir ön-çalıştırma saldırısını yenmek için vardır.
lpMint"pool_lp_mint", poolStateSPL TokenHavuzun LP token’ı. Arz = toplam LP ödenmemiş. Mint yetkilisi = CPMM authority PDA.
vault0"pool_vault", poolState, token0MintSPL Token / Token-2022Havuzun token0 bakiyesini tutar. Authority PDA tarafından sahip olunur.
vault1"pool_vault", poolState, token1MintSPL Token / Token-2022Havuzun token1 bakiyesini tutar. Authority PDA tarafından sahip olunur.
observation"observation", poolStateCPMMTWAP için kullanılan fiyat örneklerinin ring buffer’ı. Her swap’da yazılır.
Ve paylaşılan config:
HesapSeed(ler)SahibiAmaç
ammConfig"amm_config", index: u16CPMMİşlem/protokol/fon/oluşturucu ücret oranlarını ve yönetici anahtarlarını tutar. “Ücret tier’i” başına bir tane. Poolstate oluşturma sırasında birine bağlanır ve daha sonra değiştirilemez.

Bir havuzu sadece iki mint’ten türetme

import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";

const CPMM_PROGRAM_ID = new PublicKey(
  "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"
); // mainnet — reference/program-addresses sayfasına bakın

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

// token0 < token1 by byte order. Bunu yanlış yapmak, varolmayan havuza
// işaret eden geçerli bir PDA verir.
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,
  };
}
Havuz PDA’sını türetmeden önce mint’leri her zaman sıralayın. Seed, iki mint’i kullanıcı sırasında değil, byte sırasında hash’ler. (A, B) ve (B, A) ile iki havuz zincir üzerinde çakışırsa — sıralama programın haritalamasını kanonik yapan yoldur.
Havuz ID’si her zaman kanonik PDA değildir. Initialize, yukarıdaki PDA’ya ek olarak pool_state olarak keyfi bir signer keypair’i kabul eder. Geçirilen hesap kanonik PDA ile eşleşmezse, program bunun signer olması gerektirir — yani oluşturucu kendileriyle imzaladıkları yeni bir keypair geçirir. Bu ön-çalıştırma savunmasıdır: kanonik PDA’yı kapmaya çalışan üçüncü bir taraf, meşru oluşturucu tarafından bunun yerine rastgele bir keypair kullanılarak atlatabileceği anlamına gelir. Aşağı akış PDA’ları (lpMint, vault0, vault1, observation) hala poolState.key() dosyasından türetilir, bu nedenle hangi adres kullanılırsa kullanılsın benzersiz kalırlar. Havuzları dizinlerken, pool ID’sini zincir üzerindeki state’ten keşfedin (örn. CPMM programı altındaki PoolState hesapları), kanonik PDA’yı türeterek değil — ikincisi rastgele keypair havuzlarını kaçıracaktır.

Hesap düzenleri

Tam Rust tanımları raydium-cp-swap kaynağında bulunur. Aşağıdaki alanlar, bir entegrasyondan okuyacağınız alanlar.

PoolState

// programs/cp-swap/src/states/pool.rs
pub struct PoolState {
    pub amm_config: Pubkey,               // bu havuzu bir AmmConfig'e bağlar
    pub pool_creator: Pubkey,             // başlat'ı kimin çalıştırdığı
    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 veya 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,                   // lp_mint arzını yansıtır
    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; bu öncesinde swaplar reddedilir
    pub recent_epoch: u64,

    // Oluşturucu-ücret state'i (orijinal düzenden sonra eklendi):
    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],
}
Ne okuyacağınızı:
  • lp_supply — havuzun LP mint’inin toplam arzının dahili aynası. LP-hisse matematiği için kullanın; değer mint’in zincir üzerindeki arzıyla eşleşmeli, ancak bunu PoolState dosyasından okumak ekstra bir hesap getirmesini önler.
  • protocol_fees_token{0,1}, fund_fees_token{0,1} — henüz süpürülmemiş biriktirilmiş ücretler. Bunlar swap fiyatlandırmasını etkilemez; CollectProtocolFee / CollectFundFee çağrılana kadar vault’larda otururlar.
  • statusSwap, Deposit, Withdraw öğelerinin izin verilip verilmediğini kontrol eden bir bitmask. Yönetici tarafından UpdatePoolStatus aracılığıyla güncellenir. SDK bunu bir işlem oluşturmadan önce kontrol eder; doğrudan CPI yapıyorsanız, kendiniz kontrol edin.
  • token0_program / token1_program — her vault için CPI yapacak token programı. Biri klasik SPL Token, diğeri Token-2022 olabilir; bağımsızdırlar.
  • open_time — bir Unix zaman damgası. Bu zaman öncesindeki swap’lar başarısız olur. Depozitolara bu zaman öncesinde izin verilir böylece havuz tohumlanabilir.
  • creator_fee_on / enable_creator_fee — birlikte isteğe bağlı oluşturucu ücretinin bu havuz için aktif olup olmadığını ve swap’ın hangi tarafından toplandığını kontrol eder. enable_creator_fee == false oluşturucu-ücret yolunu tamamen sıfırlar. Etkinleştirildiğinde, creator_fee_on seçer: 0 = swap girişi olan hangi token’dan ücret alır (BothToken); 1 = sadece token_0 dosyasından ücret alır (token_1 → token_0 swap’larını atlar); 2 = sadece token_1 dosyasından ücret alır. InitializeWithPermission aracılığıyla havuz oluşturma sırasında ayarlanır; daha sonra değiştirilemez.
  • creator_fees_token_{0,1}CollectCreatorFee tarafından süpürülen biriktirilmiş oluşturucu ücretleri.

AmmConfig

pub struct AmmConfig {
    pub bump: u8,
    pub disable_create_pool: bool,
    pub index: u16,                       // seed ile eşleşir
    pub trade_fee_rate: u64,              // örn. 2500 = %0.25
    pub protocol_fee_rate: u64,           // işlem ücretinin protokole oranı
    pub fund_fee_rate: u64,               // işlem ücretinin fona oranı
    pub create_pool_fee: u64,             // başlat'ta bir kere ödendi (SOL veya token'da)
    pub protocol_owner: Pubkey,           // CollectProtocolFee çağırabilir
    pub fund_owner: Pubkey,               // CollectFundFee çağırabilir
    pub creator_fee_rate: u64,            // isteğe bağlı havuz-oluşturucu ücret oranı (hacim/1_000_000)
    pub padding: [u64; 15],
}
Dikkat edilecek üç şey:
  1. trade_fee_rate ve creator_fee_rate hacim fraksiyonlarıdır, ikisi de 1/1_000_000 biriminde ifade edilir. 2500 işlem hacminin %0.25 anlamına gelir. protocol_fee_rate ve fund_fee_rate işlem ücretinin fraksiyonlarıdır (hacim değil), aynı 1/1_000_000 paydası içinde. Oluşturucu ücreti işlem ücretinin fraksiyonu değildir — kendi bağımsız oranıdır. Tam aritmetik products/cpmm/fees sayfasındadır.
  2. index bir u16, bu yüzden seed hash’i 2 byte big-endian kullanır. Byte sırasındaki hatalı sayma yaygın bir entegrasyon hatası.
  3. AmmConfig havuz düzeyinde değişmez. Bir havuz oluşturma sırasında bir AmmConfig’e işaret eder ve hiçbir zaman geçiş yapmaz. Ücret değişiklikleri yayılır çünkü havuz her swap’da config’i okur — ancak havuz ücret tier’leri arasında taşınamaz.
Oluşturucu ücretleri hakkında bir not: oranı kendisi (creator_fee_rate) AmmConfig üzerinde yaşar ve ücret tier’i arasında paylaşılır. Belirli bir havuzun aslında bunu talep edip etmediği (enable_creator_fee) ve swap’ın hangi tarafına iniş yaptığı (creator_fee_on) PoolState üzerinde yaşar. Oluşturucu ücreti işlem ücretinden bağımsızdır — kendi oranı, kendi sayaçlarına (creator_fees_token_{0,1}) biriktirilmiş ve hiçbir zaman LP / protokol / fon paylarını işlem ücretinden azaltmaz. Sweep CollectCreatorFee aracılığıyla yapılır. Tam mekanikler için products/cpmm/fees sayfasına bakın.

Permission

InitializeWithPermission tarafından kullanılan küçük bir erişim kontrol hesabı. CPMM programı, diğer programların (örn. bir token’ı CPMM’ye geçiştirirken LaunchLab) belirli bir AmmConfig karşısında havuz oluşturmaya yetkili olduklarını kanıtlayabilmeleri için kısıtlanmış bir havuz-oluşturma yolunu destekler.
pub struct Permission {
    pub authority: Pubkey,    // InitializeWithPermission çağırmaya izin verilir
    pub padding: [u64; 8],
}
Permission PDA, CPMM yöneticisi tarafından CreatePermissionPda aracılığıyla oluşturulur ve ClosePermissionPda aracılığıyla iptal edilir. Son kullanıcılar bu hesapla doğrudan etkileşim kurmaz — bu, çapraz program akışları için tesisattır.

Vault’lar ve Token-2022

vault0 ve vault1, CPMM authority PDA tarafından sahip olunur ve bunların token-programı sahibi (token_program), havuz oluşturma sırasında mint’in programı tarafından belirlenen SPL Token veya Token-2022’dir. Havuz iki durumu şeffaf bir şekilde işler — her bir taraf için Swap / Deposit / Withdraw komutu hesaplarında doğru token-programı ID’sini geçersiniz. CPMM, havuz oluşturma sırasında katı bir extension allow-list (uzantı izin listesi) uygular (utils/token.rs dosyasındaki is_supported_mint). Bir Token-2022 mint, bir CPMM havuzunda kullanılabilir, ancak ve ancak taşıdığı her uzantı bu listedeyse:
  • TransferFeeConfig. Mint tarafından her transfer’de uygulanır. Havuz SwapBaseInput depozitoleri için alıcı tarafı ve çekme işlemleri için gönderen tarafındadır. Program, vault’a inen net miktarı hesaplar ve eğriyi buna göre ayarlar. Bkz. algorithms/token-2022-transfer-fees.
  • MetadataPointer ve TokenMetadata. Mint’te standart metaveri. Swap matematiğine hiçbir etkisi yoktur.
  • InterestBearingConfig. Mint’in UI miktarı faiz tahakkuk ettirir. Vault ham miktarları depolar; eğri sadece ham miktarlarda çalışır. APR gösteren UI’ler, UI miktarını oluşturmak için Token-2022 yardımcılarını çağırmalıdır.
  • ScaledUiAmount. UI-görüntü ölçekleme uzantısı. InterestBearingConfig ile aynı muamele — eğri ham miktarları kullanır.
Herhangi başka bir uzantı — PermanentDelegate, TransferHook, DefaultAccountState, NonTransferable, ConfidentialTransfer, Group/GroupMember, MintCloseAuthority, vb. — Initialize öğesinin NotSupportMint ile reddetmesine neden olur. İstisna, programdaki küçük bir hardcoded mint whitelist’i (bir avuç belirli pubkey), extension kontrolünü atlar; belirli mint’leri vakayla başa getirmek için kullanılır. Doğrulanmış uzantı listesi ve mint whitelist’i CP-Swap kaynağında programs/cp-swap/src/utils/token.rs altında yaşar ve gelecekteki program yükseltmeleriyle değişebilir.

Observation

Observation hesabı, her biri block_timestamp ve kümülatif fiyat depolayan ObservationState girişlerinin bir ring buffer’ıdır. Her swap’da, son birinden yeterli zaman geçmişse program yeni bir observation ekler. TWAP’lar iki observation okuarak ve Δcumulative / Δtime bölünerek hesaplanır.
// OBSERVATION_NUM hardcoded programda 100'dür.
pub const OBSERVATION_NUM: usize = 100;

pub struct Observation {
    pub block_timestamp:              u64,
    pub cumulative_token_0_price_x32: u128,   // Q32.32, taşma için sol taraftaki üst 64 bit
    pub cumulative_token_1_price_x32: u128,
}

pub struct ObservationState {
    pub initialized:           bool,
    pub observation_index:     u16,                            // dairesel dizin
    pub pool_id:               Pubkey,
    pub observations:          [Observation; OBSERVATION_NUM], // 100 giriş
    pub last_update_timestamp: u64,                            // en son ek'in zaman damgası
    pub padding:               [u64; 3],
}
Ring buffer 100 observation için boyutlandırılmıştır. Her observation 40 byte’tır, bu yüzden dizi tek başına 4.000 byte’tır; tam ObservationState PDA çevresindeki alanlar ve discriminator’dan sonra yaklaşık 4.100 byte’tır. İki tüketici kuralı:
  • Tek bir observation’u fiyat olarak kullanmayın. Bu bir kümülatif, spot fiyat değildir. TWAP hesaplamak için bunlardan ikisini kullanın.
  • En az bir blok arayla observation’ları seçin. Aynı blok içindeki swap’lar yeni bir observation üretmeyebilir; geri dönüşü kontrol etmek aynı kaydı döndürebilir.
products/clmm/accounts sayfasında daha fazla matematik.

Hesap yaşam döngüsü

OlayOluşturulan hesaplarYok edilen hesaplar
InitializepoolState, lpMint, vault0, vault1, observation
Deposit— (kullanıcı LP ATA’sı oluşturabilir)
Withdraw
Swap— (kullanıcı hedef ATA’sı oluşturabilir)
CollectProtocolFee
CollectFundFee
UpdatePoolStatus
CPMM havuzları ve bunların PDA’ları hiçbir zaman kapatılmaz. Sıfır likidite bile olsa poolState kalır. Bu kasıtlıdır: aynı havuzu daha sonra yeniden tohumlamak tarihsel observation buffer’ını korur ve PDA türetimi kararlı kalır.

Nereden ne okuyacağınız

Kaynaklar: