أنواع الدوال : فهم view و pure لتوفير رسوم الـ Gas
أنواع الدوال: فهم view و pure لتوفير رسوم الـ Gas
عند تطوير العقود الذكية على شبكة Ethereum أو أي شبكة متوافقة مع EVM، فإن فهم نوع الدالة ليس مجرد مسألة تنظيمية في الشيفرة، بل قرار مباشر يؤثر في الأمان، قابلية القراءة، وتكلفة التنفيذ. كثير من المطورين الجدد يكتبون الدوال بشكل يعمل وظيفياً، لكنهم لا يميزون جيداً بين الدوال التي تقرأ الحالة فقط، والدوال التي لا تحتاج حتى إلى قراءة حالة العقد.
هنا تظهر أهمية الكلمتين view و pure في Solidity. هذان التوصيفان يساعدان المترجم compiler، ويوضحان للمطورين سلوك الدالة، كما يفتحان الباب لتقليل الهدر المرتبط برسوم التنفيذ. وإذا كنت قد قرأت سابقاً مقال التكاليف (Gas Fees): كيف يحسب البلوكتشين تكلفة تنفيذ الأكواد؟ فستفهم لماذا يعد تصنيف الدوال بدقة خطوة أساسية في هندسة العقود الذكية.
ما المقصود بالدوال view و pure؟
في Solidity يمكن توصيف الدالة بحسب علاقتها ببيانات العقد المخزنة في storage. فإذا كانت الدالة تقرأ من حالة العقد دون تعديلها، فهي مرشحة لأن تكون view. أما إذا كانت لا تقرأ من حالة العقد أصلاً ولا تكتب فيها، بل تعتمد فقط على المدخلات أو الحسابات الداخلية، فهي pure.
هذا التصنيف امتداد طبيعي لفهم الدوال (Functions) في Solidity: من يمكنه قراءة وتعديل بيانات العقد؟، كما يرتبط مباشرة بمقال أساسيات لغة Solidity: أنواع البيانات والمتغيرات (State Variables)، لأن الفرق الجوهري هنا هو: هل الدالة تتعامل مع state variables أم لا؟
الدالة view: قراءة فقط دون تعديل الحالة
الدالة من نوع view تسمح بقراءة بيانات العقد، لكنها تمنع أي تعديل على الحالة الداخلية. معنى ذلك أنها تستطيع الوصول إلى متغيرات مثل الرصيد، المالك، عدد المستخدمين، أو أي قيمة محفوظة في storage، لكنها لا تستطيع تحديثها.
pragma solidity ^0.8.20;
contract SavingsVault {
address public owner;
uint256 public totalDeposits;
constructor() {
owner = msg.sender;
}
function getVaultInfo() public view returns (address, uint256) {
return (owner, totalDeposits);
}
}
في المثال السابق، الدالة getVaultInfo تقرأ فقط من المتغيرين owner و totalDeposits. لذلك تم تعريفها كدالة view. لو حاولت هذه الدالة تغيير قيمة أي متغير حالة، فإن المترجم سيرفض الشيفرة.
متى نستخدم view؟
- عند بناء دوال استعلام لواجهات
DApps. - عند عرض معلومات الحساب أو حالة العقد للمستخدم.
- عند قراءة القيم لأغراض التحقق دون الحاجة إلى تعديلها.
- عند تجهيز بيانات للاستهلاك عبر
Ethers.jsأوfrontend.
الدالة pure: منطق حسابي مستقل عن حالة العقد
الدالة pure أكثر تقييداً من view. فهي لا تعدل الحالة، ولا تقرأها أيضاً. بمعنى آخر، هي دالة تعتمد فقط على المدخلات التي تصل إليها أو على قيم ثابتة معرفة محلياً. هذا يجعلها مثالية للحسابات الرياضية، المقارنات، والتحويلات البرمجية التي لا تحتاج الوصول إلى بيانات العقد.
pragma solidity ^0.8.20;
contract MathHelper {
function calculateFee(uint256 amount, uint256 percent) public pure returns (uint256) {
return (amount * percent) / 100;
}
function isEven(uint256 number) public pure returns (bool) {
return number % 2 == 0;
}
}
في هذا المثال، كل من calculateFee و isEven لا يحتاجان للوصول إلى أي متغير معرف على مستوى العقد. لذلك فإن توصيف pure هنا هو الوصف الأدق.
متى نستخدم pure؟
- لحساب الرسوم أو النسب أو الخصومات.
- للكشف عن شروط منطقية مثل التحقق من الزوجية أو نطاق القيم.
- لبناء دوال مساعدة داخلية
helper functions. - لتقليل الاعتماد غير الضروري على حالة العقد وتحسين وضوح التصميم.
الفرق العملي بين view و pure
التمييز بين النوعين يبدو بسيطاً نظرياً، لكنه مهم جداً عند التصميم. الدالة view يمكنها قراءة state دون تعديل. أما الدالة pure فلا تقرأ ولا تكتب. لذلك كل دالة pure لا بد أن تكون منفصلة منطقياً عن بيانات العقد المخزنة.
view: تقرأ من الحالة، ولا تعدلها.pure: لا تقرأ من الحالة، ولا تعدلها.- القاسم المشترك: كلتاهما لا يفترض أن تنتجا تغييراً دائماً في بيانات العقد.
هل دوال view و pure مجانية دائماً؟
هنا يقع سوء فهم شائع. تقنياً، استدعاء دوال view و pure من خارج السلسلة عبر RPC call أو من واجهة المستخدم غالباً لا يستهلك Gas Fees لأن التنفيذ يتم محلياً على العقدة دون إنشاء معاملة على الشبكة.
لكن إذا تم استدعاء دالة view أو pure من داخل دالة أخرى تغيّر الحالة على السلسلة، فإن تعليماتها ستُنفذ ضمن سياق المعاملة، وبالتالي تستهلك من ميزانية الغاز الكلية. إذن التوفير الحقيقي يأتي من تقليل عمليات الكتابة على storage ومنع المنطق غير الضروري داخل المعاملات.
استخدام
viewأوpureلا يعني أن الغاز يختفي تلقائياً في كل السياقات. القاعدة الأهم هي: إذا لم تُرسل معاملة فلن تدفع عادةً رسوماً على القراءة، أما إذا نُفذ الاستدعاء داخل معاملة مغيرة للحالة فسيحتسب الغاز على التعليمات المنفذة.
مثال متقدم: فصل منطق الحساب عن منطق التخزين
من أفضل ممارسات التصميم أن تعزل الحسابات داخل دوال pure، وتترك دوال تحديث الحالة مسؤولة فقط عن القراءة اللازمة والكتابة النهائية. هذا يجعل العقد أوضح وأسهل في الاختبار باستخدام Hardhat أو Remix IDE. وإذا كنت في بداية المسار العملي فراجع محرر Remix IDE: كتابة ونشر أول عقد ذكي (Smart Contract) على المتصفح مباشرة.
pragma solidity ^0.8.20;
contract OrderBook {
uint256 public totalRevenue;
function calculateNetAmount(uint256 price, uint256 feePercent) public pure returns (uint256) {
uint256 fee = (price * feePercent) / 100;
return price - fee;
}
function previewRevenue() public view returns (uint256) {
return totalRevenue;
}
function recordSale(uint256 price, uint256 feePercent) public {
uint256 netAmount = calculateNetAmount(price, feePercent);
totalRevenue += netAmount;
}
}
في هذا التصميم، الدالة calculateNetAmount معزولة كحساب مستقل، بينما previewRevenue مخصصة للقراءة، وrecordSale فقط هي التي تكتب في الحالة. هذا الفصل يحسن الاختبار، ويقلل الالتباس، ويجعل مراجعة الأمان أسهل.
في المراجعات الأمنية
Security Auditing، الفصل بين دوال الحسابpureودوال القراءةviewودوال التعديل الفعلية يقلل احتمالات الأخطاء المنطقية، ويجعل اكتشاف نقاط الخطر المرتبطة بالحالة الداخلية أسرع وأكثر دقة.
أخطاء شائعة يجب تجنبها
- تعريف دالة كـ
viewرغم أنها تحتاج حساباً مستقلاً فقط، بينما الأدق هوpure. - إجراء عمليات قراءة متعددة من
storageداخل معاملة دون داعٍ. - الاعتقاد أن كل دالة لا تعدل الحالة لا تستهلك غازاً مطلقاً في جميع السيناريوهات.
- دمج منطق العرض والحساب والتحديث في دالة واحدة، ما يزيد صعوبة الاختبار والصيانة.
خلاصة عملية للمطورين
إذا كانت الدالة تحتاج الوصول إلى متغيرات العقد فقط من أجل القراءة، فاستخدم view. وإذا كانت لا تحتاج أي اتصال بحالة العقد، وتعتمد فقط على المدخلات والمنطق الداخلي، فاستخدم pure. هذا الاختيار الصغير ظاهرياً يرفع جودة العقد، ويساعد أدوات التطوير، ويجعل الواجهة الأمامية أوضح، ويمنحك أساساً أفضل لتقليل رسوم التنفيذ.
فهم هذه الفروق جزء مهم من بناء تطبيقات Web3 احترافية. وبعد استيعابها جيداً، يصبح من الأسهل عليك كتابة عقود أكثر كفاءة، سواء كنت تختبرها عبر MetaMask وبيئات Testnets كما في إعداد بيئة التطوير: تثبيت محفظة MetaMask والاتصال بشبكات الاختبار (Testnets)، أو تنشرها بعد الحصول على رصيد تجريبي من الحصول على عملات تجريبية مجانية (Faucet) للبدء في نشر واختبار العقود الذكية.
35 comments