تخطي إلى المحتوى الرئيسي

برنامج تعليمي لسك ⁦NFT⁩

Solidity
NFT
Alchemy
عقود ذكية
واجهة أمامية
Pinata
ERC-721
متوسط
smudgil
6 أكتوبر 2021
26 دقيقة للقراءة
تعديل الصفحة (opens in a new tab)

أحد أكبر التحديات التي تواجه المطورين القادمين من خلفية ويب 2 هو معرفة كيفية ربط عقدك الذكي بمشروع واجهة أمامية والتفاعل معه.

من خلال بناء أداة لسك ⁦NFT⁩ — وهي واجهة مستخدم بسيطة حيث يمكنك إدخال رابط إلى أصل رقمي خاص بك، وعنوان، ووصف — ستتعلم كيفية:

  • الاتصال بمحفظة ميتاماسك عبر مشروع الواجهة الأمامية الخاص بك
  • استدعاء وظائف العقد الذكي من واجهتك الأمامية
  • توقيع المعاملات باستخدام ميتاماسك

في هذا البرنامج التعليمي، سنستخدم React (opens in a new tab) كإطار عمل للواجهة الأمامية. نظرًا لأن هذا البرنامج التعليمي يركز بشكل أساسي على تطوير Web3، فلن نقضي الكثير من الوقت في تفصيل أساسيات React. بدلاً من ذلك، سنركز على إضافة الوظائف إلى مشروعنا.

كمتطلب أساسي، يجب أن يكون لديك فهم بمستوى المبتدئين لـ React—معرفة كيفية عمل المكونات (components)، والخصائص (props)، وuseState/useEffect، واستدعاء الوظائف الأساسية. إذا لم تسمع بأي من هذه المصطلحات من قبل، فقد ترغب في الاطلاع على البرنامج التعليمي لمقدمة React (opens in a new tab). بالنسبة للمتعلمين البصريين، نوصي بشدة بسلسلة مقاطع الفيديو الممتازة البرنامج التعليمي الشامل لـ React الحديث (opens in a new tab) بواسطة Net Ninja.

وإذا لم تكن قد فعلت ذلك بالفعل، فستحتاج بالتأكيد إلى حساب Alchemy لإكمال هذا البرنامج التعليمي بالإضافة إلى بناء أي شيء على سلسلة الكتل. قم بالتسجيل للحصول على حساب مجاني هنا (opens in a new tab).

بدون مزيد من التأخير، دعونا نبدأ!

أساسيات صنع ⁦NFTs⁩

قبل أن نبدأ حتى في النظر إلى أي كود، من المهم أن نفهم كيف تعمل عملية صنع ⁦NFT⁩. إنها تتضمن خطوتين:

نشر عقد ذكي لـ ⁦NFT⁩ على سلسلة الكتل لإيثيريوم

أكبر اختلاف بين معياري العقود الذكية لـ ⁦NFT⁩ هو أن ERC-1155 هو معيار متعدد الرموز ويتضمن وظيفة الدفعات، بينما ERC-721 هو معيار لرمز مميز واحد وبالتالي يدعم فقط نقل رمز مميز واحد في كل مرة.

استدعاء وظيفة السك

عادةً، تتطلب وظيفة السك هذه تمرير متغيرين كمعلمات، الأول هو recipient، والذي يحدد العنوان الذي سيتلقى ⁦NFT⁩ المسكوك حديثًا، والثاني هو tokenURI الخاص بـ ⁦NFT⁩، وهو سلسلة نصية تشير إلى مستند JSON يصف البيانات الوصفية لـ ⁦NFT⁩.

البيانات الوصفية لـ ⁦NFT⁩ هي حقًا ما يبعث الحياة فيه، مما يسمح له بامتلاك خصائص، مثل الاسم، والوصف، والصورة (أو أصل رقمي مختلف)، وسمات أخرى. إليك مثال على tokenURI (opens in a new tab)، والذي يحتوي على البيانات الوصفية لـ ⁦NFT⁩.

في هذا البرنامج التعليمي، سنركز على الجزء الثاني، وهو استدعاء وظيفة السك لعقد ذكي موجود لـ ⁦NFT⁩ باستخدام واجهة مستخدم React الخاصة بنا.

إليك رابط (opens in a new tab) للعقد الذكي ERC-721 لـ ⁦NFT⁩ الذي سنستدعيه في هذا البرنامج التعليمي. إذا كنت ترغب في معرفة كيف صنعناه، نوصي بشدة بالاطلاع على برنامجنا التعليمي الآخر، "كيفية إنشاء ⁦NFT⁩" (opens in a new tab).

رائع، الآن بعد أن فهمنا كيف تعمل عملية صنع ⁦NFT⁩، دعونا نستنسخ ملفات البداية الخاصة بنا!

استنساخ ملفات البداية

أولاً، انتقل إلى مستودع GitHub الخاص ببرنامج nft-minter-tutorial (opens in a new tab) للحصول على ملفات البداية لهذا المشروع. قم باستنساخ هذا المستودع في بيئتك المحلية.

