Vyper ERC-721 کنٹریکٹ کا جائزہ
تعارف
ERC-721 اسٹینڈرڈ کو Non-Fungible Tokens (NFT) کی ملکیت رکھنے کے لیے استعمال کیا جاتا ہے۔ ERC-20 ٹوکنز ایک کموڈٹی (commodity) کے طور پر برتاؤ کرتے ہیں، کیونکہ انفرادی ٹوکنز کے درمیان کوئی فرق نہیں ہوتا۔ اس کے برعکس، ERC-721 ٹوکنز ایسے اثاثوں کے لیے بنائے گئے ہیں جو ملتے جلتے تو ہیں لیکن بالکل ایک جیسے نہیں، جیسے کہ مختلف بلیوں کے کارٹونز (opens in a new tab) یا رئیل اسٹیٹ کے مختلف حصوں کے ٹائٹلز۔
اس مضمون میں ہم Ryuya Nakamura کے ERC-721 کنٹریکٹ (opens in a new tab) کا تجزیہ کریں گے۔ یہ کنٹریکٹ Vyper (opens in a new tab) میں لکھا گیا ہے، جو کہ Python جیسی ایک کنٹریکٹ زبان ہے جسے اس طرح ڈیزائن کیا گیا ہے کہ اس میں غیر محفوظ کوڈ لکھنا Solidity کی نسبت زیادہ مشکل ہو۔
کنٹریکٹ
1# @dev ERC-721 نان فنجیبل ٹوکن (non-fungible token) سٹینڈرڈ کی امپلیمینٹیشن۔2# @author Ryuya Nakamura (@nrryuya)3# یہاں سے تبدیل کیا گیا: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vyVyper میں تبصرے (comments)، بالکل Python کی طرح، ہیش (#) سے شروع ہوتے ہیں اور لائن کے آخر تک جاری رہتے ہیں۔ وہ تبصرے جن میں @<keyword> شامل ہوتا ہے، انہیں NatSpec (opens in a new tab) انسانوں کے پڑھنے کے قابل دستاویزات (documentation) بنانے کے لیے استعمال کرتا ہے۔
1from vyper.interfaces import ERC72123implements: ERC721ERC-721 انٹرفیس Vyper زبان میں پہلے سے موجود (built-in) ہے۔ آپ کوڈ کی تعریف یہاں دیکھ سکتے ہیں (opens in a new tab)۔ انٹرفیس کی تعریف Vyper کے بجائے Python میں لکھی گئی ہے، کیونکہ انٹرفیسز نہ صرف بلاک چین کے اندر استعمال ہوتے ہیں، بلکہ بیرونی کلائنٹ سے بلاک چین کو ٹرانزیکشن بھیجتے وقت بھی استعمال ہوتے ہیں، جو کہ Python میں لکھا ہو سکتا ہے۔
پہلی لائن انٹرفیس کو امپورٹ کرتی ہے، اور دوسری یہ بتاتی ہے کہ ہم اسے یہاں نافذ (implement) کر رہے ہیں۔
ERC721Receiver انٹرفیس
1# safeTransferFrom() کے ذریعے کال کیے گئے کانٹریکٹ کا انٹرفیس2interface ERC721Receiver:3 def onERC721Received(ERC-721 دو قسم کی منتقلی (transfer) کو سپورٹ کرتا ہے:
transferFrom، جو بھیجنے والے کو کسی بھی منزل کا ایڈریس (destination address) بتانے کی اجازت دیتا ہے اور منتقلی کی ذمہ داری بھیجنے والے پر ڈالتا ہے۔ اس کا مطلب ہے کہ آپ کسی نامانوس (invalid) ایڈریس پر بھی منتقلی کر سکتے ہیں، جس صورت میں NFT ہمیشہ کے لیے ضائع ہو جاتا ہے۔safeTransferFrom، جو یہ چیک کرتا ہے کہ آیا منزل کا ایڈریس کوئی کنٹریکٹ ہے۔ اگر ایسا ہے، تو ERC-721 کنٹریکٹ وصول کرنے والے کنٹریکٹ سے پوچھتا ہے کہ کیا وہ NFT وصول کرنا چاہتا ہے۔
safeTransferFrom کی درخواستوں کا جواب دینے کے لیے وصول کرنے والے کنٹریکٹ کو ERC721Receiver نافذ کرنا پڑتا ہے۔
1 _operator: address,2 _from: address,_from ایڈریس ٹوکن کا موجودہ مالک ہے۔ _operator ایڈریس وہ ہے جس نے منتقلی کی درخواست کی ہے (یہ دونوں الاؤنسز کی وجہ سے ایک جیسے نہیں بھی ہو سکتے)۔
1 _tokenId: uint256,ERC-721 ٹوکن IDs 256 بٹس کے ہوتے ہیں۔ عام طور پر یہ اس چیز کی تفصیل کو ہیش (hash) کر کے بنائے جاتے ہیں جس کی ٹوکن نمائندگی کرتا ہے۔
1 _data: Bytes[1024]درخواست میں 1024 بائٹس تک کا صارف کا ڈیٹا ہو سکتا ہے۔
1 ) -> bytes32: viewایسے معاملات کو روکنے کے لیے جن میں کوئی کنٹریکٹ غلطی سے منتقلی کو قبول کر لے، واپسی کی قدر (return value) بولین (boolean) نہیں ہوتی، بلکہ ایک مخصوص قدر کے ساتھ 256 بٹس ہوتی ہے۔
یہ فنکشن ایک view ہے، جس کا مطلب ہے کہ یہ بلاک چین کی اسٹیٹ (state) کو پڑھ سکتا ہے، لیکن اسے تبدیل نہیں کر سکتا۔
ایونٹس
ایونٹس (opens in a new tab) کو بلاک چین سے باہر کے صارفین اور سرورز کو واقعات کی اطلاع دینے کے لیے خارج (emit) کیا جاتا ہے۔ یاد رکھیں کہ ایونٹس کا مواد بلاک چین پر موجود کنٹریکٹس کے لیے دستیاب نہیں ہوتا۔
1# @dev جب کسی بھی طریقے سے کسی NFT کی ملکیت تبدیل ہوتی ہے تو یہ ایمٹ (emit) ہوتا ہے۔ یہ ایونٹ تب ایمٹ ہوتا ہے جب NFTs2# بنائے جاتے ہیں (`from` == 0) اور ختم کیے جاتے ہیں (`to` == 0)۔ استثنیٰ: کانٹریکٹ بننے کے دوران، کوئی بھی3# تعداد میں NFTs بنائے اور تفویض کیے جا سکتے ہیں بغیر Transfer ایمٹ کیے۔ کسی بھی4# ٹرانسفر کے وقت، اس NFT کا منظور شدہ ایڈریس (اگر کوئی ہو) ری سیٹ ہو کر ختم (none) ہو جاتا ہے۔5# @param _from NFT بھیجنے والا (اگر ایڈریس زیرو ایڈریس ہے تو یہ ٹوکن بننے کی نشاندہی کرتا ہے)۔6# @param _to NFT وصول کرنے والا (اگر ایڈریس زیرو ایڈریس ہے تو یہ ٹوکن ختم ہونے کی نشاندہی کرتا ہے)۔7# @param _tokenId وہ NFT جو ٹرانسفر ہوا۔8event Transfer:9 sender: indexed(address)10 receiver: indexed(address)11 tokenId: indexed(uint256)سب دکھائیںیہ ERC-20 ٹرانسفر ایونٹ سے ملتا جلتا ہے، سوائے اس کے کہ ہم رقم کے بجائے tokenId رپورٹ کرتے ہیں۔ ایڈریس صفر (zero address) کا کوئی مالک نہیں ہوتا، اس لیے روایت کے مطابق ہم اسے ٹوکنز کی تخلیق اور تباہی کی رپورٹ دینے کے لیے استعمال کرتے ہیں۔
1# @dev یہ تب ایمٹ ہوتا ہے جب کسی NFT کا منظور شدہ ایڈریس تبدیل یا دوبارہ تصدیق کیا جاتا ہے۔ زیرو2# ایڈریس ظاہر کرتا ہے کہ کوئی منظور شدہ ایڈریس نہیں ہے۔ جب کوئی Transfer ایونٹ ایمٹ ہوتا ہے، تو یہ بھی3# ظاہر کرتا ہے کہ اس NFT کا منظور شدہ ایڈریس (اگر کوئی ہو) ری سیٹ ہو کر ختم (none) ہو گیا ہے۔4# @param _owner NFT کا مالک۔5# @param _approved وہ ایڈریس جسے ہم منظور کر رہے ہیں۔6# @param _tokenId وہ NFT جسے ہم منظور کر رہے ہیں۔7event Approval:8 owner: indexed(address)9 approved: indexed(address)10 tokenId: indexed(uint256)سب دکھائیںERC-721 کی منظوری (approval) ERC-20 الاؤنس کی طرح ہے۔ ایک مخصوص ایڈریس کو ایک مخصوص ٹوکن منتقل کرنے کی اجازت دی جاتی ہے۔ یہ کنٹریکٹس کو ٹوکن قبول کرنے پر ردعمل ظاہر کرنے کا طریقہ کار فراہم کرتا ہے۔ کنٹریکٹس ایونٹس کو نہیں سن سکتے، اس لیے اگر آپ صرف ٹوکن انہیں منتقل کر دیں تو انہیں اس کے بارے میں "پتہ" نہیں چلتا۔ اس طرح مالک پہلے منظوری جمع کراتا ہے اور پھر کنٹریکٹ کو درخواست بھیجتا ہے: "میں نے آپ کو ٹوکن X منتقل کرنے کی منظوری دے دی ہے، براہ کرم ..."۔
یہ ایک ڈیزائن کا انتخاب ہے تاکہ ERC-721 اسٹینڈرڈ کو ERC-20 اسٹینڈرڈ کے مشابہ بنایا جا سکے۔ چونکہ ERC-721 ٹوکنز fungible نہیں ہوتے، اس لیے ایک کنٹریکٹ ٹوکن کی ملکیت کو دیکھ کر یہ بھی پہچان سکتا ہے کہ اسے ایک مخصوص ٹوکن ملا ہے۔
1# @dev یہ تب ایمٹ ہوتا ہے جب کسی مالک کے لیے کوئی آپریٹر فعال (enable) یا غیر فعال (disable) کیا جاتا ہے۔ آپریٹر مالک کے2# تمام NFTs کو مینج کر سکتا ہے۔3# @param _owner NFT کا مالک۔4# @param _operator وہ ایڈریس جسے ہم آپریٹر کے حقوق دے رہے ہیں۔5# @param _approved آپریٹر کے حقوق کا سٹیٹس (اگر حقوق دیے گئے ہیں تو true اور اگر6# منسوخ کیے گئے ہیں تو false)۔7event ApprovalForAll:8 owner: indexed(address)9 operator: indexed(address)10 approved: boolسب دکھائیںبعض اوقات ایک آپریٹر (operator) کا ہونا مفید ہوتا ہے جو کسی اکاؤنٹ کے ایک مخصوص قسم کے تمام ٹوکنز (وہ جو کسی مخصوص کنٹریکٹ کے زیر انتظام ہوں) کا انتظام کر سکے، بالکل پاور آف اٹارنی کی طرح۔ مثال کے طور پر، میں شاید ایسی طاقت کسی ایسے کنٹریکٹ کو دینا چاہوں جو یہ چیک کرے کہ آیا میں نے چھ ماہ سے اس سے رابطہ نہیں کیا ہے، اور اگر ایسا ہے تو میرے اثاثے میرے ورثاء میں تقسیم کر دے (اگر ان میں سے کوئی اس کی درخواست کرے، کنٹریکٹس ٹرانزیکشن کے ذریعے کال کیے بغیر کچھ نہیں کر سکتے)۔ ERC-20 میں ہم وراثت کے کنٹریکٹ کو صرف ایک بڑا الاؤنس دے سکتے ہیں، لیکن یہ ERC-721 کے لیے کام نہیں کرتا کیونکہ ٹوکنز fungible نہیں ہوتے۔ یہ اسی کا متبادل ہے۔
approved کی قدر ہمیں بتاتی ہے کہ آیا ایونٹ منظوری کے لیے ہے، یا منظوری واپس لینے کے لیے۔
اسٹیٹ ویری ایبلز
یہ ویری ایبلز ٹوکنز کی موجودہ اسٹیٹ پر مشتمل ہوتے ہیں: کون سے دستیاب ہیں اور ان کا مالک کون ہے۔ ان میں سے زیادہ تر HashMap آبجیکٹس ہیں، یک طرفہ میپنگز جو دو اقسام کے درمیان موجود ہوتی ہیں (opens in a new tab)۔
1# @dev NFT ID سے اس کے مالک کے ایڈریس تک کی میپنگ (Mapping)۔2idToOwner: HashMap[uint256, address]34# @dev NFT ID سے منظور شدہ ایڈریس تک کی میپنگ۔5idToApprovals: HashMap[uint256, address]ایتھریم میں صارف اور کنٹریکٹ کی شناخت 160-بٹ ایڈریسز سے ظاہر کی جاتی ہے۔ یہ دو ویری ایبلز ٹوکن IDs سے ان کے مالکان اور انہیں منتقل کرنے کی منظوری پانے والوں (ہر ایک کے لیے زیادہ سے زیادہ ایک) تک میپ کرتے ہیں۔ ایتھریم میں، غیر شروع شدہ (uninitialized) ڈیٹا ہمیشہ صفر ہوتا ہے، لہذا اگر کوئی مالک یا منظور شدہ ٹرانسفرر نہیں ہے تو اس ٹوکن کی قدر صفر ہوتی ہے۔
1# @dev مالک کے ایڈریس سے اس کے ٹوکنز کی تعداد تک کی میپنگ۔2ownerToNFTokenCount: HashMap[address, uint256]یہ ویری ایبل ہر مالک کے لیے ٹوکنز کی گنتی رکھتا ہے۔ مالکان سے ٹوکنز تک کوئی میپنگ نہیں ہے، اس لیے کسی مخصوص مالک کے ٹوکنز کی شناخت کرنے کا واحد طریقہ یہ ہے کہ بلاک چین کی ایونٹ ہسٹری میں پیچھے مڑ کر دیکھیں اور مناسب Transfer ایونٹس تلاش کریں۔ ہم اس ویری ایبل کا استعمال یہ جاننے کے لیے کر سکتے ہیں کہ ہمارے پاس تمام NFTs کب آ گئے ہیں اور ہمیں مزید پیچھے دیکھنے کی ضرورت نہیں ہے۔
یاد رکھیں کہ یہ الگورتھم صرف یوزر انٹرفیسز اور بیرونی سرورز کے لیے کام کرتا ہے۔ خود بلاک چین پر چلنے والا کوڈ ماضی کے ایونٹس کو نہیں پڑھ سکتا۔
1# @dev مالک کے ایڈریس سے آپریٹر ایڈریسز کی میپنگ تک کی میپنگ۔2ownerToOperators: HashMap[address, HashMap[address, bool]]ایک اکاؤنٹ کے ایک سے زیادہ آپریٹرز ہو سکتے ہیں۔ ان کا ٹریک رکھنے کے لیے ایک سادہ HashMap ناکافی ہے، کیونکہ ہر کلید (key) ایک ہی قدر کی طرف لے جاتی ہے۔ اس کے بجائے، آپ قدر کے طور پر HashMap[address, bool] استعمال کر سکتے ہیں۔ پہلے سے طے شدہ (default) طور پر ہر ایڈریس کی قدر False ہوتی ہے، جس کا مطلب ہے کہ یہ آپریٹر نہیں ہے۔ آپ ضرورت کے مطابق اقدار کو True پر سیٹ کر سکتے ہیں۔
1# @dev منٹر (minter) کا ایڈریس، جو ٹوکن منٹ (mint) کر سکتا ہے2minter: addressنئے ٹوکنز کو کسی نہ کسی طرح بنانا پڑتا ہے۔ اس کنٹریکٹ میں صرف ایک ہی ہستی کو ایسا کرنے کی اجازت ہے، جو کہ minter ہے۔ مثال کے طور پر، یہ کسی گیم کے لیے کافی ہو سکتا ہے۔ دیگر مقاصد کے لیے، زیادہ پیچیدہ بزنس لاجک بنانا ضروری ہو سکتا ہے۔
1# @dev انٹرفیس آئی ڈی سے بولین (bool) تک کی میپنگ کہ آیا یہ سپورٹڈ ہے یا نہیں2supportedInterfaces: HashMap[bytes32, bool]34# @dev ERC165 کی ERC165 انٹرفیس آئی ڈی5ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a767# @dev ERC721 کی ERC165 انٹرفیس آئی ڈی8ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cdERC-165 (opens in a new tab) ایک کنٹریکٹ کے لیے یہ ظاہر کرنے کا طریقہ کار بتاتا ہے کہ ایپلی کیشنز اس کے ساتھ کیسے بات چیت کر سکتی ہیں، اور یہ کن ERCs کی تعمیل کرتا ہے۔ اس صورت میں، کنٹریکٹ ERC-165 اور ERC-721 کی تعمیل کرتا ہے۔
فنکشنز
یہ وہ فنکشنز ہیں جو دراصل ERC-721 کو نافذ کرتے ہیں۔
کنسٹرکٹر
1@external2def __init__():Vyper میں، Python کی طرح، کنسٹرکٹر فنکشن کو __init__ کہا جاتا ہے۔
1 # @dev Contract constructor.Python اور Vyper میں، آپ ایک ملٹی لائن سٹرنگ (جو """ سے شروع اور ختم ہوتی ہے) بتا کر بھی ایک تبصرہ بنا سکتے ہیں، اور اسے کسی بھی طرح استعمال نہیں کر سکتے۔ ان تبصروں میں NatSpec (opens in a new tab) بھی شامل ہو سکتا ہے۔
1 self.supportedInterfaces[ERC165_INTERFACE_ID] = True2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True3 self.minter = msg.senderاسٹیٹ ویری ایبلز تک رسائی حاصل کرنے کے لیے آپ self.<variable name> استعمال کرتے ہیں (دوبارہ، بالکل Python کی طرح)۔
ویو فنکشنز
یہ وہ فنکشنز ہیں جو بلاک چین کی اسٹیٹ کو تبدیل نہیں کرتے، اور اس لیے اگر انہیں بیرونی طور پر کال کیا جائے تو انہیں مفت میں چلایا جا سکتا ہے۔ اگر ویو فنکشنز کو کسی کنٹریکٹ کے ذریعے کال کیا جائے تو انہیں پھر بھی ہر نوڈ پر چلانا پڑتا ہے اور اس لیے گیس خرچ ہوتی ہے۔
1@view2@externalفنکشن کی تعریف سے پہلے یہ کلیدی الفاظ (keywords) جو ایٹ سائن (@) سے شروع ہوتے ہیں، انہیں ڈیکوریشنز (decorations) کہا جاتا ہے۔ یہ ان حالات کی وضاحت کرتے ہیں جن میں کسی فنکشن کو کال کیا جا سکتا ہے۔
@viewبتاتا ہے کہ یہ فنکشن ایک ویو (view) ہے۔@externalبتاتا ہے کہ اس مخصوص فنکشن کو ٹرانزیکشنز اور دیگر کنٹریکٹس کے ذریعے کال کیا جا سکتا ہے۔
1def supportsInterface(_interfaceID: bytes32) -> bool:Python کے برعکس، Vyper ایک اسٹیٹک ٹائپڈ زبان (static typed language) (opens in a new tab) ہے۔ آپ ڈیٹا ٹائپ (opens in a new tab) کی شناخت کیے بغیر کسی ویری ایبل، یا فنکشن پیرامیٹر کا اعلان نہیں کر سکتے۔ اس صورت میں ان پٹ پیرامیٹر bytes32 ہے، جو کہ 256-بٹ کی قدر ہے (256 بٹس Ethereum Virtual Machine کا مقامی ورڈ سائز ہے)۔ آؤٹ پٹ ایک بولین قدر ہے۔ روایت کے مطابق، فنکشن پیرامیٹرز کے نام انڈرسکور (_) سے شروع ہوتے ہیں۔
1 # @dev انٹرفیس کی شناخت ERC-165 میں بیان کی گئی ہے۔2 @param _interfaceID انٹرفیس کی آئی ڈی3 return self.supportedInterfaces[_interfaceID]self.supportedInterfaces HashMap سے قدر واپس کریں، جو کنسٹرکٹر (__init__) میں سیٹ کی گئی ہے۔
1# ## ویو فنکشنز (VIEW FUNCTIONS) ###یہ وہ ویو فنکشنز ہیں جو ٹوکنز کے بارے میں معلومات صارفین اور دیگر کنٹریکٹس کو دستیاب کراتے ہیں۔
1@view2@external3def balanceOf(_owner: address) -> uint256:4 # @dev `_owner` کی ملکیت میں موجود NFTs کی تعداد واپس کرتا ہے۔5 اگر `_owner` زیرو ایڈریس ہو تو ایرر تھرو کرتا ہے۔ زیرو ایڈریس کو تفویض کردہ NFTs کو نامعتبر سمجھا جاتا ہے۔6 @param _owner وہ ایڈریس جس کا بیلنس معلوم کرنا ہے۔7 assert _owner != ZERO_ADDRESSیہ لائن یقینی بناتی ہے (asserts) (opens in a new tab) کہ _owner صفر نہیں ہے۔ اگر ایسا ہے، تو ایک خرابی (error) ہوتی ہے اور آپریشن کو ریورٹ (revert) کر دیا جاتا ہے۔
1 return self.ownerToNFTokenCount[_owner]23@view4@external5def ownerOf(_tokenId: uint256) -> address:6 # @dev NFT کے مالک کا ایڈریس واپس کرتا ہے۔7 اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے۔8 @param _tokenId NFT کا شناخت کنندہ (identifier)۔9 owner: address = self.idToOwner[_tokenId]10 # اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے11 assert owner != ZERO_ADDRESS12 return ownerسب دکھائیںEthereum Virtual Machine (EVM) میں کوئی بھی اسٹوریج جس میں کوئی قدر محفوظ نہ ہو، وہ صفر ہوتی ہے۔ اگر _tokenId پر کوئی ٹوکن نہیں ہے تو self.idToOwner[_tokenId] کی قدر صفر ہوتی ہے۔ اس صورت میں فنکشن ریورٹ ہو جاتا ہے۔
1@view2@external3def getApproved(_tokenId: uint256) -> address:4 # @dev کسی ایک NFT کا منظور شدہ ایڈریس حاصل کریں۔5 اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے۔6 @param _tokenId اس NFT کی آئی ڈی جس کی منظوری معلوم کرنی ہے۔7 # اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے8 assert self.idToOwner[_tokenId] != ZERO_ADDRESS9 return self.idToApprovals[_tokenId]سب دکھائیںیاد رکھیں کہ getApproved صفر واپس کر سکتا ہے۔ اگر ٹوکن درست ہے تو یہ self.idToApprovals[_tokenId] واپس کرتا ہے۔ اگر کوئی منظوری دینے والا (approver) نہیں ہے تو وہ قدر صفر ہوتی ہے۔
1@view2@external3def isApprovedForAll(_owner: address, _operator: address) -> bool:4 # @dev چیک کرتا ہے کہ کیا `_operator`، `_owner` کے لیے ایک منظور شدہ آپریٹر ہے۔5 @param _owner وہ ایڈریس جو NFTs کا مالک ہے۔6 @param _operator وہ ایڈریس جو مالک کی طرف سے کام کرتا ہے۔7 return (self.ownerToOperators[_owner])[_operator]یہ فنکشن چیک کرتا ہے کہ آیا _operator کو اس کنٹریکٹ میں _owner کے تمام ٹوکنز کا انتظام کرنے کی اجازت ہے۔ چونکہ ایک سے زیادہ آپریٹرز ہو سکتے ہیں، اس لیے یہ دو سطح کا HashMap ہے۔
ٹرانسفر ہیلپر فنکشنز
یہ فنکشنز ان آپریشنز کو نافذ کرتے ہیں جو ٹوکنز کی منتقلی یا انتظام کا حصہ ہیں۔
12# ## ٹرانسفر فنکشن ہیلپرز (TRANSFER FUNCTION HELPERS) ###34@view5@internalاس ڈیکوریشن، @internal، کا مطلب ہے کہ فنکشن صرف اسی کنٹریکٹ کے اندر موجود دیگر فنکشنز سے قابل رسائی ہے۔ روایت کے مطابق، ان فنکشنز کے نام بھی انڈرسکور (_) سے شروع ہوتے ہیں۔
1def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool:2 # @dev بتاتا ہے کہ آیا دیا گیا سپینڈر (spender) دی گئی ٹوکن آئی ڈی کو ٹرانسفر کر سکتا ہے یا نہیں3 @param spender سپینڈر کا ایڈریس جس کے بارے میں معلوم کرنا ہے4 @param tokenId ٹرانسفر کیے جانے والے ٹوکن کی uint256 آئی ڈی5 @return bool کیا msg.sender دی گئی ٹوکن آئی ڈی کے لیے منظور شدہ ہے،6 مالک کا آپریٹر ہے، یا ٹوکن کا مالک ہے7 owner: address = self.idToOwner[_tokenId]8 spenderIsOwner: bool = owner == _spender9 spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId]10 spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender]11 return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAllسب دکھائیںتین طریقے ہیں جن سے کسی ایڈریس کو ٹوکن منتقل کرنے کی اجازت دی جا سکتی ہے:
- ایڈریس ٹوکن کا مالک ہے
- ایڈریس کو وہ ٹوکن خرچ کرنے کی منظوری دی گئی ہے
- ایڈریس ٹوکن کے مالک کا آپریٹر ہے
اوپر دیا گیا فنکشن ایک ویو ہو سکتا ہے کیونکہ یہ اسٹیٹ کو تبدیل نہیں کرتا۔ آپریٹنگ اخراجات کو کم کرنے کے لیے، کوئی بھی فنکشن جو ویو ہو سکتا ہے اسے ویو ہونا چاہیے۔
1@internal2def _addTokenTo(_to: address, _tokenId: uint256):3 # @dev دیے گئے ایڈریس میں ایک NFT شامل کریں4 اگر `_tokenId` پہلے سے کسی کی ملکیت ہو تو ایرر تھرو کرتا ہے۔5 # اگر `_tokenId` پہلے سے کسی کی ملکیت ہو تو ایرر تھرو کرتا ہے6 assert self.idToOwner[_tokenId] == ZERO_ADDRESS7 # مالک کو تبدیل کریں8 self.idToOwner[_tokenId] = _to9 # کاؤنٹ ٹریکنگ کو تبدیل کریں10 self.ownerToNFTokenCount[_to] += 1111213@internal14def _removeTokenFrom(_from: address, _tokenId: uint256):15 # @dev دیے گئے ایڈریس سے ایک NFT ہٹائیں16 اگر `_from` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے۔17 # اگر `_from` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے18 assert self.idToOwner[_tokenId] == _from19 # مالک کو تبدیل کریں20 self.idToOwner[_tokenId] = ZERO_ADDRESS21 # کاؤنٹ ٹریکنگ کو تبدیل کریں22 self.ownerToNFTokenCount[_from] -= 1سب دکھائیںجب منتقلی میں کوئی مسئلہ ہوتا ہے تو ہم کال کو ریورٹ کر دیتے ہیں۔
1@internal2def _clearApproval(_owner: address, _tokenId: uint256):3 # @dev دیے گئے ایڈریس کی منظوری (approval) کو کلیئر کریں4 اگر `_owner` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے۔5 # اگر `_owner` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے6 assert self.idToOwner[_tokenId] == _owner7 if self.idToApprovals[_tokenId] != ZERO_ADDRESS:8 # منظوریوں (approvals) کو ری سیٹ کریں9 self.idToApprovals[_tokenId] = ZERO_ADDRESSسب دکھائیںقدر کو صرف اسی صورت میں تبدیل کریں جب ضروری ہو۔ اسٹیٹ ویری ایبلز اسٹوریج میں رہتے ہیں۔ اسٹوریج میں لکھنا EVM (Ethereum Virtual Machine) کے سب سے مہنگے آپریشنز میں سے ایک ہے (گیس کے لحاظ سے)۔ اس لیے، اسے کم سے کم کرنا ایک اچھا خیال ہے، یہاں تک کہ موجودہ قدر کو لکھنے کی بھی زیادہ قیمت ہوتی ہے۔
1@internal2def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address):3 # @dev NFT کا ٹرانسفر انجام دیں۔4 ایرر تھرو کرتا ہے جب تک کہ `msg.sender` موجودہ مالک، ایک مجاز آپریٹر، یا اس NFT کے لیے منظور شدہ5 ایڈریس نہ ہو۔ (نوٹ: پرائیویٹ فنکشن میں `msg.sender` کی اجازت نہیں ہے اس لیے `_sender` پاس کریں۔)6 اگر `_to` زیرو ایڈریس ہو تو ایرر تھرو کرتا ہے۔7 اگر `_from` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے۔8 اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے۔ہمارے پاس یہ اندرونی (internal) فنکشن اس لیے ہے کیونکہ ٹوکنز منتقل کرنے کے دو طریقے ہیں (باقاعدہ اور محفوظ)، لیکن ہم کوڈ میں صرف ایک ہی جگہ چاہتے ہیں جہاں ہم یہ کریں تاکہ آڈیٹنگ آسان ہو سکے۔
1 # ضروریات چیک کریں2 assert self._isApprovedOrOwner(_sender, _tokenId)3 # اگر `_to` زیرو ایڈریس ہو تو ایرر تھرو کرتا ہے4 assert _to != ZERO_ADDRESS5 # منظوری کلیئر کریں۔ اگر `_from` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے6 self._clearApproval(_from, _tokenId)7 # NFT ہٹائیں۔ اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے8 self._removeTokenFrom(_from, _tokenId)9 # NFT شامل کریں10 self._addTokenTo(_to, _tokenId)11 # ٹرانسفر کو لاگ (Log) کریں12 log Transfer(_from, _to, _tokenId)سب دکھائیںVyper میں ایونٹ خارج کرنے کے لیے آپ log اسٹیٹمنٹ استعمال کرتے ہیں (مزید تفصیلات کے لیے یہاں دیکھیں (opens in a new tab))۔
ٹرانسفر فنکشنز
12# ## ٹرانسفر فنکشنز (TRANSFER FUNCTIONS) ###34@external5def transferFrom(_from: address, _to: address, _tokenId: uint256):6 # @dev ایرر تھرو کرتا ہے جب تک کہ `msg.sender` موجودہ مالک، ایک مجاز آپریٹر، یا اس NFT کے لیے منظور شدہ7 ایڈریس نہ ہو۔8 اگر `_from` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے۔9 اگر `_to` زیرو ایڈریس ہو تو ایرر تھرو کرتا ہے۔10 اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے۔11 @notice کال کرنے والے کی ذمہ داری ہے کہ وہ تصدیق کرے کہ `_to` NFTs وصول کرنے کی صلاحیت رکھتا ہے ورنہ12 وہ ہمیشہ کے لیے ضائع ہو سکتے ہیں۔13 @param _from NFT کا موجودہ مالک۔14 @param _to نیا مالک۔15 @param _tokenId ٹرانسفر کیا جانے والا NFT۔16 self._transferFrom(_from, _to, _tokenId, msg.sender)سب دکھائیںیہ فنکشن آپ کو کسی بھی من مانے (arbitrary) ایڈریس پر منتقل کرنے کی اجازت دیتا ہے۔ جب تک کہ ایڈریس کوئی صارف نہ ہو، یا کوئی ایسا کنٹریکٹ نہ ہو جو ٹوکنز منتقل کرنا جانتا ہو، آپ کا منتقل کردہ کوئی بھی ٹوکن اس ایڈریس میں پھنس جائے گا اور بیکار ہو جائے گا۔
1@external2def safeTransferFrom(3 _from: address,4 _to: address,5 _tokenId: uint256,6 _data: Bytes[1024]=b""7 ):8 # @dev ایک NFT کی ملکیت کو ایک ایڈریس سے دوسرے ایڈریس پر ٹرانسفر کرتا ہے۔9 ایرر تھرو کرتا ہے جب تک کہ `msg.sender` موجودہ مالک، ایک مجاز آپریٹر، یا اس NFT کے لیے10 منظور شدہ ایڈریس نہ ہو۔11 اگر `_from` موجودہ مالک نہ ہو تو ایرر تھرو کرتا ہے۔12 اگر `_to` زیرو ایڈریس ہو تو ایرر تھرو کرتا ہے۔13 اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے۔14 اگر `_to` ایک سمارٹ کانٹریکٹ ہے، تو یہ `_to` پر `onERC721Received` کو کال کرتا ہے اور اگر15 ریٹرن ویلیو `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` نہ ہو تو ایرر تھرو کرتا ہے۔16 نوٹ: bytes4 کو پیڈنگ کے ساتھ bytes32 سے ظاہر کیا جاتا ہے17 @param _from NFT کا موجودہ مالک۔18 @param _to نیا مالک۔19 @param _tokenId ٹرانسفر کیا جانے والا NFT۔20 @param _data بغیر کسی مخصوص فارمیٹ کے اضافی ڈیٹا، جو `_to` کی کال میں بھیجا جاتا ہے۔21 self._transferFrom(_from, _to, _tokenId, msg.sender)سب دکھائیںپہلے منتقلی کرنا ٹھیک ہے کیونکہ اگر کوئی مسئلہ ہوا تو ہم ویسے بھی ریورٹ کرنے والے ہیں، اس لیے کال میں کیا گیا ہر کام منسوخ ہو جائے گا۔
1 if _to.is_contract: # چیک کریں کہ کیا `_to` ایک کانٹریکٹ ایڈریس ہےپہلے یہ چیک کریں کہ آیا ایڈریس کوئی کنٹریکٹ ہے (اگر اس میں کوڈ ہے)۔ اگر نہیں، تو فرض کریں کہ یہ صارف کا ایڈریس ہے اور صارف ٹوکن استعمال کرنے یا اسے منتقل کرنے کے قابل ہو گا۔ لیکن اسے آپ کو تحفظ کے جھوٹے احساس میں مبتلا نہ ہونے دیں۔ آپ ٹوکنز کھو سکتے ہیں، یہاں تک کہ safeTransferFrom کے ساتھ بھی، اگر آپ انہیں کسی ایسے ایڈریس پر منتقل کرتے ہیں جس کی پرائیویٹ کی (private key) کوئی نہیں جانتا۔
1 returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)ٹارگٹ کنٹریکٹ کو کال کریں تاکہ یہ دیکھا جا سکے کہ آیا وہ ERC-721 ٹوکنز وصول کر سکتا ہے۔
1 # اگر ٹرانسفر کی منزل ایک ایسا کانٹریکٹ ہے جو 'onERC721Received' کو امپلیمنٹ نہیں کرتا تو ایرر تھرو کرتا ہے2 assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32)اگر منزل کوئی کنٹریکٹ ہے، لیکن ایسا جو ERC-721 ٹوکنز قبول نہیں کرتا (یا جس نے اس مخصوص منتقلی کو قبول نہ کرنے کا فیصلہ کیا ہے)، تو ریورٹ کریں۔
1@external2def approve(_approved: address, _tokenId: uint256):3 # @dev کسی NFT کے لیے منظور شدہ ایڈریس سیٹ کریں یا دوبارہ تصدیق کریں۔ زیرو ایڈریس ظاہر کرتا ہے کہ کوئی منظور شدہ ایڈریس نہیں ہے۔4 ایرر تھرو کرتا ہے جب تک کہ `msg.sender` موجودہ NFT کا مالک، یا موجودہ مالک کا کوئی مجاز آپریٹر نہ ہو۔5 اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے۔ (نوٹ: یہ EIP میں نہیں لکھا گیا)6 اگر `_approved` موجودہ مالک ہو تو ایرر تھرو کرتا ہے۔ (نوٹ: یہ EIP میں نہیں لکھا گیا)7 @param _approved دی گئی NFT آئی ڈی کے لیے منظور کیا جانے والا ایڈریس۔8 @param _tokenId منظور کیے جانے والے ٹوکن کی آئی ڈی۔9 owner: address = self.idToOwner[_tokenId]10 # اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے11 assert owner != ZERO_ADDRESS12 # اگر `_approved` موجودہ مالک ہو تو ایرر تھرو کرتا ہے13 assert _approved != ownerسب دکھائیںروایت کے مطابق اگر آپ نہیں چاہتے کہ کوئی منظوری دینے والا ہو تو آپ صفر ایڈریس (zero address) مقرر کرتے ہیں، خود کو نہیں۔
1 # ضروریات چیک کریں2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender3 senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]4 assert (senderIsOwner or senderIsApprovedForAll)منظوری سیٹ کرنے کے لیے آپ یا تو مالک ہو سکتے ہیں، یا مالک کی طرف سے مجاز آپریٹر۔
1 # منظوری (approval) سیٹ کریں2 self.idToApprovals[_tokenId] = _approved3 log Approval(owner, _approved, _tokenId)456@external7def setApprovalForAll(_operator: address, _approved: bool):8 # @dev کسی تیسرے فریق ("آپریٹر") کے لیے `msg.sender` کے تمام اثاثوں کو مینج کرنے کی منظوری کو فعال یا غیر فعال کرتا ہے۔9 یہ ApprovalForAll ایونٹ بھی ایمٹ کرتا ہے۔10 اگر `_operator` ہی `msg.sender` ہو تو ایرر تھرو کرتا ہے۔ (نوٹ: یہ EIP میں نہیں لکھا گیا)11 @notice یہ تب بھی کام کرتا ہے جب بھیجنے والے کے پاس اس وقت کوئی ٹوکن نہ ہو۔12 @param _operator مجاز آپریٹرز کے سیٹ میں شامل کرنے کے لیے ایڈریس۔13 @param _approved اگر آپریٹرز منظور شدہ ہیں تو True، منظوری منسوخ کرنے کے لیے false۔14 # اگر `_operator` ہی `msg.sender` ہو تو ایرر تھرو کرتا ہے15 assert _operator != msg.sender16 self.ownerToOperators[msg.sender][_operator] = _approved17 log ApprovalForAll(msg.sender, _operator, _approved)سب دکھائیںنئے ٹوکنز منٹ کریں اور موجودہ کو تباہ کریں
وہ اکاؤنٹ جس نے کنٹریکٹ بنایا ہے وہ minter ہے، وہ سپر یوزر جو نئے NFTs منٹ (mint) کرنے کا مجاز ہے۔ تاہم، اسے بھی موجودہ ٹوکنز کو برن (burn) کرنے کی اجازت نہیں ہے۔ صرف مالک، یا مالک کی طرف سے مجاز کوئی ہستی، ایسا کر سکتی ہے۔
1# ## منٹ اور برن فنکشنز (MINT & BURN FUNCTIONS) ###23@external4def mint(_to: address, _tokenId: uint256) -> bool:یہ فنکشن ہمیشہ True واپس کرتا ہے، کیونکہ اگر آپریشن ناکام ہو جاتا ہے تو اسے ریورٹ کر دیا جاتا ہے۔
1 # @dev ٹوکنز منٹ (mint) کرنے کا فنکشن2 اگر `msg.sender` منٹر (minter) نہ ہو تو ایرر تھرو کرتا ہے۔3 اگر `_to` زیرو ایڈریس ہو تو ایرر تھرو کرتا ہے۔4 اگر `_tokenId` پہلے سے کسی کی ملکیت ہو تو ایرر تھرو کرتا ہے۔5 @param _to وہ ایڈریس جو منٹ کیے گئے ٹوکنز وصول کرے گا۔6 @param _tokenId منٹ کی جانے والی ٹوکن آئی ڈی۔7 @return ایک بولین (boolean) جو ظاہر کرتا ہے کہ آیا آپریشن کامیاب رہا۔8 # اگر `msg.sender` منٹر (minter) نہ ہو تو ایرر تھرو کرتا ہے9 assert msg.sender == self.minterسب دکھائیںصرف منٹر (وہ اکاؤنٹ جس نے ERC-721 کنٹریکٹ بنایا) نئے ٹوکنز منٹ کر سکتا ہے۔ مستقبل میں یہ ایک مسئلہ ہو سکتا ہے اگر ہم منٹر کی شناخت تبدیل کرنا چاہیں۔ پروڈکشن کنٹریکٹ میں آپ شاید ایک ایسا فنکشن چاہیں گے جو منٹر کو منٹر کے حقوق کسی اور کو منتقل کرنے کی اجازت دے۔
1 # اگر `_to` زیرو ایڈریس ہو تو ایرر تھرو کرتا ہے2 assert _to != ZERO_ADDRESS3 # NFT شامل کریں۔ اگر `_tokenId` پہلے سے کسی کی ملکیت ہو تو ایرر تھرو کرتا ہے4 self._addTokenTo(_to, _tokenId)5 log Transfer(ZERO_ADDRESS, _to, _tokenId)6 return Trueروایت کے مطابق، نئے ٹوکنز کی منٹنگ کو ایڈریس صفر سے منتقلی کے طور پر شمار کیا جاتا ہے۔
12@external3def burn(_tokenId: uint256):4 # @dev ایک مخصوص ERC721 ٹوکن کو برن (burn) کرتا ہے۔5 ایرر تھرو کرتا ہے جب تک کہ `msg.sender` موجودہ مالک، ایک مجاز آپریٹر، یا اس NFT کے لیے منظور شدہ6 ایڈریس نہ ہو۔7 اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے۔8 @param _tokenId برن کیے جانے والے ERC721 ٹوکن کی uint256 آئی ڈی۔9 # ضروریات چیک کریں10 assert self._isApprovedOrOwner(msg.sender, _tokenId)11 owner: address = self.idToOwner[_tokenId]12 # اگر `_tokenId` ایک معتبر NFT نہ ہو تو ایرر تھرو کرتا ہے13 assert owner != ZERO_ADDRESS14 self._clearApproval(owner, _tokenId)15 self._removeTokenFrom(owner, _tokenId)16 log Transfer(owner, ZERO_ADDRESS, _tokenId)سب دکھائیںکوئی بھی شخص جسے ٹوکن منتقل کرنے کی اجازت ہے اسے برن کرنے کی بھی اجازت ہے۔ اگرچہ برن کرنا صفر ایڈریس پر منتقل کرنے کے مترادف لگتا ہے، لیکن صفر ایڈریس دراصل ٹوکن وصول نہیں کرتا۔ یہ ہمیں اس تمام اسٹوریج کو خالی کرنے کی اجازت دیتا ہے جو ٹوکن کے لیے استعمال کی گئی تھی، جس سے ٹرانزیکشن کی گیس کی قیمت کم ہو سکتی ہے۔
اس کنٹریکٹ کا استعمال
Solidity کے برعکس، Vyper میں وراثت (inheritance) نہیں ہوتی۔ یہ ایک دانستہ ڈیزائن کا انتخاب ہے تاکہ کوڈ کو واضح اور اس لیے محفوظ بنانے میں آسانی ہو۔ لہذا اپنا Vyper ERC-721 کنٹریکٹ بنانے کے لیے آپ یہ کنٹریکٹ (opens in a new tab) لیتے ہیں اور اپنی مطلوبہ بزنس لاجک کو نافذ کرنے کے لیے اس میں ترمیم کرتے ہیں۔
نتیجہ
جائزے کے لیے، اس کنٹریکٹ میں کچھ اہم ترین خیالات یہ ہیں:
- محفوظ منتقلی کے ساتھ ERC-721 ٹوکنز وصول کرنے کے لیے، کنٹریکٹس کو
ERC721Receiverانٹرفیس نافذ کرنا پڑتا ہے۔ - یہاں تک کہ اگر آپ محفوظ منتقلی کا استعمال کرتے ہیں، تب بھی ٹوکنز پھنس سکتے ہیں اگر آپ انہیں کسی ایسے ایڈریس پر بھیجتے ہیں جس کی پرائیویٹ کی نامعلوم ہو۔
- جب کسی آپریشن میں کوئی مسئلہ ہو تو کال کو
revertکرنا ایک اچھا خیال ہے، بجائے اس کے کہ صرف ناکامی کی قدر واپس کی جائے۔ - ERC-721 ٹوکنز اس وقت موجود ہوتے ہیں جب ان کا کوئی مالک ہو۔
- NFT منتقل کرنے کا مجاز ہونے کے تین طریقے ہیں۔ آپ مالک ہو سکتے ہیں، کسی مخصوص ٹوکن کے لیے منظور شدہ ہو سکتے ہیں، یا مالک کے تمام ٹوکنز کے لیے آپریٹر ہو سکتے ہیں۔
- ماضی کے ایونٹس صرف بلاک چین کے باہر نظر آتے ہیں۔ بلاک چین کے اندر چلنے والا کوڈ انہیں نہیں دیکھ سکتا۔
اب جائیں اور محفوظ Vyper کنٹریکٹس نافذ کریں۔
میرے مزید کام کے لیے یہاں دیکھیں (opens in a new tab)۔
صفحہ کی آخری اپ ڈیٹ: 3 مارچ، 2026