الانتقال إلى المحتوى الرئيسي

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.

هذه الصفحة مُترجَمة آليًا بواسطة الذكاء الاصطناعي. النسخة الإنجليزية هي المرجع المعتمد.عرض النسخة الإنجليزية →
معرّفات البرنامج وبذور PDA للـ CPMM مدرجة بشكل رسمي في reference/program-addresses. تركز هذه الصفحة على الغرض من كل حساب والثوابت التي يحافظ عليها، وليس على العناوين المشفرة.

الحسابات الستة لمجموعة CPMM

يتم وصف كل مجموعة CPMM بالكامل من خلال ستة عناوين مشتقة من البرنامج (PDAs) تحت برنامج CPMM، بالإضافة إلى حساب AmmConfig مشترك واحد يُشير إليه. بمجرد حصولك على الـ mint الاثنين، يمكنك اشتقاق كل شيء بشكل حتمي دون لمس الشبكة.
الحسابالبذرة(ات)المالكالغرض
authority"vault_and_lp_mint_auth_seed"CPMMالموقّع لكل حركة خزينة وكل عملية mint/burn من LP. مشترك عبر جميع مجموعات CPMM.
poolState"pool"، ammConfig، token0Mint، token1Mint أو أي زوج مفاتيح عشوائي يوفره الموقّعCPMMهيكل حالة المجموعة — زوج mint والأرصدة في الخزائن وإمداد LP وتراكم الرسوم وموقّع المراقبة. تقبل تعليمات CPMM Initialize إما PDA الكنسي المشتق من البذور الأربع أو زوج مفاتيح تعسفي موقّع من قِبل المُنشئ. يوجد مسار زوج المفاتيح العشوائي لهزيمة هجوم front-running حيث يراقب خصم المجموعة الذاكرة ويتسابق لاحتلال PDA الكنسي قبل المُنشئ الشرعي.
lpMint"pool_lp_mint"، poolStateSPL Tokenرمز LP للمجموعة. الإمداد = إجمالي LP المعترف به. سلطة mint = CPMM authority PDA.
vault0"pool_vault"، poolState، token0MintSPL Token / Token-2022يحتفظ برصيد المجموعة من token0. مملوك بواسطة authority PDA.
vault1"pool_vault"، poolState، token1MintSPL Token / Token-2022يحتفظ برصيد المجموعة من token1. مملوك بواسطة authority PDA.
observation"observation"، poolStateCPMMحلقة مخزن مؤقت من عينات الأسعار المستخدمة للـ TWAP. يتم الكتابة في كل swap.
والإعدادات المشتركة:
الحسابالبذرة(ات)المالكالغرض
ammConfig"amm_config"، index: u16CPMMيحتفظ برسوم التداول والبروتوكول والصندوق والمُنشئ والمفاتيح الإدارية. واحد لكل “طبقة رسوم”. يرتبط poolstate بواحدة عند الإنشاء ولا يمكن تغييره لاحقاً.

استخراج مجموعة من لا شيء سوى اثنين من mints

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,
  };
}
رتّب الـ mints دائماً قبل استخراج PDA المجموعة. تقوم البذرة بمصادفة الـ mints بترتيب البايت، وليس بترتيب المستخدم. كان لدى مجموعتين مع (A, B) و (B, A) ستتصادم على السلسلة — الترتيب هو كيفية جعل البرنامج للتخطيط الكنسي.
معرّف المجموعة ليس دائماً الـ PDA الكنسي. تقبل Initialize زوج مفاتيح موقّع تعسفي كـ pool_state بالإضافة إلى PDA أعلاه. إذا لم يطابق الحساب المُمرر الـ PDA الكنسي، يتطلب البرنامج أن يكون موقّعاً — أي يمرر المُنشئ زوج مفاتيح جديد يوقّعون به. هذا هو الدفاع ضد front-running: يمكن لأي طرف ثالث يتسابق لالتقاط الـ PDA الكنسي أن يتم تجاوزه من قِبل المُنشئ الشرعي باستخدام زوج مفاتيح عشوائي بدلاً من ذلك. يتم اشتقاق الـ PDAs اللاحقة (lpMint، vault0، vault1، observation) من poolState.key()، لذا تبقى فريدة لأي عنوان تم استخدامه. عندما تقوم بفهرسة المجموعات، قم دائماً باكتشاف معرّف المجموعة من الحالة على السلسلة (مثل حسابات PoolState تحت برنامج CPMM)، وليس بمحاولة استخراج الـ PDA الكنسي — سيفقد الأخير مجموعات أزواج المفاتيح العشوائية.

تخطيطات الحساب

