كتابة البيانات وإرسال المعاملات (Transactions) من واجهة الويب إلى العقد الذكي
كتابة البيانات وإرسال المعاملات (Transactions) من واجهة الويب إلى العقد الذكي
بعد أن يتعلم المطور كيفية قراءة البيانات من البلوكتشين وعرضها في واجهة الموقع مجاناً، تأتي الخطوة الأهم في بناء تطبيقات DApps: تنفيذ عمليات كتابة فعلية على السلسلة. هنا لا نتحدث عن استدعاء مجاني لدوال view فقط، بل عن إرسال معاملة موقعة من محفظة المستخدم لتغيير حالة العقد الذكي.
هذا النوع من التفاعل يعتمد على فهم عميق للعلاقة بين الواجهة الأمامية، المحفظة، مزود الشبكة، والعقد المنشور. وإذا كنت قد قرأت سابقاً هندسة الويب اللامركزي (Web3.js & Ethers.js): كيف نربط الواجهات بالعقود الذكية؟ فستدرك أن إرسال المعاملة ليس مجرد استدعاء دالة، بل سلسلة من الخطوات الأمنية والتنفيذية الدقيقة.
ما معنى كتابة البيانات على البلوكتشين؟
عندما تستدعي دالة تعدّل متغيراً داخل العقد، مثل تحديث عداد أو إضافة سجل جديد، فإنك تنفذ عملية state-changing transaction. هذه العملية تحتاج توقيعاً من المستخدم، وتستهلك Gas لأنها تغيّر حالة شبكة EVM.
بعكس عمليات القراءة، لا تنفذ هذه الدوال محلياً داخل المتصفح فقط، بل يتم بث المعاملة إلى الشبكة، انتظار تأكيدها من المدققين أو المعدنين، ثم تحديث حالة العقد بعد إدراجها في كتلة. لذلك فإن تجربة المستخدم هنا يجب أن تعكس بوضوح حالة العملية: انتظار توقيع، إرسال، انتظار تأكيد، ثم نجاح أو فشل.
مكوّنات مسار المعاملة من الواجهة إلى العقد
1. الواجهة الأمامية
الواجهة تجمع مدخلات المستخدم وتحوّلها إلى معاملات قابلة للإرسال. غالباً تُبنى باستخدام React أو أي إطار حديث، ثم تتصل بالمحفظة عبر Ethers.js.
2. محفظة المستخدم
محفظة مثل MetaMask تمثل طبقة التوقيع. وإذا كنت قد طبقت مسبقاً الاتصال بمحفظة المستخدم: كتابة كود يطلب من الزائر ربط محفظة MetaMask بموقعك فستعرف أن الواجهة لا تملك المفتاح الخاص، بل تطلب من المحفظة التوقيع نيابة عن المستخدم.
3. العقد الذكي
العقد يحتوي الدالة التي ستعدّل البيانات. ويجب أن تكون الدالة غير موسومة بـ view أو pure إذا كانت ستغير الحالة، وهي نقطة مرتبطة مباشرة بمقال أنواع الدوال : فهم view و pure لتوفير رسوم الـ Gas.
مثال عملي: عقد لتخزين رسالة نصية
لنبدأ بعقد بسيط يستقبل نصاً جديداً من المستخدم ويخزنه داخل متغير حالة، ثم يطلق حدثاً ليسهل على الواجهة تتبع التحديثات. استخدام event هنا مهم جداً، كما شرحنا في الأحداث (Events): كيف يخبر العقد الذكي واجهة الموقع (React) بأن شيئاً ما قد حدث؟.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MessageBoard {
string public message;
address public owner;
event MessageUpdated(address indexed user, string newMessage);
constructor(string memory initialMessage) {
owner = msg.sender;
message = initialMessage;
}
function updateMessage(string calldata newMessage) external {
require(bytes(newMessage).length > 0, "Message cannot be empty");
message = newMessage;
emit MessageUpdated(msg.sender, newMessage);
}
}
هذا المثال يجمع عدة مفاهيم أساسية: متغير حالة، مُنشئ constructor، ودالة كتابة تستخدم calldata لتقليل نسخ البيانات داخل الذاكرة، وهي ممارسة مرتبطة بمقال إدارة الذاكرة بذكاء: الفرق الحاسم بين Storage, Memory, و Calldata.
كود الواجهة لإرسال المعاملة باستخدام Ethers.js
بعد نشر العقد عبر أتمتة نشر العقود (Deployment): كتابة سكربت لرفع العقد إلى شبكة Ethereum و Polygon أو عبر محرر Remix IDE: كتابة ونشر أول عقد ذكي (Smart Contract) على المتصفح مباشرة، ننتقل إلى الواجهة. نحتاج إلى عنوان العقد وواجهة ABI الصحيحة.
// مثال JavaScript داخل الواجهة - يوضع هنا كنص تقني فقط ضمن هيكل المقال
const contractAddress = "0xYourContractAddress";
const abi = [
"function updateMessage(string newMessage) external",
"function message() view returns (string)"
];
async function updateMessageOnChain(newMessage) {
if (!window.ethereum) {
throw new Error("MetaMask is not installed");
}
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contract = new ethers.Contract(contractAddress, abi, signer);
const tx = await contract.updateMessage(newMessage);
console.log("Transaction hash:", tx.hash);
const receipt = await tx.wait();
console.log("Confirmed in block:", receipt.blockNumber);
return receipt;
}
الخطوات التي ينفذها هذا الكود واضحة ومهمة:
- إنشاء مزود
providerمن الكائنwindow.ethereum. - الحصول على
signerالمرتبط بالمحفظة الحالية. - بناء كائن العقد باستخدام العنوان و
ABI. - استدعاء الدالة التي تغيّر الحالة، فينتج كائن معاملة
TransactionResponse. - انتظار التأكيد النهائي عبر
tx.wait()للحصول علىreceipt.
دورة حياة المعاملة داخل تطبيق Web3
طلب التوقيع
بمجرد استدعاء الدالة من الواجهة، تظهر نافذة المحفظة للمستخدم كي يوافق على المعاملة. في هذه اللحظة لم تُرسل المعاملة بعد؛ بل ما زالت بانتظار التوقيع.
البث إلى الشبكة
بعد الموافقة، تُرسل المعاملة إلى الشبكة مع عناصر مثل nonce وgasLimit وmaxFeePerGas. فهم هذه العناصر يصبح أسهل إذا راجعت التكاليف (Gas Fees): كيف يحسب البلوكتشين تكلفة تنفيذ الأكواد؟.
الانتظار للتأكيد
وجود hash لا يعني أن العملية نجحت نهائياً. قد تبقى المعاملة معلقة أو تفشل أثناء التنفيذ. لذلك يجب على الواجهة ألا تعرض النجاح مباشرة قبل وصول receipt.status المناسب.
أفضل ممارسات تجربة المستخدم عند إرسال المعاملات
- عطّل زر الإرسال أثناء التنفيذ لمنع النقر المتكرر.
- اعرض حالة مرئية مثل: “جاري طلب التوقيع” ثم “جاري الإرسال” ثم “تم التأكيد”.
- تحقق من الشبكة الحالية قبل تنفيذ العملية، خاصة إذا كان العقد على
SepoliaأوPolygon. - تحقق من صحة المدخلات في الواجهة قبل دفع المستخدم إلى دفع رسوم غير ضرورية.
- بعد التأكيد، اقرأ الحالة الجديدة من العقد أو استمع إلى
eventلتحديث الواجهة بشكل موثوق.
لا تعتمد على التحقق من الواجهة فقط. أي شرط مهم مثل صلاحية المستخدم أو صحة البيانات يجب فرضه داخل العقد عبر
requireوmodifiers. لمراجعة ذلك بعمق، ارجع إلى التعامل مع الأخطاء وإرجاع الأموال: استخدام require, assert, revert والمعدلات (Modifiers): حماية الدوال برمجياً.
الأمان وتقليل استهلاك الغاز
كتابة البيانات إلى السلسلة عملية حساسة لأنها تجمع بين أموال المستخدم، أذونات التنفيذ، وتكلفة الشبكة. كل دالة قابلة للاستدعاء من الواجهة يجب أن تُراجع من منظور أمني ومنظور اقتصادي في الوقت نفسه.
لتقليل
Gas Fees، اجعل دوال الكتابة مختصرة، استخدم أنواع بيانات مناسبة، وفضّلcalldataللوسائط الخارجية النصية أو المصفوفات عندما لا تحتاج تعديلها. كما أن تقليل عدد عمليات الكتابة إلىstorageيخفض التكلفة بشكل ملحوظ.
ومن الجانب الأمني، إذا كانت الدالة تتعامل مع تحويلات أموال أو استدعاءات خارجية، فيجب الحذر من ثغرات مثل Reentrancy. لهذا يُستحسن مراجعة أمن العقود الذكية (1): ثغرة إعادة الدخول (Reentrancy Attack) ثم الحماية من ثغرة Reentrancy باستخدام ReentrancyGuard.
الخاتمة
إرسال المعاملات من واجهة الويب إلى العقد الذكي هو اللحظة التي يتحول فيها تطبيق Web3 من واجهة قراءة إلى نظام تفاعلي حقيقي. نجاح هذه الخطوة يتطلب فهم التوقيع، بنية المعاملة، التأكيد على الشبكة، وتصميم واجهة تحترم الوقت والرسوم والأمان.
كلما كان العقد منظماً، والواجهة واضحة، والتعامل مع الأخطاء مدروساً، أصبحت تجربة المستخدم أكثر احترافية وثقة. وهذه هي القاعدة التي تُبنى عليها لاحقاً تطبيقات أكثر تعقيداً مثل التصويت اللامركزي، سك الرموز، أو إدارة الأصول الرقمية عبر عقود ذكية متقدمة.
11 comments