فخ Uniswap السام: 130 ألف دولار رسومًا في 29 ساعة
Blockbeatsالعنوان الأصلي: «سعر معروض 0% وتنفيذ 12.8%: مراجعة فخ الرسوم الديناميكية في Uniswap v4»
المؤلف الأصلي: وانغ زيهاو، BitsLab
في الفترة من 22 إلى 23 أغسطس 2026، عرض مجمع أسعار على سلسلة BNB Chain رسومًا بنسبة 0% لمجمع رسوم ديناميكي USDC/WBNB على Uniswap v4، بينما دفع المستخدمون فعليًا رسوم مزود سيولة بنسبة 12.8%. خلال نحو 29 ساعة، نفذ المجمع 21,086 صفقة ورسومًا تراكمية بلغت 131,888 دولارًا، ثم أُغلق. نجحت جميع الصفقات المتضررة: لم تُخترق العقود، وكان الناتج أعلى من الحد الأدنى الذي حدده المستخدم، ولم تظهر أي إشارات غير طبيعية — تحول الفارق بهدوء إلى رسوم.
يبدأ هذا المقال بصفقة عينة:
(0x7615dec2ce80506ec461a47739eb8533ac2bc7c605ca56c255964481b76363d0: أودع المستخدم 5,926.90 USDT واستلم 757.43 USDT أقل من المتوقع)، لتفكيك كيف حقق هذا المجمع «سعرًا معروضًا 0% وتنفيذًا 12.8%»: الحكم ثلاثي الطبقات لدالة الرسوم، وحماية الانزلاق المعطلة، والقائمة البيضاء التي تمنع مزودي السيولة الآخرين. تستند جميع الاستنتاجات إلى بيانات على السلسلة وتحقق محلي خاضع للرقابة.
01 المسار من السعر المعروض إلى التنفيذ
بعد أن يبدأ المستخدم عملية تبادل على واجهة المجمع، يسير المسار بالكامل على النحو التالي:

هناك حقيقتان خلفيتان على هذا المسار، وكل حكم لاحق مبني عليهما.
انظر أولًا إلى جانب v4: يسمح للخطاف بتجاوز الرسوم لكل صفقة. يعلن PoolKey.fee = 0x800000 عن مجمع رسوم ديناميكي؛ ويحمل الخطاف في القيمة المرجعة الثالثة لـ beforeSwap القيمة 0x400000 | fee. الكود الذي يستقبل هذه القيمة المرجعة في v4-core (Pool.sol:303-305):

تتحقق isOverride() من علامة التجاوز، وتزيل removeOverrideFlagAndValidate() علامة 0x400000 وتتحقق من عدم تجاوز MAX_LP_FEE = 1,000,000 (100%)، ثم تنفذ صفقة التبادل الحالية بهذه الرسوم وتكتب حدث Swap. بمعنى آخر، ما يعيده الخطاف هو ما يدفعه المستخدم — طالما ظل الناتج أعلى من amountOutMin. هذه ميزة رسمية، وتعديل الرسوم وفقًا للتقلبات أو المخزون استخدام مشروع.
انظر الآن إلى الخطاف نفسه: أعلن عن صلاحيتين فقط. يشفر v4 الصلاحيات في آخر 14 بتًا من عنوان العقد (Hooks.sol:29-47)، ويتم التحقق منها عند النشر ولا يمكن تغييرها لاحقًا. تعريفات العلامات في v4-core وفك ترميز العنوان للمقارنة:

أظهر الاختبار الفعلي على السلسلة أن getHookPermissions() يعيد قيمة مطابقة للصورة النقطية: هذان البتان فقط صحيحان.
الخطاف بدون كود مصدري. فكك هذا المقال دالة الرسوم بالكامل من البايت كود — وفيما يلي تحليل سلوكي لهذا المنطق الذي عمل بالفعل على السلسلة:

لننظر الآن طبقة تلو الأخرى. تجيب الطبقتان الأوليان عن سؤال «هل هذه محاكاة؟»، بينما تحدد الطبقة الثالثة مقدار ما تدفعه الصفقة الحقيقية.
02 الحكم ثلاثي الطبقات لدالة الرسوم
الحكم الأول: تمييز المستدعي
بعد استدعاء دالة الرسوم، تقارن أولًا tx.origin: إذا كان يساوي أيًا من ثلاثة عناوين ثابتة، تعيد مباشرة فئة الاحتياط. فئة الاحتياط مهيأة حاليًا بنسبة 0%.
العناوين الثلاثة ليست عشوائية. عندما لا يحدد eth_call عنوان from، يقع أصل المعاملة على العنوان الصفري — ولا يحدد الاستعلام الجماعي للمجمع عادةً عنوانًا. تحقق مباشر في fork محلي:

الطبقة الأولى تتعرف فقط على هذه العناوين الثلاثة. لنرَ في الطبقة التالية كيف يمكن للخطاف التمييز بعد تحديد عنوان عادي.
الحكم الثاني: فحص الغاز
عند تغيير tx.origin إلى عنوان عادي وإعادة المحاولة، تظل النتيجة 0% — تم تجاوز الطبقة الأولى، لكن الرسوم لم تتغير. هذا يدل على أن الطبقة الأولى ليست الحكم الوحيد، وننتقل إلى الطبقة الثانية: gasleft() * 10100 / 10000 >= gate.
يميل المجمع إلى منح غاز مرتفع جدًا في عروض الأسعار لمنع فشل المسارات المرشحة في المحاكاة بسبب نقص الغاز؛ وقبل دخول الصفقة الحقيقية إلى الخطاف، يكون الغاز قد استهلك بالفعل بواسطة الموجّه والتفويض والقفزات الأمامية. نفس الكود مع ثلاثة مستويات غاز:

يقع خط التقسيم الفعلي بين 16.85M و16.9M غاز. الفرق بين 30M و2.59M هو الفرق بين «السعر المعروض» و«التنفيذ».
الحكم الثالث: عشوائية زائفة لكل صفقة
تدخل الصفقات الحقيقية التي اجتازت الطبقتين الأوليين إلى الطبقة الثالثة: تجزئة بصمة بيئة الكتلة ثم أخذ باقي القسمة على 10,000، وإعادة فئة الرسوم المقابلة وفقًا للفاصل الذي تقع فيه النتيجة. التهيئة الحالية ثلاث فئات 8% / 10% / 10%، وإذا لم تقع في أي فاصل تعود إلى 0%.
لهذه الطبقة تصميمان جديران بالملاحظة. تخلط البصمة ثلاثة حقول من calldata — تختلف calldata الخاصة بعرض سعر المجمع عن تلك الخاصة بالتوجيه الحقيقي أصلًا، لذا تختلف البصمة طبيعيًا. كما أن العشوائية الزائفة لكل صفقة تمنح الرسوم توزيعًا يصعب ملاحظة نمطه من بضع صفقات، ولا يمكن اعتراضه بتعداد قواعد بسيط.
ظهرت 19 فئة رسوم تاريخية (6.8%–28%) في جميع أحداث السلسلة البالغ عددها 21,086، مما يدل على أن فئات الرسوم كانت تُعاد تهيئتها حسب الحاجة عبر دالة المدير؛ وكانت الفئة السارية عند تنفيذ صفقة العينة 12.8%.
الصورة المجمعة للطبقات الثلاث

يمكننا الآن تعريف «المجمع السام» — مجمع v4 يحقق ثلاثة شروط في آن واحد: يحمل PoolKey.fee علامة الرسوم الديناميكية؛ ويقرأ قرار الرسوم إشارات بيئة التنفيذ (الغاز، الأصل، بيئة الكتلة) بدلًا من حالة السوق العامة؛ وتتشعب رسوم محاكاة السعر والتنفيذ على السلسلة بشكل منهجي لصالح ناشر المجمع السام. يكمن الخط الفاصل في ما تقرؤه الرسوم: إعادة نفس الرسوم لأي مستدعٍ هو تصميم سوق قابل للبرمجة؛ أما إعادة رسوم مختلفة حسب «من يسأل عن السعر» فهو خداع لنظام التوجيه.
اكتملت هنا آلية تشعب الرسوم. لكن بعد تحصيل الرسوم، لماذا تنجح الصفقة أصلًا؟ هذا هو السؤال التالي.
03 لماذا لم تتراجع حماية الانزلاق
لننظر أولًا إلى المسار الكامل والمبالغ لهذه الصفقة:

سجل حدث Swap الخاص بهذه القفزة الرسوم السارية في حقل fee: 128000. وحدة الرسوم في v4 تعتبر 1,000,000 تساوي 100%، لذا فإن 128000 تعني 12.8%. يمكن قياس خسارة هذه الصفقة من زاويتين: من منظور المجمع، تُفرض الرسوم على مدخلات WBNB؛ ومن منظور المسار الكامل، بلغ فارق العملات المستقرة الداخلة والخارجة 757.43 USDT، أي خسارة 12.7794%، تشمل أيضًا رسوم القفزة الأمامية وانحراف الربط. الفرق بين الرقمين 0.02 نقطة مئوية فقط، مما يدل على أن المصدر الرئيسي للخسارة هو رسوم مزود السيولة لهذا المجمع.
يتحقق amountOutMin من الحد الأدنى للناتج النهائي فقط، ولا ينظر إلى مقدار الرسوم التي اقتطعتها كل قفزة وسيطة:

تقع رسوم 12.8% بالكامل داخل هامش 20.32%، ويظل الناتج أعلى من الحد الأدنى — تنجح الصفقة ولا تتراجع. الافتراضي المعتاد لمسارات العملات المستقرة الرئيسية هو 0.1%–1%، بينما منح هذا المسار مساحة تزيد عن 20 ضعفًا. ظن المستخدم أن الهامش الواسع «أكثر أمانًا»، لكنه في الواقع سلّم كل عقد على المسار لانضباط الطرف المقابل. لنرسم هذه الحسابات:

04 عمليات ناشر المجمع السام
القائمة البيضاء: منع مزودي السيولة الآخرين
منطق القائمة البيضاء موجود في beforeAddLiquidity، ونتيجة فك الترميز كما يلي:

تمر إضافة السيولة عبر ثلاث بوابات: أولًا فحص استدعاء PoolManager الموجود في أي خطاف v4، ثم يجب المرور عبر PositionManager الرسمي، وأخيرًا التحقق من القائمة البيضاء لحاملي المراكز. العناوين غير المدرجة في القائمة يرفضها العقد هنا، فلا يستطيع مزودو السيولة الآخرون الدخول، ولا تتوزع إيرادات الرسوم، بل تذهب بالكامل إلى الناشر. تُدار القائمة نفسها عبر دالة مدير (المُحدد 0xc4452e52) حسب العنوان.
فئة 0% والتداول الوهمي
عند النظر إلى 21,086 صفقة مجتمعة، يظهر التوزيع نمطًا واضحًا: فئة 0% شملت 6,946 صفقة بحجم 12,947,751 دولارًا ومتوسط 1,864 دولارًا للصفقة؛ وفئات الرسوم شملت 14,140 صفقة بحجم 1,120,106 دولارات ومتوسط 79 دولارًا للصفقة. تتركز الصفقات الكبيرة في فئة 0%، بينما تتركز الرسوم على الصفقات الصغيرة. التفسير الأكثر منطقية لكبر حجم فئة 0% هو أن الناشر أجرى تداولات ذاتية بغاز مرتفع: الغاز المرتفع يصيب نفس حكم عرض سعر المجمع، وتكلفة رسوم التداول الذاتي تقارب الصفر. دفع حجم التداول المزيف المجمع إلى صدارة تصنيفات مواقع الأسعار (أظهرت لقطة حجم تداول 24 ساعة نحو 10.82 مليون دولار و16,671 صفقة)، وفي نظر المجمع، هذا مجمع بعمق جيد ورسوم منخفضة.
إعادة تهيئة فئات الرسوم حسب الحاجة
ذكر الحكم الثالث 19 فئة رسوم تاريخية، وهي تأتي من دالة تهيئة الرسوم المفكوكة (المُحدد 0x4d909a45، غير مسجل في التوقيعات العامة):

تُحزم فئات الرسوم الأربع والعتبات معًا وتُكتب في خانة التخزين keccak(poolId, 2). أظهر الاختبار الفعلي على السلسلة أن قراءة هذه الخانة هي 0x0138800186a00186a0، وتتوافق مقطعًا بمقطع مع تنسيق حزم الدالة. فئات الرسوم هنا معاملات قابلة للتعديل في أي وقت، وليست مثبتة عند النشر.
دورة الحياة
أُنشئ هذا المجمع في 08-22 الساعة 06:57، وظهرت أول صفقة تبادل في 07:13، وكانت صفقة العينة في هذا المقال في 21:39؛ وآخر صفقة كانت في 08-23 الساعة 12:25، ثم أُغلق. بلغ إجمالي فترة النشاط نحو 29 ساعة، وحجم التداول الإجمالي 14,067,857 دولارًا، وإيرادات الرسوم 131,888 دولارًا (دون خصم تكاليف الناشر). عند إعادة الفحص، عادت علامة التسجيل إلى التفعيل — المجمع ما زال موجودًا ويمكن إعادة تفعيله في أي وقت.
05 المسار الكامل
لنربط سلسلة الهجوم كاملة:

