Vyper ERC-721 कॉन्ट्रॅक्ट वॉकथ्रू
परिचय
ERC-721 स्टँडर्डचा वापर नॉन-फंजिबल टोकन्स (NFT) ची मालकी ठेवण्यासाठी केला जातो. ERC-20 टोकन कमोडिटी (वस्तू) प्रमाणे वागतात, कारण वैयक्तिक टोकन्समध्ये कोणताही फरक नसतो. याउलट, ERC-721 टोकन अशा मालमत्तांसाठी डिझाइन केलेले आहेत जे समान आहेत परंतु तंतोतंत एकसारखे नाहीत, जसे की भिन्न कॅट कार्टून्स (नवीन टॅबमध्ये उघडते) किंवा रिअल इस्टेटच्या वेगवेगळ्या तुकड्यांचे टायटल्स.
या लेखामध्ये आपण रयुया नाकामुरा यांच्या ERC-721 कॉन्ट्रॅक्टचे (नवीन टॅबमध्ये उघडते) विश्लेषण करू. हे कॉन्ट्रॅक्ट Vyper (नवीन टॅबमध्ये उघडते) मध्ये लिहिलेले आहे, जी Python सारखी कॉन्ट्रॅक्ट भाषा आहे आणि ती Solidity पेक्षा असुरक्षित कोड लिहिणे अधिक कठीण करण्यासाठी डिझाइन केलेली आहे.
कॉन्ट्रॅक्ट
# @dev ERC-721 नॉन-फंजिबल टोकन स्टँडर्डची अंमलबजावणी.
# @author Ryuya Nakamura (@nrryuya)
# येथून सुधारित: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy
Vyper मधील टिप्पण्या (comments), Python प्रमाणेच, हॅश (ethereum.ercs) ने सुरू होतात आणि ओळीच्या शेवटपर्यंत चालू राहतात. ज्या टिप्पण्यांमध्ये
@<keyword> समाविष्ट असते त्यांचा वापर NatSpec (नवीन टॅबमध्ये उघडते) द्वारे मानवांना वाचता येण्याजोगे दस्तऐवजीकरण तयार करण्यासाठी केला जातो.
from vyper.interfaces import ERC721
implements: ERC721
ERC-721 इंटरफेस Vyper भाषेमध्ये अंगभूत आहे. तुम्ही कोडची व्याख्या येथे पाहू शकता (नवीन टॅबमध्ये उघडते). इंटरफेसची व्याख्या Vyper ऐवजी Python मध्ये लिहिलेली आहे, कारण इंटरफेस केवळ ब्लॉकचेनमध्येच वापरले जात नाहीत, तर बाह्य क्लायंटकडून ब्लॉकचेनला व्यवहार पाठवताना देखील वापरले जातात, जे Python मध्ये लिहिलेले असू शकतात.
पहिली ओळ इंटरफेस इम्पोर्ट करते आणि दुसरी ओळ निर्दिष्ट करते की आपण त्याची येथे अंमलबजावणी करत आहोत.
#pragma version >0.3.10
#pragma version >0.3.10
ERC721Receiver इंटरफेस
# safeTransferFrom() द्वारे कॉल केलेल्या कॉन्ट्रॅक्टसाठी इंटरफेस
interface ERC721Receiver:
def onERC721Received(
ERC-721 दोन प्रकारच्या हस्तांतरणास (transfer) समर्थन देते:
transferFrom, जे प्रेषकाला कोणताही गंतव्य पत्ता निर्दिष्ट करू देते आणि हस्तांतरणाची जबाबदारी प्रेषकावर टाकते. याचा अर्थ असा की तुम्ही अवैध पत्त्यावर हस्तांतरण करू शकता, अशा परिस्थितीत NFT कायमचे गमावले जाते.safeTransferFrom, जे गंतव्य पत्ता कॉन्ट्रॅक्ट आहे की नाही हे तपासते. तसे असल्यास, ERC-721 कॉन्ट्रॅक्ट प्राप्त करणाऱ्या कॉन्ट्रॅक्टला विचारते की त्याला NFT प्राप्त करायचे आहे का.
safeTransferFrom विनंत्यांना उत्तर देण्यासाठी प्राप्त करणाऱ्या कॉन्ट्रॅक्टला ERC721Receiver ची अंमलबजावणी करावी लागते.
_operator: address,
_from: address,
_from पत्ता हा टोकनचा सध्याचा मालक आहे. _operator पत्ता तो आहे ज्याने हस्तांतरणाची विनंती केली आहे (मंजुरीमुळे (allowances) हे दोन्ही समान नसू शकतात). प्रथेनुसार, या कॉन्ट्रॅक्टमधील बहुतेक फंक्शन पॅरामीटर्स अंडरस्कोर (_) ने सुरू होतात.
_tokenId: uint256,
ERC-721 टोकन आयडी 256 बिट्सचे असतात. सामान्यतः ते टोकन ज्याचे प्रतिनिधित्व करते त्याच्या वर्णनाचे हॅशिंग करून तयार केले जातात.
_data: Bytes[1024]
विनंतीमध्ये 1024 बाइट्सपर्यंत वापरकर्ता डेटा असू शकतो.
) -> bytes4: nonpayable
एखादे कॉन्ट्रॅक्ट चुकून हस्तांतरण स्वीकारते अशा प्रकरणांना टाळण्यासाठी रिटर्न व्हॅल्यू बुलियन नसते, तर एक विशिष्ट चार-बाइट व्हॅल्यू असते, जी onERC721Received चे फंक्शन सिलेक्टर असते. हे फंक्शन nonpayable आहे कारण प्राप्त करणारे कॉन्ट्रॅक्ट जेव्हा टोकन स्वीकारते तेव्हा ते स्वतःची स्थिती बदलू शकते.
घटना
ब्लॉकचेनच्या बाहेरील वापरकर्त्यांना आणि सर्व्हरला घटनांची माहिती देण्यासाठी घटना (Events) उत्सर्जित केल्या जातात. लक्षात घ्या की घटनांची सामग्री ब्लॉकचेनवरील कॉन्ट्रॅक्ट्ससाठी उपलब्ध नसते. तीन ERC-721 घटना आपण इम्पोर्ट केलेल्या IERC721 इंटरफेसद्वारे परिभाषित केल्या आहेत, त्यामुळे हे कॉन्ट्रॅक्ट स्वतः त्यांना घोषित करत नाही; ते त्यांना log IERC721.<Event>(...) सह उत्सर्जित करते, जसे आपण खालील हस्तांतरण फंक्शन्समध्ये पाहू.
Transfer (sender, receiver, token_id) NFT च्या मालकीमधील बदलाची नोंद करते. हे ERC-20 Transfer घटनेसारखेच आहे, फक्त आपण रकमेऐवजी token_id नोंदवतो. शून्य पत्ता कोणाच्याही मालकीचा नसतो, त्यामुळे प्रथेनुसार आपण त्याचा वापर टोकन्सची निर्मिती आणि नाश नोंदवण्यासाठी करतो. याला एक अपवाद म्हणजे कॉन्ट्रॅक्ट निर्मिती, ज्या दरम्यान Transfer उत्सर्जित न करता कितीही NFTs तयार केले जाऊ शकतात आणि नियुक्त केले जाऊ शकतात.
ERC-721 मंजुरी ही ERC-20 मंजुरीसारखीच असते: एका विशिष्ट पत्त्याला विशिष्ट टोकन हस्तांतरित करण्याची परवानगी दिली जाते, आणि जेव्हा तो मंजूर पत्ता सेट केला जातो किंवा पुन्हा निश्चित केला जातो तेव्हा Approval (owner, approved, token_id) उत्सर्जित केले जाते. हे कॉन्ट्रॅक्ट्सना टोकन स्वीकारताना प्रतिसाद देण्यासाठी एक यंत्रणा देते. कॉन्ट्रॅक्ट्स घटना ऐकू शकत नाहीत, त्यामुळे जर तुम्ही त्यांना फक्त टोकन हस्तांतरित केले तर त्यांना त्याबद्दल "माहित" नसते. अशा प्रकारे मालक प्रथम मंजुरी सबमिट करतो आणि नंतर कॉन्ट्रॅक्टला विनंती पाठवतो: "मी तुम्हाला टोकन X हस्तांतरित करण्यास मंजुरी दिली आहे, कृपया ... करा". ERC-721 स्टँडर्डला ERC-20 स्टँडर्डसारखे बनवण्यासाठी ही एक डिझाइन निवड आहे. कारण ERC-721 टोकन्स फंजिबल नसतात, कॉन्ट्रॅक्ट टोकनच्या मालकीकडे पाहून त्याला विशिष्ट टोकन मिळाले आहे हे देखील ओळखू शकते.
शेवटी, जेव्हा मालकासाठी एखादा ऑपरेटर सक्षम किंवा अक्षम केला जातो तेव्हा ApprovalForAll (owner, operator, approved) उत्सर्जित केले जाते. पॉवर ऑफ ॲटर्नी प्रमाणेच, एखाद्या खात्याचे विशिष्ट प्रकारचे सर्व टोकन्स (जे विशिष्ट कॉन्ट्रॅक्टद्वारे व्यवस्थापित केले जातात) व्यवस्थापित करू शकणारा ऑपरेटर असणे कधीकधी उपयुक्त असते. उदाहरणार्थ, मला अशी शक्ती अशा कॉन्ट्रॅक्टला द्यायची असेल जे मी सहा महिने त्याच्याशी संपर्क साधला नाही की नाही हे तपासते, आणि तसे असल्यास माझी मालमत्ता माझ्या वारसांना वितरित करते (जर त्यांच्यापैकी कोणी त्याची मागणी केली तर, व्यवहाराद्वारे कॉल केल्याशिवाय कॉन्ट्रॅक्ट्स काहीही करू शकत नाहीत). ERC-20 मध्ये आपण वारसा कॉन्ट्रॅक्टला फक्त उच्च मंजुरी देऊ शकतो, परंतु ERC-721 साठी ते कार्य करत नाही कारण टोकन्स फंजिबल नसतात. हा त्याचा समतुल्य पर्याय आहे. approved व्हॅल्यू आपल्याला सांगते की घटना मंजुरीसाठी आहे की मंजुरी काढून घेण्यासाठी आहे.
स्थिती व्हेरिएबल्स
या व्हेरिएबल्समध्ये टोकन्सची सद्य स्थिती असते: कोणते उपलब्ध आहेत आणि त्यांच्या मालकीचे कोण आहेत. यापैकी बहुतांश HashMap ऑब्जेक्ट्स आहेत, दोन प्रकारांमध्ये अस्तित्वात असलेले युनिडायरेक्शनल मॅपिंग्ज (नवीन टॅबमध्ये उघडते).
# @dev NFT ID वरून त्याच्या मालकीच्या पत्त्यावर मॅपिंग.
idToOwner: HashMap[uint256, address]
# @dev NFT ID वरून मंजूर पत्त्यावर मॅपिंग.
idToApprovals: HashMap[uint256, address]
इथेरियममधील वापरकर्ता आणि कॉन्ट्रॅक्ट ओळख 160-बिट पत्त्यांद्वारे दर्शविली जाते. हे दोन व्हेरिएबल्स टोकन आयडीवरून त्यांच्या मालकांना आणि त्यांना हस्तांतरित करण्यासाठी मंजूर केलेल्यांना (प्रत्येकासाठी जास्तीत जास्त एक) मॅप करतात. इथेरियममध्ये, अनइनिशियलाइज्ड डेटा नेहमी शून्य असतो, त्यामुळे जर कोणताही मालक किंवा मंजूर हस्तांतरणकर्ता नसेल तर त्या टोकनसाठी व्हॅल्यू शून्य असते.
# @dev मालकाच्या पत्त्यावरून त्याच्या टोकन्सच्या संख्येपर्यंत मॅपिंग.
ownerToNFTokenCount: HashMap[address, uint256]
हे व्हेरिएबल प्रत्येक मालकासाठी टोकन्सची संख्या ठेवते. मालकांकडून टोकन्सपर्यंत कोणतेही मॅपिंग नाही, त्यामुळे विशिष्ट मालकाच्या मालकीचे टोकन्स ओळखण्याचा एकमेव मार्ग म्हणजे ब्लॉकचेनच्या घटनांच्या इतिहासामध्ये मागे पाहणे आणि योग्य Transfer घटना पाहणे. आपल्याकडे सर्व NFTs कधी आहेत आणि आपल्याला वेळेत आणखी मागे पाहण्याची आवश्यकता नाही हे जाणून घेण्यासाठी आपण या व्हेरिएबलचा वापर करू शकतो.
लक्षात घ्या की हा अल्गोरिदम केवळ वापरकर्ता इंटरफेस आणि बाह्य सर्व्हरसाठी कार्य करतो. ब्लॉकचेनवर चालणारा कोड मागील घटना वाचू शकत नाही.
# @dev मालकाच्या पत्त्यावरून ऑपरेटर पत्त्यांच्या मॅपिंगपर्यंत मॅपिंग.
ownerToOperators: HashMap[address, HashMap[address, bool]]
एका खात्यामध्ये एकापेक्षा जास्त ऑपरेटर असू शकतात. त्यांचा मागोवा ठेवण्यासाठी साधे HashMap अपुरे आहे, कारण प्रत्येक की एकाच व्हॅल्यूकडे नेते. त्याऐवजी, तुम्ही व्हॅल्यू म्हणून HashMap[address, bool] वापरू शकता. डीफॉल्टनुसार प्रत्येक पत्त्यासाठी व्हॅल्यू False असते, ज्याचा अर्थ तो ऑपरेटर नाही. तुम्ही आवश्यकतेनुसार व्हॅल्यूज True वर सेट करू शकता.
# @dev मिंटरचा पत्ता, जो टोकन मिंट करू शकतो
minter: address
नवीन टोकन्स कसेतरी तयार करावे लागतात. या कॉन्ट्रॅक्टमध्ये असे करण्याची परवानगी असलेली एकच संस्था आहे, minter. उदाहरणार्थ, गेमसाठी हे पुरेसे असण्याची शक्यता आहे. इतर उद्देशांसाठी, अधिक गुंतागुंतीचे बिझनेस लॉजिक तयार करणे आवश्यक असू शकते.
# @dev समर्थित ERC165 इंटरफेस आयडींची स्टॅटिक यादी
SUPPORTED_INTERFACES: constant(bytes4[2]) = [
# ERC165 चा ERC165 इंटरफेस आयडी
0x01ffc9a7,
# ERC721 चा ERC165 इंटरफेस आयडी
0x80ac58cd,
]
ERC-165 (नवीन टॅबमध्ये उघडते) कॉन्ट्रॅक्टसाठी ॲप्लिकेशन्स त्याच्याशी कसा संवाद साधू शकतात, ते कोणत्या ERCs चे पालन करते हे उघड करण्यासाठी एक यंत्रणा निर्दिष्ट करते. SUPPORTED_INTERFACES ही दोन चार-बाइट इंटरफेस आयडींची एक स्थिर यादी आहे ज्यांचे हे कॉन्ट्रॅक्ट पालन करते: स्वतः ERC-165 आणि ERC-721.
फंक्शन्स
ही ती फंक्शन्स आहेत जी प्रत्यक्षात ERC-721 ची अंमलबजावणी करतात.
कन्स्ट्रक्टर
@deploy
def __init__():
Vyper मध्ये, Python प्रमाणेच, कन्स्ट्रक्टर फंक्शनला __init__ म्हटले जाते. हे @deploy डेकोरेशनने चिन्हांकित केले आहे, ज्याचा अर्थ असा की जेव्हा कॉन्ट्रॅक्ट डिप्लॉय केले जाते तेव्हा ते एकदाच चालते.
"""
@dev कॉन्ट्रॅक्ट कन्स्ट्रक्टर.
"""
Python मध्ये, आणि Vyper मध्ये, तुम्ही मल्टी-लाइन स्ट्रिंग (जी """ ने सुरू होते आणि संपते) निर्दिष्ट करून आणि तिचा कोणत्याही प्रकारे वापर न करता टिप्पणी देखील तयार करू शकता. या टिप्पण्यांमध्ये NatSpec (नवीन टॅबमध्ये उघडते) देखील समाविष्ट असू शकते.
self.minter = msg.sender
स्थिती व्हेरिएबल्स ॲक्सेस करण्यासाठी तुम्ही self.<variable name> वापरता (पुन्हा, Python प्रमाणेच). कन्स्ट्रक्टर ज्या खात्याने कॉन्ट्रॅक्ट डिप्लॉय केले आहे त्याची minter म्हणून नोंद करतो.
व्ह्यू फंक्शन्स
ही अशी फंक्शन्स आहेत जी ब्लॉकचेनची स्थिती बदलत नाहीत, आणि म्हणून जर त्यांना बाह्यरित्या कॉल केले तर ती विनामूल्य कार्यान्वित केली जाऊ शकतात. जर व्ह्यू फंक्शन्सना कॉन्ट्रॅक्टद्वारे कॉल केले गेले तर ती अद्याप प्रत्येक नोडवर कार्यान्वित करावी लागतात आणि त्यामुळे गॅस खर्च होतो.
@view
@external
फंक्शन व्याख्येच्या आधीचे हे कीवर्ड जे ॲट चिन्हाने (@) सुरू होतात त्यांना डेकोरेशन्स म्हणतात. ते कोणत्या परिस्थितीत फंक्शन कॉल केले जाऊ शकते हे निर्दिष्ट करतात.
@viewनिर्दिष्ट करते की हे फंक्शन एक व्ह्यू आहे.@externalनिर्दिष्ट करते की हे विशिष्ट फंक्शन व्यवहारांद्वारे आणि इतर कॉन्ट्रॅक्ट्सद्वारे कॉल केले जाऊ शकते.
def supportsInterface(interface_id: bytes4) -> bool:
Python च्या विपरीत, Vyper ही एक स्टॅटिक टाइप केलेली भाषा (नवीन टॅबमध्ये उघडते) आहे. तुम्ही डेटा प्रकार (नवीन टॅबमध्ये उघडते) ओळखल्याशिवाय व्हेरिएबल किंवा फंक्शन पॅरामीटर घोषित करू शकत नाही. या प्रकरणात इनपुट पॅरामीटर bytes4 आहे, एक चार-बाइट व्हॅल्यू, आणि आउटपुट एक बुलियन व्हॅल्यू आहे.
"""
@dev इंटरफेस ओळख ERC-165 मध्ये निर्दिष्ट केली आहे.
@param interface_id इंटरफेसचा आयडी
"""
return interface_id in SUPPORTED_INTERFACES
जर interface_id हा SUPPORTED_INTERFACES यादीतील इंटरफेस आयडींपैकी एक असेल तर True परत करा.
### व्ह्यू फंक्शन्स ###
ही व्ह्यू फंक्शन्स आहेत जी वापरकर्त्यांना आणि इतर कॉन्ट्रॅक्ट्सना टोकन्सबद्दल माहिती उपलब्ध करून देतात.
@view
@external
def balanceOf(_owner: address) -> uint256:
"""
@dev `_owner` च्या मालकीच्या NFTs ची संख्या परत करते.
जर `_owner` शून्य पत्ता असेल तर थ्रो (Throws) करते. शून्य पत्त्यावर नियुक्त केलेले NFTs अवैध मानले जातात.
@param _owner पत्ता ज्याच्यासाठी शिल्लक तपासायची आहे.
"""
assert _owner != empty(address)
ही ओळ असेर्ट (asserts) (नवीन टॅबमध्ये उघडते) करते की _owner हा शून्य पत्ता नाही, जो empty(address) म्हणून लिहिला जातो. जर तो असेल, तर त्रुटी येते आणि ऑपरेशन पूर्ववत केले जाते.
return self.ownerToNFTokenCount[_owner]
@view
@external
def ownerOf(_tokenId: uint256) -> address:
"""
@dev NFT च्या मालकाचा पत्ता परत करते.
जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते.
@param _tokenId NFT साठी आयडेंटिफायर.
"""
owner: address = self.idToOwner[_tokenId]
# जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते
assert owner != empty(address)
return owner
इथेरियम व्हर्च्युअल मशीन (EVM) मध्ये ज्या स्टोरेजमध्ये कोणतीही व्हॅल्यू साठवलेली नसते ते शून्य असते. जर _tokenId वर कोणतेही टोकन नसेल तर self.idToOwner[_tokenId] ची व्हॅल्यू शून्य असते. अशा परिस्थितीत फंक्शन पूर्ववत केले जाते.
@view
@external
def getApproved(_tokenId: uint256) -> address:
"""
@dev एकाच NFT साठी मंजूर पत्ता मिळवा.
जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते.
@param _tokenId NFT चा आयडी ज्याची मंजुरी तपासायची आहे.
"""
# जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते
assert self.idToOwner[_tokenId] != empty(address)
return self.idToApprovals[_tokenId]
लक्षात घ्या की getApproved शून्य परत करू शकते. जर टोकन वैध असेल तर ते self.idToApprovals[_tokenId] परत करते. जर कोणताही मंजूरकर्ता नसेल तर ती व्हॅल्यू शून्य असते.
@view
@external
def isApprovedForAll(_owner: address, _operator: address) -> bool:
"""
@dev `_operator` हा `_owner` साठी मंजूर ऑपरेटर आहे की नाही हे तपासते.
@param _owner पत्ता ज्याच्या मालकीचे NFTs आहेत.
@param _operator पत्ता जो मालकाच्या वतीने कार्य करतो.
"""
return (self.ownerToOperators[_owner])[_operator]
हे फंक्शन _operator ला या कॉन्ट्रॅक्टमधील _owner चे सर्व टोकन्स व्यवस्थापित करण्याची परवानगी आहे की नाही हे तपासते. कारण एकाधिक ऑपरेटर असू शकतात, हे दोन स्तरांचे HashMap आहे.
हस्तांतरण हेल्पर फंक्शन्स
ही फंक्शन्स ऑपरेशन्सची अंमलबजावणी करतात जी टोकन्स हस्तांतरित करण्याचा किंवा व्यवस्थापित करण्याचा भाग आहेत.
### हस्तांतरण फंक्शन हेल्पर्स ###
@view
@internal
हे डेकोरेशन, @internal, याचा अर्थ असा की फंक्शन केवळ त्याच कॉन्ट्रॅक्टमधील इतर फंक्शन्समधून ॲक्सेस करण्यायोग्य आहे. प्रथेनुसार, या फंक्शनची नावे देखील अंडरस्कोर (_) ने सुरू होतात.
def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool:
"""
@dev दिलेला खर्च करणारा (spender) दिलेला टोकन आयडी हस्तांतरित करू शकतो की नाही हे परत करते
@param spender खर्च करणाऱ्याचा पत्ता ज्याची चौकशी करायची आहे
@param tokenId हस्तांतरित केल्या जाणाऱ्या टोकनचा uint256 आयडी
@return bool msg.sender दिलेल्या टोकन आयडीसाठी मंजूर आहे की नाही,
मालकाचा ऑपरेटर आहे, किंवा टोकनचा मालक आहे
"""
owner: address = self.idToOwner[_tokenId]
spenderIsOwner: bool = owner == _spender
spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId]
spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender]
return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll
एखाद्या पत्त्याला टोकन हस्तांतरित करण्याची परवानगी देण्याचे तीन मार्ग आहेत:
- पत्ता टोकनचा मालक आहे
- पत्त्याला ते टोकन खर्च करण्यास मंजुरी आहे
- पत्ता टोकनच्या मालकासाठी ऑपरेटर आहे
वरील फंक्शन एक व्ह्यू असू शकते कारण ते स्थिती बदलत नाही. ऑपरेटिंग खर्च कमी करण्यासाठी, कोणतेही फंक्शन जे व्ह्यू असू शकते ते व्ह्यू असावे.
@internal
def _addTokenTo(_to: address, _tokenId: uint256):
"""
@dev दिलेल्या पत्त्यावर NFT जोडा
जर `_tokenId` कोणाच्या तरी मालकीचा असेल तर थ्रो (Throws) करते.
"""
# जर `_tokenId` कोणाच्या तरी मालकीचा असेल तर थ्रो (Throws) करते
assert self.idToOwner[_tokenId] == empty(address)
# मालक बदला
self.idToOwner[_tokenId] = _to
# मोजणी ट्रॅकिंग बदला
self.ownerToNFTokenCount[_to] += 1
@internal
def _removeTokenFrom(_from: address, _tokenId: uint256):
"""
@dev दिलेल्या पत्त्यावरून NFT काढा
जर `_from` सध्याचा मालक नसेल तर थ्रो (Throws) करते.
"""
# जर `_from` सध्याचा मालक नसेल तर थ्रो (Throws) करते
assert self.idToOwner[_tokenId] == _from
# मालक बदला
self.idToOwner[_tokenId] = empty(address)
# मोजणी ट्रॅकिंग बदला
self.ownerToNFTokenCount[_from] -= 1
जेव्हा हस्तांतरणामध्ये समस्या असते तेव्हा आपण कॉल पूर्ववत करतो.
@internal
def _clearApproval(_owner: address, _tokenId: uint256):
"""
@dev दिलेल्या पत्त्याची मंजुरी साफ करा
जर `_owner` सध्याचा मालक नसेल तर थ्रो (Throws) करते.
"""
# जर `_owner` सध्याचा मालक नसेल तर थ्रो (Throws) करते
assert self.idToOwner[_tokenId] == _owner
if self.idToApprovals[_tokenId] != empty(address):
# मंजुरी रीसेट करा
self.idToApprovals[_tokenId] = empty(address)
केवळ आवश्यक असल्यासच व्हॅल्यू बदला. स्थिती व्हेरिएबल्स स्टोरेजमध्ये राहतात. स्टोरेजमध्ये लिहिणे हे EVM (इथेरियम व्हर्च्युअल मशीन) करत असलेल्या सर्वात महागड्या ऑपरेशन्सपैकी एक आहे (गॅस च्या संदर्भात). म्हणून, ते कमी करणे ही एक चांगली कल्पना आहे, अगदी विद्यमान व्हॅल्यू लिहिण्याचा खर्च देखील जास्त असतो.
@internal
def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address):
"""
@dev NFT चे हस्तांतरण कार्यान्वित करा.
जोपर्यंत `msg.sender` सध्याचा मालक, अधिकृत ऑपरेटर किंवा या NFT साठी मंजूर
पत्ता नसेल तोपर्यंत थ्रो (Throws) करते. (टीप: खाजगी फंक्शनमध्ये `msg.sender` ला परवानगी नाही म्हणून `_sender` पास करा.)
जर `_to` शून्य पत्ता असेल तर थ्रो (Throws) करते.
जर `_from` सध्याचा मालक नसेल तर थ्रो (Throws) करते.
जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते.
"""
आपल्याकडे हे अंतर्गत फंक्शन आहे कारण टोकन्स हस्तांतरित करण्याचे दोन मार्ग आहेत (नियमित आणि सुरक्षित), परंतु ऑडिटिंग सोपे करण्यासाठी आपल्याला कोडमध्ये फक्त एकच ठिकाण हवे आहे जिथे आपण ते करतो.
# आवश्यकता तपासा
assert self._isApprovedOrOwner(_sender, _tokenId)
# जर `_to` शून्य पत्ता असेल तर थ्रो (Throws) करते
assert _to != empty(address)
# मंजुरी साफ करा. जर `_from` सध्याचा मालक नसेल तर थ्रो (Throws) करते
self._clearApproval(_from, _tokenId)
# NFT काढा. जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते
self._removeTokenFrom(_from, _tokenId)
# NFT जोडा
self._addTokenTo(_to, _tokenId)
# हस्तांतरण लॉग करा
log IERC721.Transfer(sender=_from, receiver=_to, token_id=_tokenId)
Vyper मध्ये घटना उत्सर्जित करण्यासाठी तुम्ही log स्टेटमेंट वापरता (अधिक तपशीलांसाठी येथे पहा (नवीन टॅबमध्ये उघडते)). कारण घटना इम्पोर्ट केलेल्या इंटरफेसच्या आहेत, आपण त्यांना IERC721.Transfer म्हणून संदर्भित करतो आणि त्यांचे फील्ड्स कीवर्डद्वारे पास करतो.
### हस्तांतरण फंक्शन्स ###
@external
@payable
def transferFrom(_from: address, _to: address, _tokenId: uint256):
"""
@dev जोपर्यंत `msg.sender` सध्याचा मालक, अधिकृत ऑपरेटर किंवा या NFT साठी मंजूर
पत्ता नसेल तोपर्यंत थ्रो (Throws) करते.
जर `_from` सध्याचा मालक नसेल तर थ्रो (Throws) करते.
जर `_to` शून्य पत्ता असेल तर थ्रो (Throws) करते.
जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते.
@notice कॉलरने हे निश्चित करणे आवश्यक आहे की `_to` NFTs प्राप्त करण्यास सक्षम आहे, अन्यथा
ते कायमचे गमावले जाऊ शकतात.
@param _from NFT चा सध्याचा मालक.
@param _to नवीन मालक.
@param _tokenId हस्तांतरित करण्यासाठी NFT.
"""
self._transferFrom(_from, _to, _tokenId, msg.sender)
हे फंक्शन तुम्हाला कोणत्याही पत्त्यावर हस्तांतरण करण्याची अनुमती देते. जोपर्यंत पत्ता वापरकर्ता नसेल, किंवा टोकन कसे हस्तांतरित करायचे हे माहित असलेले कॉन्ट्रॅक्ट नसेल, तोपर्यंत तुम्ही हस्तांतरित केलेले कोणतेही टोकन त्या पत्त्यावर अडकून राहील आणि निरुपयोगी होईल.
येथे @payable डेकोरेशन आहे कारण IERC721 इंटरफेस transferFrom, safeTransferFrom, आणि approve ला payable म्हणून घोषित करतो, त्यामुळे इंटरफेसची अंमलबजावणी करणाऱ्या कॉन्ट्रॅक्टला त्या स्वाक्षऱ्यांशी (signatures) जुळवून घ्यावे लागते.
@external
@payable
def safeTransferFrom(
_from: address,
_to: address,
_tokenId: uint256,
_data: Bytes[1024]=b""
):
"""
@dev NFT ची मालकी एका पत्त्यावरून दुसऱ्या पत्त्यावर हस्तांतरित करते.
जोपर्यंत `msg.sender` सध्याचा मालक, अधिकृत ऑपरेटर किंवा या NFT साठी
मंजूर पत्ता नसेल तोपर्यंत थ्रो (Throws) करते.
जर `_from` सध्याचा मालक नसेल तर थ्रो (Throws) करते.
जर `_to` शून्य पत्ता असेल तर थ्रो (Throws) करते.
जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते.
जर `_to` स्मार्ट कॉन्ट्रॅक्ट असेल, तर ते `_to` वर `onERC721Received` कॉल करते आणि जर
परतावा मूल्य (return value) `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` नसेल तर थ्रो (Throws) करते.
@param _from NFT चा सध्याचा मालक.
@param _to नवीन मालक.
@param _tokenId हस्तांतरित करण्यासाठी NFT.
@param _data कोणतेही निर्दिष्ट स्वरूप नसलेला अतिरिक्त डेटा, जो `_to` ला कॉलमध्ये पाठवला जातो.
"""
self._transferFrom(_from, _to, _tokenId, msg.sender)
प्रथम हस्तांतरण करणे ठीक आहे कारण जर काही समस्या असेल तर आपण तरीही पूर्ववत करणार आहोत, त्यामुळे कॉलमध्ये केलेले सर्व काही रद्द केले जाईल.
if _to.is_contract: # `_to` हा कॉन्ट्रॅक्ट पत्ता आहे की नाही ते तपासा
प्रथम पत्ता कॉन्ट्रॅक्ट आहे की नाही ते तपासा (जर त्यात कोड असेल). नसल्यास, तो वापरकर्ता पत्ता आहे असे गृहीत धरा आणि वापरकर्ता टोकन वापरण्यास किंवा हस्तांतरित करण्यास सक्षम असेल. परंतु यामुळे तुम्हाला सुरक्षिततेची खोटी जाणीव होऊ देऊ नका. जर तुम्ही टोकन्स अशा पत्त्यावर हस्तांतरित केले ज्याची खाजगी की कोणालाही माहित नाही, तर तुम्ही safeTransferFrom सह देखील टोकन्स गमावू शकता.
returnValue: bytes4 = extcall ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)
लक्ष्य (target) कॉन्ट्रॅक्ट ERC-721 टोकन प्राप्त करू शकते की नाही हे पाहण्यासाठी त्याला कॉल करा. Vyper 0.4 मध्ये इतर कॉन्ट्रॅक्ट्सना केलेले कॉल्स चिन्हांकित करणे आवश्यक आहे, त्यामुळे कॉलच्या आधी extcall लावले जाते.
# जर हस्तांतरणाचे गंतव्यस्थान (destination) असे कॉन्ट्रॅक्ट असेल जे 'onERC721Received' ची अंमलबजावणी करत नसेल तर थ्रो (Throws) करते
assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes4)
जर गंतव्यस्थान (destination) कॉन्ट्रॅक्ट असेल, परंतु ते ERC-721 टोकन स्वीकारत नसेल (किंवा त्याने हे विशिष्ट हस्तांतरण न स्वीकारण्याचे ठरवले असेल), तर पूर्ववत करा.
@external
@payable
def approve(_approved: address, _tokenId: uint256):
"""
@dev NFT साठी मंजूर पत्ता सेट करा किंवा पुन्हा निश्चित करा. शून्य पत्ता दर्शवतो की कोणताही मंजूर पत्ता नाही.
जोपर्यंत `msg.sender` सध्याचा NFT मालक, किंवा सध्याच्या मालकाचा अधिकृत ऑपरेटर नसेल तोपर्यंत थ्रो (Throws) करते.
जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते. (टीप: हे EIP मध्ये लिहिलेले नाही)
जर `_approved` सध्याचा मालक असेल तर थ्रो (Throws) करते. (टीप: हे EIP मध्ये लिहिलेले नाही)
@param _approved दिलेल्या NFT आयडीसाठी मंजूर केला जाणारा पत्ता.
@param _tokenId मंजूर केल्या जाणाऱ्या टोकनचा आयडी.
"""
owner: address = self.idToOwner[_tokenId]
# जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते
assert owner != empty(address)
# जर `_approved` सध्याचा मालक असेल तर थ्रो (Throws) करते
assert _approved != owner
प्रथेनुसार जर तुम्हाला मंजूरकर्ता (approver) नको असेल तर तुम्ही स्वतःला नाही, तर शून्य पत्ता नियुक्त करता.
# आवश्यकता तपासा
senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender
senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]
assert (senderIsOwner or senderIsApprovedForAll)
मंजुरी सेट करण्यासाठी तुम्ही एकतर मालक असू शकता, किंवा मालकाद्वारे अधिकृत ऑपरेटर असू शकता.
# मंजुरी सेट करा
self.idToApprovals[_tokenId] = _approved
log IERC721.Approval(owner=owner, approved=_approved, token_id=_tokenId)
@external
def setApprovalForAll(_operator: address, _approved: bool):
"""
@dev `msg.sender` च्या सर्व मालमत्ता व्यवस्थापित करण्यासाठी तृतीय पक्षाला ("ऑपरेटर") मंजुरी सक्षम किंवा अक्षम करते.
हे ApprovalForAll घटना (event) देखील उत्सर्जित करते.
जर `_operator` हा `msg.sender` असेल तर थ्रो (Throws) करते. (टीप: हे EIP मध्ये लिहिलेले नाही)
@notice जरी प्रेषकाकडे (sender) त्यावेळी कोणतेही टोकन नसले तरीही हे कार्य करते.
@param _operator अधिकृत ऑपरेटर्सच्या संचामध्ये जोडण्यासाठी पत्ता.
@param _approved जर ऑपरेटर मंजूर असेल तर True, मंजुरी रद्द करण्यासाठी false.
"""
# जर `_operator` हा `msg.sender` असेल तर थ्रो (Throws) करते
assert _operator != msg.sender
self.ownerToOperators[msg.sender][_operator] = _approved
log IERC721.ApprovalForAll(owner=msg.sender, operator=_operator, approved=_approved)
नवीन टोकन्स मिंट करा आणि विद्यमान नष्ट करा
ज्या खात्याने कॉन्ट्रॅक्ट तयार केले आहे ते minter आहे, सुपर युजर ज्याला नवीन NFTs मिंट करण्यासाठी अधिकृत केले आहे. तथापि, त्यालाही विद्यमान टोकन्स जाळण्याची परवानगी नाही. केवळ मालक, किंवा मालकाद्वारे अधिकृत संस्थाच ते करू शकते.
### मिंट आणि जाळणे फंक्शन्स ###
@external
def mint(_to: address, _tokenId: uint256) -> bool:
हे फंक्शन नेहमी True परत करते, कारण जर ऑपरेशन अयशस्वी झाले तर ते पूर्ववत केले जाते.
"""
@dev टोकन मिंट करण्यासाठी फंक्शन
जर `msg.sender` मिंटर नसेल तर थ्रो (Throws) करते.
जर `_to` शून्य पत्ता असेल तर थ्रो (Throws) करते.
जर `_tokenId` कोणाच्या तरी मालकीचा असेल तर थ्रो (Throws) करते.
@param _to पत्ता ज्याला मिंट केलेले टोकन प्राप्त होतील.
@param _tokenId मिंट करण्यासाठी टोकन आयडी.
@return एक बुलियन (boolean) जे ऑपरेशन यशस्वी झाले की नाही हे दर्शवते.
"""
# जर `msg.sender` मिंटर नसेल तर थ्रो (Throws) करते
assert msg.sender == self.minter
केवळ मिंटर (ज्या खात्याने ERC-721 कॉन्ट्रॅक्ट तयार केले आहे) नवीन टोकन्स मिंट करू शकतो. भविष्यात जर आपल्याला मिंटरची ओळख बदलायची असेल तर ही समस्या असू शकते. प्रोडक्शन कॉन्ट्रॅक्टमध्ये तुम्हाला कदाचित असे फंक्शन हवे असेल जे मिंटरला मिंटरचे विशेषाधिकार दुसऱ्या कोणालातरी हस्तांतरित करण्याची परवानगी देते.
# जर `_to` शून्य पत्ता असेल तर थ्रो (Throws) करते
assert _to != ZERO_ADDRESS
# NFT जोडा. जर `_tokenId` कोणाच्या तरी मालकीचा असेल तर थ्रो (Throws) करते
self._addTokenTo(_to, _tokenId)
log Transfer(ZERO_ADDRESS, _to, _tokenId)
return True
प्रथेनुसार, नवीन टोकन्सचे मिंटिंग शून्य पत्त्यावरून हस्तांतरण म्हणून मोजले जाते.
@external
def burn(_tokenId: uint256):
"""
@dev विशिष्ट ERC721 टोकन जाळते.
जोपर्यंत `msg.sender` सध्याचा मालक, अधिकृत ऑपरेटर किंवा या NFT साठी मंजूर पत्ता नसेल तोपर्यंत थ्रो (Throws) करते.
जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते.
@param _tokenId जाळल्या जाणाऱ्या ERC721 टोकनचा uint256 आयडी.
"""
# आवश्यकता तपासा
assert self._isApprovedOrOwner(msg.sender, _tokenId)
owner: address = self.idToOwner[_tokenId]
# जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते
assert owner != ZERO_ADDRESS
self._clearApproval(owner, _tokenId)
self._removeTokenFrom(owner, _tokenId)
log Transfer(owner, ZERO_ADDRESS, _tokenId)
ज्या कोणाला टोकन हस्तांतरित करण्याची परवानगी आहे त्याला ते जाळण्याची परवानगी आहे. जरी जाळणे हे शून्य पत्त्यावर हस्तांतरित करण्यासारखे वाटत असले तरी, शून्य पत्त्याला प्रत्यक्षात टोकन प्राप्त होत नाही. हे आपल्याला टोकनसाठी वापरलेले सर्व स्टोरेज मोकळे करण्यास अनुमती देते, ज्यामुळे व्यवहाराचा गॅस खर्च कमी होऊ शकतो.
हे कॉन्ट्रॅक्ट वापरणे
Solidity च्या विपरीत, Vyper मध्ये इनहेरिटन्स नाही. कोड अधिक स्पष्ट करण्यासाठी आणि त्यामुळे सुरक्षित करणे सोपे करण्यासाठी ही एक जाणीवपूर्वक केलेली डिझाइन निवड आहे. त्यामुळे तुमचे स्वतःचे Vyper ERC-721 कॉन्ट्रॅक्ट तयार करण्यासाठी तुम्ही हे कॉन्ट्रॅक्ट (नवीन टॅबमध्ये उघडते) घ्या आणि तुम्हाला हवे असलेले व्यवसाय लॉजिक लागू करण्यासाठी त्यात बदल करा.
निष्कर्ष
पुनरावलोकनासाठी, या कॉन्ट्रॅक्टमधील काही सर्वात महत्त्वाच्या कल्पना येथे आहेत:
- सुरक्षित हस्तांतरणासह ERC-721 टोकन प्राप्त करण्यासाठी, कॉन्ट्रॅक्ट्सना
ERC721Receiverइंटरफेसची अंमलबजावणी करावी लागते. - जरी तुम्ही सुरक्षित हस्तांतरण वापरत असलात तरी, जर तुम्ही टोकन्स अशा पत्त्यावर पाठवले ज्याची खाजगी की अज्ञात आहे, तर ते अद्याप अडकू शकतात.
- जेव्हा एखाद्या ऑपरेशनमध्ये समस्या असते तेव्हा केवळ अपयश मूल्य परत करण्याऐवजी कॉल
revertकरणे ही एक चांगली कल्पना आहे. - ERC-721 टोकन तेव्हा अस्तित्वात असतात जेव्हा त्यांचा मालक असतो.
- NFT हस्तांतरित करण्यासाठी अधिकृत होण्याचे तीन मार्ग आहेत. तुम्ही मालक असू शकता, विशिष्ट टोकनसाठी मंजूर असू शकता, किंवा मालकाच्या सर्व टोकन्ससाठी ऑपरेटर असू शकता.
- मागील घटना केवळ ब्लॉकचेनच्या बाहेर दृश्यमान असतात. ब्लॉकचेनच्या आत चालणारा कोड त्या पाहू शकत नाही.
आता जा आणि सुरक्षित Vyper कॉन्ट्रॅक्ट्सची अंमलबजावणी करा.