تعيش التعريفات الكاملة لـ Rust في مصدر raydium-cp-swap. الحقول أدناه هي التي ستقرأها من التكامل.

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],
}
ما يجب قراءته فعلياً:
  • lp_supply — مرآة داخلية للمجموعة لإجمالي إمداد LP mint. استخدمه لرياضيات LP-share؛ يجب أن تطابق القيمة الإمداد على السلسلة من mint، لكن قراءتها من PoolState تتجنب جلب حساب إضافي.
  • protocol_fees_token{0,1}، fund_fees_token{0,1} — الرسوم المتراكمة التي لم يتم حصدها بعد. هذه لا تؤثر على تسعير swap؛ تجلس في الخزائن حتى استدعاء CollectProtocolFee / CollectFundFee.
  • status — قناع بت يتحكم في السماح بـ Swap و Deposit و Withdraw. تم تحديثه بواسطة الإدارة عبر UpdatePoolStatus. يتحقق الـ SDK من هذا قبل بناء معاملة؛ إذا كنت تقوم بـ CPI مباشرة، تحقق منها بنفسك.
  • token0_program / token1_program — برنامج الرمز للـ CPI في لكل خزينة. يمكن لأحدهما أن يكون SPL Token الكلاسيكي والآخر Token-2022؛ يكونان مستقلين.
  • open_time — طابع زمني Unix. يفشل swaps قبل هذا الوقت. يُسمح بالإيداعات قبل open_time حتى يمكن زرع المجموعة.
  • creator_fee_on / enable_creator_fee — معاً يتحكمان في ما إذا كانت رسوم المُنشئ الاختيارية نشطة لهذه المجموعة وأي جانب من swap يتم جمعها. enable_creator_fee == false يصفر مسار رسوم المُنشئ بالكامل. عند التفعيل، يختار creator_fee_on: 0 = خذ الرسم من أي رمز هو مدخل swap (BothToken1 = خذ الرسم من token_0 فقط (تخطي في swaps token_1 → token_02 = خذ الرسم من token_1 فقط. تم التعيين عند إنشاء المجموعة عبر InitializeWithPermission؛ لا يمكن تغييره لاحقاً.
  • creator_fees_token_{0,1} — الرسوم المتراكمة للمُنشئ، يتم حصدها بواسطة 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],
}
ثلاثة أشياء يجب الحذر منها:
  1. trade_fee_rate و creator_fee_rate هما كسور من الحجم، كلاهما مقسم بوحدات 1/1_000_000. 2500 تعني 0.25% من حجم التداول. protocol_fee_rate و fund_fee_rate هما كسور من رسوم التداول (وليس من الحجم)، بنفس مقسم 1/1_000_000. رسوم المُنشئ ليست كسراً من رسوم التداول — فهي معدل مستقل خاص بها. الحسابات الكاملة موجودة في products/cpmm/fees.
  2. index هو u16، لذا تستخدم بذرة الـ hash 2 بايت بترتيب big-endian. عدم التطابق على ترتيب البايت هو خطأ تكامل شائع.
  3. AmmConfig ثابت على مستوى المجموعة. تشير المجموعة إلى AmmConfig واحد عند الإنشاء ولا تتحول أبداً. تنتشر تغييرات الرسوم لأن المجموعة تقرأ الإعدادات في كل swap — لكن المجموعة لا يمكن نقلها بين طبقات الرسوم.
ملاحظة على رسوم المُنشئ: المعدل نفسه (creator_fee_rate) يعيش على AmmConfig ومشترك عبر طبقة الرسوم. ما إذا كانت مجموعة معينة تفرضها فعلاً (enable_creator_fee) وأي جانب من swap تقع عليه (creator_fee_on) يعيش على PoolState. رسوم المُنشئ مستقلة عن رسوم التداول — فهي معدل خاص بها، متراكمة على عدادات خاصة بها (creator_fees_token_{0,1})، وأبداً لا تقلل من أسهم LP / البروتوكول / الصندوق من رسوم التداول. الحصد عبر CollectCreatorFee. انظر products/cpmm/fees للميكانيكا الكاملة.

Permission

حساب تحكم الوصول الصغير المستخدم بواسطة InitializeWithPermission. يدعم برنامج CPMM مسار إنشاء مجموعة محدود بحيث يمكن للبرامج الأخرى (على سبيل المثال LaunchLab عند تخريج رمز إلى CPMM) إثبات أنهم مخوّلون بإنشاء مجموعة ضد AmmConfig معين.
pub struct Permission {
    pub authority: Pubkey,    // who is allowed to call InitializeWithPermission
    pub padding: [u64; 8],
}
يتم إنشاء PDA Permission بواسطة إدارة CPMM عبر CreatePermissionPda وإلغاء صلاحيته عبر ClosePermissionPda. لا يتفاعل المستخدمون النهائيون مع هذا الحساب مباشرة — إنه أنابيب لتدفقات برنامج متقاطع.

