تطوير واجهة (DApp) تتصل بشبكة Solana باستخدام محفظة Phantom
تطوير واجهة DApp تتصل بشبكة Solana باستخدام محفظة Phantom
بناء واجهة لامركزية على شبكة Solana يختلف تقنياً عن نمط التطوير التقليدي في شبكات EVM. هنا لا نتعامل مع Smart Contracts بالشكل نفسه، بل مع Programs وحسابات Accounts مصممة لأداء مرتفع وزمن تأكيد منخفض.
في هذا الدليل سنبني تصوراً عملياً لواجهة Web3 تتصل بمحفظة Phantom، تقرأ عنوان المستخدم، تتحقق من الشبكة، ثم ترسل معاملة بسيطة عبر مكتبة @solana/web3.js. وإذا كنت تريد خلفية أوسع عن سبب شعبية هذه الشبكة، فراجع مقال استكشاف البلوكتشين البديلة: لماذا يفضل المطورون Solana للسرعة العالية؟.
لماذا تعتبر Phantom خياراً مركزياً في واجهات Solana DApps؟
محفظة Phantom تؤدي دوراً مشابهاً لما تؤديه MetaMask في عالم Ethereum، لكنها مصممة حول بنية Solana من الأساس. هي توفر حقن كائن داخل المتصفح يسمح للواجهة بطلب الاتصال، توقيع الرسائل، وتوقيع المعاملات بدون كشف المفتاح الخاص.
هذه الآلية ترتبط مباشرة بمفهوم المحافظ والمفاتيح العامة والخاصة المشروح في مقال التشفير والمفاتيح: كيف تعمل المحافظ الرقمية (Public & Private Keys) برمجياً؟. الفكرة الأساسية أن الواجهة لا تملك سلطة تنفيذ المعاملة وحدها؛ بل تبني بياناتها، ثم تطلب من المستخدم توقيعها داخل المحفظة.
المتطلبات الأساسية قبل كتابة الكود
قبل بناء الواجهة، ستحتاج إلى تجهيز بيئة تطوير حديثة تحتوي على:
- مشروع
ReactأوNext.js. - تثبيت مكتبة
@solana/web3.js. - إضافة محفظة
Phantomإلى المتصفح. - الحصول على رصيد تجريبي على شبكة
DevnetأوTestnet.
إذا كنت لا تزال في مرحلة تجهيز بيئة التطوير اللامركزية عموماً، فسيكون مفيداً الرجوع إلى إعداد واجهة React.js وتثبيت مكتبة Ethers.js للاتصال بالبلوكتشين لفهم البنية العامة للواجهة، ثم مقارنة ذلك بما سنفعله هنا على Solana.
تثبيت الحزمة الأساسية
الخطوة الأولى هي تثبيت مكتبة الاتصال الرسمية:
npm install @solana/web3.js
ورغم أن الأمر السابق ليس كود Solidity فعلياً، إلا أن وضعه داخل كتلة برمجية ثابتة يسهل نقله إلى محررات ووردبريس بدون كسر التنسيق.
كيف تكتشف الواجهة وجود محفظة Phantom؟
عند تحميل الصفحة، يجب التأكد من أن الامتداد موجود داخل المتصفح. تضيف Phantom كائناً يمكن قراءته عبر window.phantom.solana. ومن الأفضل التحقق أيضاً من الخاصية isPhantom حتى لا تتعامل الواجهة مع مزود غير متوقع.
const getProvider = () => {
if ("phantom" in window) {
const provider = window.phantom?.solana;
if (provider?.isPhantom) {
return provider;
}
}
return null;
};
const connectWallet = async () => {
const provider = getProvider();
if (!provider) {
window.open("https://phantom.app/", "_blank");
return;
}
const response = await provider.connect();
console.log("Connected wallet:", response.publicKey.toString());
};
هذا النمط قريب في فكرته من مقال الاتصال بمحفظة المستخدم: كتابة كود يطلب من الزائر ربط محفظة MetaMask بموقعك، لكن التنفيذ هنا يعتمد على واجهات Solana Provider APIs بدلاً من window.ethereum.
إنشاء اتصال بالشبكة وقراءة الرصيد
بعد ربط المحفظة، نحتاج إلى كائن اتصال من نوع Connection. هذا الكائن يسمح للواجهة بالقراءة من الشبكة دون الحاجة لتوقيع المستخدم، مثل قراءة الرصيد أو جلب بيانات الحسابات.
import {
Connection,
PublicKey,
clusterApiUrl,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const getWalletBalance = async (address) => {
const publicKey = new PublicKey(address);
const balance = await connection.getBalance(publicKey);
return balance / LAMPORTS_PER_SOL;
};
في شبكة Solana تقاس الوحدة الأصغر باسم Lamports، لذلك نقسم القيمة على LAMPORTS_PER_SOL لإظهار الرصيد بصيغة مفهومة للمستخدم. هذه المرحلة تمثل جانب القراءة المجانية الذي يشبه ما شرحناه في قراءة البيانات من البلوكتشين وعرضها في واجهة الموقع مجاناً.
إرسال معاملة بسيطة من الواجهة
جوهر أي DApp حقيقي هو القدرة على بناء معاملة ثم إرسالها إلى المحفظة للتوقيع. في Solana يتضمن ذلك إنشاء Transaction ثم إضافة تعليمات Instructions إليها.
import {
Connection,
PublicKey,
Transaction,
SystemProgram,
clusterApiUrl,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
const sendSol = async (provider, toAddress, amount) => {
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const fromPubkey = provider.publicKey;
const toPubkey = new PublicKey(toAddress);
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey,
toPubkey,
lamports: amount * LAMPORTS_PER_SOL,
})
);
transaction.feePayer = fromPubkey;
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
const signed = await provider.signTransaction(transaction);
const signature = await connection.sendRawTransaction(signed.serialize());
await connection.confirmTransaction(signature, "confirmed");
return signature;
};
هذا المثال يوضح الفرق العملي بين القراءة والكتابة: القراءة تعتمد على RPC فقط، بينما الكتابة تحتاج إلى توقيع فعلي من المحفظة. وهو قريب من المنطق العام المشروح في كتابة البيانات وإرسال المعاملات (Transactions) من واجهة الويب إلى العقد الذكي، مع اختلاف بنية المعاملة بين الشبكتين.
لا تعتمد على القيم التي يدخلها المستخدم مباشرة قبل بناء المعاملة. تحقق دائماً من صحة العنوان، ومن أن قيمة التحويل موجبة، ومن أن الواجهة تعمل على
Devnetأو الشبكة المتوقعة. أخطاء التحقق في الواجهة لا تعوّض غياب الأمان في البرنامج على السلسلة، لكنها تقلل المعاملات الفاشلة وتحسن تجربة المستخدم.
إدارة حالة الواجهة بشكل احترافي
في التطبيقات المتقدمة، لا يكفي مجرد زر اتصال. يجب أن تدير الواجهة حالات متعددة مثل:
- هل المحفظة مثبتة أم لا.
- هل المستخدم متصل حالياً.
- ما هو العنوان العام الحالي.
- ما الرصيد الحالي.
- هل توجد معاملة قيد الإرسال.
- هل فشلت العملية وما سبب الفشل.
أفضل ممارسة هي استخدام React hooks مثل useEffect وuseState، مع فصل منطق المحفظة في ملف مثل walletService.js أو useSolanaWallet.js.
أهمية الاستماع لأحداث الاتصال والانفصال
توفر المحفظة عادة أحداثاً مثل connect وdisconnect. ربط هذه الأحداث بالواجهة يضمن تحديث الرصيد والعنوان فورياً دون إعادة تحميل الصفحة، وهي فلسفة مشابهة لفكرة مزامنة الواجهة مع الأحداث في الاستماع إلى الأحداث (Events) وتحديث واجهة React لحظياً عند تغير البيانات.
كيف يتكامل هذا مع برامج Solana المكتوبة بـ Rust وAnchor؟
عندما تنتقل من تحويل بسيط إلى تطبيق حقيقي، ستبدأ في استدعاء تعليمات برنامج منشور على السلسلة. هنا تظهر أهمية فهم بنية IDL وطرق Anchor في توليد استدعاءات جاهزة للواجهة. لذلك من المفيد دراسة نظرة سريعة على لغة Rust: أساسيات لبرمجة العقود الذكية على شبكة Solana ثم تثبيت بيئة عمل Solana وإطار عمل Anchor لتسهيل كتابة العقود.
بعدها ستصبح الواجهة قادرة على استدعاء تعليمات مخصصة مثل إنشاء حساب، تحديث بيانات، أو تنفيذ منطق DeFi وNFT على الشبكة.
في
Solanaلا نتحدث غالباً عنGas Feesبنفس نموذجEthereum، لكن تحسين عدد التعليمات وتقليل العمليات غير الضرورية داخل المعاملة يظل مهماً للأداء وتقليل الفشل ورفع قابلية التوسع. ويمكن مقارنة ذلك مفاهيمياً مع مقال التكاليف (Gas Fees): كيف يحسب البلوكتشين تكلفة تنفيذ الأكواد؟.
أخطاء شائعة يجب تجنبها
- الاعتماد على شبكة خاطئة بين الواجهة والمحفظة.
- نسيان تعيين
recentBlockhashقبل التوقيع. - إرسال عنوان نصي غير صالح دون تحويله إلى
PublicKey. - إهمال معالجة الاستثناءات عند رفض المستخدم التوقيع.
- خلط منطق الواجهة مع منطق الشبكة في مكون واحد كبير يصعب اختباره وصيانته.
خاتمة عملية
تطوير واجهة DApp متصلة بشبكة Solana عبر محفظة Phantom يبدأ من ثلاث طبقات واضحة: اكتشاف المحفظة، إنشاء اتصال شبكي، ثم بناء المعاملات وتوقيعها. وعندما تفهم هذه الطبقات جيداً، يصبح الانتقال إلى برامج أكثر تعقيداً أمراً طبيعياً.
الخطوة التالية المنطقية بعد هذا الدليل هي بناء واجهة تتصل ببرنامج منشور فعلياً على Localnet أو Devnet بعد مراجعة كتابة ونشر أول برنامج (Program) على شبكة Solana المحلية. بهذا الأسلوب تنتقل من مجرد ربط محفظة إلى بناء تطبيق لامركزي كامل قابل للتوسع والاختبار والنشر الاحترافي.