ERC-20 کنٹریکٹ کا تفصیلی جائزہ
تعارف
ایتھریم (Ethereum) کے سب سے عام استعمالات میں سے ایک کسی گروپ کے لیے قابلِ تجارت ٹوکن بنانا ہے، جو ایک لحاظ سے ان کی اپنی کرنسی ہوتی ہے۔ یہ ٹوکنز عام طور پر ایک معیار، ERC-20 کی پیروی کرتے ہیں۔ یہ معیار ایسے ٹولز لکھنا ممکن بناتا ہے، جیسے کہ لیکویڈیٹی پولز اور والیٹس، جو تمام ERC-20 ٹوکنز کے ساتھ کام کرتے ہیں۔ اس مضمون میں ہم OpenZeppelin Solidity ERC20 implementation (opens in a new tab) کے ساتھ ساتھ interface definition (opens in a new tab) کا تجزیہ کریں گے۔
یہ تشریح شدہ سورس کوڈ ہے۔ اگر آپ ERC-20 کو نافذ کرنا چاہتے ہیں، تو یہ ٹیوٹوریل پڑھیں (opens in a new tab)۔
انٹرفیس
ERC-20 جیسے معیار کا مقصد بہت سے ٹوکنز کے نفاذ کی اجازت دینا ہے جو ایپلی کیشنز، جیسے والیٹس اور ڈی سینٹرلائزڈ ایکسچینجز کے درمیان باہم کام کر سکیں (interoperable ہوں)۔ اسے حاصل کرنے کے لیے، ہم ایک انٹرفیس (opens in a new tab) بناتے ہیں۔ کوئی بھی کوڈ جسے ٹوکن کنٹریکٹ استعمال کرنے کی ضرورت ہو وہ انٹرفیس میں انہی تعریفوں (definitions) کا استعمال کر سکتا ہے اور ان تمام ٹوکن کنٹریکٹس کے ساتھ ہم آہنگ (compatible) ہو سکتا ہے جو اسے استعمال کرتے ہیں، چاہے وہ MetaMask جیسا والیٹ ہو، etherscan.io جیسی ڈیپ (dapp) ہو، یا لیکویڈیٹی پول جیسا کوئی مختلف کنٹریکٹ ہو۔
اگر آپ ایک تجربہ کار پروگرامر ہیں، تو آپ کو شاید Java (opens in a new tab) یا یہاں تک کہ C header files (opens in a new tab) میں اسی طرح کے اسٹرکچرز دیکھنا یاد ہوگا۔
یہ OpenZeppelin کی جانب سے ERC-20 Interface (opens in a new tab) کی تعریف ہے۔ یہ انسانی پڑھنے کے قابل معیار (opens in a new tab) کا Solidity کوڈ میں ترجمہ ہے۔ یقیناً، انٹرفیس بذات خود یہ واضح نہیں کرتا کہ کوئی کام کیسے کرنا ہے۔ اس کی وضاحت ذیل میں کنٹریکٹ کے سورس کوڈ میں کی گئی ہے۔
1// SPDX-License-Identifier: MITSolidity فائلوں میں لائسنس شناخت کنندہ (identifier) شامل ہونا چاہیے۔ آپ یہاں لائسنسز کی فہرست دیکھ سکتے ہیں (opens in a new tab)۔ اگر آپ کو کوئی مختلف لائسنس درکار ہے، تو بس اسے تبصروں (comments) میں واضح کریں۔
1pragma solidity >=0.6.0 <0.8.0;Solidity زبان اب بھی تیزی سے ترقی کر رہی ہے، اور نئے ورژنز پرانے کوڈ کے ساتھ ہم آہنگ نہیں ہو سکتے (یہاں دیکھیں (opens in a new tab))۔ اس لیے، یہ ایک اچھا خیال ہے کہ نہ صرف زبان کا کم از کم ورژن متعین کیا جائے، بلکہ زیادہ سے زیادہ ورژن بھی، یعنی وہ تازہ ترین ورژن جس کے ساتھ آپ نے کوڈ کو ٹیسٹ کیا ہے۔
1/* *2 * @dev EIP میں بیان کردہ ERC20 سٹینڈرڈ کا انٹرفیس۔ */کمنٹ میں @dev NatSpec فارمیٹ (opens in a new tab) کا حصہ ہے، جو سورس کوڈ سے دستاویزات (documentation) تیار کرنے کے لیے استعمال ہوتا ہے۔
1interface IERC20 {روایت کے مطابق، انٹرفیس کے نام I سے شروع ہوتے ہیں۔
1 /* *2 * @dev موجودہ ٹوکنز کی مقدار واپس کرتا ہے۔ */3 function totalSupply() external view returns (uint256);یہ فنکشن external ہے، جس کا مطلب ہے اسے صرف کنٹریکٹ کے باہر سے کال کیا جا سکتا ہے (opens in a new tab)۔ یہ کنٹریکٹ میں ٹوکنز کی کل سپلائی واپس کرتا ہے۔ یہ قدر (value) ایتھریم میں سب سے عام قسم، unsigned 256 bits کا استعمال کرتے ہوئے واپس کی جاتی ہے (256 بٹس EVM کا مقامی ورڈ سائز ہے)۔ یہ فنکشن ایک view بھی ہے، جس کا مطلب ہے کہ یہ اسٹیٹ (state) کو تبدیل نہیں کرتا، لہذا اسے بلاک چین میں ہر نوڈ پر چلانے کے بجائے ایک ہی نوڈ پر عمل میں لایا جا سکتا ہے۔ اس قسم کا فنکشن کوئی ٹرانزیکشن نہیں بناتا اور اس پر گیس خرچ نہیں ہوتی۔
نوٹ: نظریاتی طور پر ایسا لگ سکتا ہے کہ کنٹریکٹ بنانے والا اصل قدر سے کم کل سپلائی واپس کر کے دھوکہ دے سکتا ہے، جس سے ہر ٹوکن اپنی اصل قیمت سے زیادہ قیمتی معلوم ہو۔ تاہم، یہ خوف بلاک چین کی اصل نوعیت کو نظر انداز کرتا ہے۔ بلاک چین پر ہونے والی ہر چیز کی تصدیق ہر نوڈ کے ذریعے کی جا سکتی ہے۔ اسے حاصل کرنے کے لیے، ہر کنٹریکٹ کا مشین لینگویج کوڈ اور اسٹوریج ہر نوڈ پر دستیاب ہوتا ہے۔ اگرچہ آپ کو اپنے کنٹریکٹ کے لیے Solidity کوڈ شائع کرنے کی ضرورت نہیں ہے، لیکن کوئی بھی آپ کو اس وقت تک سنجیدگی سے نہیں لے گا جب تک کہ آپ سورس کوڈ اور Solidity کا وہ ورژن شائع نہ کریں جس کے ساتھ اسے مرتب (compile) کیا گیا تھا، تاکہ آپ کے فراہم کردہ مشین لینگویج کوڈ سے اس کی تصدیق کی جا سکے۔ مثال کے طور پر، یہ کنٹریکٹ (opens in a new tab) دیکھیں۔
1 /* *2 * @dev `account` کی ملکیت میں موجود ٹوکنز کی مقدار واپس کرتا ہے۔ */3 function balanceOf(address account) external view returns (uint256);جیسا کہ نام سے ظاہر ہے، balanceOf کسی اکاؤنٹ کا بیلنس واپس کرتا ہے۔ ایتھریم اکاؤنٹس کی شناخت Solidity میں address ٹائپ کا استعمال کرتے ہوئے کی جاتی ہے، جو 160 بٹس پر مشتمل ہوتا ہے۔ یہ بھی external اور view ہے۔
1 /* *2 * @dev کالر کے اکاؤنٹ سے `recipient` کو `amount` ٹوکنز منتقل کرتا ہے۔3 *4 * ایک بولین (boolean) ویلیو واپس کرتا ہے جو بتاتی ہے کہ آیا آپریشن کامیاب ہوا یا نہیں۔5 *6 * ایک {Transfer} ایونٹ خارج (emit) کرتا ہے۔ */7 function transfer(address recipient, uint256 amount) external returns (bool);transfer فنکشن کالر سے کسی مختلف ایڈریس پر ٹوکنز منتقل کرتا ہے۔ اس میں اسٹیٹ کی تبدیلی شامل ہے، لہذا یہ view نہیں ہے۔ جب کوئی صارف اس فنکشن کو کال کرتا ہے تو یہ ایک ٹرانزیکشن بناتا ہے اور اس پر گیس خرچ ہوتی ہے۔ یہ ایک ایونٹ، Transfer بھی خارج (emit) کرتا ہے، تاکہ بلاک چین پر موجود ہر شخص کو اس ایونٹ کی اطلاع دی جا سکے۔
اس فنکشن میں دو مختلف قسم کے کالرز کے لیے دو قسم کی آؤٹ پٹ ہوتی ہے:
- وہ صارفین جو یوزر انٹرفیس سے براہ راست فنکشن کو کال کرتے ہیں۔ عام طور پر صارف ایک ٹرانزیکشن جمع کراتا ہے اور جواب کا انتظار نہیں کرتا، جس میں غیر معینہ وقت لگ سکتا ہے۔ صارف ٹرانزیکشن کی رسید (جس کی شناخت ٹرانزیکشن ہیش سے ہوتی ہے) یا
Transferایونٹ کو دیکھ کر جان سکتا ہے کہ کیا ہوا۔ - دیگر کنٹریکٹس، جو مجموعی ٹرانزیکشن کے حصے کے طور پر فنکشن کو کال کرتے ہیں۔ ان کنٹریکٹس کو فوری طور پر نتیجہ مل جاتا ہے، کیونکہ وہ اسی ٹرانزیکشن میں چلتے ہیں، لہذا وہ فنکشن کی ریٹرن ویلیو استعمال کر سکتے ہیں۔
اسی قسم کی آؤٹ پٹ دیگر فنکشنز کے ذریعے بھی بنائی جاتی ہے جو کنٹریکٹ کی اسٹیٹ کو تبدیل کرتے ہیں۔
الاؤنسز (Allowances) کسی اکاؤنٹ کو کچھ ایسے ٹوکنز خرچ کرنے کی اجازت دیتے ہیں جو کسی دوسرے مالک کے ہوتے ہیں۔ یہ مفید ہے، مثال کے طور پر، ان کنٹریکٹس کے لیے جو بیچنے والے (sellers) کے طور پر کام کرتے ہیں۔ کنٹریکٹس ایونٹس کی نگرانی نہیں کر سکتے، لہذا اگر کوئی خریدار براہ راست بیچنے والے کے کنٹریکٹ میں ٹوکنز منتقل کرے تو اس کنٹریکٹ کو معلوم نہیں ہوگا کہ اسے ادائیگی کی گئی ہے۔ اس کے بجائے، خریدار بیچنے والے کے کنٹریکٹ کو ایک مخصوص رقم خرچ کرنے کی اجازت دیتا ہے، اور بیچنے والا وہ رقم منتقل کر لیتا ہے۔ یہ ایک ایسے فنکشن کے ذریعے کیا جاتا ہے جسے بیچنے والے کا کنٹریکٹ کال کرتا ہے، تاکہ بیچنے والے کا کنٹریکٹ جان سکے کہ آیا یہ کامیاب رہا۔
1 /* *2 * @dev ان باقی ماندہ ٹوکنز کی تعداد واپس کرتا ہے جو `spender` کو {transferFrom} کے ذریعے `owner` کی جانب سے خرچ کرنے کی اجازت ہوگی۔ یہ بائی ڈیفالٹ صفر (zero) ہوتا ہے۔3 *4 * یہ ویلیو تب تبدیل ہوتی ہے جب {approve} یا {transferFrom} کو کال کیا جاتا ہے۔ */5 function allowance(address owner, address spender) external view returns (uint256);allowance فنکشن کسی کو بھی یہ جاننے کی اجازت دیتا ہے کہ وہ کون سا الاؤنس ہے جو ایک ایڈریس (owner) دوسرے ایڈریس (spender) کو خرچ کرنے دیتا ہے۔
1 /* *2 * @dev کالر کے ٹوکنز پر `spender` کے الاؤنس کے طور پر `amount` سیٹ کرتا ہے۔3 *4 * ایک بولین ویلیو واپس کرتا ہے جو بتاتی ہے کہ آیا آپریشن کامیاب ہوا یا نہیں۔5 *6 * اہم: ہوشیار رہیں کہ اس طریقے سے الاؤنس کو تبدیل کرنے سے یہ خطرہ پیدا ہوتا ہے کہ کوئی بدقسمت ٹرانزیکشن آرڈرنگ کے ذریعے پرانے اور نئے دونوں الاؤنسز کا استعمال کر سکتا ہے۔ اس ریس کنڈیشن (race condition) کو کم کرنے کا ایک ممکنہ حل یہ ہے کہ پہلے خرچ کرنے والے (spender) کے الاؤنس کو 0 کر دیا جائے اور اس کے بعد مطلوبہ ویلیو سیٹ کی جائے:7 * https://github.com/ethereum/EIPs/issues/20#issuecomment-2635247298 *9 * ایک {Approval} ایونٹ خارج کرتا ہے۔ */10 function approve(address spender, uint256 amount) external returns (bool);سب دکھائیںapprove فنکشن ایک الاؤنس بناتا ہے۔ اس بارے میں پیغام ضرور پڑھیں کہ اس کا غلط استعمال کیسے ہو سکتا ہے۔ ایتھریم میں آپ اپنی ٹرانزیکشنز کی ترتیب کو کنٹرول کرتے ہیں، لیکن آپ اس ترتیب کو کنٹرول نہیں کر سکتے جس میں دوسرے لوگوں کی ٹرانزیکشنز پر عمل درآمد کیا جائے گا، جب تک کہ آپ اپنی ٹرانزیکشن اس وقت تک جمع نہ کرائیں جب تک کہ آپ یہ نہ دیکھ لیں کہ دوسرے فریق کی ٹرانزیکشن ہو چکی ہے۔
1 /* *2 * @dev الاؤنس میکانزم کا استعمال کرتے ہوئے `sender` سے `recipient` کو `amount` ٹوکنز منتقل کرتا ہے۔ پھر کالر کے الاؤنس سے `amount` کی کٹوتی کی جاتی ہے۔3 *4 * ایک بولین ویلیو واپس کرتا ہے جو بتاتی ہے کہ آیا آپریشن کامیاب ہوا یا نہیں۔5 *6 * ایک {Transfer} ایونٹ خارج کرتا ہے۔ */7 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);آخر میں، transferFrom کو خرچ کرنے والے (spender) کے ذریعے دراصل الاؤنس خرچ کرنے کے لیے استعمال کیا جاتا ہے۔
12 /* *3 * @dev تب خارج ہوتا ہے جب `value` ٹوکنز ایک اکاؤنٹ (`from`) سے دوسرے اکاؤنٹ (`to`) میں منتقل کیے جاتے ہیں۔4 *5 * نوٹ کریں کہ `value` صفر بھی ہو سکتی ہے۔ */6 event Transfer(address indexed from, address indexed to, uint256 value);78 /* *9 * @dev تب خارج ہوتا ہے جب {approve} کی کال کے ذریعے کسی `owner` کے لیے `spender` کا الاؤنس سیٹ کیا جاتا ہے۔ `value` نیا الاؤنس ہے۔ */10 event Approval(address indexed owner, address indexed spender, uint256 value);11}سب دکھائیںیہ ایونٹس اس وقت خارج ہوتے ہیں جب ERC-20 کنٹریکٹ کی اسٹیٹ تبدیل ہوتی ہے۔
اصل کنٹریکٹ
یہ وہ اصل کنٹریکٹ ہے جو ERC-20 معیار کو نافذ کرتا ہے، جسے یہاں سے لیا گیا ہے (opens in a new tab)۔ اس کا مقصد اسے جوں کا توں استعمال کرنا نہیں ہے، بلکہ آپ اسے قابل استعمال بنانے کے لیے اس سے وراثت (inherit) (opens in a new tab) لے سکتے ہیں۔
1// SPDX-License-Identifier: MIT2pragma solidity >=0.6.0 <0.8.0;
امپورٹ اسٹیٹمنٹس
اوپر دی گئی انٹرفیس کی تعریفوں کے علاوہ، کنٹریکٹ کی تعریف دو دیگر فائلوں کو امپورٹ کرتی ہے:
12import "../../GSN/Context.sol";3import "./IERC20.sol";4import "../../math/SafeMath.sol";GSN/Context.solوہ تعریفیں ہیں جو OpenGSN (opens in a new tab) استعمال کرنے کے لیے درکار ہیں، یہ ایک ایسا سسٹم ہے جو بغیر ایتھر والے صارفین کو بلاک چین استعمال کرنے کی اجازت دیتا ہے۔ نوٹ کریں کہ یہ ایک پرانا ورژن ہے، اگر آپ OpenGSN کے ساتھ انضمام (integrate) کرنا چاہتے ہیں تو یہ ٹیوٹوریل استعمال کریں (opens in a new tab)۔- SafeMath لائبریری (opens in a new tab)، جو Solidity ورژنز <0.8.0 کے لیے ریاضیاتی اوور فلو/انڈر فلو (overflows/underflows) کو روکتی ہے۔ Solidity ≥0.8.0 میں، ریاضیاتی آپریشنز اوور فلو/انڈر فلو پر خود بخود ریورٹ (revert) ہو جاتے ہیں، جس سے SafeMath غیر ضروری ہو جاتا ہے۔ یہ کنٹریکٹ پرانے کمپائلر ورژنز کے ساتھ بیک ورڈ کمپیٹیبلٹی (backward compatibility) کے لیے SafeMath کا استعمال کرتا ہے۔
یہ کمنٹ کنٹریکٹ کے مقصد کی وضاحت کرتا ہے۔
1/* *2 * @dev {IERC20} انٹرفیس کی امپلیمینٹیشن (Implementation)۔3 *4 * یہ امپلیمینٹیشن ٹوکنز بنانے کے طریقے سے لاپرواہ (agnostic) ہے۔ اس کا مطلب ہے کہ {_mint} کا استعمال کرتے ہوئے اخذ کردہ (derived) کنٹریکٹ میں سپلائی میکانزم شامل کرنا ہوگا۔ عمومی میکانزم کے لیے {ERC20PresetMinterPauser} دیکھیں۔5 *6 * ٹپ: تفصیلی تحریر کے لیے ہماری گائیڈ دیکھیں7 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How8 * to implement supply mechanisms]۔9 *10 * ہم نے OpenZeppelin کی عمومی گائیڈ لائنز کی پیروی کی ہے: فنکشنز ناکامی پر `false` واپس کرنے کے بجائے ریورٹ (revert) ہو جاتے ہیں۔ یہ رویہ بہرحال روایتی ہے اور ERC20 ایپلی کیشنز کی توقعات سے متصادم نہیں ہے۔11 *12 * مزید برآں، {transferFrom} کی کالز پر ایک {Approval} ایونٹ خارج ہوتا ہے۔ یہ ایپلی کیشنز کو صرف مذکورہ ایونٹس کو سن کر تمام اکاؤنٹس کے لیے الاؤنس کو دوبارہ ترتیب دینے کی اجازت دیتا ہے۔ EIP کی دیگر امپلیمینٹیشنز شاید یہ ایونٹس خارج نہ کریں، کیونکہ یہ تصریح (specification) کے لحاظ سے ضروری نہیں ہے۔13 *14 * آخر میں، الاؤنسز سیٹ کرنے کے حوالے سے معروف مسائل کو کم کرنے کے لیے غیر معیاری (non-standard) {decreaseAllowance} اور {increaseAllowance}15 * فنکشنز شامل کیے گئے ہیں۔ {IERC20-approve} دیکھیں۔ */16سب دکھائیںکنٹریکٹ کی تعریف
1contract ERC20 is Context, IERC20 {یہ لائن وراثت (inheritance) کی وضاحت کرتی ہے، اس صورت میں اوپر دیے گئے IERC20 اور OpenGSN کے لیے Context سے۔
12 using SafeMath for uint256;3یہ لائن SafeMath لائبریری کو uint256 ٹائپ کے ساتھ منسلک کرتی ہے۔ آپ یہ لائبریری یہاں (opens in a new tab) تلاش کر سکتے ہیں۔
متغیرات کی تعریفیں
یہ تعریفیں کنٹریکٹ کے اسٹیٹ متغیرات (state variables) کی وضاحت کرتی ہیں۔ ان متغیرات کو private قرار دیا گیا ہے، لیکن اس کا مطلب صرف یہ ہے کہ بلاک چین پر موجود دیگر کنٹریکٹس انہیں پڑھ نہیں سکتے۔ بلاک چین پر کوئی راز نہیں ہوتے، ہر نوڈ پر موجود سافٹ ویئر کے پاس ہر بلاک پر ہر کنٹریکٹ کی اسٹیٹ ہوتی ہے۔ روایت کے مطابق، اسٹیٹ متغیرات کا نام _<something> رکھا جاتا ہے۔
پہلے دو متغیرات میپنگز (mappings) (opens in a new tab) ہیں، جس کا مطلب ہے کہ وہ تقریباً ایسوسی ایٹو ایریز (associative arrays) (opens in a new tab) کی طرح برتاؤ کرتے ہیں، سوائے اس کے کہ کیز (keys) عددی قدریں (numeric values) ہوتی ہیں۔ اسٹوریج صرف ان اندراجات (entries) کے لیے مختص کی جاتی ہے جن کی قدریں ڈیفالٹ (صفر) سے مختلف ہوتی ہیں۔
1 mapping (address => uint256) private _balances;پہلی میپنگ، _balances، ایڈریسز اور اس ٹوکن کے ان کے متعلقہ بیلنسز ہیں۔ بیلنس تک رسائی کے لیے، یہ سنٹیکس استعمال کریں: _balances[<address>]۔
1 mapping (address => mapping (address => uint256)) private _allowances;یہ متغیر، _allowances، ان الاؤنسز کو اسٹور کرتا ہے جن کی وضاحت پہلے کی گئی تھی۔ پہلا انڈیکس ٹوکنز کا مالک ہے، اور دوسرا وہ کنٹریکٹ ہے جس کے پاس الاؤنس ہے۔ اس رقم تک رسائی حاصل کرنے کے لیے جو ایڈریس A ایڈریس B کے اکاؤنٹ سے خرچ کر سکتا ہے، _allowances[B][A] استعمال کریں۔
1 uint256 private _totalSupply;جیسا کہ نام سے ظاہر ہے، یہ متغیر ٹوکنز کی کل سپلائی کا ٹریک رکھتا ہے۔
1 string private _name;2 string private _symbol;3 uint8 private _decimals;یہ تین متغیرات پڑھنے کی اہلیت (readability) کو بہتر بنانے کے لیے استعمال ہوتے ہیں۔ پہلے دو خود وضاحتی ہیں، لیکن _decimals نہیں ہے۔
ایک طرف، ایتھریم میں فلوٹنگ پوائنٹ یا فریکشنل (کسری) متغیرات نہیں ہوتے۔ دوسری طرف، انسان ٹوکنز کو تقسیم کرنے کے قابل ہونا پسند کرتے ہیں۔ لوگوں کے کرنسی کے لیے سونے پر متفق ہونے کی ایک وجہ یہ تھی کہ جب کوئی گائے کے بدلے بطخ خریدنا چاہتا تھا تو ریزگاری (change) بنانا مشکل تھا۔
اس کا حل یہ ہے کہ انٹیجرز (integers) کا ٹریک رکھا جائے، لیکن اصلی ٹوکن کے بجائے ایک فریکشنل ٹوکن گنا جائے جو تقریباً بے وقعت ہو۔ ایتھر کے معاملے میں، فریکشنل ٹوکن کو wei کہا جاتا ہے، اور 10^18 wei ایک ETH کے برابر ہے۔ لکھتے وقت، 10,000,000,000,000 wei تقریباً ایک امریکی یا یورو سینٹ کے برابر ہے۔
ایپلی کیشنز کو یہ جاننے کی ضرورت ہوتی ہے کہ ٹوکن بیلنس کیسے دکھایا جائے۔ اگر کسی صارف کے پاس 3,141,000,000,000,000,000 wei ہیں، تو کیا یہ 3.14 ETH ہے؟ 31.41 ETH؟ 3,141 ETH؟ ایتھر کے معاملے میں یہ 10^18 wei فی ETH کے طور پر بیان کیا گیا ہے، لیکن اپنے ٹوکن کے لیے آپ ایک مختلف قدر منتخب کر سکتے ہیں۔ اگر ٹوکن کو تقسیم کرنے کا کوئی مطلب نہیں بنتا، تو آپ _decimals کی قدر صفر استعمال کر سکتے ہیں۔ اگر آپ ETH جیسا ہی معیار استعمال کرنا چاہتے ہیں، تو قدر 18 استعمال کریں۔
کنسٹرکٹر
1 /* *2 * @dev {name} اور {symbol} کے لیے ویلیوز سیٹ کرتا ہے، {decimals} کو3 * 18 کی ڈیفالٹ ویلیو کے ساتھ انیشلائز (initialize) کرتا ہے۔4 *5 * {decimals} کے لیے مختلف ویلیو منتخب کرنے کے لیے، {_setupDecimals} استعمال کریں۔6 *7 * یہ تینوں ویلیوز ناقابلِ تبدیلی (immutable) ہیں: انہیں کنسٹرکشن کے دوران صرف ایک بار8 * سیٹ کیا جا سکتا ہے۔ */9 constructor (string memory name_, string memory symbol_) public {10 // Solidity ≥0.7.0 میں، 'public' مضمر (implicit) ہے اور اسے چھوڑا جا سکتا ہے۔1112 _name = name_;13 _symbol = symbol_;14 _decimals = 18;15 }سب دکھائیںکنسٹرکٹر اس وقت کال کیا جاتا ہے جب کنٹریکٹ پہلی بار بنایا جاتا ہے۔ روایت کے مطابق، فنکشن پیرامیٹرز کا نام <something>_ رکھا جاتا ہے۔
یوزر انٹرفیس فنکشنز
1 /* *2 * @dev ٹوکن کا نام واپس کرتا ہے۔ */3 function name() public view returns (string memory) {4 return _name;5 }67 /* *8 * @dev ٹوکن کا سمبل (symbol) واپس کرتا ہے، جو عام طور پر نام کا9 * مختصر ورژن ہوتا ہے۔ */10 function symbol() public view returns (string memory) {11 return _symbol;12 }1314 /* *15 * @dev اس کی یوزر ریپریزنٹیشن (user representation) حاصل کرنے کے لیے استعمال ہونے والے ڈیسیملز (decimals) کی تعداد واپس کرتا ہے۔16 * مثال کے طور پر، اگر `decimals` کی ویلیو `2` ہے، تو `505` ٹوکنز کا بیلنس17 * صارف کو `5,05` (`505 / 10 ** 2`) کے طور پر دکھایا جانا چاہیے۔18 *19 * ٹوکنز عام طور پر ایتھر (ether) اور wei کے درمیان تعلق کی نقل کرتے ہوئے20 * 18 کی ویلیو کا انتخاب کرتے ہیں۔ یہ وہ ویلیو ہے جو {ERC20} استعمال کرتا ہے، جب تک کہ {_setupDecimals} کو21 * کال نہ کیا جائے۔22 *23 * نوٹ: یہ معلومات صرف _دکھانے_ (display) کے مقاصد کے لیے استعمال ہوتی ہے: یہ24 * کسی بھی طرح کنٹریکٹ کے حساب کتاب (arithmetic) کو متاثر نہیں کرتی، بشمول25 * {IERC20-balanceOf} اور {IERC20-transfer}۔ */26 function decimals() public view returns (uint8) {27 return _decimals;28 }سب دکھائیںیہ فنکشنز، name، symbol، اور decimals یوزر انٹرفیسز کو آپ کے کنٹریکٹ کے بارے میں جاننے میں مدد کرتے ہیں تاکہ وہ اسے مناسب طریقے سے دکھا سکیں۔
ریٹرن ٹائپ string memory ہے، جس کا مطلب ہے ایک ایسی اسٹرنگ واپس کرنا جو میموری میں اسٹور ہو۔ متغیرات، جیسے کہ اسٹرنگز، کو تین مقامات پر اسٹور کیا جا سکتا ہے:
| لائف ٹائم (Lifetime) | کنٹریکٹ تک رسائی | گیس کی لاگت | |
|---|---|---|---|
| Memory | فنکشن کال | پڑھنا/لکھنا | دسیوں یا سینکڑوں (اعلی مقامات کے لیے زیادہ) |
| Calldata | فنکشن کال | صرف پڑھنا | ریٹرن ٹائپ کے طور پر استعمال نہیں کیا جا سکتا، صرف فنکشن پیرامیٹر ٹائپ کے طور پر |
| Storage | تبدیل ہونے تک | پڑھنا/لکھنا | زیادہ (پڑھنے کے لیے 800، لکھنے کے لیے 20k) |
اس صورت میں، memory بہترین انتخاب ہے۔
ٹوکن کی معلومات پڑھنا
یہ وہ فنکشنز ہیں جو ٹوکن کے بارے میں معلومات فراہم کرتے ہیں، یا تو کل سپلائی یا کسی اکاؤنٹ کا بیلنس۔
1 /* *2 * @dev {IERC20-totalSupply} دیکھیں۔ */3 function totalSupply() public view override returns (uint256) {4 return _totalSupply;5 }totalSupply فنکشن ٹوکنز کی کل سپلائی واپس کرتا ہے۔
1 /* *2 * @dev {IERC20-balanceOf} دیکھیں۔ */3 function balanceOf(address account) public view override returns (uint256) {4 return _balances[account];5 }کسی اکاؤنٹ کا بیلنس پڑھیں۔ نوٹ کریں کہ کسی کو بھی کسی دوسرے کے اکاؤنٹ کا بیلنس حاصل کرنے کی اجازت ہے۔ اس معلومات کو چھپانے کی کوشش کرنے کا کوئی فائدہ نہیں، کیونکہ یہ بہرحال ہر نوڈ پر دستیاب ہے۔ بلاک چین پر کوئی راز نہیں ہوتے۔
ٹوکنز منتقل کرنا
1 /* *2 * @dev {IERC20-transfer} دیکھیں۔3 *4 * تقاضے:5 *6 * - `recipient` زیرو ایڈریس (zero address) نہیں ہو سکتا۔7 * - کالر کے پاس کم از کم `amount` کا بیلنس ہونا چاہیے۔ */8 function transfer(address recipient, uint256 amount) public virtual override returns (bool) {transfer فنکشن بھیجنے والے کے اکاؤنٹ سے کسی دوسرے اکاؤنٹ میں ٹوکنز منتقل کرنے کے لیے کال کیا جاتا ہے۔ نوٹ کریں کہ اگرچہ یہ ایک بولین (boolean) قدر واپس کرتا ہے، وہ قدر ہمیشہ true ہوتی ہے۔ اگر منتقلی ناکام ہو جاتی ہے تو کنٹریکٹ کال کو ریورٹ کر دیتا ہے۔
1 _transfer(_msgSender(), recipient, amount);2 return true;3 }_transfer فنکشن اصل کام کرتا ہے۔ یہ ایک پرائیویٹ فنکشن ہے جسے صرف کنٹریکٹ کے دیگر فنکشنز ہی کال کر سکتے ہیں۔ روایت کے مطابق پرائیویٹ فنکشنز کا نام _<something> رکھا جاتا ہے، بالکل اسٹیٹ متغیرات کی طرح۔
عام طور پر Solidity میں ہم پیغام بھیجنے والے کے لیے msg.sender استعمال کرتے ہیں۔ تاہم، یہ OpenGSN (opens in a new tab) کو توڑ دیتا ہے۔ اگر ہم اپنے ٹوکن کے ساتھ بغیر ایتھر والی ٹرانزیکشنز کی اجازت دینا چاہتے ہیں، تو ہمیں _msgSender() استعمال کرنے کی ضرورت ہے۔ یہ عام ٹرانزیکشنز کے لیے msg.sender واپس کرتا ہے، لیکن بغیر ایتھر والی ٹرانزیکشنز کے لیے اصل دستخط کنندہ (signer) واپس کرتا ہے نہ کہ وہ کنٹریکٹ جس نے پیغام کو ریلے (relay) کیا ہو۔
الاؤنس فنکشنز
یہ وہ فنکشنز ہیں جو الاؤنس کی فعالیت کو نافذ کرتے ہیں: allowance، approve، transferFrom، اور _approve۔ مزید برآں، OpenZeppelin کا نفاذ بنیادی معیار سے آگے بڑھ کر کچھ ایسی خصوصیات شامل کرتا ہے جو سیکیورٹی کو بہتر بناتی ہیں: increaseAllowance، اور decreaseAllowance۔
allowance فنکشن
1 /* *2 * @dev {IERC20-allowance} دیکھیں۔ */3 function allowance(address owner, address spender) public view virtual override returns (uint256) {4 return _allowances[owner][spender];5 }allowance فنکشن ہر کسی کو کوئی بھی الاؤنس چیک کرنے کی اجازت دیتا ہے۔
approve فنکشن
1 /* *2 * @dev {IERC20-approve} دیکھیں۔3 *4 * تقاضے:5 *6 * - `spender` زیرو ایڈریس نہیں ہو سکتا۔ */7 function approve(address spender, uint256 amount) public virtual override returns (bool) {یہ فنکشن الاؤنس بنانے کے لیے کال کیا جاتا ہے۔ یہ اوپر دیے گئے transfer فنکشن سے ملتا جلتا ہے:
- یہ فنکشن صرف ایک اندرونی فنکشن (اس صورت میں،
_approve) کو کال کرتا ہے جو اصل کام کرتا ہے۔ - یہ فنکشن یا تو
trueواپس کرتا ہے (اگر کامیاب ہو) یا ریورٹ ہو جاتا ہے (اگر نہیں)۔
1 _approve(_msgSender(), spender, amount);2 return true;3 }ہم اندرونی فنکشنز کا استعمال ان جگہوں کی تعداد کو کم کرنے کے لیے کرتے ہیں جہاں اسٹیٹ میں تبدیلیاں ہوتی ہیں۔ کوئی بھی فنکشن جو اسٹیٹ کو تبدیل کرتا ہے وہ ایک ممکنہ سیکیورٹی خطرہ ہے جس کا سیکیورٹی کے لیے آڈٹ ہونا ضروری ہے۔ اس طرح ہمارے پاس غلطی کرنے کے امکانات کم ہوتے ہیں۔
transferFrom فنکشن
یہ وہ فنکشن ہے جسے خرچ کرنے والا (spender) الاؤنس خرچ کرنے کے لیے کال کرتا ہے۔ اس کے لیے دو آپریشنز درکار ہیں: خرچ کی جانے والی رقم کو منتقل کرنا اور الاؤنس کو اس رقم سے کم کرنا۔
1 /* *2 * @dev {IERC20-transferFrom} دیکھیں۔3 *4 * اپڈیٹ شدہ الاؤنس کی نشاندہی کرنے والا ایک {Approval} ایونٹ خارج کرتا ہے۔ EIP کی طرف سے یہ5 * درکار نہیں ہے۔ {ERC20} کے شروع میں موجود نوٹ دیکھیں۔6 *7 * تقاضے:8 *9 * - `sender` اور `recipient` زیرو ایڈریس نہیں ہو سکتے۔10 * - `sender` کے پاس کم از کم `amount` کا بیلنس ہونا چاہیے۔11 * - کالر کے پاس ``sender`` کے ٹوکنز کے لیے کم از کم12 * `amount` کا الاؤنس ہونا چاہیے۔ */13 function transferFrom(address sender, address recipient, uint256 amount) public virtual14 override returns (bool) {15 _transfer(sender, recipient, amount);سب دکھائیں
a.sub(b, "message") فنکشن کال دو کام کرتی ہے۔ پہلا، یہ a-b کا حساب لگاتی ہے، جو نیا الاؤنس ہے۔ دوسرا، یہ چیک کرتی ہے کہ یہ نتیجہ منفی (negative) تو نہیں ہے۔ اگر یہ منفی ہے تو کال فراہم کردہ پیغام کے ساتھ ریورٹ ہو جاتی ہے۔ نوٹ کریں کہ جب کوئی کال ریورٹ ہوتی ہے تو اس کال کے دوران پہلے کی گئی کوئی بھی پروسیسنگ نظر انداز کر دی جاتی ہے لہذا ہمیں _transfer کو ان ڈو (undo) کرنے کی ضرورت نہیں ہے۔
1 _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount,2 "ERC20: transfer amount exceeds allowance"));3 return true;4 }OpenZeppelin کے حفاظتی اضافے
کسی غیر صفر (non-zero) الاؤنس کو کسی دوسری غیر صفر قدر پر سیٹ کرنا خطرناک ہے، کیونکہ آپ صرف اپنی ٹرانزیکشنز کی ترتیب کو کنٹرول کرتے ہیں، کسی اور کی نہیں۔ تصور کریں کہ آپ کے پاس دو صارفین ہیں، ایلس (Alice) جو سادہ لوح ہے اور بل (Bill) جو بے ایمان ہے۔ ایلس بل سے کچھ سروس چاہتی ہے، جس کی قیمت اس کے خیال میں پانچ ٹوکنز ہے - لہذا وہ بل کو پانچ ٹوکنز کا الاؤنس دیتی ہے۔
پھر کچھ تبدیل ہوتا ہے اور بل کی قیمت بڑھ کر دس ٹوکنز ہو جاتی ہے۔ ایلس، جو اب بھی سروس چاہتی ہے، ایک ٹرانزیکشن بھیجتی ہے جو بل کا الاؤنس دس پر سیٹ کر دیتی ہے۔ جس لمحے بل اس نئی ٹرانزیکشن کو ٹرانزیکشن پول میں دیکھتا ہے وہ ایک ٹرانزیکشن بھیجتا ہے جو ایلس کے پانچ ٹوکنز خرچ کرتی ہے اور اس کی گیس کی قیمت بہت زیادہ ہوتی ہے تاکہ اسے تیزی سے مائن (mine) کیا جا سکے۔ اس طرح بل پہلے پانچ ٹوکنز خرچ کر سکتا ہے اور پھر، جب ایلس کا نیا الاؤنس مائن ہو جاتا ہے، تو مزید دس خرچ کر سکتا ہے جس کی کل قیمت پندرہ ٹوکنز بنتی ہے، جو کہ ایلس کی اجازت سے زیادہ ہے۔ اس تکنیک کو فرنٹ رننگ (front-running) (opens in a new tab) کہا جاتا ہے۔
| ایلس کی ٹرانزیکشن | ایلس کا نانس (Nonce) | بل کی ٹرانزیکشن | بل کا نانس | بل کا الاؤنس | ایلس سے بل کی کل آمدنی |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | ||
| approve(Bill, 10) | 11 | 10 | 5 | ||
| transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 |
اس مسئلے سے بچنے کے لیے، یہ دو فنکشنز (increaseAllowance اور decreaseAllowance) آپ کو الاؤنس کو ایک مخصوص رقم سے تبدیل کرنے کی اجازت دیتے ہیں۔ لہذا اگر بل پہلے ہی پانچ ٹوکنز خرچ کر چکا ہے، تو وہ صرف مزید پانچ خرچ کر سکے گا۔ ٹائمنگ پر منحصر ہے، اس کے کام کرنے کے دو طریقے ہیں، اور دونوں صورتوں میں بل کو صرف دس ٹوکنز ہی ملیں گے:
A:
| ایلس کی ٹرانزیکشن | ایلس کا نانس | بل کی ٹرانزیکشن | بل کا نانس | بل کا الاؤنس | ایلس سے بل کی کل آمدنی |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | ||
| increaseAllowance(Bill, 5) | 11 | 0+5 = 5 | 5 | ||
| transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 |
B:
| ایلس کی ٹرانزیکشن | ایلس کا نانس | بل کی ٹرانزیکشن | بل کا نانس | بل کا الاؤنس | ایلس سے بل کی کل آمدنی |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| increaseAllowance(Bill, 5) | 11 | 5+5 = 10 | 0 | ||
| transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 |
1 /* *2 * @dev کالر کی طرف سے `spender` کو دیے گئے الاؤنس میں ایٹمی طور پر (atomically) اضافہ کرتا ہے۔3 *4 * یہ {approve} کا متبادل ہے جسے {IERC20-approve} میں بیان کردہ5 * مسائل کو کم کرنے کے لیے استعمال کیا جا سکتا ہے۔6 *7 * اپڈیٹ شدہ الاؤنس کی نشاندہی کرنے والا ایک {Approval} ایونٹ خارج کرتا ہے۔8 *9 * تقاضے:10 *11 * - `spender` زیرو ایڈریس نہیں ہو سکتا۔ */12 function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {13 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));14 return true;15 }سب دکھائیںa.add(b) فنکشن ایک محفوظ اضافہ (safe add) ہے۔ اس غیر امکانی صورت میں کہ a+b>=2^256 ہو، یہ اس طرح ریپ اراؤنڈ (wrap around) نہیں کرتا جس طرح عام اضافہ کرتا ہے۔
12 /* *3 * @dev کالر کی طرف سے `spender` کو دیے گئے الاؤنس میں ایٹمی طور پر کمی کرتا ہے۔4 *5 * یہ {approve} کا متبادل ہے جسے {IERC20-approve} میں بیان کردہ6 * مسائل کو کم کرنے کے لیے استعمال کیا جا سکتا ہے۔7 *8 * اپڈیٹ شدہ الاؤنس کی نشاندہی کرنے والا ایک {Approval} ایونٹ خارج کرتا ہے۔9 *10 * تقاضے:11 *12 * - `spender` زیرو ایڈریس نہیں ہو سکتا۔13 * - `spender` کے پاس کالر کے لیے کم از کم14 * `subtractedValue` کا الاؤنس ہونا چاہیے۔ */15 function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {16 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue,17 "ERC20: decreased allowance below zero"));18 return true;19 }سب دکھائیںٹوکن کی معلومات میں ترمیم کرنے والے فنکشنز
یہ وہ چار فنکشنز ہیں جو اصل کام کرتے ہیں: _transfer، _mint، _burn، اور _approve۔
_transfer فنکشن
1 /* *2 * @dev `sender` سے `recipient` کو `amount` ٹوکنز منتقل کرتا ہے۔3 *4 * یہ انٹرنل (internal) فنکشن {transfer} کے مساوی ہے، اور اسے5 * مثال کے طور پر خودکار ٹوکن فیس، سلیشنگ (slashing) میکانزم وغیرہ لاگو کرنے کے لیے استعمال کیا جا سکتا ہے۔6 *7 * ایک {Transfer} ایونٹ خارج کرتا ہے۔8 *9 * تقاضے:10 *11 * - `sender` زیرو ایڈریس نہیں ہو سکتا۔12 * - `recipient` زیرو ایڈریس نہیں ہو سکتا۔13 * - `sender` کے پاس کم از کم `amount` کا بیلنس ہونا چاہیے۔ */14 function _transfer(address sender, address recipient, uint256 amount) internal virtual {سب دکھائیںیہ فنکشن، _transfer، ایک اکاؤنٹ سے دوسرے اکاؤنٹ میں ٹوکنز منتقل کرتا ہے۔ اسے transfer (بھیجنے والے کے اپنے اکاؤنٹ سے منتقلی کے لیے) اور transferFrom (کسی اور کے اکاؤنٹ سے منتقل کرنے کے لیے الاؤنسز کا استعمال کرتے ہوئے) دونوں کے ذریعے کال کیا جاتا ہے۔
1 require(sender != address(0), "ERC20: transfer from the zero address");2 require(recipient != address(0), "ERC20: transfer to the zero address");ایتھریم میں دراصل کوئی بھی ایڈریس زیرو (address zero) کا مالک نہیں ہے (یعنی، کوئی بھی ایسی پرائیویٹ کی (private key) نہیں جانتا جس کی مماثل پبلک کی (public key) زیرو ایڈریس میں تبدیل ہو جائے)۔ جب لوگ اس ایڈریس کا استعمال کرتے ہیں، تو یہ عام طور پر ایک سافٹ ویئر بگ ہوتا ہے - لہذا اگر زیرو ایڈریس کو بھیجنے والے یا وصول کنندہ کے طور پر استعمال کیا جائے تو ہم اسے فیل (fail) کر دیتے ہیں۔
1 _beforeTokenTransfer(sender, recipient, amount);2اس کنٹریکٹ کو استعمال کرنے کے دو طریقے ہیں:
- اسے اپنے کوڈ کے لیے ایک ٹیمپلیٹ کے طور پر استعمال کریں
- اس سے وراثت لیں (opens in a new tab)، اور صرف ان فنکشنز کو اوور رائیڈ (override) کریں جنہیں آپ کو تبدیل کرنے کی ضرورت ہے
دوسرا طریقہ بہت بہتر ہے کیونکہ OpenZeppelin ERC-20 کوڈ کا پہلے ہی آڈٹ کیا جا چکا ہے اور اسے محفوظ دکھایا گیا ہے۔ جب آپ وراثت کا استعمال کرتے ہیں تو یہ واضح ہوتا ہے کہ آپ کن فنکشنز میں ترمیم کرتے ہیں، اور آپ کے کنٹریکٹ پر بھروسہ کرنے کے لیے لوگوں کو صرف ان مخصوص فنکشنز کا آڈٹ کرنے کی ضرورت ہوتی ہے۔
ہر بار ٹوکنز کے ہاتھ بدلنے پر کوئی فنکشن انجام دینا اکثر مفید ہوتا ہے۔ تاہم، _transfer ایک بہت اہم فنکشن ہے اور اسے غیر محفوظ طریقے سے لکھنا ممکن ہے (نیچے دیکھیں)، لہذا اسے اوور رائیڈ نہ کرنا ہی بہتر ہے۔ اس کا حل _beforeTokenTransfer ہے، جو ایک ہک فنکشن (hook function) (opens in a new tab) ہے۔ آپ اس فنکشن کو اوور رائیڈ کر سکتے ہیں، اور اسے ہر منتقلی پر کال کیا جائے گا۔
1 _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");2 _balances[recipient] = _balances[recipient].add(amount);یہ وہ لائنیں ہیں جو دراصل منتقلی کرتی ہیں۔ نوٹ کریں کہ ان کے درمیان کچھ نہیں ہے، اور یہ کہ ہم وصول کنندہ میں شامل کرنے سے پہلے بھیجنے والے سے منتقل شدہ رقم کو منہا (subtract) کرتے ہیں۔ یہ اہم ہے کیونکہ اگر درمیان میں کسی مختلف کنٹریکٹ کو کال کی جاتی، تو اسے اس کنٹریکٹ کو دھوکہ دینے کے لیے استعمال کیا جا سکتا تھا۔ اس طرح منتقلی ایٹامک (atomic) ہوتی ہے، اس کے درمیان کچھ نہیں ہو سکتا۔
1 emit Transfer(sender, recipient, amount);2 }آخر میں، ایک Transfer ایونٹ خارج کریں۔ ایونٹس اسمارٹ کنٹریکٹس کے لیے قابل رسائی نہیں ہیں، لیکن بلاک چین کے باہر چلنے والا کوڈ ایونٹس کو سن سکتا ہے اور ان پر ردعمل ظاہر کر سکتا ہے۔ مثال کے طور پر، ایک والیٹ اس بات کا ٹریک رکھ سکتا ہے کہ مالک کو کب مزید ٹوکنز ملتے ہیں۔
_mint اور _burn فنکشنز
یہ دو فنکشنز (_mint اور _burn) ٹوکنز کی کل سپلائی میں ترمیم کرتے ہیں۔ یہ اندرونی (internal) ہیں اور اس کنٹریکٹ میں کوئی ایسا فنکشن نہیں ہے جو انہیں کال کرتا ہو، لہذا یہ صرف اس صورت میں مفید ہیں جب آپ کنٹریکٹ سے وراثت لیتے ہیں اور اپنی منطق (logic) شامل کرتے ہیں تاکہ یہ فیصلہ کیا جا سکے کہ کن حالات میں نئے ٹوکنز منٹ (mint) کرنے ہیں یا موجودہ ٹوکنز کو برن (burn) کرنا ہے۔
نوٹ: ہر ERC-20 ٹوکن کی اپنی کاروباری منطق ہوتی ہے جو ٹوکن مینجمنٹ کا تعین کرتی ہے۔ مثال کے طور پر، ایک فکسڈ سپلائی کنٹریکٹ صرف کنسٹرکٹر میں _mint کو کال کر سکتا ہے اور کبھی _burn کو کال نہیں کر سکتا۔ ایک کنٹریکٹ جو ٹوکنز بیچتا ہے وہ ادائیگی ہونے پر _mint کو کال کرے گا، اور ممکنہ طور پر بے قابو افراط زر (inflation) سے بچنے کے لیے کسی موقع پر _burn کو کال کرے گا۔
1 /* * @dev `amount` ٹوکنز بناتا ہے اور انہیں `account` کو تفویض (assign) کرتا ہے، جس سے2 * کل سپلائی (total supply) میں اضافہ ہوتا ہے۔3 *4 * ایک {Transfer} ایونٹ خارج کرتا ہے جس میں `from` کو زیرو ایڈریس پر سیٹ کیا گیا ہوتا ہے۔5 *6 * تقاضے:7 *8 * - `to` زیرو ایڈریس نہیں ہو سکتا۔ */9 function _mint(address account, uint256 amount) internal virtual {10 require(account != address(0), "ERC20: mint to the zero address");11 _beforeTokenTransfer(address(0), account, amount);12 _totalSupply = _totalSupply.add(amount);13 _balances[account] = _balances[account].add(amount);14 emit Transfer(address(0), account, amount);15 }سب دکھائیںاس بات کو یقینی بنائیں کہ جب ٹوکنز کی کل تعداد تبدیل ہو تو _totalSupply کو اپ ڈیٹ کریں۔
1 /* *2 * @dev `account` سے `amount` ٹوکنز کو تباہ (destroy) کرتا ہے، جس سے3 * کل سپلائی کم ہو جاتی ہے۔4 *5 * ایک {Transfer} ایونٹ خارج کرتا ہے جس میں `to` کو زیرو ایڈریس پر سیٹ کیا گیا ہوتا ہے۔6 *7 * تقاضے:8 *9 * - `account` زیرو ایڈریس نہیں ہو سکتا۔10 * - `account` کے پاس کم از کم `amount` ٹوکنز ہونے چاہئیں۔ */11 function _burn(address account, uint256 amount) internal virtual {12 require(account != address(0), "ERC20: burn from the zero address");1314 _beforeTokenTransfer(account, address(0), amount);1516 _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");17 _totalSupply = _totalSupply.sub(amount);18 emit Transfer(account, address(0), amount);19 }سب دکھائیں_burn فنکشن تقریباً _mint جیسا ہی ہے، سوائے اس کے کہ یہ دوسری سمت میں جاتا ہے۔
_approve فنکشن
یہ وہ فنکشن ہے جو دراصل الاؤنسز کی وضاحت کرتا ہے۔ نوٹ کریں کہ یہ مالک کو ایسا الاؤنس متعین کرنے کی اجازت دیتا ہے جو مالک کے موجودہ بیلنس سے زیادہ ہو۔ یہ ٹھیک ہے کیونکہ بیلنس منتقلی کے وقت چیک کیا جاتا ہے، جب یہ اس بیلنس سے مختلف ہو سکتا ہے جو الاؤنس بناتے وقت تھا۔
1 /* *2 * @dev `owner` کے ٹوکنز پر `spender` کے الاؤنس کے طور پر `amount` سیٹ کرتا ہے۔3 *4 * یہ انٹرنل فنکشن `approve` کے مساوی ہے، اور اسے5 * مثال کے طور پر مخصوص سب سسٹمز کے لیے خودکار الاؤنسز سیٹ کرنے وغیرہ کے لیے استعمال کیا جا سکتا ہے۔6 *7 * ایک {Approval} ایونٹ خارج کرتا ہے۔8 *9 * تقاضے:10 *11 * - `owner` زیرو ایڈریس نہیں ہو سکتا۔12 * - `spender` زیرو ایڈریس نہیں ہو سکتا۔ */13 function _approve(address owner, address spender, uint256 amount) internal virtual {14 require(owner != address(0), "ERC20: approve from the zero address");15 require(spender != address(0), "ERC20: approve to the zero address");1617 _allowances[owner][spender] = amount;سب دکھائیں
ایک Approval ایونٹ خارج کریں۔ اس بات پر منحصر ہے کہ ایپلی کیشن کیسے لکھی گئی ہے، خرچ کرنے والے کنٹریکٹ کو منظوری کے بارے میں یا تو مالک کے ذریعے یا کسی ایسے سرور کے ذریعے بتایا جا سکتا ہے جو ان ایونٹس کو سنتا ہے۔
1 emit Approval(owner, spender, amount);2 }3Decimals متغیر میں ترمیم کریں
123 /* *4 * @dev {decimals} کو 18 کی ڈیفالٹ ویلیو کے علاوہ کسی اور ویلیو پر سیٹ کرتا ہے۔5 *6 * انتباہ: اس فنکشن کو صرف کنسٹرکٹر (constructor) سے کال کیا جانا چاہیے۔ ٹوکن کنٹریکٹس کے ساتھ تعامل کرنے والی زیادہ تر7 * ایپلی کیشنز یہ توقع نہیں کریں گی کہ8 * {decimals} کبھی تبدیل ہوں گے، اور اگر ایسا ہوتا ہے تو وہ غلط طریقے سے کام کر سکتی ہیں۔ */9 function _setupDecimals(uint8 decimals_) internal {10 _decimals = decimals_;11 }سب دکھائیںیہ فنکشن _decimals متغیر میں ترمیم کرتا ہے جو یوزر انٹرفیسز کو یہ بتانے کے لیے استعمال ہوتا ہے کہ رقم کی تشریح کیسے کی جائے۔ آپ کو اسے کنسٹرکٹر سے کال کرنا چاہیے۔ اسے کسی بھی بعد کے موقع پر کال کرنا بے ایمانی ہوگی، اور ایپلی کیشنز اسے ہینڈل کرنے کے لیے ڈیزائن نہیں کی گئی ہیں۔
ہکس (Hooks)
12 /* *3 * @dev ہک (Hook) جسے ٹوکنز کی کسی بھی منتقلی سے پہلے کال کیا جاتا ہے۔ اس میں4 * منٹنگ (minting) اور برننگ (burning) شامل ہیں۔5 *6 * کالنگ کی شرائط:7 *8 * - جب `from` اور `to` دونوں نان-زیرو (non-zero) ہوں، تو ``from`` کے `amount` ٹوکنز9 * `to` کو منتقل کیے جائیں گے۔10 * - جب `from` زیرو ہو، تو `to` کے لیے `amount` ٹوکنز منٹ (mint) کیے جائیں گے۔11 * - جب `to` زیرو ہو، تو ``from`` کے `amount` ٹوکنز برن (burn) کیے جائیں گے۔12 * - `from` اور `to` کبھی بھی دونوں زیرو نہیں ہوتے۔13 *14 * ہکس کے بارے میں مزید جاننے کے لیے، xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks] پر جائیں۔ */15 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }16}سب دکھائیںیہ وہ ہک فنکشن ہے جسے منتقلی کے دوران کال کیا جانا ہے۔ یہ یہاں خالی ہے، لیکن اگر آپ کو اس سے کچھ کروانے کی ضرورت ہے تو آپ بس اسے اوور رائیڈ کر دیں۔
نتیجہ
جائزے کے لیے، اس کنٹریکٹ میں کچھ اہم ترین خیالات یہ ہیں (میری رائے میں، آپ کی رائے مختلف ہو سکتی ہے):
- بلاک چین پر کوئی راز نہیں ہوتے۔ کوئی بھی معلومات جس تک اسمارٹ کنٹریکٹ رسائی حاصل کر سکتا ہے وہ پوری دنیا کے لیے دستیاب ہے۔
- آپ اپنی ٹرانزیکشنز کی ترتیب کو کنٹرول کر سکتے ہیں، لیکن یہ نہیں کہ دوسرے لوگوں کی ٹرانزیکشنز کب ہوتی ہیں۔ یہی وجہ ہے کہ الاؤنس کو تبدیل کرنا خطرناک ہو سکتا ہے، کیونکہ یہ خرچ کرنے والے کو دونوں الاؤنسز کا مجموعہ خرچ کرنے دیتا ہے۔
uint256ٹائپ کی قدریں ریپ اراؤنڈ (wrap around) ہوتی ہیں۔ دوسرے لفظوں میں، 0-1=2^256-1۔ اگر یہ مطلوبہ رویہ نہیں ہے، تو آپ کو اسے چیک کرنا ہوگا (یا SafeMath لائبریری استعمال کریں جو آپ کے لیے یہ کرتی ہے)۔ نوٹ کریں کہ یہ Solidity 0.8.0 (opens in a new tab) میں تبدیل ہو گیا ہے۔- ایک مخصوص قسم کی تمام اسٹیٹ تبدیلیاں ایک مخصوص جگہ پر کریں، کیونکہ اس سے آڈیٹنگ آسان ہو جاتی ہے۔ یہی وجہ ہے کہ ہمارے پاس، مثال کے طور پر،
_approveہے، جسےapprove،transferFrom،increaseAllowance، اورdecreaseAllowanceکے ذریعے کال کیا جاتا ہے۔ - اسٹیٹ کی تبدیلیاں ایٹامک (atomic) ہونی چاہئیں، ان کے درمیان کسی اور کارروائی کے بغیر (جیسا کہ آپ
_transferمیں دیکھ سکتے ہیں)۔ اس کی وجہ یہ ہے کہ اسٹیٹ کی تبدیلی کے دوران آپ کے پاس ایک غیر مستقل (inconsistent) اسٹیٹ ہوتی ہے۔ مثال کے طور پر، اس وقت کے درمیان جب آپ بھیجنے والے کے بیلنس سے کٹوتی کرتے ہیں اور اس وقت کے درمیان جب آپ وصول کنندہ کے بیلنس میں اضافہ کرتے ہیں، وجود میں آنے والے ٹوکنز کی تعداد اس سے کم ہوتی ہے جتنی ہونی چاہیے۔ اگر ان کے درمیان آپریشنز ہوں، خاص طور پر کسی مختلف کنٹریکٹ کو کالز، تو اس کا ممکنہ طور پر غلط استعمال کیا جا سکتا ہے۔
اب جب کہ آپ نے دیکھ لیا ہے کہ OpenZeppelin ERC-20 کنٹریکٹ کیسے لکھا جاتا ہے، اور خاص طور پر اسے کیسے زیادہ محفوظ بنایا جاتا ہے، تو جائیں اور اپنے محفوظ کنٹریکٹس اور ایپلی کیشنز لکھیں۔
میرے مزید کام کے لیے یہاں دیکھیں (opens in a new tab)۔
صفحہ کی آخری اپ ڈیٹ: 22 اکتوبر، 2025