الخزائن و Token-2022

يمتلك vault0 و vault1 CPMM authority PDA، وفي حين أن مالك برنامج الرمز (token_program) هو إما SPL Token أو Token-2022، تم تحديده عند إنشاء المجموعة ببرنامج mint. تتعامل المجموعة مع الحالتين بشفافية — يمكنك تمرير معرّف برنامج الرمز الصحيح لكل جانب في حسابات تعليمات Swap / Deposit / Withdraw. تفرض CPMM قائمة سماح امتدادات صارمة عند إنشاء المجموعة (is_supported_mint في utils/token.rs). يمكن استخدام mint Token-2022 في مجموعة CPMM فقط إذا كان كل امتداد يحمله موجود على هذه القائمة:
  • TransferFeeConfig. يتم تطبيقه بواسطة mint في كل نقل. المجموعة على الجانب المستقبل لإيداعات SwapBaseInput والجانب الإرسال للانسحابات. يقوم البرنامج بحساب الكمية الصافية الهابطة في الخزينة ويعيّن المنحنى وفقاً لذلك. انظر algorithms/token-2022-transfer-fees.
  • MetadataPointer و TokenMetadata. البيانات الوصفية المعيارية على mint. لا تأثير على رياضيات swap.
  • InterestBearingConfig. يتراكم مبلغ واجهة المستخدم في mint الفائدة. الخزينة تخزن مبالغ خام؛ المنحنى يعمل على مبالغ خام فقط. يجب على الواجهات التي تعرض APR استدعاء مساعدات Token-2022 لتقديم مبلغ الواجهة.
  • ScaledUiAmount. امتداد تحجيم عرض الواجهة. معاملة نفس InterestBearingConfig — يستخدم المنحنى مبالغ خام.
أي امتداد آخر — PermanentDelegate، TransferHook، DefaultAccountState، NonTransferable، ConfidentialTransfer، Group/GroupMember، MintCloseAuthority، إلخ. — يسبب Initialize في الرفض مع NotSupportMint. الاستثناء هو قائمة mint بيضاء صغيرة مشفرة في البرنامج (حفنة من pubkeys محددة) تتجاوز فحص الامتداد؛ يتم استخدامها لتوزيع mints محددة حسب الحالة. تعيش قائمة الامتدادات المدققة وقائمة بيضاء mint في مصدر CP-Swap ضمن programs/cp-swap/src/utils/token.rs ويمكن أن تتغير مع ترقيات البرنامج المستقبلية.

المراقبة

حساب المراقبة هو حلقة مخزن مؤقت لإدخالات ObservationState، تخزن كل واحدة block_timestamp و سعر تراكمي. في كل swap، يقوم البرنامج بإضافة ملاحظة جديدة إذا مر وقت كاف منذ آخر ملاحظة. يتم حساب TWAPs بقراءة ملاحظتين وقسم Δ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],
}
تم تحجيم حلقة المخزن المؤقت لـ 100 ملاحظة. كل ملاحظة هي 40 بايت، لذا الصفيف وحده هو 4,000 بايت؛ ObservationState PDA الكامل حوالي 4,100 بايت بعد الحقول المحيطة والمميز. قاعدتا مستهلك:
  • لا تستخدم ملاحظة واحدة كسعر. إنها تراكمية، وليست سعر فوري. استخدم اثنتين منهما لحساب TWAP.
  • اختر ملاحظات على الأقل كتلة واحدة بعيدة. قد لا تُنتج swaps داخل نفس الكتلة ملاحظة جديدة؛ القراءة المتتالية يمكن أن تعود السجل نفسه.
المزيد من الرياضيات في products/clmm/accounts.

دورة حياة الحساب

الحدثالحسابات التي تم إنشاؤهاالحسابات المحذوفة
InitializepoolState، lpMint، vault0، vault1، observation
Deposit— (قد تنشئ ATA LP للمستخدم)
Withdraw
Swap— (قد تنشئ ATA وجهة المستخدم)
CollectProtocolFee
CollectFundFee
UpdatePoolStatus
المجموعات CPMM و PDAs الخاصة بها لا تُغلق أبداً. حتى في سيولة الصفر، poolState يبقى. هذا مقصود: إعادة زراعة نفس المجموعة لاحقاً تحافظ على مخزن المراقبة التاريخي و مشتقات PDA تبقى مستقرة.

ما يجب قراءته من أين

المصادر: