انتقل إلى المحتوى الرئيسي
بلوكتشينMar 28, 2026

Deep EVM #5: مقدمة في Yul — لغة التجميع السرية لـ Solidity

OS
Open Soft Team

Engineering Team

ما هي Yul؟

Yul هي لغة وسيطة يمكن تجميعها إلى بايتكود لعدة خلفيات مختلفة، بما في ذلك EVM. يمكنك استخدامها مباشرة داخل Solidity عبر كتل assembly { } أو كلغة مستقلة.

لماذا Yul وليس Solidity العادية؟

  • تحسين الغاز — Yul يمنحك تحكماً دقيقاً في الكود المُولد
  • عمليات منخفضة المستوى — الوصول المباشر للذاكرة والتخزين وcalldata
  • أنماط مستحيلة في Solidity — مثل returndatasize كبديل رخيص لـ push 0

البنية الأساسية

Yul تبدو مثل Solidity مبسطة بدون أنواع (كل شيء uint256):

assembly {
    // المتغيرات
    let x := 42
    let y := add(x, 1)  // y = 43
    
    // الشروط
    if gt(x, 0) {
        y := mul(x, 2)
    }
    
    // الحلقات
    for { let i := 0 } lt(i, 10) { i := add(i, 1) } {
        // جسم الحلقة
    }
    
    // الدوال
    function double(val) -> result {
        result := mul(val, 2)
    }
}

الوصول إلى الذاكرة

في Yul، تتعامل مع الذاكرة مباشرة باستخدام mload وmstore:

assembly {
    // كتابة 32 بايت في الذاكرة عند الإزاحة 0x00
    mstore(0x00, 0x1234)
    
    // قراءة 32 بايت من الذاكرة عند الإزاحة 0x00
    let val := mload(0x00)
    
    // مؤشر الذاكرة الحرة
    let ptr := mload(0x40)
    mstore(ptr, someValue)
    mstore(0x40, add(ptr, 0x20))  // تقديم المؤشر
}

الوصول إلى التخزين

assembly {
    // قراءة من فتحة التخزين 0
    let val := sload(0)
    
    // كتابة إلى فتحة التخزين 0
    sstore(0, 42)
    
    // حساب فتحة التعيين
    mstore(0x00, key)
    mstore(0x20, mappingSlot)
    let slot := keccak256(0x00, 0x40)
    let mappedValue := sload(slot)
}

التعامل مع Calldata

assembly {
    // تحميل أول 4 بايت (محدد الدالة)
    let selector := shr(224, calldataload(0))
    
    // تحميل المعامل الأول (بعد المحدد)
    let arg1 := calldataload(4)
    
    // حجم calldata
    let size := calldatasize()
}

أنماط تحسين الغاز في Yul

بديل PUSH0

assembly {
    // بدلاً من PUSH1 0x00 (3 غاز + 1 بايت)
    // استخدم returndatasize() قبل أي استدعاء خارجي
    let zero := returndatasize() // 2 غاز
}

ترميز ABI يدوي

assembly {
    // بدلاً من abi.encode(a, b)
    mstore(0x00, a)
    mstore(0x20, b)
    // البيانات المرمزة الآن في الذاكرة [0x00..0x40]
}

إرجاع مخصص

assembly {
    mstore(0x00, value)
    return(0x00, 0x20)  // إرجاع 32 بايت من الذاكرة
}

Yul كلغة مستقلة

يمكنك كتابة عقود كاملة في Yul:

object "SimpleStore" {
    code {
        // كود المُنشئ: نسخ كود التشغيل وإعادته
        datacopy(0, dataoffset("runtime"), datasize("runtime"))
        return(0, datasize("runtime"))
    }
    object "runtime" {
        code {
            // محدد الدالة
            switch shr(224, calldataload(0))
            case 0x6d4ce63c /* get() */ {
                mstore(0, sload(0))
                return(0, 32)
            }
            case 0x60fe47b1 /* set(uint256) */ {
                sstore(0, calldataload(4))
            }
            default {
                revert(0, 0)
            }
        }
    }
}

متى تستخدم Yul

استخدم Yul عندما:

  1. تحسين الغاز ضروري — المسارات الساخنة في عقود DeFi
  2. عمليات منخفضة المستوى مطلوبة — التعامل مع returndata، إنشاء عقود عبر CREATE2
  3. Solidity لا تستطيع التعبير عما تريد — أنماط ذاكرة مخصصة

لا تستخدم Yul عندما:

  1. Solidity العادية تكفي — قابلية القراءة أهم
  2. الفريق لا يعرف EVM جيداً — Yul أسهل لكتابة الأخطاء
  3. التدقيق مطلوب — كود Yul أصعب في التدقيق

الخلاصة

Yul هي جسر بين Solidity عالية المستوى والبايتكود الخام. تمنحك تحكماً دقيقاً في الكود المُولد مع الحفاظ على قابلية القراءة أفضل من البايتكود المباشر. إتقان Yul هو خطوة أساسية نحو فهم EVM بعمق وكتابة عقود محسنة للغاز.