كل علاقات السبب والنتيجة في السلسلة موجودة في الرسم أعلاه، والشروط الثلاثة لا غنى عنها: إصابة المحاكاة للطبقتين الأوليين، ووقوع الصفقة الحقيقية في الطبقة الثالثة، وكون الهامش أكبر من الرسوم. إذا اختل أي شرط، تفشل هذه الطريقة.
06 الإصلاحات والتوصيات
الموضع الأول في طريقة عرض أسعار المجمع. السبب الجذري هو أن المحاكاة والتنفيذ لا يسيران على المسار نفسه: يستخدم عرض السعر استعلامًا مثاليًا على مستوى المجمع مع عنوان صفري وغاز مرتفع؛ بينما تحمل الصفقة الحقيقية هوية حقيقية وغازًا مستهلكًا بالفعل. يستطيع الخطاف قراءة هذه الفروق بالضبط، فتتشعب الرسوم. العلاج هو جعل المحاكاة أقرب إلى التنفيذ — استخدام calldata الموجّه الفعلي الذي سيُرسل، وfrom/to حقيقيين، وغاز قريب من صفقة السلسلة؛ وبعد التنفيذ، فك ترميز حدث Swap للتحقق من الرسوم، وإذا لم تطابق عرض السعر فتُخفَّض الأولوية أو يُزال المسار.
الموضع الثاني في إعدادات الانزلاق لدى المستخدم. السبب الجذري هو أن min_out مُرخى جدًا: هامش 20.32% يبتلع رسوم 12.8% بالكامل، فتنجح الصفقة كالمعتاد. يكفي 0.1%–1% لمسارات العملات المستقرة الرئيسية للاستخدام اليومي، فانظر إلى هذا الرقم قبل بدء الصفقة؛ وتشديد المحافظ والواجهات الأمامية والخلفية للقيم الافتراضية يمكن أن يصد معظم المخاطر عن المستخدمين.
الموضع الثالث في قبول المسارات. السبب الجذري هو أن أي مجمع رسوم ديناميكي يمكنه المشاركة مباشرة في منافسة عروض الأسعار: الخطافات غير مفتوحة المصدر وغير المدققة يمكنها دخول المسارات الموصى بها بفضل حجم التداول المزيف. عدم إدراج مجمعات الرسوم الديناميكية التي لا تملك ملفًا موثوقًا في المسارات افتراضيًا أو خفض أولويتها بشكل كبير هو المعالجة الأكثر أمانًا.
البيانات والملاحظات
· صفقة العينة:
https://bscscan.com/tx/0x7615dec2ce80506ec461a47739eb8533ac2bc7c605ca56c255964481b76363d0 (الكتلة 117497524)
· صفقة تهيئة المجمع:
https://bscscan.com/tx/0x8fa72ef72d77b61f715dc9ce90548249ab045c6d78716078cd5ab425bbe69a05
· PoolManager:
0x28e2ea090877bf75740558f6bfb36a5ffee9e9df
· معرف المجمع:
0x36e5540e9dedc02229fe8a82aa5b10c0bf07d1fa74e4f2ffe0efd00fa1a36aea
· الخطاف:
0xd111b3ddd92e627f1864520c770e913ec04e0880 (بدون كود مصدري، الكود الزائف مفكوك بشكل مستقل في هذا المقال؛ المدير 0x08b03e1a5444d469f4dc954e74d3f662c94a6b13)
· الصفقات والرسوم: تجميع صفقة بصفقة لجميع أحداث Swap البالغ عددها 21,086 في هذا المجمع (eth_getLogs)، مع تحويل WBNB بسعر التنفيذ لكل صفقة؛ الإيرادات إجمالية دون خصم تكاليف الناشر
هذا المحتوى لأغراض معلوماتية وتعليمية فقط، ولا يمثل نصيحة استثمارية تتعلق بـ BTCC. تبذل BTCC قصارى جهدها ولكنها لا تضمن صحة أو دقة أو أصالة المحتوى المذكور أعلاه.