عندما تفتح مستودع nft-minter-tutorial المستنسخ هذا، ستلاحظ أنه يحتوي على مجلدين: minter-starter-files و nft-minter.

  • يحتوي minter-starter-files على ملفات البداية (بشكل أساسي واجهة مستخدم React) لهذا المشروع. في هذا البرنامج التعليمي، سنعمل في هذا الدليل، حيث ستتعلم كيفية إحياء واجهة المستخدم هذه عن طريق ربطها بمحفظة إيثيريوم الخاصة بك وعقد ذكي لـ ⁦NFT⁩.
  • يحتوي nft-minter على البرنامج التعليمي المكتمل بالكامل وهو موجود كـ مرجع لك إذا واجهت أي صعوبة.

بعد ذلك، افتح نسختك من minter-starter-files في محرر الأكواد الخاص بك، ثم انتقل إلى مجلد src.

جميع الأكواد التي سنكتبها ستكون داخل مجلد src. سنقوم بتعديل مكون Minter.js وكتابة ملفات JavaScript إضافية لمنح مشروعنا وظائف Web3.

الخطوة 2: التحقق من ملفات البداية الخاصة بنا

قبل أن نبدأ في كتابة الأكواد، من المهم التحقق مما تم توفيره لنا بالفعل في ملفات البداية.

تشغيل مشروع React الخاص بك

دعونا نبدأ بتشغيل مشروع React في متصفحنا. جمال React هو أنه بمجرد تشغيل مشروعنا في متصفحنا، سيتم تحديث أي تغييرات نحفظها مباشرة في متصفحنا.

لتشغيل المشروع، انتقل إلى الدليل الجذر لمجلد minter-starter-files، ثم قم بتشغيل npm install في الطرفية (terminal) لتثبيت تبعيات المشروع:

cd minter-starter-files
npm install

بمجرد الانتهاء من التثبيت، قم بتشغيل npm start في الطرفية:

npm start

القيام بذلك يجب أن يفتح http://localhost:3000/ (opens in a new tab) في متصفحك، حيث سترى الواجهة الأمامية لمشروعنا. يجب أن تتكون من 3 حقول: مكان لإدخال رابط لأصل ⁦NFT⁩ الخاص بك، وإدخال اسم ⁦NFT⁩ الخاص بك، وتقديم وصف.

إذا حاولت النقر على زري "Connect Wallet" أو "Mint NFT"، ستلاحظ أنهما لا يعملان—وذلك لأننا لا نزال بحاجة إلى برمجة وظائفهما! :)

مكون Minter.js

ملاحظة: تأكد من أنك في مجلد minter-starter-files وليس في مجلد nft-minter!

دعونا نعود إلى مجلد src في محررنا ونفتح ملف Minter.js. من المهم جدًا أن نفهم كل شيء في هذا الملف، لأنه مكون React الأساسي الذي سنعمل عليه.

في الجزء العلوي من هذا الملف، لدينا متغيرات الحالة التي سنقوم بتحديثها بعد أحداث معينة.

//متغيرات الحالة
const [walletAddress, setWallet] = useState("")
const [status, setStatus] = useState("")
const [name, setName] = useState("")
const [description, setDescription] = useState("")
const [url, setURL] = useState("")

لم تسمع من قبل عن متغيرات حالة React أو خطافات الحالة (state hooks)؟ تحقق من هذه (opens in a new tab) المستندات.

إليك ما يمثله كل من المتغيرات:

  • walletAddress - سلسلة نصية تخزن عنوان محفظة المستخدم
  • status - سلسلة نصية تحتوي على رسالة لعرضها في أسفل واجهة المستخدم
  • name - سلسلة نصية تخزن اسم ⁦NFT⁩
  • description - سلسلة نصية تخزن وصف ⁦NFT⁩
  • url - سلسلة نصية تمثل رابطًا للأصل الرقمي لـ ⁦NFT⁩

بعد متغيرات الحالة، سترى ثلاث وظائف غير منفذة: useEffect، و connectWalletPressed، و onMintPressed. ستلاحظ أن جميع هذه الوظائف هي async، وذلك لأننا سنجري استدعاءات API غير متزامنة بداخلها! أسماؤها تعكس وظائفها:

  • useEffect (opens in a new tab) - هذا خطاف React يتم استدعاؤه بعد تصيير (render) المكون الخاص بك. نظرًا لأنه يتم تمرير خاصية مصفوفة فارغة [] إليه (انظر السطر 3)، فسيتم استدعاؤه فقط في التصيير الأول للمكون. هنا سنستدعي مستمع المحفظة الخاص بنا ووظيفة محفظة أخرى لتحديث واجهة المستخدم الخاصة بنا لتعكس ما إذا كانت المحفظة متصلة بالفعل.
  • connectWalletPressed - سيتم استدعاء هذه الوظيفة لربط محفظة ميتاماسك الخاصة بالمستخدم بتطبيقنا اللامركزي (dapp).
  • onMintPressed - سيتم استدعاء هذه الوظيفة لسك ⁦NFT⁩ الخاص بالمستخدم.

بالقرب من نهاية هذا الملف، لدينا واجهة المستخدم الخاصة بمكوننا. إذا قمت بفحص هذا الكود بعناية، ستلاحظ أننا نقوم بتحديث متغيرات الحالة url، و name، و description عندما يتغير الإدخال في حقول النص المقابلة لها.

