كتابة سكربت لرفع بيانات وصور الـ NFTs آلياً إلى شبكة IPFS اللامركزية
كتابة سكربت لرفع بيانات وصور الـ NFTs آلياً إلى شبكة IPFS اللامركزية
عند بناء مشروع ERC-721: ما هي الرموز غير القابلة للاستبدال (NFTs) برمجياً؟ فإن التحدي الحقيقي لا يقتصر على عقد السك فقط، بل يمتد إلى إدارة الصور وملفات metadata بشكل موثوق وقابل للاستمرار. إذا بقيت بيانات الـ NFT على خادم مركزي عادي، فذلك يعاكس فلسفة اللامركزية ويعرّض المشروع لخطر تعطل الروابط أو تعديل المحتوى بعد الإصدار.
لهذا السبب يُعد تخزين بيانات الـ NFTs: لماذا نستخدم IPFS بدلاً من السيرفرات العادية؟ خطوة أساسية ضمن معمارية أي مشروع احترافي. شبكة IPFS تعتمد على content addressing بدلاً من تحديد موقع الملف، ما يعني أن الملف يُشار إليه عبر بصمة محتواه CID.
في هذا المقال سنبني سكربتاً عملياً يرفع الصور وملفات الوصف الخاصة بمجموعة NFTs إلى IPFS آلياً، ثم ينتج روابط جاهزة لاستهلاكها داخل عقد السك الذي قد تكون أنشأته في مشروع تطبيقي: برمجة عقد ذكي لسك (Minting) مجموعة NFTs لصور فنية. كما سنوضح كيف تدمج النتائج لاحقاً مع Hardhat وواجهة العقد.
ما الذي سنرفعه إلى IPFS فعلياً؟
في مشاريع NFT الاحترافية لا يكفي رفع صورة فقط. هناك عادة ملف JSON لكل عنصر يحتوي الاسم والوصف والرابط الخاص بالصورة وحقول السمات attributes. هذا الملف هو ما يعيده العقد عادة عبر الدالة tokenURI.
- رفع ملفات الصور أولاً للحصول على روابط
ipfs://CID. - توليد ملفات
metadataديناميكياً لكل صورة. - رفع ملفات الوصف نفسها إلى
IPFS. - حفظ الروابط النهائية لاستخدامها أثناء النشر أو السك الجماعي.
الأدوات المطلوبة لبناء السكربت
سنستخدم بيئة Node.js مع مشروع Hardhat لأن ذلك يمنحك سهولة دمج السكربت لاحقاً في سير عمل النشر والاختبار. وإذا لم تكن جهزت البيئة بعد فارجع إلى إعداد بيئة التطوير: تثبيت محفظة MetaMask والاتصال بشبكات الاختبار (Testnets).
hardhataxiosلإرسال الطلباتform-dataلرفع الملفاتfsوpathلإدارة الملفات المحلية- حساب في مزود تثبيت مثل
Pinataأو خدمة مماثلة
هيكل الملفات المقترح
images/للصور الأصليةmetadata/لملفاتJSONالمؤقتةscripts/upload.jsلسكربت الرفع
منطق الأتمتة: من الصورة إلى tokenURI
الفكرة البرمجية بسيطة لكنها مهمة: يقرأ السكربت جميع الصور من مجلد محلي، يرفع كل صورة، يلتقط CID الناتج، ثم يبني ملف وصف يربط خاصية image بعنوان ipfs://.... بعد ذلك يرفع ملف الوصف نفسه ويخزن رابطه النهائي.
هذه المقاربة تمنع الأخطاء اليدوية الشائعة، مثل ربط كل tokenId برابط خاطئ أو نسيان تحديث مسار الصورة داخل JSON. كما أنها تتوسع بسهولة عند التعامل مع عشرات أو مئات الملفات.
سكربت رفع الصور والبيانات إلى IPFS
الكود التالي يستخدم واجهة Pinata API كمثال عملي. يمكنك تعديله لاستخدام مزود آخر، لكن بنية العمل ستظل متشابهة: رفع الملف ثم استلام قيمة IpfsHash.
const fs = require("fs");
const path = require("path");
const axios = require("axios");
const FormData = require("form-data");
const PINATA_JWT = process.env.PINATA_JWT;
const IMAGES_FOLDER = path.join(__dirname, "../images");
const METADATA_FOLDER = path.join(__dirname, "../metadata");
async function uploadFileToIPFS(filePath) {
const data = new FormData();
data.append("file", fs.createReadStream(filePath));
const response = await axios.post(
"https://api.pinata.cloud/pinning/pinFileToIPFS",
data,
{
maxBodyLength: Infinity,
headers: {
...data.getHeaders(),
Authorization: `Bearer ${PINATA_JWT}`,
},
}
);
return `ipfs://${response.data.IpfsHash}`;
}
async function uploadJSONToIPFS(jsonData) {
const response = await axios.post(
"https://api.pinata.cloud/pinning/pinJSONToIPFS",
jsonData,
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${PINATA_JWT}`,
},
}
);
return `ipfs://${response.data.IpfsHash}`;
}
function buildMetadata(name, imageURI) {
return {
name: name,
description: `${name} NFT stored on IPFS`,
image: imageURI,
attributes: [
{
trait_type: "Collection",
value: "Qeeid Genesis"
},
{
trait_type: "File Storage",
value: "IPFS"
}
]
};
}
async function main() {
if (!fs.existsSync(METADATA_FOLDER)) {
fs.mkdirSync(METADATA_FOLDER);
}
const files = fs.readdirSync(IMAGES_FOLDER);
const tokenURIs = [];
for (const fileName of files) {
const imagePath = path.join(IMAGES_FOLDER, fileName);
const baseName = path.parse(fileName).name;
console.log(`Uploading image: ${fileName}`);
const imageURI = await uploadFileToIPFS(imagePath);
const metadata = buildMetadata(baseName, imageURI);
const metadataPath = path.join(METADATA_FOLDER, `${baseName}.json`);
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
console.log(`Uploading metadata: ${baseName}.json`);
const metadataURI = await uploadJSONToIPFS(metadata);
tokenURIs.push(metadataURI);
}
console.log("Final tokenURIs:");
console.log(tokenURIs);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
كيف تشغّل السكربت داخل المشروع؟
- أنشئ ملف
.envوضع فيه قيمةPINATA_JWT. - ضع الصور داخل مجلد
images. - ثبّت الحزم المطلوبة عبر
npm install axios form-data dotenv. - شغّل السكربت باستخدام
node scripts/upload.js. - انسخ مصفوفة
tokenURIsالناتجة لاستخدامها في عقد السك.
ربط الروابط مع العقد الذكي
بعد تجهيز الروابط يمكنك تمريرها إلى العقد أثناء النشر أو حفظها في مصفوفة داخلية بحسب تصميم المشروع. إذا كنت تراجع بنية الدوال فمقال الدوال (Functions) في Solidity: من يمكنه قراءة وتعديل بيانات العقد؟ مفيد لفهم موضع استخدام دالة mint أو safeMint.
وفي حال كنت تستخدم مكتبة استخدام مكتبة OpenZeppelin لكتابة عقود ذكية آمنة ومختبرة مسبقاً فغالباً ستستفيد من الامتداد ERC721URIStorage أو من أسلوب بناء baseURI إذا كانت الروابط منظمة داخل مجلد موحد.
لا تضع مفاتيح الوصول أو قيمة
JWTداخل الكود المصدري مباشرة. خزّنها في متغيرات بيئة، واستبعد ملفات الأسرار من الرفع إلى المستودع عبر.gitignore. هذا جزء أساسي من ممارساتSecurity Auditing.
تحسينات متقدمة على السكربت
النسخة السابقة مناسبة جداً للبداية، لكن في المشاريع الأكبر قد تحتاج إلى طبقة أكثر تقدماً من الأتمتة. مثلاً يمكنك التحقق من امتدادات الملفات قبل رفعها، وإضافة إعادة المحاولة retry logic عند فشل الشبكة، أو حتى قراءة السمات من ملف منفصل بدلاً من توليدها بشكل ثابت.
- إضافة تحقق من أن كل صورة تملك بيانات وصف مقابلة.
- فرز الملفات رقمياً لتطابق ترتيب
tokenId. - تصدير النتائج إلى ملف
tokenUris.json. - دمج السكربت مع عملية النشر داخل
Hardhat tasks.
إذا كان عقدك يخزن كل
tokenURIبشكل منفصل على السلسلة، فتذكر أن ذلك يزيد استهلاك التخزين وبالتالي رسومGas Fees. راجع التكاليف (Gas Fees): كيف يحسب البلوكتشين تكلفة تنفيذ الأكواد؟ وفكر باستخدامbaseURIعندما تكون البنية مناسبة لتقليل الكلفة.
أخطاء شائعة يجب تجنبها
من أكثر الأخطاء شيوعاً رفع الصور إلى IPFS ثم نسيان تحديث حقل image داخل ملفات الوصف. خطأ آخر هو استخدام روابط http مؤقتة بدلاً من ipfs:// داخل البيانات الأساسية.
كذلك يجب الانتباه إلى أن أي تغيير في محتوى الملف يولّد CID جديداً، لذلك لا تعدّل الملفات بعد ربطها بالعقد المنشور على شبكة حقيقية. هذه النقطة جوهرية في ثبات البيانات وقابلية التحقق منها داخل منظومة مدخل إلى Web3: ما هو البلوكتشين ولماذا يغير شكل الإنترنت والأنظمة المالية؟.
خاتمة
إن بناء سكربت آلي لرفع الصور وملفات metadata إلى IPFS ليس مجرد تحسين تنظيمي، بل هو جزء معماري من احترافية مشروع NFT. بهذه الأتمتة تقل الأخطاء، تصبح الروابط أكثر اتساقاً، وتتحول عملية السك إلى مسار قابل للتكرار والتوسع.
وعندما تدمج هذا السكربت مع عقد ذكي مبني جيداً باستخدام Solidity وأدوات مثل Hardhat وOpenZeppelin، فأنت تنتقل من تجربة تعليمية بسيطة إلى خط إنتاج فعلي لمجموعات رقمية قابلة للنشر بثقة على الشبكات العامة.
2 comments