مشروع شامل (الجزء 3): نشر العقد وبناء واجهة React للسماح للناس بالتبرع للمشاريع
مشروع شامل (الجزء 3): نشر العقد وبناء واجهة React للسماح للناس بالتبرع للمشاريع
في هذا الجزء ننتقل من مرحلة كتابة المنطق واختباره إلى المرحلة التي تجعل المشروع قابلاً للاستخدام فعلياً من الجمهور: نشر العقد الذكي على شبكة اختبار، ثم بناء واجهة React متصلة بالعقد عبر Ethers.js بحيث يستطيع المستخدم تصفح المشاريع، قراءة بياناتها، ثم إرسال التبرعات مباشرة من محفظته.
إذا كنت تابعت الأجزاء السابقة، فأنت الآن تملك الأساس البرمجي للعقد من مقال مشروع شامل (الجزء 1): بناء منصة تمويل جماعي (Crowdfunding) لامركزية – العقد الذكي، كما تملك تغطية قوية لاختبارات الأمان من مقال مشروع شامل (الجزء 2): كتابة اختبارات الأمان (Tests) لمنصة التمويل الجماعي. في هذا المقال سنربط كل ذلك بواجهة استخدام عملية وقابلة للتوسّع.
تهيئة مرحلة النشر وربط الواجهة
قبل كتابة الواجهة، يجب أولاً نشر العقد عبر Hardhat على شبكة اختبار، والاحتفاظ بعنوان العقد وملف ABI. هذه الخطوة هي الجسر الحقيقي بين طبقة Smart Contracts وواجهة DApp.
إن لم تكن أعددت بيئة النشر بعد، فراجع الانتقال إلى بيئة العمل الاحترافية: تثبيت إطار عمل Hardhat باستخدام Node.js وأتمتة نشر العقود (Deployment): كتابة سكربت لرفع العقد إلى شبكة Ethereum و Polygon، لأنهما يشرحان إدارة الشبكات، المفاتيح البيئية، وملفات النشر بشكل احترافي.
نموذج مختصر للعقد المستخدم
سنفترض أن عقد التمويل الجماعي يوفّر دوالاً لإنشاء مشروع، جلب بيانات مشروع، والتبرع له. كما يعتمد على الأحداث (Events): كيف يخبر العقد الذكي واجهة الموقع (React) بأن شيئاً ما قد حدث؟ حتى يتم تحديث الواجهة لاحقاً دون إعادة تحميل الصفحة.
pragma solidity ^0.8.20;
contract Crowdfunding {
struct Project {
uint256 id;
address payable owner;
string title;
string description;
uint256 goal;
uint256 donated;
bool closed;
}
uint256 public projectCount;
mapping(uint256 => Project) public projects;
event ProjectCreated(uint256 indexed id, address indexed owner, string title, uint256 goal);
event DonationReceived(uint256 indexed id, address indexed donor, uint256 amount);
function createProject(
string calldata _title,
string calldata _description,
uint256 _goal
) external {
require(_goal > 0, "Invalid goal");
projectCount++;
projects[projectCount] = Project({
id: projectCount,
owner: payable(msg.sender),
title: _title,
description: _description,
goal: _goal,
donated: 0,
closed: false
});
emit ProjectCreated(projectCount, msg.sender, _title, _goal);
}
function donateToProject(uint256 _id) external payable {
Project storage project = projects[_id];
require(project.id != 0, "Project not found");
require(!project.closed, "Project closed");
require(msg.value > 0, "Zero donation");
project.donated += msg.value;
emit DonationReceived(_id, msg.sender, msg.value);
}
function getProject(uint256 _id) external view returns (Project memory) {
return projects[_id];
}
}
هذا البناء يعتمد على الهياكل (Structs) والقواميس (Mappings) لتخزين المشاريع بكفاءة. كما أن استخدام view في دالة القراءة يخفف استهلاك الغاز من طرف المستخدم، وهي فكرة مرتبطة بمقال أنواع الدوال : فهم view و pure لتوفير رسوم الـ Gas.
نشر العقد واستخراج بيانات الربط
بعد نجاح الاختبارات، ننفذ سكربت النشر على شبكة اختبار مثل Sepolia. تأكد مسبقاً من ربط المحفظة والحصول على رصيد تجريبي من الحصول على عملات تجريبية مجانية (Faucet) للبدء في نشر واختبار العقود الذكية.
// deployment is usually done with JavaScript in Hardhat,
// but the important output here is:
// 1) deployed contract address
// 2) generated ABI from artifacts
عملياً، تحتاج الواجهة إلى عنصرين فقط:
- عنوان العقد
contractAddress. - واجهة التطبيق الثنائية
ABIلاستخدام الدوال والأحداث.
لا تنسَ أن النشر على شبكة اختبار لا يعني أن العقد آمن تلقائياً. حتى في المشاريع التعليمية، يجب مراجعة سيناريوهات التبرع، الإغلاق، والسحب، خصوصاً إذا كانت هناك تحويلات
Etherلاحقة. راجع أيضاً أمن العقود الذكية (1): ثغرة إعادة الدخول (Reentrancy Attack) والحماية من ثغرة Reentrancy باستخدام ReentrancyGuard.
إعداد واجهة React وربط MetaMask
الطبقة الأمامية ستُبنى باستخدام React، مع الاعتماد على Ethers.js لإنشاء provider وsigner ومن ثم استدعاء العقد. ولتفصيل هذه الأساسيات يمكنك العودة إلى إعداد واجهة React.js وتثبيت مكتبة Ethers.js للاتصال بالبلوكتشين والاتصال بمحفظة المستخدم: كتابة كود يطلب من الزائر ربط محفظة MetaMask بموقعك.
من الناحية المعمارية، من الأفضل تقسيم التطبيق إلى ثلاث طبقات:
- طبقة اتصال بالمحفظة.
- طبقة خدمات خاصة بالعقد
contract service. - طبقة مكونات الواجهة
components.
إنشاء كائن العقد في الواجهة
الفكرة الجوهرية هي استخدام مزود المتصفح window.ethereum ثم تغليفه عبر BrowserProvider وإنشاء نسخة من العقد باستخدام العنوان وABI.
بعدها يمكن قراءة بيانات المشاريع مجاناً عبر مزود القراءة، بينما تحتاج عمليات التبرع إلى signer لأنها ترسل معاملة موقعة. هذا يتوافق مباشرة مع مفاهيم قراءة البيانات من البلوكتشين وعرضها في واجهة الموقع مجاناً وكتابة البيانات وإرسال المعاملات (Transactions) من واجهة الويب إلى العقد الذكي.
منطق جلب المشاريع وعرضها للمستخدم
بدلاً من الاعتماد على صفحة ثابتة، يجب على الواجهة قراءة عدد المشاريع من العقد ثم تنفيذ حلقة لعرض كل مشروع في بطاقة مستقلة. بطاقات المشروع ينبغي أن تعرض العنوان، الوصف، الهدف التمويلي، إجمالي ما جُمع، ونسبة التقدم. هذه العناصر ليست تجميلية فقط، بل جزء من تجربة الثقة والشفافية في أي Crowdfunding DApp.
من الأفضل أيضاً تنسيق القيم القادمة من wei إلى ether داخل الواجهة حتى يرى المستخدم أرقاماً مفهومة. هذا يختصر الأخطاء ويمنع إدخال مبالغ غير منطقية أثناء التبرع.
في جانب تحسين الغاز، لا تجعل العقد يعيد مصفوفات ضخمة من المشاريع إذا كان العدد قابلاً للنمو. القراءة من الواجهة عبر استدعاءات متدرجة أو باستخدام أحداث مفهرسة
indexed eventsأفضل على المدى البعيد من دوال ثقيلة تعيد بيانات كبيرة دفعة واحدة.
تنفيذ التبرع من الواجهة بشكل صحيح
عند ضغط المستخدم على زر التبرع، يجب تحويل القيمة النصية المدخلة إلى ether ثم إرسالها في حقل value ضمن استدعاء الدالة donateToProject. هنا يجب إدارة ثلاث حالات واجهة أساسية:
- حالة انتظار توقيع المستخدم داخل
MetaMask. - حالة انتظار تأكيد المعاملة على الشبكة.
- حالة النجاح أو الفشل مع رسالة واضحة.
هذه التفاصيل ترفع موثوقية المنتج بشكل كبير، لأن المستخدم في تطبيقات Web3 يتعامل مع أموال فعلية أو شبه فعلية، وبالتالي يحتاج إلى ملاحظات حالة دقيقة أكثر من التطبيقات التقليدية.
تحديث الواجهة لحظياً باستخدام الأحداث
أفضل واجهة تمويل لامركزي لا تعتمد فقط على إعادة التحميل اليدوي. عند سماع حدث DonationReceived يمكن للواجهة تحديث بيانات المشروع مباشرة، سواء بإعادة جلب بياناته أو بتعديل الحالة الداخلية state في React.
هذا النمط موضح بعمق في الاستماع إلى الأحداث (Events) وتحديث واجهة React لحظياً عند تغير البيانات. كما أن فهم بنية هندسة الويب اللامركزي (Web3.js & Ethers.js): كيف نربط الواجهات بالعقود الذكية؟ يساعدك على اختيار آلية الاشتراك المناسبة حسب الشبكة ومزوّد الاتصال.
اعتبارات هندسية وأمنية قبل الإطلاق
رغم أن المشروع يبدو بسيطاً، إلا أن الجودة الاحترافية تظهر في التفاصيل: تحقق من الشبكة النشطة، امنع التبرعات للمشاريع المغلقة، أظهر عنوان العقد للمستخدم، ووفّر روابط إلى مستكشف البلوكتشين لمراجعة المعاملات. هذه العناصر ترفع الشفافية وتخدم معايير الثقة التقنية التي أصبحت ضرورية في أي محتوى أو منتج مرتبط بالمال.
إذا كنت تطوّر نسخة إنتاجية، ففكّر لاحقاً في إضافة فهرسة خارجية عبر The Graph أو واجهة إدارة للحسابات الإدارية، مع توسيع الاختبارات لتشمل سيناريوهات واجهة الاستخدام وليس العقد فقط.
خاتمة
بهذه الخطوات تكون قد أنهيت الحلقة الأهم في بناء تطبيق تمويل جماعي لامركزي: نقل العقد من بيئة التطوير إلى شبكة اختبار، ثم إنشاء واجهة React تتيح للناس التبرع للمشاريع بطريقة مباشرة وشفافة. هذا التكامل بين النشر، الاتصال بالمحفظة، قراءة البيانات، إرسال المعاملات، والاستماع إلى الأحداث هو ما يحوّل كود Solidity إلى منتج Web3 قابل للتجربة والنمو.
والأهم من ذلك أن بناء واجهة جيدة لا يعني الشكل فقط، بل يعني احترام منطق الأمان، وضوح حالات المعاملة، وكفاءة تصميم التفاعل مع البلوكتشين. عندما تجمع هذه العناصر مع اختبارات قوية وبنية عقد نظيفة، فأنت تضع أساساً حقيقياً لمشروع لامركزي يمكن تطويره لاحقاً إلى منصة أكثر اكتمالاً واحترافية.
3 comments