سترى أيضًا أنه يتم استدعاء connectWalletPressed و onMintPressed عند النقر على الأزرار ذات المعرفات mintButton و walletButton على التوالي.

أخيرًا، دعونا نتناول أين يتم إضافة مكون Minter هذا.

إذا ذهبت إلى ملف App.js، وهو المكون الرئيسي في React الذي يعمل كحاوية لجميع المكونات الأخرى، سترى أن مكون Minter الخاص بنا يتم حقنه في السطر 7.

في هذا البرنامج التعليمي، سنقوم فقط بتعديل Minter.js file وإضافة ملفات في مجلد src الخاص بنا.

الآن بعد أن فهمنا ما نعمل عليه، دعونا نعد محفظة إيثيريوم الخاصة بنا!

إعداد محفظة إيثيريوم الخاصة بك

لكي يتمكن المستخدمون من التفاعل مع عقدك الذكي، سيحتاجون إلى ربط محفظة إيثيريوم الخاصة بهم بتطبيقك اللامركزي (dapp).

تنزيل ميتاماسك

في هذا البرنامج التعليمي، سنستخدم ميتاماسك، وهي محفظة افتراضية في المتصفح تُستخدم لإدارة عنوان حساب إيثيريوم الخاص بك. إذا كنت ترغب في فهم المزيد حول كيفية عمل المعاملات على إيثيريوم، تحقق من هذه الصفحة.

يمكنك تنزيل وإنشاء حساب ميتاماسك مجانًا هنا (opens in a new tab). عند إنشاء حساب، أو إذا كان لديك حساب بالفعل، تأكد من التبديل إلى "Ropsten Test Network" في الزاوية العلوية اليمنى (حتى لا نتعامل بأموال حقيقية).

إضافة إيثر من صنبور

من أجل سك ⁦NFTs⁩ الخاصة بنا (أو توقيع أي معاملات على سلسلة الكتل لإيثيريوم)، سنحتاج إلى بعض الـ ETH الوهمي. للحصول على ETH، يمكنك الذهاب إلى صنبور روبستن (opens in a new tab) وإدخال عنوان حساب روبستن الخاص بك، ثم النقر على "Send Ropsten Eth". يجب أن ترى ETH في حساب ميتاماسك الخاص بك بعد فترة وجيزة!

التحقق من رصيدك

للتأكد من وجود رصيدنا، دعونا نجري طلب eth_getBalance (opens in a new tab) باستخدام أداة الملحن الخاصة بـ Alchemy (opens in a new tab). سيعيد هذا مقدار ETH في محفظتنا. بعد إدخال عنوان حساب ميتاماسك الخاص بك والنقر على "Send Request"، يجب أن ترى استجابة مثل هذه:

{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}

ملاحظة: هذه النتيجة بوحدة Wei وليس ETH. تُستخدم Wei كأصغر فئة من الإيثر. التحويل من Wei إلى ETH هو: 1 eth = 10¹⁸ wei. لذا إذا قمنا بتحويل 0xde0b6b3a7640000 إلى النظام العشري نحصل على 1*10¹⁸ والذي يساوي 1 eth.

يا للعجب! أموالنا الوهمية كلها موجودة!

ربط ميتاماسك بواجهة المستخدم الخاصة بك

الآن بعد إعداد محفظة ميتاماسك الخاصة بنا، دعونا نربط تطبيقنا اللامركزي (dapp) بها!

لأننا نريد الالتزام بنموذج MVC (opens in a new tab)، سنقوم بإنشاء ملف منفصل يحتوي على وظائفنا لإدارة المنطق، والبيانات، والقواعد الخاصة بتطبيقنا اللامركزي (dapp)، ثم تمرير تلك الوظائف إلى واجهتنا الأمامية (مكون Minter.js الخاص بنا).

وظيفة connectWallet

للقيام بذلك، دعونا ننشئ مجلدًا جديدًا يسمى utils في دليل src الخاص بك ونضيف ملفًا يسمى interact.js بداخله، والذي سيحتوي على جميع وظائف التفاعل مع المحفظة والعقد الذكي.

في ملف interact.js الخاص بنا، سنكتب وظيفة connectWallet، والتي سنقوم بعد ذلك باستيرادها واستدعائها في مكون Minter.js الخاص بنا.

في ملف interact.js الخاص بك، أضف ما يلي

دعونا نفصل ما يفعله هذا الكود:

أولاً، تتحقق وظيفتنا مما إذا كان window.ethereum ممكّنًا في متصفحك.

window.ethereum هو API عالمي يتم حقنه بواسطة ميتاماسك ومزودي المحافظ الآخرين والذي يسمح لمواقع الويب بطلب حسابات إيثيريوم الخاصة بالمستخدمين. إذا تمت الموافقة، يمكنه قراءة البيانات من سلاسل الكتل التي يتصل بها المستخدم، واقتراح أن يقوم المستخدم بتوقيع الرسائل والمعاملات. تحقق من مستندات ميتاماسك (opens in a new tab) لمزيد من المعلومات!

إذا كان window.ethereum غير موجود، فهذا يعني أن ميتاماسك غير مثبت. يؤدي هذا إلى إرجاع كائن JSON، حيث يكون address المُرجع عبارة عن سلسلة نصية فارغة، وينقل كائن JSX status أنه يجب على المستخدم تثبيت ميتاماسك.

معظم الوظائف التي نكتبها ستعيد كائنات JSON يمكننا استخدامها لتحديث متغيرات الحالة وواجهة المستخدم الخاصة بنا.

الآن إذا كان window.ethereum موجودًا، فهنا تصبح الأمور مثيرة للاهتمام.

باستخدام حلقة try/catch، سنحاول الاتصال بميتاماسك عن طريق استدعاء window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). سيؤدي استدعاء هذه الوظيفة إلى فتح ميتاماسك في المتصفح، حيث سيُطلب من المستخدم ربط محفظته بتطبيقك اللامركزي (dapp).

  • إذا اختار المستخدم الاتصال، سيعيد method: "eth_requestAccounts" مصفوفة تحتوي على جميع عناوين حسابات المستخدم المتصلة بالتطبيق اللامركزي (dapp). إجمالاً، ستعيد وظيفة connectWallet الخاصة بنا كائن JSON يحتوي على أول address في هذه المصفوفة (انظر السطر 9) ورسالة status تطالب المستخدم بكتابة رسالة إلى العقد الذكي.
  • إذا رفض المستخدم الاتصال، فسيحتوي كائن JSON على سلسلة نصية فارغة لـ address المُرجع ورسالة status تعكس أن المستخدم رفض الاتصال.

إضافة وظيفة connectWallet إلى مكون واجهة المستخدم Minter.js الخاص بك

الآن بعد أن كتبنا وظيفة connectWallet هذه، دعونا نربطها بمكون Minter.js. الخاص بنا.

أولاً، سيتعين علينا استيراد وظيفتنا إلى ملف Minter.js الخاص بنا عن طريق إضافة import { connectWallet } from "./utils/interact.js"; إلى الجزء العلوي من ملف Minter.js. يجب أن تبدو الأسطر الـ 11 الأولى من Minter.js الآن هكذا:

ثم، داخل وظيفة connectWalletPressed الخاصة بنا، سنستدعي وظيفة connectWallet المستوردة، هكذا:

const connectWalletPressed = async () => {
  const walletResponse = await connectWallet()
  setStatus(walletResponse.status)
  setWallet(walletResponse.address)
}

هل تلاحظ كيف يتم تجريد معظم وظائفنا بعيدًا عن مكون Minter.js الخاص بنا من ملف interact.js؟ هذا لكي نتوافق مع نموذج M-V-C!

في connectWalletPressed، نقوم ببساطة بإجراء استدعاء await لوظيفة connectWallet المستوردة، وباستخدام استجابتها، نقوم بتحديث متغيرات status و walletAddress الخاصة بنا عبر خطافات الحالة الخاصة بها.

الآن، دعونا نحفظ كلا الملفين Minter.js و interact.js ونختبر واجهة المستخدم الخاصة بنا حتى الآن.

افتح متصفحك على localhost:3000، واضغط على زر "Connect Wallet" في أعلى يمين الصفحة.

إذا كان لديك ميتاماسك مثبتًا، فسيُطلب منك ربط محفظتك بتطبيقك اللامركزي (dapp). اقبل دعوة الاتصال.

يجب أن ترى أن زر المحفظة يعكس الآن أن عنوانك متصل.

بعد ذلك، حاول تحديث الصفحة... هذا غريب. يطالبنا زر المحفظة الخاص بنا بربط ميتاماسك، على الرغم من أنه متصل بالفعل...

لا تقلق مع ذلك! يمكننا إصلاح ذلك بسهولة عن طريق تنفيذ وظيفة تسمى getCurrentWalletConnected، والتي ستتحقق مما إذا كان هناك عنوان متصل بالفعل بتطبيقنا اللامركزي (dapp) وتحديث واجهة المستخدم الخاصة بنا وفقًا لذلك!

وظيفة getCurrentWalletConnected

في ملف interact.js الخاص بك، أضف وظيفة getCurrentWalletConnected التالية:

هذا الكود مشابه جدًا لوظيفة connectWallet التي كتبناها للتو في وقت سابق.

الفرق الرئيسي هو أنه بدلاً من استدعاء الطريقة eth_requestAccounts، والتي تفتح ميتاماسك للمستخدم لربط محفظته، نستدعي هنا الطريقة eth_accounts، والتي تعيد ببساطة مصفوفة تحتوي على عناوين ميتاماسك المتصلة حاليًا بتطبيقنا اللامركزي (dapp).

لرؤية هذه الوظيفة قيد العمل، دعونا نستدعيها في وظيفة useEffect لمكون Minter.js الخاص بنا.

كما فعلنا مع connectWallet، يجب علينا استيراد هذه الوظيفة من ملف interact.js الخاص بنا إلى ملف Minter.js الخاص بنا هكذا:

import { useEffect, useState } from "react"
import {
  connectWallet,
  getCurrentWalletConnected, //الاستيراد هنا
} from "./utils/interact.js"

الآن، نقوم ببساطة باستدعائها في وظيفة useEffect الخاصة بنا:

useEffect(async () => {
  const { address, status } = await getCurrentWalletConnected()
  setWallet(address)
  setStatus(status)
}, [])

