الجمل الشرطية (If/Else) وحلقات التكرار (For/While) داخل العقود الذكية

دقائق القراءة: 6

الجمل الشرطية (If/Else) وحلقات التكرار (For/While) داخل العقود الذكية

تُعد بنى التحكم في سير التنفيذ مثل if وelse وfor وwhile من أهم الأدوات التي تمنح العقد الذكي القدرة على اتخاذ قرارات وتنفيذ منطق برمجي ديناميكي. لكن داخل بيئة Blockchain لا تُستخدم هذه الأدوات بالطريقة نفسها الشائعة في التطبيقات التقليدية، لأن كل خطوة تنفيذية قد تترجم إلى تكلفة فعلية في Gas Fees.

في عقود Solidity، لا يكفي أن ينجح الكود منطقياً فقط، بل يجب أن يكون آمناً، قابلاً للتنبؤ، ومقتصداً في استهلاك الغاز. لذلك فإن فهم الجمل الشرطية وحلقات التكرار ليس موضوعاً نحوياً برمجياً فحسب، بل هو جزء أساسي من هندسة العقود الذكية ذات الجودة العالية.

إذا كنت قد قرأت سابقاً الدوال (Functions) في Solidity: من يمكنه قراءة وتعديل بيانات العقد؟ والتكاليف (Gas Fees): كيف يحسب البلوكتشين تكلفة تنفيذ الأكواد؟، فستلاحظ أن هذا المقال يربط بين منطق اتخاذ القرار وطريقة تنفيذ الدوال فعلياً داخل EVM.

ما دور الجمل الشرطية وحلقات التكرار في العقد الذكي؟

العقد الذكي لا “يفكر” بذاته، بل ينفذ شروطاً محددة سلفاً. عند استدعاء دالة ما، قد تحتاج إلى التحقق من هوية المرسل، رصيد المستخدم، حالة الطلب، أو ما إذا كانت مهلة زمنية قد انتهت. هنا تظهر أهمية if/else.

أما حلقات التكرار فتُستخدم حين تريد المرور على عناصر داخل Arrays أو تنفيذ عملية متكررة على مجموعة بيانات. لكن هذا الاستخدام يجب أن يكون محسوباً بدقة لأن التكرار غير المنضبط قد يجعل الدالة باهظة جداً أو حتى غير قابلة للتنفيذ.

الجملة الشرطية if/else في Solidity

تعمل if على اختبار شرط منطقي. إذا كانت النتيجة true يتم تنفيذ كتلة معينة، وإذا كانت false يمكن الانتقال إلى else أو else if. هذا النمط شائع جداً في التحقق من الصلاحيات، إدارة الحالات، وتطبيق قواعد العمل داخل العقود.

في الأمثلة الواقعية، غالباً ما تُدمج الجمل الشرطية مع متغيرات الحالة التي تم شرحها في أساسيات لغة Solidity: أنواع البيانات والمتغيرات (State Variables)، لأن القرار البرمجي يعتمد عادة على بيانات مخزنة على السلسلة.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract AccessControlledVault {
    address public owner;
    uint256 public storedBalance;
    bool public paused;

    constructor() {
        owner = msg.sender;
    }

    function deposit() external payable {
        if (paused) {
            revert("Contract is paused");
        }

        if (msg.value > 0) {
            storedBalance += msg.value;
        } else {
            revert("Send ETH");
        }
    }

    function setPaused(bool _paused) external {
        if (msg.sender == owner) {
            paused = _paused;
        } else {
            revert("Not owner");
        }
    }

    function withdraw(uint256 amount) external {
        if (msg.sender != owner) {
            revert("Only owner");
        } else if (amount > storedBalance) {
            revert("Insufficient balance");
        } else {
            storedBalance -= amount;
            payable(owner).transfer(amount);
        }
    }
}

متى نستخدم if بدل require؟

من الناحية العملية، كثير من المطورين يخلطون بين الجمل الشرطية وأدوات التحقق مثل require. إذا كان هدفك الأساسي هو التحقق من شرط قبل الاستمرار، فغالباً يكون require أوضح وأكثر قابلية للقراءة. أما if/else فتناسب الحالات التي يوجد فيها أكثر من مسار تنفيذي مختلف داخل الدالة نفسها.

لأغراض Security Auditing، من الأفضل جعل شروط التحقق الحرجة واضحة ومباشرة في أول الدالة. كلما كان مسار التنفيذ أقل تعقيداً، كان تحليل العقد واكتشاف الثغرات أسهل.

حلقة for داخل العقود الذكية

حلقة for هي الأكثر استخداماً في Solidity عندما يكون عدد مرات التكرار معروفاً أو مرتبطاً بطول مصفوفة. مثالها الشائع هو المرور على قائمة عناوين أو حساب مجموع قيم مخزنة. وهي ترتبط مباشرة بمفاهيم المصفوفات (Arrays) في Solidity: تخزين وإدارة قوائم البيانات داخل العقد الذكي.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ScoreTracker {
    uint256[] public scores;

    function addScore(uint256 score) external {
        scores.push(score);
    }

    function getTotalScore() external view returns (uint256 total) {
        for (uint256 i = 0; i < scores.length; i++) {
            total += scores[i];
        }
    }

    function countPassedScores(uint256 minScore) external view returns (uint256 passed) {
        for (uint256 i = 0; i < scores.length; i++) {
            if (scores[i] >= minScore) {
                passed++;
            }
        }
    }
}

في هذا المثال، تُظهر الحلقة كيف يمكن الجمع بين for وif لتصفية البيانات أثناء المرور عليها. هذا النمط شائع جداً في بناء منطق الإحصاءات، التقييمات، والتحقق من حالات متعددة داخل العقود.

مخاطر استخدام for على بيانات كبيرة

رغم سهولة الفكرة، إلا أن الحلقة قد تصبح خطرة إذا كانت المصفوفة تنمو بلا حدود. كل عنصر إضافي يعني تكراراً جديداً، وبالتالي استهلاكاً إضافياً للغاز. وعندما يتجاوز التنفيذ الحد المقبول، قد تفشل المعاملة بالكامل.

أفضل ممارسة في Gas Optimization هي تجنب الحلقات غير المحدودة داخل الدوال التي تغيّر الحالة state-changing functions. إذا كانت البيانات كبيرة، ففكّر في تقسيم التنفيذ إلى دفعات batch processing أو نقل العمليات الحسابية الثقيلة إلى الواجهة الأمامية أو الخادم المساعد.

حلقة while: متى تُستخدم ولماذا يجب الحذر؟

حلقة while تنفذ الكود طالما ظل الشرط صحيحاً. نظرياً هي مرنة، لكن عملياً تُستخدم أقل بكثير في العقود الذكية من for، لأن المطور يحتاج إلى ضمان توقف الحلقة بشكل مؤكد وقابل للتوقع.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract EvenCounter {
    function countEven(uint256[] memory numbers) external pure returns (uint256 count) {
        uint256 i = 0;

        while (i < numbers.length) {
            if (numbers[i] % 2 == 0) {
                count++;
            }
            i++;
        }
    }
}

هذا المثال آمن نسبياً لأن شرط التوقف واضح، والمؤشر i يزداد في كل دورة. لكن أي خطأ في تحديث هذا المتغير قد يؤدي إلى حلقة لا نهائية، وهو سيناريو كارثي في العقود الذكية لأن المعاملة ستستهلك الغاز ثم تفشل.

متى تكون الحلقات مقبولة داخل view وpure؟

إذا كانت الحلقة موجودة داخل دالة من نوع view أو pure وتُستدعى محلياً من الواجهة دون إرسال معاملة، فإنك لا تدفع رسوماً مباشرة كمستخدم. لكن هذا لا يعني أن التكرار غير المحدود فكرة جيدة، لأن العقد قد يصبح بطيئاً أو غير عملي عند الاستدعاء من تطبيقات DApps.

ولفهم الفروقات العملية بين هذه الأنواع من الدوال، راجع أنواع الدوال : فهم view و pure لتوفير رسوم الـ Gas، لأن قرار وضع الحلقة داخل دالة قراءة أو كتابة يؤثر مباشرة على تجربة المستخدم والتكلفة.

أفضل الممارسات عند استخدام if/else وfor/while

خلاصة عملية للمطورين

الجمل الشرطية وحلقات التكرار هي أدوات أساسية لبناء منطق العقود الذكية، لكنها في بيئة Web3 تأتي مع مسؤولية أعلى من البرمجة التقليدية. استخدام if/else يجب أن يكون واضحاً ومنظماً، بينما يجب التعامل مع for وwhile بحذر شديد بسبب كلفة التنفيذ وحدود الغاز.

كل سطر داخل العقد الذكي يجب أن يخدم هدفاً واضحاً: أمان أعلى، تكلفة أقل، وسلوك يمكن التنبؤ به. وعندما تتقن هذه البنى، تصبح قادراً على بناء عقود أكثر احترافية للتعامل مع القوائم، التحقق من الشروط، وإدارة منطق التطبيقات اللامركزية بكفاءة وموثوقية.

2 comments

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *