معيار ERC-20: ما هي الرموز المميزة (Tokens) وكيف يتم إنشاؤها؟
معيار ERC-20: ما هي الرموز المميزة (Tokens) وكيف يتم إنشاؤها؟
عند الانتقال من فهم مدخل إلى Web3: ما هو البلوكتشين ولماذا يغير شكل الإنترنت والأنظمة المالية؟ إلى بناء تطبيقات عملية، تظهر الرموز المميزة بوصفها واحدة من أهم اللبنات في اقتصاد Blockchain. هذه الرموز ليست مجرد أرقام داخل قاعدة بيانات، بل أصول رقمية قابلة للبرمجة تعمل فوق شبكات مثل Ethereum وتُدار بواسطة Smart Contracts.
المعيار الأشهر لإنشاء هذه الرموز هو ERC-20. أهميته لا تأتي من كونه كوداً سحرياً، بل من كونه اتفاقاً تقنياً يضمن أن المحافظ ومنصات التداول والتطبيقات اللامركزية تستطيع فهم الرمز والتعامل معه بطريقة موحدة. من دون هذا التوحيد، كان كل رمز سيحتاج تكاملاً مخصصاً ومكلفاً.
في هذا المقال سنفكك المعيار برمجياً، ونشرح بنيته الداخلية، وآلية نقل الأرصدة، وفكرة السماحيات Allowances، ثم نبني عقداً ذكياً متوافقاً معه باستخدام Solidity بطريقة احترافية قابلة للتطوير.
ما المقصود بالرموز المميزة Tokens؟
الرمز المميز هو أصل رقمي يُعرِّفه عقد ذكي ويخزن منطق ملكيته وتحويله داخل الشبكة. بخلاف العملة الأصلية للشبكة مثل ETH، فإن Token لا يعيش في بروتوكول الشبكة نفسه، بل يُنشأ فوقها عبر عقد ذكي يعمل داخل EVM.
يمكن أن يمثل الرمز المميز نقاط ولاء، أو عملة داخل لعبة، أو حصة تصويت في بروتوكول حوكمة، أو أصلاً مالياً مرتبطاً بخدمة أو مشروع. لهذا السبب تُعد الرموز المميزة حجر أساس في كثير من تطبيقات DeFi وDApps.
ما هو معيار ERC-20 ولماذا أصبح معياراً أساسياً؟
ERC-20 هو مواصفة تحدد مجموعة من الدوال والأحداث التي يجب أن يوفرها العقد الذكي حتى تتعامل معه الأدوات الخارجية كرَمز قابل للاستبدال Fungible Token. كلمة قابل للاستبدال تعني أن كل وحدة من الرمز تساوي أي وحدة أخرى من النوع نفسه، مثل العملات التقليدية.
عندما تلتزم بالمعيار، تصبح المحافظ قادرة على عرض الرصيد، والمنصات قادرة على تنفيذ التحويلات، والعقود الأخرى قادرة على استدعاء وظائف الرمز دون معرفة تفاصيل تنفيذه الداخلية. هذا شبيه بمفهوم الواجهات (Interfaces): التحدث مع عقود ذكية لا تملك كودها المصدري، حيث يكفي الالتزام بالعقد البرمجي المتفق عليه.
أشهر عناصر المعيار
- الدوال الوصفية مثل
nameوsymbolوdecimals. - إجمالي المعروض عبر
totalSupply. - قراءة الأرصدة عبر
balanceOf. - تحويل الرموز عبر
transfer. - منح صلاحية إنفاق لطرف آخر عبر
approveوقراءة الصلاحية عبرallowance. - تنفيذ التحويل نيابة عن المالك عبر
transferFrom.
كيف تُخزَّن أرصدة المستخدمين داخل العقد؟
بنية ERC-20 تعتمد غالباً على القواميس (Mappings): أسرع طريقة لربط عناوين المحافظ بأرصدتها. هذا يسمح بربط كل عنوان بقيمة الرصيد الخاصة به بكفاءة عالية من حيث القراءة والكتابة.
أما صلاحيات الإنفاق فتُخزن عادة داخل mapping متداخل: المالك ← المنفق ← القيمة المسموح بها. هذا التصميم يجعل بروتوكولات التداول والعقود الوسيطة قادرة على سحب كمية محددة من الرمز نيابة عن المستخدم بعد موافقته الصريحة.
العقد الذكي الكامل لإنشاء رمز ERC-20
قبل قراءة الكود، من المفيد فهم أساسيات لغة Solidity: أنواع البيانات والمتغيرات (State Variables)، إضافة إلى الدوال (Functions) في Solidity: من يمكنه قراءة وتعديل بيانات العقد؟، لأن عقد الرمز يعتمد عليهما مباشرة.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MyERC20Token {
string public name = "Qeeid Token";
string public symbol = "QTD";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) private balances;
mapping(address => mapping(address => uint256)) private allowances;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
constructor(uint256 initialSupply) {
uint256 supply = initialSupply * (10 ** uint256(decimals));
totalSupply = supply;
balances[msg.sender] = supply;
emit Transfer(address(0), msg.sender, supply);
}
function balanceOf(address account) public view returns (uint256) {
return balances[account];
}
function transfer(address to, uint256 amount) public returns (bool) {
require(to != address(0), "Invalid recipient");
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
function allowance(address owner, address spender) public view returns (uint256) {
return allowances[owner][spender];
}
function approve(address spender, uint256 amount) public returns (bool) {
require(spender != address(0), "Invalid spender");
allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
require(to != address(0), "Invalid recipient");
require(balances[from] >= amount, "Insufficient balance");
require(allowances[from][msg.sender] >= amount, "Allowance exceeded");
allowances[from][msg.sender] -= amount;
balances[from] -= amount;
balances[to] += amount;
emit Transfer(from, to, amount);
return true;
}
}
شرح منطق العقد خطوة بخطوة
1) تعريف البيانات الأساسية
المتغيرات name وsymbol وdecimals تقدم وصفاً للرمز أمام المحافظ وواجهات الاستخدام. أما totalSupply فيمثل العدد الكلي للوحدات الصادرة.
2) سكّ المعروض الأولي
داخل constructor يتم تحويل القيمة المدخلة إلى أصغر وحدة وفق عدد المنازل العشرية، ثم إسناد كامل المعروض إلى ناشر العقد. إطلاق الحدث Transfer من العنوان الصفري هو النمط الشائع للدلالة على عملية سكّ Mint.
3) التحويل المباشر بين المحافظ
الدالة transfer تتحقق من صحة المستلم ومن كفاية الرصيد، ثم تخصم من المرسل وتضيف للمستلم. هذا النوع من التحقق يعتمد على مبادئ التعامل مع الأخطاء وإرجاع الأموال: استخدام require, assert, revert.
4) السماحيات والتفويض
الدالة approve تمنح عنواناً آخر صلاحية إنفاق كمية محددة. بعد ذلك يستطيع هذا العنوان استخدام transferFrom لتنفيذ التحويل ضمن الحد المسموح.
هذا النمط ضروري لتشغيل المنصات اللامركزية، لأن العقد الوسيط لا يمكنه تحريك أصول المستخدم من تلقاء نفسه إلا بعد تسجيل موافقته داخل السلسلة.
كيف تنشر الرمز عملياً؟
يمكنك استخدام محرر Remix IDE: كتابة ونشر أول عقد ذكي (Smart Contract) على المتصفح مباشرة للتجربة السريعة، أو بيئة Hardhat للمشاريع الاحترافية. وقبل النشر على شبكة اختبار، تأكد من إعداد تثبيت محفظة MetaMask والاتصال بشبكات الاختبار (Testnets)، ثم احصل على رصيد تجريبي من عملات Faucet.
- أنشئ ملف العقد وألصق الكود.
- اختر نسخة
compilerالمتوافقة. - قم بعملية
deployمع تحديدinitialSupply. - اختبر الدوال:
balanceOfثمtransferثمapprove.
لا تنشر عقد رمز مميز على الشبكة الرئيسية قبل إجراء اختبارات وحدات
Unit Testsواختبارات حالات الحافة، خصوصاً ما يتعلق بالسماحيات والتحويلات. أي خطأ صغير في منطق الأرصدة قد يؤدي إلى خسائر غير قابلة للتراجع.
أفضل الممارسات الأمنية واستهلاك الغاز
بناء رمز متوافق مع ERC-20 ليس مجرد كتابة دوال، بل يشمل وعياً بمخاطر التنفيذ داخل EVM. لذلك يفضّل كثير من المطورين الاعتماد على مكتبات مدققة مثل OpenZeppelin بدلاً من إعادة اختراع المعيار يدوياً.
من زاوية الأمان، مشكلة
approve/allowance race conditionمعروفة في بعض السيناريوهات. الأفضل في التطبيقات الحساسة استخدام أنماط تحديث آمنة أو دوال مساعدة مثل الزيادة والنقصان التدريجي للسماحية.
من زاوية تحسين الرسوم، افهم جيداً التكاليف (Gas Fees): كيف يحسب البلوكتشين تكلفة تنفيذ الأكواد؟، واستخدم الدوال
viewعند القراءة فقط كما هو موضح في أنواع الدوال : فهم view و pure لتوفير رسوم الـ Gas. كما أن تقليل عمليات الكتابة إلى التخزينStorageيساعد مباشرة في خفض التكلفة.
متى تحتاج إلى تطوير الرمز إلى نسخة أكثر تقدماً؟
العقد السابق مناسب للتعليم والفهم الداخلي للمعيار، لكنه لا يغطي مزايا الإنتاج الاحترافي مثل التحكم في السكّ والحرق Mint/Burn، أو الصلاحيات الإدارية، أو الإيقاف الطارئ، أو دعم التصاريح بالتوقيع Permit.
عند بناء بروتوكول حقيقي، ستحتاج غالباً إلى توسيع العقد عبر الوراثة (Inheritance): بناء عقود ذكية متقدمة بالاعتماد على أكواد عقود سابقة، وربما ربطه بعقود أخرى كما في التفاعل بين العقود الذكية: كيف تجعل عقداً يستدعي دالة من عقد آخر؟.
خلاصة
معيار ERC-20 هو القاعدة التي سمحت بازدهار اقتصاد الرموز فوق Ethereum. قوته الحقيقية تكمن في التوافق القياسي: رصيد موحد، تحويلات مفهومة، وأذونات قابلة للتكامل مع المحافظ والمنصات والعقود الأخرى.
إذا فهمت كيف يعمل balanceOf وtransfer وapprove وtransferFrom، فأنت لا تتعلم مجرد عقد واحد، بل تتعلم أساساً بنيوياً تقوم عليه نسبة ضخمة من تطبيقات Web3 الحديثة.
11 comments