> ## 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.

# رياضيات CPMM

> متغيّر الناتج الثابت، SwapBaseInput مقابل SwapBaseOutput، معالجة رسوم نقل Token-2022، وكيفية تحديث حساب الملاحظات.

<Info>
  **هذه الصفحة مُترجَمة آليًا بواسطة الذكاء الاصطناعي. النسخة الإنجليزية هي المرجع المعتمد.**

  [عرض النسخة الإنجليزية →](/products/cpmm/math)
</Info>

## المتغيّر الثابت

يحافظ CPMM على متغيّر الناتج الثابت الكلاسيكي على خزانتيه:

$$
x \cdot y = k
$$

حيث `x` هو رصيد vault0 **بعد** أي رسوم نقل Token-2022 عند الاستقبال، وبالمثل لـ `y`. يجب أن تترك كل مبادلة `k' ≥ k` بعد احتساب رسوم التداول المُعترف بها للـ LP (بحيث لا تُحتسب دلاء البروتوكول والصندوق والمنشئ نحو `k` — فهي تجلس في الخزان لكن تُستثنى من عرض المنحنى، انظر [الرسوم على المنحنى](#الرسوم-على-المنحنى) أدناه). لذلك `k` ينمو بشكل مطرد بمرور الوقت مع تراكم الرسوم لـ LPs.

يتم تسعير حصص LP من خلال احتياطيات المجموعة، وليس من خلال `k`:

$$
\text{سعر LP في token0} = \frac{x}{\text{lpSupply}}, \qquad \text{سعر LP في token1} = \frac{y}{\text{lpSupply}}
$$

حرق `ΔLP` من رموز LP يرجع بالضبط `ΔLP × x / lpSupply` من token0 و `ΔLP × y / lpSupply` من token1. لا المنحنى ولا `k` يتحرك عند الإيداع أو السحب — فقط المبادلات تغيّر السعر.

## نموذج الرسوم على مسار المبادلة

يطبق CPMM **رسومًا مستقلة بمعدلات منفصلة** على كل مبادلة:

* **رسم التداول** يُؤخذ من جانب الإدخال، مُحتسبًا عند `AmmConfig.trade_fee_rate`. ثم يتم تقسيمه إلى حصص LP والبروتوكول والصندوق (تبقى حصة LP في الخزان وتنمو `k`؛ حصص البروتوكول والصندوق تُستخرج من محاسبة الخزان).
* **رسم المنشئ** (نشط فقط عندما يكون `enable_creator_fee == true`) يُحتسب عند `AmmConfig.creator_fee_rate`. يُؤخذ من جانب **الإدخال** أو جانب **الإخراج** بناءً على `PoolState.creator_fee_on` واتجاه المبادلة (انظر [`products/cpmm/fees`](/ar/products/cpmm/fees#الجانب-الذي-يُؤخذ-منه-الرسم-من-الصفقة)). إنه دلو خاص به — ليس أبدًا جزءًا من رسم التداول.

دع:

* `FEE_RATE_DENOMINATOR = 1_000_000`
* `trade_fee_rate` — من `AmmConfig`، مثلاً `2500` = 0.25% من الحجم ذي الصلة
* `creator_fee_rate` — من `AmmConfig`، مثلاً `1000` = 0.10% من الحجم ذي الصلة
* `protocol_fee_rate`, `fund_fee_rate` — مقسومة بوحدات `1/FEE_RATE_DENOMINATOR` **من رسم التداول**، وليس من الحجم

عندما يكون رسم المنشئ على جانب الإدخال:

```
total_input_fee = ceil(amount_in * (trade_fee_rate + creator_fee_rate) / FEE_RATE_DENOMINATOR)
creator_fee     = floor(total_input_fee * creator_fee_rate / (trade_fee_rate + creator_fee_rate))
trade_fee       = total_input_fee - creator_fee
amount_in_after_fees = amount_in - total_input_fee
```

عندما يكون رسم المنشئ على جانب الإخراج:

```
trade_fee            = ceil(amount_in * trade_fee_rate / FEE_RATE_DENOMINATOR)
amount_in_after_fees = amount_in - trade_fee
amount_out_curve     = curve_output(amount_in_after_fees, ...)
creator_fee          = ceil(amount_out_curve * creator_fee_rate / FEE_RATE_DENOMINATOR)
amount_out           = amount_out_curve - creator_fee
```

في كلا الحالتين يتم تقسيم رسم التداول بنفس الطريقة:

```
protocol_fee   = floor(trade_fee * protocol_fee_rate / FEE_RATE_DENOMINATOR)
fund_fee       = floor(trade_fee * fund_fee_rate     / FEE_RATE_DENOMINATOR)
lp_fee         = trade_fee - protocol_fee - fund_fee     // creator_fee لا يُطرح هنا
```

يُحتفظ بمبلغ `protocol_fee + fund_fee + creator_fee` في الخزانات لكن يُتتبع بشكل منفصل في حالة المجموعة (`protocol_fees_token*`, `fund_fees_token*`, `creator_fees_token*`). عندما تتحقق فحوصات متغيّر الناتج الثابت من `k' ≥ k`، تستخدم أرصدة الخزان **ناقصًا** جميع الرسوم الثلاثة المتراكمة وغير المجمّعة — لذا يجني LPs فقط `lp_fee`.

انظر [`products/cpmm/fees`](/ar/products/cpmm/fees) لتعليمات الجمع والأمثلة الرقمية المفصلة.

## SwapBaseInput (إدخال دقيق)

"المستخدم يعطينا بالضبط `amount_in` من mint الإدخال ويتلقى على الأقل `minimum_amount_out` من mint الإخراج."

تجاهل Token-2022 للحظة:

```
amount_in_after_trade_fee = amount_in - trade_fee
amount_out                = y − (x * y) / (x + amount_in_after_trade_fee)
```

بالجبر:

$$
\text{amount\_out} = \frac{y \cdot \Delta x_{\text{net}}}{x + \Delta x_{\text{net}}}
$$

حيث `Δx_net = amount_in_after_trade_fee`.

يُحدّث البرنامج بعد ذلك محاسبة الخزان بحيث تجلس الحصة من `trade_fee` المستحقة للبروتوكول والصندوق والمنشئ في دلاء "متراكمة" (لم تُضمّن في منحنى `x` التالي)، بينما تنضم حصة LP إلى `x` للمبادلة التالية.

### Token-2022 على جانب الإدخال

إذا كان mint الإدخال يحتوي على امتداد رسم النقل، فإن **mint** يخصم رسمه عند النقل من المستخدم → الخزان. لذا يتلقى الخزان فعليًا `amount_in − transfer_fee_in(amount_in)`. يحسب برنامج CPMM بالتالي:

```
amount_actually_received = amount_in − transfer_fee_in(amount_in)
trade_fee                = ceil(amount_actually_received * trade_fee_rate / FEE_RATE_DENOMINATOR)
amount_in_after_trade_fee = amount_actually_received − trade_fee
```

ويشغّل المنحنى مقابل `amount_in_after_trade_fee`. هذا مهم لأن **سعر المنحنى يُحسب من صافي المبلغ الذي وصل إلى الخزان**، وليس من مبلغ المستخدم الإجمالي.

### Token-2022 على جانب الإخراج

إذا كان mint الإخراج يفرض رسم نقل، فإن المجموعة ترسل `amount_out` من خزانها للمستخدم. **mint** سيقتطع رسمه على الطريق، لذا يتلقى المستخدم `amount_out − transfer_fee_out(amount_out)`. يحسب البرنامج `amount_out` من المنحنى كالمعتاد، لكن من مسؤولية المُدمج تحويل رقم "إرسال الخزان" الخاص بالمجموعة إلى رقم "استقبال المستخدم" عند عرض العروض الأسعار.

### فحص الانزلاق

بعد حساب `amount_out`:

```
require(amount_out >= minimum_amount_out, "AmountSpecifiedLessThanMinimum")
```

إذا كان mint الإخراج يفرض رسم نقل، فإن SDK يطبق رسم النقل **قبل** تعيين `minimum_amount_out` بحيث يكون ثابت الانزلاق مقسومًا بما سيستقبله المستخدم فعليًا، وليس بما يرسله الخزان.

## SwapBaseOutput (إخراج دقيق)

"المستخدم سيتلقى بالضبط `amount_out` من mint الإخراج وعلى استعداد للدفع بما يصل إلى `maximum_amount_in` من mint الإدخال."

قلب المنحنى للحصول على `Δx_net`:

$$
\Delta x_{\text{net}} = \left\lceil \frac{x \cdot \text{amount\_out}}{y - \text{amount\_out}} \right\rceil
$$

السقف مهم — فهو يضمن `k' ≥ k` بعد الاقتطاع الصحيح. ثم:

```
// العمل بشكل عكسي من صافي الإدخال إلى إجمالي الإدخال.
// يتم تطبيق الرسم على الإجمالي، لذلك:
//   net = gross − ceil(gross * rate / D)
//       ≈ gross * (D − rate) / D
// قلب مع السقف في الأماكن الصحيحة:
gross_needed = ceil(Δx_net * D / (D − trade_fee_rate))
```

على Token-2022 من جهة الإدخال، اغلفها بـ:

```
gross_needed_before_mint_fee
  = inflate_for_transfer_fee(gross_needed, input_mint)
```

بحيث يدفع المستخدم ما يكفي بحيث بعد خصم رسم النقل الخاص بـ mint يستقبل الخزان `gross_needed`.

### فحص الانزلاق

```
require(gross_needed_before_mint_fee <= maximum_amount_in, "AmountSpecifiedExceedsMaximum")
```

## مثال عملي

حالة المجموعة، تجاهل Token-2022:

* `x = 1_000_000_000_000` (1,000,000.000000 من token0، 6 كسور عشرية)
* `y = 2_000_000_000_000` (2,000,000.000000 من token1، 6 كسور عشرية)
* `AmmConfig`: `trade_fee_rate = 2500`, `protocol_fee_rate = 120_000`, `fund_fee_rate = 40_000`, `creator_fee_rate = 0`

المستخدم: `SwapBaseInput` مع `amount_in = 1_000_000_000` (1,000.000000 من token0). رسم المنشئ معطّل (`enable_creator_fee = false`).

```
trade_fee                = ceil(1_000_000_000 * 2500 / 1_000_000)       = 2_500_000
  protocol_fee           = floor(2_500_000 * 120_000 / 1_000_000)       = 300_000
  fund_fee               = floor(2_500_000 *  40_000 / 1_000_000)       = 100_000
  lp_fee                 = 2_500_000 − 300_000 − 100_000                 = 2_100_000
creator_fee              = 0                                              // معطّل

amount_in_after_trade_fee = 1_000_000_000 − 2_500_000                    = 997_500_000

amount_out = y − (x * y) / (x + Δx_net)
           = 2_000_000_000_000
             − (1_000_000_000_000 * 2_000_000_000_000)
               / (1_000_000_000_000 + 997_500_000)
           ≈ 1_995_015_009

new_vault0_raw   = x + amount_in                                        = 1_001_000_000_000
new_vault1       = y − amount_out                                       ≈ 1_998_004_984_991

// من الـ 1_000_000_000 المستقبل في vault0، 400_000 هو "رسم متراكم"
// (بروتوكول + صندوق) يجب على المنحنى استثناؤه:
curve_x          = new_vault0_raw − (protocol_fees_token0 + fund_fees_token0)
                 = 1_001_000_000_000 − 400_000
                 = 1_000_999_600_000

k' = curve_x * new_vault1 ≈ 2.000_002_501_E24  ≥  k = 2.0E24   ✓
```

إذا كانت نفس المجموعة لديها `enable_creator_fee = true` مع `creator_fee_rate = 1000` (0.10%) على جانب الإدخال، فإن البرنامج سيفرض `total_input_fee = ceil(1_000_000_000 * 3500 / 1_000_000) = 3_500_000`، ثم يقسمها إلى `creator_fee = 1_000_000` و `trade_fee = 2_500_000`. حساب البروتوكول والصندوق والـ LP على `trade_fee` لم يتغير عن المثال أعلاه — رسم المنشئ هو دلو خاص به، متراكم إلى `creator_fees_token0` واستُثني من `curve_x` جنبًا إلى جنب مع دلاء البروتوكول والصندوق.

إذا كان mint الإدخال يحتوي على رسم نقل 1% من Token-2022، فإن الخزان يستقبل `990_000_000` رموز بدلاً من `1_000_000_000`، وكل عملية حساب لاحقة تستخدم هذا المبلغ الصافي.

## قاعدة تحديث الملاحظة

على كل مبادلة، يقيّم البرنامج ما إذا كان يجب دفع ملاحظة جديدة في مخزن مؤقت الحلقة:

```
let since_last = now − observations[head].block_timestamp;
if since_last >= MIN_OBSERVATION_INTERVAL {
    let price0 = (vault1 << 32) / vault0;            // Q32.32-ish
    let price1 = (vault0 << 32) / vault1;
    let head' = (head + 1) % OBSERVATION_NUM;
    observations[head'] = Observation {
        block_timestamp: now,
        cumulative_token0_price_x32:
            observations[head].cumulative_token0_price_x32 + price0 * since_last,
        cumulative_token1_price_x32:
            observations[head].cumulative_token1_price_x32 + price1 * since_last,
    };
    head = head';
}
```

خاصيتان:

* **السعر التراكمي، وليس السعر الفوري.** ملاحظة واحدة ليست سعرًا. للحصول على TWAP من الوقت `t0` إلى `t1`، اقرأ الملاحظات الأقرب إلى كل نهاية واحسب `(cumulative(t1) − cumulative(t0)) / (t1 − t0)`.
* **العينات محدودة المعدل.** قد تشارك المبادلات المتتالية في نفس الفتحة ملاحظة واحدة. قراءة ملاحظة فورًا بعد مبادلة يمكن بالتالي أن تبدو قديمة بفتحة واحدة — هذا أمر طبيعي.

المزيد في [`products/clmm/accounts`](/ar/products/clmm/accounts).

## الرسوم على المنحنى

هذا هو الجزء الدقيق ويستحق الإشارة إليه. يعمل حساب المنحنى مقابل أرصدة الخزان **الصافية** — أي رصيد SPL الخام ناقصًا رسوم البروتوكول والصندوق والمنشئ المتراكمة (جميعها ثلاثة دلاء مستقلة — انظر [`products/cpmm/fees`](/ar/products/cpmm/fees)). صورة ملموسة:

```
raw_vault_balance   = ما يعيده getTokenAccountBalance من RPC
accrued_fees        = protocol_fees_token{0,1} + fund_fees_token{0,1} + creator_fees_token{0,1}
curve_balance       = raw_vault_balance − accrued_fees
invariant           = curve_balance0 * curve_balance1
```

العواقب للمُدمجين:

* **لا تقتبس من الأرصدة الخام.** اطرح حقول الرسوم المتراكمة أولاً، أو استدعِ `SwapBaseInput` كمحاكاة وخذ عودتها.
* **`CollectProtocolFee` ينقل الرموز خارج الخزان.** بعد الجمع، ينخفض `raw_vault_balance` لكن `curve_balance` لم يتغير؛ سعر المجموعة لا يتحرك. هذا متعمد.

## الدقة والفيض

* جميع عمليات حساب المنحنى تستخدم وسطاء `u128` لمنع الفيض عند `x * y`.
* القسمة تقريب نحو الصفر باستثناء `SwapBaseOutput` الخاص بـ `Δx_net`، الذي يقرب لأعلى، وحساب الرسم، الذي يقرب لأعلى على `trade_fee` ولأسفل على الانقسامات الفرعية. يتم اختيار اتجاهات التقريب هذه بحيث لا ينخفض المتغيّر أبدًا بسبب الاقتطاع الصحيح.
* قد تضرب المجموعات ذات نسب الخزان المتطرفة (مليارات : 1) أرضيات دقة على التداولات الصغيرة؛ يعود البرنامج `ZeroTradingTokens` في تلك الحالة. انظر [`reference/error-codes`](/ar/reference/error-codes).

## ماذا بعد

* [`products/cpmm/fees`](/ar/products/cpmm/fees) — دلالات طبقات الرسوم الكاملة والجمع.
* [`products/cpmm/instructions`](/ar/products/cpmm/instructions) — التعليمات التي تستدعي هذه الرياضيات.
* [`algorithms/constant-product`](/ar/algorithms/constant-product) — الاشتقاق وحالات الحواف الخاصة بـ `x · y = k` المشتركة عبر AMM v4 و CPMM.

المصادر:

* [`raydium-io/raydium-cp-swap` — رياضيات المبادلة في `states/curve.rs`](https://github.com/raydium-io/raydium-cp-swap)
* تقارير تدقيق Raydium المرتبطة في [`security/audits`](/ar/security/audits)
