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 →
PDA’lar (program-derived addresses) ve CPI’ler (cross-program invocation) Raydium’u mümkün kılan iki ilkeldir. PDA’lar, bir programın özel anahtarlar olmadan deterministik adresler “sahip olmasını” sağlar — havuz yöneticileri ve vault’lar bu şekilde çalışır. CPI’ler, bir programın başka bir programı çağırmasını sağlar — Raydium’un SPL Token programı aracılığıyla token’ları değiştirmesi ve entegratörlerin Raydium’u kendi akışlarına dahil etmesi bu şekilde gerçekleşir. Raydium kaynak kodunu okumadan önce her ikisini anlamakta fayda vardır.

PDA’lar: anahtarsız adresler

Bir Program-Derived Address şu özelliklere sahip bir açık anahtardır:
  • ed25519 eğrisinin üzerinde değildir (bunun için özel bir anahtar yoktur).
  • Bir program ID’si ve bir dizi seed’den deterministik olarak türetilir.
  • Yalnızca türetme programı tarafından invoke_signed aracılığıyla imzalanabilir.
Her Raydium havuz yöneticisi, her havuz durumu hesabı, her vault, her farm durumu — hepsi PDA’dır.

Türetme

Bir PDA, program ID’si ve seed’ler hash’lenerek hesaplanır, ardından sonucu eğri dışına çıkaran bir “bump” baytı bulunur. Eğri dışı bir adres üreten ilk bump (genellikle 255’ten başlayıp azalan) başarılı olur; bu, kanonik bump’tır.
import { PublicKey } from "@solana/web3.js";

const [poolAuthority, bump] = PublicKey.findProgramAddressSync(
  [Buffer.from("authority"), poolId.toBuffer()],
  CPMM_PROGRAM_ID,
);
Seed’ler herhangi bir şey olabilir — dizeler, diğer pubkey’ler, u64 değerleri little-endian baytlar olarak. Raydium’un geleneği, insan tarafından okunabilir bir ön ek ve sonrasında benzersiz tanımlayıcıları kullanmaktır.

Raydium PDA desenleri

Raydium programlarındaki yaygın PDA’lar:
PDASeed’lerProgram
AMM yöneticisi (AMM v4)[b"amm authority"] + bumpAMM v4
Havuz durumu (CPMM)[b"pool", amm_config, mint_a, mint_b]CPMM
Havuz vault’u (CPMM)[b"pool_vault", pool, mint]CPMM
Yönetici (CPMM)[b"vault_and_lp_mint_auth_seed"]CPMM
Havuz durumu (CLMM)[b"pool", amm_config, mint_0, mint_1]CLMM
Tick dizisi (CLMM)[b"tick_array", pool, start_tick_index]CLMM
Gözlem (CLMM)[b"observation", pool]CLMM
Kişisel pozisyon (CLMM)[b"position", position_nft_mint]CLMM
Farm durumu (Farm v6)[b"pool_farm_state", farm_id]Farm v6
Kullanıcı defteri (Farm v6)[b"user_ledger", farm, user]Farm v6
Kullanıcılar ve entegratörler, hiçbir şey getirmeden bu adresler hesaplayabilir — genel girdiler (havuz ID’si, farm ID’si, kullanıcı anahtarı) verildiğinde, PDA deterministiktir.

Kanonik bump

İlk olarak eğri dışı adresler üreten birden fazla bump olabilse de, Raydium programları her zaman kanonik bump kullanır (255’ten azaltılarak bulunur). Bu, PDA’nın hesap verilerine depolanır, böylece sonraki işlemler bunu iletebilir ve (pahalı) türetme döngüsünü atlayabilir:
#[account]
pub struct PoolState {
    pub bump: [u8; 1],
    // ... havuz durumunun geri kalanı
}
Sonraki işlemlerde, bump havuz durumundan okunur ve yeniden hesaplanmaz.

CPI’ler: diğer programları çağırma

Cross-Program Invocation, bir programın başka bir programın talimatlarını tek bir işlem içinde satır içi çağırmasını sağlar. Raydium, CPI’leri yoğun şekilde kullanır:
  • Swap talimatları, token’ları taşımak için SPL Token programını çağırır.
  • CLMM, pozisyon NFT’sini basmak için Metaplex çağırır.
  • Havuz oluşturma, hesapları tahsis etmek için Sistem Programını çağırır.
  • Farm v6, ödülleri aktarmak için SPL Token çağırır.
Entegratörler ayrıca Raydium’a çağırmak için CPI’leri kullanır — vault stratejileri, kaldıraçlı-LP protokolleri ve otomatik-compounder’lar bu şekilde çalışır. Bkz. integration-guides/cpi-integration.

invoke vs invoke_signed

Solana çalışma zamanı iki CPI ilkeli sunar:
  • invoke: başka bir programı çağırın; çağrılan program, dış işlemin imzacılarını devralır.
  • invoke_signed: bir programı bir PDA adına çağırın; çalışma zamanı PDA’nın seed’lerini doğrular ve imzayı yetkilendirir.
invoke_signed, programların özel anahtarları yönetmeden hesaplar üzerinde yetki sahibi olmasını sağlayan sihirdir.

Örnek: Raydium’un havuz vault’undan transfer etmesi

Bir havuz vault’u, yöneticisi havuz programının bir PDA’sı olan bir Token Hesabıdır. Bir swap sırasında token’ları dışarı aktarmak için, havuz programı bu PDA olarak imza atmalıdır:
// Havuz yöneticisinin seed'leri
let authority_seeds: &[&[u8]] = &[
    b"vault_and_lp_mint_auth_seed",
    &[authority_bump],
];
let signer_seeds: &[&[&[u8]]] = &[authority_seeds];

// CPI bağlamını oluştur
let cpi_accounts = Transfer {
    from:      input_vault.to_account_info(),
    to:        user_ata.to_account_info(),
    authority: pool_authority.to_account_info(),
};
let cpi_program = token_program.to_account_info();
let cpi_ctx     = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);

// Yürüt
token::transfer(cpi_ctx, amount)?;
Çalışma zamanı, invoke_signed’ın CPMM programı tarafından çağrıldığını görür, vault_and_lp_mint_auth_seed + bump’ın CPMM program ID’si ile hash’lendiğinde pool_authority’nin adresine türetilip türetilmediğini doğrular ve token transfer’inde yönetici imzasına izin verir. Özel anahtar söz konusu değildir.

Örnek: entegratör Raydium CPMM’yi çağırma

Bir entegratör programı (örn. emanet) Raydium’un swap_base_input talimatını CPI aracılığıyla çağırabilir:
use raydium_cpmm::cpi::{self, accounts::Swap};

let cpi_accounts = Swap {
    payer:                order.to_account_info(),      // PDA, imzalayacak
    authority:            pool_authority_info,
    amm_config:           amm_config_info,
    pool_state:           pool_info,
    input_token_account:  order_input_ata,
    output_token_account: order_output_ata,
    input_vault:          input_vault_info,
    output_vault:         output_vault_info,
    input_token_program:  input_token_program_info,
    output_token_program: output_token_program_info,
    input_token_mint:     input_mint_info,
    output_token_mint:    output_mint_info,
    observation_state:    observation_info,
};

let seeds = &[b"order", user.key.as_ref(), &[order.bump]];
let cpi_ctx = CpiContext::new_with_signer(
    cpmm_program.to_account_info(),
    cpi_accounts,
    &[seeds],
);

cpi::swap_base_input(cpi_ctx, amount_in, min_out)?;
Bu, kanonik entegrasyon desenidir — tam emanet örneği için integration-guides/cpi-integration bölümünü görebilirsiniz.

CPI derinlik sınırı

Solana, CPI derinliğini 4 seviye ile sınırlandırır. Bir işlemin üst düzey talimatı derinlik 0 olarak sayılır; her CPI çağırması derinliği arttırır. Pratik etki: Raydium’un kendi swap’ı zaten 1-2 seviye CPI kullanır (Raydium → SPL Token). Raydium’u çağıran bir entegratör 2 seviye kullanır. Eğer bu entegratör başka bir entegratör tarafından çağrılırsa, 3 seviyedir. 4. seviye sınırdır. Çoğu bileşim bunu kolayca aşar, ancak derin iç içe geçme (aggregator → router → Raydium → hook) buna çarpabilir. Düz tasarım, derin olmayan tasarım yapın.

Kalan hesaplar

Bir Raydium talimatı değişken sayıda hesaba ihtiyaç duyduğunda (örn. bilinmeyen sayıda tick dizisini geçen CLMM swap’ı), ekstra hesaplar kalan hesaplar olarak iletilir — sabit hesap listesine eklenir, konuma göre yorumlanır. CPMM’nin SwapV2’si, transfer-hook programlarının ekstra gerekli hesapları için kalan hesapları kullanır. İstemciler gerekli hesapları getirir ve ekler:
const swapIx = await raydium.cpmm.swap({
  /* ... */
  // SDK kalan hesapları otomatik olarak yönetir
});
CPI seviyesinde, entegratörler kalan hesapları kendi talimatları aracılığıyla iletmelidir:
pub struct Swap<'info> {
    // ... sabit hesaplar
    // Plus ctx.remaining_accounts aracılığıyla ilenen kalan hesaplar
}

// kalan_hesapları CPI'ye ilet
cpi::swap_base_input(
    cpi_ctx.with_remaining_accounts(ctx.remaining_accounts.to_vec()),
    amount_in,
    min_out,
)?;

PDA tuzakları

Yanlış seed’ler → yanlış adres

Seed’lerin yanlış sırada, yanlış kodlanmış veya ekstra bir bayt içeren/dışarı çıkan bir hata sessizce farklı bir PDA üretir. İşlem belirsiz şekilde başarısız olur (program varolmayan bir hesabı okumaya çalışır). Her zaman seed türetmesini bilinen altın değerlere karşı birim-test edin.

Bump depolanmıyor

Eğer her işlemde bump’ı yeniden türetirseniz, türetme döngüsü için hesaplama ödeyersiniz. Kanonik bump’ı PDA’nın verilerine depolayın ve oradan okuyun.

Kanonik vs kanonik olmayan bump karıştırılması

Kanonik olmayan bump’lar (eğri dışı üreten bulunursa) invoke_signed tarafından izin verilir, ancak Raydium programları tarafından assert_eq!(bump, canonical_bump) aracılığıyla reddedilir. Birisi kanonik olmayan bir bump ile PDA talep etmeye çalışırsa, tx başarısız olur.

PDA’yı imzacı olarak iletme, ancak sahip programı olmadığında

Yalnızca PDA’nın türetilmesindeki ID’si bulunan program invoke_signed’ı seed’leriyle çağırabilir. Eğer çalışırsanız, çalışma zamanı reddeder.

CPI tuzakları

remaining_accounts iletmeyi unutma

Eğer dış talimatınız transfer-hook hesaplarını remaining_accounts’da iletiyorsa, ancak Raydium’a yapılan CPI onları iletmiyorsa, Raydium başarısız olur çünkü hook hesaplarını bulamaz. Her zaman ihtiyaç duyduğunda CPI’lere with_remaining_accounts ekleyin.

Yazılabilir bayrakları uyuşmazlığı

Dış talimatın yazılabilir olarak işaretlediği bir hesap, CPI çağrısında da yazılabilir olmalıdır eğer çağrılan program bunu yazmayı amaçlıyorsa. Uyuşmazlık → çalışma zamanı reddi.

Kira hesaplamıyor

Hesap oluşturan bir programa CPI (örn. ATA oluşturma), ödeyen’in kira için yeterli SOL’a sahip olmasını gerektirir. Başarısız kira kontrolleri belirsiz hatalar olarak görünür.

Çalışan örnek: Raydium CPMM PDA’larını hesaplama

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

const CPMM_PROGRAM_ID = new PublicKey("CPMMoo8L3F4NbTegBCKVNunggL7H1Zpdmwpwh8KMoZ0F");

function computeCpmmPdas(ammConfig, mintA, mintB) {
  const [poolState, poolBump] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("pool"),
      ammConfig.toBuffer(),
      mintA.toBuffer(),
      mintB.toBuffer(),
    ],
    CPMM_PROGRAM_ID,
  );

  const [authority] = PublicKey.findProgramAddressSync(
    [Buffer.from("vault_and_lp_mint_auth_seed")],
    CPMM_PROGRAM_ID,
  );

  const [vaultA] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), mintA.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  const [vaultB] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), mintB.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  const [observation] = PublicKey.findProgramAddressSync(
    [Buffer.from("observation"), poolState.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  return { poolState, authority, vaultA, vaultB, observation, poolBump };
}
Bu, getPoolInfoFromRpc({ poolId }) çağırdığınızda Raydium SDK’sının kapaklar altında yaptığı şeydir — gidiş-dönüş olmadan ilişkili PDA’ları türetir.

İşaretçiler

Kaynaklar: