أساسيات لغة Solidity: أنواع البيانات والمتغيرات (State Variables)
أساسيات لغة Solidity: أنواع البيانات والمتغيرات (State Variables)
عند الانتقال من فهم مدخل إلى Web3: ما هو البلوكتشين ولماذا يغير شكل الإنترنت والأنظمة المالية؟ إلى بناء عقود ذكية حقيقية، تصبح لغة Solidity هي نقطة الانطلاق الأساسية على شبكات Ethereum والشبكات المتوافقة مع EVM. ومن أهم المفاهيم التي يجب إتقانها مبكراً: أنواع البيانات وكيفية تعريف المتغيرات، خصوصاً State Variables التي تحتفظ بحالة العقد الذكي بشكل دائم على السلسلة.
فهم هذه الأساسيات لا يفيد فقط في كتابة كود صحيح، بل يؤثر مباشرة على الأمان، وقابلية الصيانة، وتكلفة التنفيذ المعروفة باسم Gas Fees. كما أن اختيار نوع بيانات غير مناسب قد يؤدي إلى منطق تجاري معيب أو استهلاك أعلى للتخزين، وهو أغلى جزء تقريباً في دورة حياة كثير من العقود الذكية.
ما المقصود بـ State Variables داخل العقد الذكي؟
المتغيرات من نوع State Variables هي متغيرات تُعرّف داخل جسم العقد الذكي وخارج الدوال، وتُخزَّن في مساحة التخزين الدائم storage. هذا يعني أن قيمتها تبقى محفوظة بين المعاملات المختلفة، ويمكن قراءتها أو تعديلها بحسب صلاحيات العقد ومنطقه البرمجي.
على عكس المتغيرات المحلية داخل الدوال، فإن State Variables تشكّل الحالة العامة للعقد. أمثلة ذلك: مالك العقد، الرصيد المسجل، عدد المستخدمين، أو حالة تفعيل ميزة معيّنة. ولهذا السبب تعد جزءاً محورياً في تصميم أي DApp.
أنواع البيانات الأساسية في Solidity
1) الأعداد الصحيحة: uint و int
النوع uint يُستخدم للأعداد غير السالبة، بينما int يدعم القيم الموجبة والسالبة. افتراضياً، uint تعني uint256، وهو الحجم الأكثر شيوعاً على مستوى EVM.
يمكن أيضاً استخدام أحجام أصغر مثل uint8 أو uint64 عند الحاجة إلى ضغط التخزين، لكن فائدتها الحقيقية تظهر فقط عند ترتيبها بعناية داخل نفس الحزمة التخزينية storage slot.
2) القيم المنطقية: bool
يُستخدم النوع bool لتخزين حالتين فقط: true أو false. وهو مثالي لتتبع حالات بسيطة مثل إيقاف العقد مؤقتاً، أو التأكد من تنفيذ خطوة معيّنة، أو توثيق ما إذا كان مستخدم ما مسجلاً بالفعل.
3) العناوين: address
النوع address يمثل عنوان حساب على الشبكة، سواء كان حساب مستخدم أو عقداً ذكياً. غالباً ما يُستخدم لتخزين عنوان المالك owner أو المستفيد أو المتعامل مع العقد.
إذا كنت قد أنهيت مقال التشفير والمفاتيح: كيف تعمل المحافظ الرقمية (Public & Private Keys) برمجياً؟ فستفهم جيداً كيف يرتبط كل عنوان بمفتاح عام وآلية توقيع المعاملات، وهو ما يفسر اعتماد العقود الذكية على msg.sender للتحقق من هوية الجهة المستدعية.
4) النصوص والبيانات الثنائية: string و bytes
النوع string مخصص للنصوص، بينما bytes يستخدم لتمثيل بيانات ثنائية أو سلاسل بايتات قد تكون أكثر كفاءة في بعض السيناريوهات. تخزين النصوص الطويلة داخل السلسلة مكلف، لذلك يجب التفكير جيداً قبل استخدام string كمتغير حالة دائم.
مثال عملي على تعريف متغيرات الحالة
الآن ننتقل إلى نموذج عملي يوضح كيفية استخدام أشهر الأنواع داخل عقد ذكي بسيط. يمكنك تجربة المثال مباشرة عبر محرر Remix IDE: كتابة ونشر أول عقد ذكي (Smart Contract) على المتصفح مباشرة، أو ربطه لاحقاً بأدوات مثل Hardhat و Ethers.js.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract StateVariablesExample {
address public owner;
string public contractName;
uint256 public counter;
bool public isActive;
constructor(string memory _name) {
owner = msg.sender;
contractName = _name;
counter = 0;
isActive = true;
}
function increment() public {
require(isActive, "Contract is inactive");
counter += 1;
}
function deactivate() public {
require(msg.sender == owner, "Only owner can deactivate");
isActive = false;
}
}
هذا العقد يوضح عدة نقاط مهمة:
- المتغير
ownerيخزن عنوان ناشر العقد. - المتغير
contractNameيحتفظ باسم وصفي للعقد. - المتغير
counterيمثل حالة رقمية قابلة للتحديث. - المتغير
isActiveيتحكم في منطق التنفيذ داخل الدوال. - الكلمة
publicتنشئ تلقائياً دالة قراءةgetterلكل متغير.
الفرق بين State Variables والمتغيرات المحلية
المتغير المحلي يُعرّف داخل الدالة، ويعيش فقط أثناء تنفيذها، ثم يختفي بعد انتهاء المعاملة أو الاستدعاء. أما State Variables فتبقى مخزنة على السلسلة وتؤثر على حالة العقد عبر الزمن.
هذا الفرق مهم جداً لأن أي تعديل على حالة العقد يستهلك غازاً أعلى من تنفيذ عمليات مؤقتة في الذاكرة memory. لذلك يجب عدم تحويل كل قيمة إلى متغير حالة إلا إذا كانت هناك حاجة حقيقية لاستمراريتها بين المعاملات.
استخدم
State Variablesفقط للقيم التي يحتاج العقد للاحتفاظ بها على المدى الطويل. تخزين بيانات غير ضرورية داخلstorageيرفع تكلفةGasويصعّب صيانة العقد مستقبلاً.
القيم الافتراضية للمتغيرات
في Solidity، كل متغير حالة يملك قيمة افتراضية حتى قبل التهيئة. فمثلاً:
uintيبدأ من0.boolيبدأ منfalse.addressيبدأ بعنوان صفري0x0000000000000000000000000000000000000000.stringتبدأ كسلسلة فارغة.
هذه القيم الافتراضية مفيدة، لكنها قد تتحول إلى مصدر أخطاء منطقية إذا افترض المطور أن التهيئة حدثت بالفعل. لذلك من الأفضل في العقود الحساسة ضبط القيم داخل constructor بوضوح.
نصائح أمنية وعملية عند تصميم المتغيرات
بمجرد البدء في النشر على شبكات الاختبار بعد إعداد بيئة التطوير: تثبيت محفظة MetaMask والاتصال بشبكات الاختبار (Testnets)، ثم تمويل المحفظة عبر الحصول على عملات تجريبية مجانية (Faucet) للبدء في نشر واختبار العقود الذكية، ستلاحظ أن أي قرار في تعريف المتغيرات ينعكس على التجربة الفعلية للعقد أثناء الاختبار.
لا تجعل متغيرات حساسة مثل
ownerقابلة للتغيير بدون قيود تحقق صارمة. أي خطأ في منطق الصلاحياتAccess Controlقد يؤدي إلى فقدان السيطرة على العقد أو استغلاله من مهاجمين.
- اختر نوع البيانات الأصغر فقط عندما تكون مستفيداً فعلياً من تجميع التخزين.
- استخدم أسماء واضحة مثل
totalSupplyأوisPausedبدلاً من أسماء مبهمة. - لا تخزن بيانات كبيرة على السلسلة إذا كان يمكن الإشارة إليها خارجياً عبر بنية أكثر كفاءة.
- استخدم
requireلحماية التعديلات على الحالة قبل تنفيذ أي تحديث فعلي.
خاتمة
إتقان أنواع البيانات في Solidity وفهم طبيعة State Variables هو الخطوة التي تميّز بين كتابة عقد تجريبي بسيط وتصميم عقد ذكي قابل للاعتماد في بيئة حقيقية. فكل متغير داخل العقد ليس مجرد مكان لتخزين قيمة، بل قرار هندسي يؤثر على الأمان، المنطق، واستهلاك الغاز.
كلما أحسنت اختيار النوع المناسب، وحددت ما يجب أن يبقى في storage وما يجب أن يبقى مؤقتاً داخل الدوال، أصبحت عقودك أكثر احترافية وكفاءة. وهذه القاعدة ستظل أساسية في كل ما ستبنيه لاحقاً من رموز Tokens، أنظمة تصويت، أو بروتوكولات مالية لامركزية.
33 comments