لاحظ، نستخدم استجابة استدعائنا لـ getCurrentWalletConnected لتحديث متغيرات الحالة walletAddress و status الخاصة بنا.

بمجرد إضافة هذا الكود، حاول تحديث نافذة المتصفح الخاصة بنا. يجب أن يقول الزر أنك متصل، ويعرض معاينة لعنوان محفظتك المتصلة - حتى بعد التحديث!

تنفيذ addWalletListener

الخطوة الأخيرة في إعداد محفظة التطبيق اللامركزي (dapp) الخاص بنا هي تنفيذ مستمع المحفظة بحيث يتم تحديث واجهة المستخدم الخاصة بنا عندما تتغير حالة محفظتنا، مثل عندما يقوم المستخدم بقطع الاتصال أو تبديل الحسابات.

في ملف Minter.js الخاص بك، أضف وظيفة addWalletListener تبدو كالتالي:

دعونا نفصل بسرعة ما يحدث هنا:

  • أولاً، تتحقق وظيفتنا مما إذا كان window.ethereum ممكّنًا (أي أن ميتاماسك مثبت).
    • إذا لم يكن كذلك، نقوم ببساطة بتعيين متغير الحالة status الخاص بنا إلى سلسلة نصية JSX تطالب المستخدم بتثبيت ميتاماسك.
    • إذا كان ممكّنًا، نقوم بإعداد المستمع window.ethereum.on("accountsChanged") في السطر 3 والذي يستمع لتغييرات الحالة في محفظة ميتاماسك، والتي تشمل عندما يقوم المستخدم بربط حساب إضافي بالتطبيق اللامركزي (dapp)، أو تبديل الحسابات، أو قطع اتصال حساب. إذا كان هناك حساب واحد على الأقل متصل، يتم تحديث متغير الحالة walletAddress كأول حساب في مصفوفة accounts التي يعيدها المستمع. بخلاف ذلك، يتم تعيين walletAddress كسلسلة نصية فارغة.

أخيرًا، يجب علينا استدعاؤها في وظيفة useEffect الخاصة بنا:

useEffect(async () => {
  const { address, status } = await getCurrentWalletConnected()
  setWallet(address)
  setStatus(status)

  addWalletListener()
}, [])

وها نحن ذا! لقد أكملنا برمجة جميع وظائف محفظتنا! الآن بعد إعداد محفظتنا، دعونا نكتشف كيفية سك ⁦NFT⁩ الخاص بنا!

أساسيات البيانات الوصفية لـ ⁦NFT⁩

لذا تذكر البيانات الوصفية لـ ⁦NFT⁩ التي تحدثنا عنها للتو في الخطوة 0 من هذا البرنامج التعليمي—إنها تبعث الحياة في ⁦NFT⁩، مما يسمح له بامتلاك خصائص، مثل أصل رقمي، واسم، ووصف، وسمات أخرى.

سنحتاج إلى تكوين هذه البيانات الوصفية ككائن JSON وتخزينها، حتى نتمكن من تمريرها كمعلمة tokenURI عند استدعاء وظيفة mintNFT الخاصة بعقدنا الذكي.

سيشكل النص الموجود في حقول "Link to Asset"، و "Name"، و "Description" الخصائص المختلفة للبيانات الوصفية لـ ⁦NFT⁩ الخاص بنا. سنقوم بتنسيق هذه البيانات الوصفية ككائن JSON، ولكن هناك خياران لمكان تخزين كائن JSON هذا:

  • يمكننا تخزينه على سلسلة الكتل لإيثيريوم؛ ومع ذلك، فإن القيام بذلك سيكون مكلفًا للغاية.
  • يمكننا تخزينه على خادم مركزي، مثل AWS أو Firebase. لكن ذلك سيتعارض مع روح اللامركزية لدينا.
  • يمكننا استخدام IPFS، وهو بروتوكول لامركزي وشبكة نظير إلى نظير لتخزين ومشاركة البيانات في نظام ملفات موزع. نظرًا لأن هذا البروتوكول لامركزي ومجاني، فهو خيارنا الأفضل!

لتخزين بياناتنا الوصفية على IPFS، سنستخدم Pinata (opens in a new tab)، وهي مجموعة أدوات و API مريحة لـ IPFS. في الخطوة التالية، سنشرح بالضبط كيفية القيام بذلك!

استخدام Pinata لتثبيت بياناتك الوصفية على IPFS

إذا لم يكن لديك حساب Pinata (opens in a new tab)، قم بالتسجيل للحصول على حساب مجاني هنا (opens in a new tab) وأكمل الخطوات للتحقق من بريدك الإلكتروني وحسابك.

إنشاء مفتاح API الخاص بـ Pinata

انتقل إلى صفحة https://pinata.cloud/keys (opens in a new tab)، ثم حدد زر "New Key" في الأعلى، وقم بتعيين أداة المسؤول (Admin widget) كممكّنة، وقم بتسمية مفتاحك.

ستظهر لك بعد ذلك نافذة منبثقة تحتوي على معلومات API الخاصة بك. تأكد من وضع هذا في مكان آمن.

الآن بعد إعداد مفتاحنا، دعونا نضيفه إلى مشروعنا حتى نتمكن من استخدامه.

إنشاء ملف .env

يمكننا تخزين مفتاح Pinata والسر الخاص بنا بأمان في ملف بيئة. دعونا نثبت حزمة dotenv (opens in a new tab) في دليل مشروعك.

افتح علامة تبويب جديدة في الطرفية الخاصة بك (منفصلة عن تلك التي تشغل المضيف المحلي) وتأكد من أنك في مجلد minter-starter-files، ثم قم بتشغيل الأمر التالي في الطرفية الخاصة بك:

npm install dotenv --save

بعد ذلك، قم بإنشاء ملف .env في الدليل الجذر لـ minter-starter-files الخاص بك عن طريق إدخال ما يلي في سطر الأوامر الخاص بك:

vim.env

سيؤدي هذا إلى فتح ملف .env الخاص بك في vim (محرر نصوص). لحفظه، اضغط على "esc" + ":" + "q" على لوحة المفاتيح بهذا الترتيب.

بعد ذلك، في VSCode، انتقل إلى ملف .env الخاص بك وأضف مفتاح API الخاص بـ Pinata وسر API إليه، هكذا:

REACT_APP_PINATA_KEY = <pinata-api-key>
REACT_APP_PINATA_SECRET = <pinata-api-secret>

احفظ الملف، وبعد ذلك ستكون جاهزًا للبدء في كتابة الوظيفة لتحميل بيانات JSON الوصفية الخاصة بك إلى IPFS!

تنفيذ pinJSONToIPFS

لحسن الحظ بالنسبة لنا، لدى Pinata API مخصص لتحميل بيانات JSON إلى IPFS (opens in a new tab) ومثال JavaScript مريح مع axios يمكننا استخدامه، مع بعض التعديلات الطفيفة.

في مجلد utils الخاص بك، دعونا ننشئ ملفًا آخر يسمى pinata.js ثم نستورد سر ومفتاح Pinata الخاصين بنا من ملف .env هكذا:

require("dotenv").config()
const key = process.env.REACT_APP_PINATA_KEY
const secret = process.env.REACT_APP_PINATA_SECRET

بعد ذلك، الصق الكود الإضافي من الأسفل في ملف pinata.js الخاص بك. لا تقلق، سنفصل ما يعنيه كل شيء!

إذن ماذا يفعل هذا الكود بالضبط؟

أولاً، يستورد axios (opens in a new tab)، وهو عميل HTTP قائم على الوعود (promise based) للمتصفح و Node.js، والذي سنستخدمه لتقديم طلب إلى Pinata.

ثم لدينا وظيفتنا غير المتزامنة pinJSONToIPFS، والتي تأخذ JSONBody كمدخل لها ومفتاح وسر API الخاص بـ Pinata في ترويستها (header)، كل ذلك لتقديم طلب POST إلى API pinJSONToIPFS الخاص بهم.

  • إذا كان طلب POST هذا ناجحًا، فإن وظيفتنا تعيد كائن JSON مع القيمة المنطقية success كـ true و pinataUrl حيث تم تثبيت بياناتنا الوصفية. سنستخدم pinataUrl المُرجع هذا كمدخل tokenURI لوظيفة السك الخاصة بعقدنا الذكي.
  • إذا فشل طلب POST هذا، فإن وظيفتنا تعيد كائن JSON مع القيمة المنطقية success كـ false وسلسلة نصية message تنقل خطأنا.

كما هو الحال مع أنواع إرجاع وظيفة connectWallet الخاصة بنا، نحن نعيد كائنات JSON حتى نتمكن من استخدام معلماتها لتحديث متغيرات الحالة وواجهة المستخدم الخاصة بنا.

تحميل عقدك الذكي

الآن بعد أن أصبح لدينا طريقة لتحميل البيانات الوصفية لـ ⁦NFT⁩ الخاص بنا إلى IPFS عبر وظيفة pinJSONToIPFS الخاصة بنا، سنحتاج إلى طريقة لتحميل مثيل لعقدنا الذكي حتى نتمكن من استدعاء وظيفة mintNFT الخاصة به.

كما ذكرنا سابقًا، في هذا البرنامج التعليمي سنستخدم هذا العقد الذكي الحالي لـ ⁦NFT⁩ (opens in a new tab)؛ ومع ذلك، إذا كنت ترغب في معرفة كيف صنعناه، أو صنع واحد بنفسك، نوصي بشدة بالاطلاع على برنامجنا التعليمي الآخر، "كيفية إنشاء ⁦NFT⁩." (opens in a new tab).

ABI الخاص بالعقد

إذا فحصت ملفاتنا عن كثب، ستلاحظ أنه في دليل src الخاص بنا، يوجد ملف contract-abi.json. يعد ABI ضروريًا لتحديد الوظيفة التي سيستدعيها العقد بالإضافة إلى ضمان أن الوظيفة ستعيد البيانات بالتنسيق الذي تتوقعه.

سنحتاج أيضًا إلى مفتاح API الخاص بـ Alchemy و API الخاص بـ Alchemy Web3 للاتصال بسلسلة الكتل لإيثيريوم وتحميل عقدنا الذكي.

إنشاء مفتاح API الخاص بـ Alchemy

إذا لم يكن لديك حساب Alchemy بالفعل، قم بالتسجيل مجانًا هنا. (opens in a new tab)

بمجرد إنشاء حساب Alchemy، يمكنك إنشاء مفتاح API عن طريق إنشاء تطبيق. سيسمح لنا هذا بتقديم طلبات إلى شبكة اختبار روبستن.

انتقل إلى صفحة "Create App" في لوحة تحكم Alchemy الخاصة بك عن طريق التمرير فوق "Apps" في شريط التنقل والنقر على "Create App".

قم بتسمية تطبيقك، لقد اخترنا "My First NFT!"، وقدم وصفًا قصيرًا، وحدد "Staging" للبيئة المستخدمة لمسك دفاتر تطبيقك، واختر "Ropsten" لشبكتك.

انقر على "Create app" وهذا كل شيء! يجب أن يظهر تطبيقك في الجدول أدناه.

رائع، الآن بعد أن أنشأنا عنوان URL الخاص بـ HTTP Alchemy API، انسخه إلى الحافظة الخاصة بك...

…ثم دعونا نضيفه إلى ملف .env الخاص بنا. إجمالاً، يجب أن يبدو ملف .env الخاص بك هكذا:

REACT_APP_PINATA_KEY = <pinata-key>
REACT_APP_PINATA_SECRET = <pinata-secret>
REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key>

الآن بعد أن أصبح لدينا ABI الخاص بالعقد ومفتاح API الخاص بـ Alchemy، نحن جاهزون لتحميل عقدنا الذكي باستخدام Alchemy Web3 (opens in a new tab).

إعداد نقطة نهاية Alchemy Web3 والعقد الخاص بك

أولاً، إذا لم يكن لديك بالفعل، فستحتاج إلى تثبيت Alchemy Web3 (opens in a new tab) عن طريق الانتقال إلى الدليل الرئيسي: nft-minter-tutorial في الطرفية:

cd ..
npm install @alch/alchemy-web3

بعد ذلك دعونا نعود إلى ملف interact.js الخاص بنا. في الجزء العلوي من الملف، أضف الكود التالي لاستيراد مفتاح Alchemy الخاص بك من ملف .env الخاص بك وإعداد نقطة نهاية Alchemy Web3 الخاصة بك:

require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)

Alchemy Web3 (opens in a new tab) هو غلاف حول Web3.js (opens in a new tab)، يوفر طرق API محسنة وفوائد حاسمة أخرى لجعل حياتك كمطور Web3 أسهل. تم تصميمه ليتطلب الحد الأدنى من التكوين حتى تتمكن من البدء في استخدامه في تطبيقك على الفور!

بعد ذلك، دعونا نضيف ABI الخاص بالعقد وعنوان العقد إلى ملفنا.

require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)

const contractABI = require("../contract-abi.json")
const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE"

بمجرد أن يكون لدينا كلاهما، نحن جاهزون للبدء في كتابة كود وظيفة السك الخاصة بنا!

تنفيذ وظيفة mintNFT

داخل ملف interact.js الخاص بك، دعونا نحدد وظيفتنا، mintNFT، والتي ستقوم بسك ⁦NFT⁩ الخاص بنا كما يوحي اسمها.

لأننا سنجري العديد من الاستدعاءات غير المتزامنة (إلى Pinata لتثبيت بياناتنا الوصفية على IPFS، و Alchemy Web3 لتحميل عقدنا الذكي، وميتاماسك لتوقيع معاملاتنا)، ستكون وظيفتنا أيضًا غير متزامنة.

المدخلات الثلاثة لوظيفتنا ستكون url للأصل الرقمي الخاص بنا، و name، و description. أضف توقيع الوظيفة التالي أسفل وظيفة connectWallet:

export const mintNFT = async (url, name, description) => {}

معالجة أخطاء الإدخال

بطبيعة الحال، من المنطقي أن يكون هناك نوع من معالجة أخطاء الإدخال في بداية الوظيفة، لذلك نخرج من هذه الوظيفة إذا لم تكن معلمات الإدخال الخاصة بنا صحيحة. داخل وظيفتنا، دعونا نضيف الكود التالي:

بشكل أساسي، إذا كان أي من معلمات الإدخال عبارة عن سلسلة نصية فارغة، فإننا نعيد كائن JSON حيث تكون القيمة المنطقية success كـ false، وتنقل السلسلة النصية status أنه يجب إكمال جميع الحقول في واجهة المستخدم الخاصة بنا.

تحميل البيانات الوصفية إلى IPFS

بمجرد أن نعرف أن بياناتنا الوصفية منسقة بشكل صحيح، فإن الخطوة التالية هي تغليفها في كائن JSON وتحميلها إلى IPFS عبر pinJSONToIPFS التي كتبناها!

للقيام بذلك، نحتاج أولاً إلى استيراد وظيفة pinJSONToIPFS إلى ملف interact.js الخاص بنا. في الجزء العلوي من interact.js، دعونا نضيف:

import { pinJSONToIPFS } from "./pinata.js"

تذكر أن pinJSONToIPFS تأخذ جسم JSON. لذا قبل أن نجري استدعاءً لها، سنحتاج إلى تنسيق معلمات url، و name، و description الخاصة بنا في كائن JSON.

دعونا نحدث الكود الخاص بنا لإنشاء كائن JSON يسمى metadata ثم نجري استدعاءً لـ pinJSONToIPFS باستخدام معلمة metadata هذه:

لاحظ، نقوم بتخزين استجابة استدعائنا لـ pinJSONToIPFS(metadata) في كائن pinataResponse. ثم، نقوم بتحليل هذا الكائن بحثًا عن أي أخطاء.

إذا كان هناك خطأ، فإننا نعيد كائن JSON حيث تكون القيمة المنطقية success كـ false وتنقل السلسلة النصية status الخاصة بنا أن استدعاءنا فشل. بخلاف ذلك، نستخرج pinataURL من pinataResponse ونخزنه كمتغير tokenURI الخاص بنا.

الآن حان الوقت لتحميل عقدنا الذكي باستخدام API الخاص بـ Alchemy Web3 الذي قمنا بتهيئته في الجزء العلوي من ملفنا. أضف سطر الكود التالي إلى أسفل وظيفة mintNFT لتعيين العقد في المتغير العام window.contract:

window.contract = await new web3.eth.Contract(contractABI, contractAddress)

آخر شيء يجب إضافته في وظيفة mintNFT الخاصة بنا هو معاملة إيثيريوم الخاصة بنا:

إذا كنت معتادًا بالفعل على معاملات إيثيريوم، ستلاحظ أن الهيكل مشابه جدًا لما رأيته.

  • أولاً، نقوم بإعداد معلمات معاملاتنا.
    • يحدد to عنوان المستلم (عقدنا الذكي)
    • يحدد from موقع المعاملة (عنوان المستخدم المتصل بميتاماسك: window.ethereum.selectedAddress)
    • يحتوي data على استدعاء لطريقة mintNFT الخاصة بعقدنا الذكي، والتي تتلقى tokenURI الخاص بنا وعنوان محفظة المستخدم، window.ethereum.selectedAddress، كمدخلات
  • ثم، نجري استدعاء await، window.ethereum.request, حيث نطلب من ميتاماسك توقيع المعاملة. لاحظ، في هذا الطلب، نحدد طريقة eth الخاصة بنا (eth_SentTransaction) ونمرر transactionParameters الخاص بنا. في هذه المرحلة، سيتم فتح ميتاماسك في المتصفح، ويطالب المستخدم بتوقيع المعاملة أو رفضها.
    • إذا كانت المعاملة ناجحة، ستعيد الوظيفة كائن JSON حيث يتم تعيين القيمة المنطقية success إلى true وتطالب السلسلة النصية status المستخدم بالتحقق من Etherscan لمزيد من المعلومات حول معاملته.
    • إذا فشلت المعاملة، ستعيد الوظيفة كائن JSON حيث يتم تعيين القيمة المنطقية success إلى false، وتنقل السلسلة النصية status رسالة الخطأ.

إجمالاً، يجب أن تبدو وظيفة mintNFT الخاصة بنا هكذا:

هذه وظيفة عملاقة! الآن، نحتاج فقط إلى ربط وظيفة mintNFT الخاصة بنا بمكون Minter.js الخاص بنا...

ربط mintNFT بواجهتنا الأمامية Minter.js

افتح ملف Minter.js الخاص بك وقم بتحديث سطر import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; في الأعلى ليكون:

import {
  connectWallet,
  getCurrentWalletConnected,
  mintNFT,
} from "./utils/interact.js"

أخيرًا، قم بتنفيذ وظيفة onMintPressed لإجراء استدعاء await لوظيفة mintNFT المستوردة الخاصة بك وتحديث متغير الحالة status ليعكس ما إذا كانت معاملتنا قد نجحت أو فشلت:

const onMintPressed = async () => {
  const { status } = await mintNFT(url, name, description)
  setStatus(status)
}

نشر ⁦NFT⁩ الخاص بك على موقع ويب مباشر

هل أنت مستعد لجعل مشروعك مباشرًا ليتفاعل معه المستخدمون؟ تحقق من هذا البرنامج التعليمي (opens in a new tab) لنشر أداة السك (Minter) الخاصة بك على موقع ويب مباشر.

خطوة أخيرة...

اكتساح عالم سلسلة الكتل

أمزح فقط، لقد وصلت إلى نهاية البرنامج التعليمي!

للتلخيص، من خلال بناء أداة لسك ⁦NFT⁩، لقد تعلمت بنجاح كيفية:

  • الاتصال بمحفظة ميتاماسك عبر مشروع الواجهة الأمامية الخاص بك
  • استدعاء وظائف العقد الذكي من واجهتك الأمامية
  • توقيع المعاملات باستخدام ميتاماسك

من المفترض أنك ترغب في أن تكون قادرًا على التباهي بـ ⁦NFTs⁩ المسكوكة عبر تطبيقك اللامركزي (dapp) في محفظتك — لذا تأكد من الاطلاع على برنامجنا التعليمي السريع كيفية عرض ⁦NFT⁩ الخاص بك في محفظتك (opens in a new tab)!

وكما هو الحال دائمًا، إذا كان لديك أي أسئلة، فنحن هنا للمساعدة في ديسكورد Alchemy (opens in a new tab). لا يسعنا الانتظار لرؤية كيف ستطبق المفاهيم من هذا البرنامج التعليمي على مشاريعك المستقبلية!

آخر تحديث للصفحة: 3 أبريل 2026