Vyper ERC-721 कॉन्ट्रॅक्ट वॉकथ्रू
परिचय
ERC-721 स्टँडर्डचा वापर नॉन-फंजिबल टोकन्स (NFT) ची मालकी ठेवण्यासाठी केला जातो. ERC-20 टोकन कमोडिटी (वस्तू) प्रमाणे वागतात, कारण वैयक्तिक टोकन्समध्ये कोणताही फरक नसतो. याउलट, ERC-721 टोकन अशा मालमत्तांसाठी डिझाइन केलेले आहेत जे समान आहेत परंतु तंतोतंत एकसारखे नाहीत, जसे की भिन्न कॅट कार्टून्स (opens in a new tab) किंवा रिअल इस्टेटच्या वेगवेगळ्या तुकड्यांचे टायटल्स.
या लेखामध्ये आपण रयुया नाकामुरा यांच्या ERC-721 कॉन्ट्रॅक्टचे (opens in a new tab) विश्लेषण करू. हे कॉन्ट्रॅक्ट Vyper (opens in a new tab) मध्ये लिहिलेले आहे, जी Python सारखी कॉन्ट्रॅक्ट भाषा आहे आणि ती Solidity पेक्षा असुरक्षित कोड लिहिणे अधिक कठीण करण्यासाठी डिझाइन केलेली आहे.
कॉन्ट्रॅक्ट
# @dev ERC-721 नॉन-फंजिबल टोकन स्टँडर्डची अंमलबजावणी.
# @author Ryuya Nakamura (@nrryuya)
# येथून सुधारित: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy
Vyper मधील टिप्पण्या (comments), Python प्रमाणेच, हॅश (#) ने सुरू होतात आणि ओळीच्या शेवटपर्यंत चालू राहतात. ज्या टिप्पण्यांमध्ये
@<keyword> समाविष्ट असते त्यांचा वापर NatSpec (opens in a new tab) द्वारे मानवांना वाचता येण्याजोगे दस्तऐवजीकरण तयार करण्यासाठी केला जातो.
from vyper.interfaces import ERC721
implements: ERC721
ERC-721 इंटरफेस Vyper भाषेमध्ये अंगभूत आहे. तुम्ही कोडची व्याख्या येथे पाहू शकता (opens in a new tab). इंटरफेसची व्याख्या Vyper ऐवजी Python मध्ये लिहिलेली आहे, कारण इंटरफेस केवळ ब्लॉकचेनमध्येच वापरले जात नाहीत, तर बाह्य क्लायंटकडून ब्लॉकचेनला व्यवहार पाठवताना देखील वापरले जातात, जे Python मध्ये लिहिलेले असू शकतात.
पहिली ओळ इंटरफेस इम्पोर्ट करते आणि दुसरी ओळ निर्दिष्ट करते की आपण त्याची येथे अंमलबजावणी करत आहोत.
ERC721Receiver इंटरफेस
# safeTransferFrom() द्वारे कॉल केलेल्या कॉन्ट्रॅक्टसाठी इंटरफेस
interface ERC721Receiver:
def onERC721Received(
ERC-721 दोन प्रकारच्या हस्तांतरणास समर्थन देते:
transferFrom, जे प्रेषकाला कोणताही गंतव्य पत्ता निर्दिष्ट करू देते आणि हस्तांतरणाची जबाबदारी प्रेषकावर टाकते. याचा अर्थ असा की तुम्ही अवैध पत्त्यावर हस्तांतरण करू शकता, अशा परिस्थितीत NFT कायमचे गमावले जाते.safeTransferFrom, जे गंतव्य पत्ता कॉन्ट्रॅक्ट आहे की नाही हे तपासते. तसे असल्यास, ERC-721 कॉन्ट्रॅक्ट प्राप्त करणाऱ्या कॉन्ट्रॅक्टला विचारते की त्याला NFT प्राप्त करायचे आहे का.
safeTransferFrom विनंत्यांना उत्तर देण्यासाठी प्राप्त करणाऱ्या कॉन्ट्रॅक्टला ERC721Receiver ची अंमलबजावणी करावी लागते.
_operator: address,
_from: address,
_from पत्ता हा टोकनचा वर्तमान मालक आहे. _operator पत्ता तो आहे ज्याने हस्तांतरणाची विनंती केली आहे (मंजुरीमुळे हे दोन्ही समान नसू शकतात).
_tokenId: uint256,
ERC-721 टोकन आयडी 256 बिट्सचे असतात. सामान्यतः ते टोकन ज्याचे प्रतिनिधित्व करते त्याच्या वर्णनाचे हॅशिंग करून तयार केले जातात.
_data: Bytes[1024]
विनंतीमध्ये 1024 बाइट्सपर्यंत वापरकर्ता डेटा असू शकतो.
) -> bytes32: view
एखादे कॉन्ट्रॅक्ट चुकून हस्तांतरण स्वीकारते अशा प्रकरणांना प्रतिबंध करण्यासाठी रिटर्न व्हॅल्यू बुलियन नसते, तर विशिष्ट मूल्यासह 256 बिट्स असते.
हे फंक्शन एक view आहे, ज्याचा अर्थ असा की ते ब्लॉकचेनची स्थिती वाचू शकते, परंतु त्यात बदल करू शकत नाही.
घटना
ब्लॉकचेनच्या बाहेरील वापरकर्त्यांना आणि सर्व्हरला घटनांची माहिती देण्यासाठी घटना उत्सर्जित केल्या जातात. लक्षात घ्या की घटनांची सामग्री ब्लॉकचेनवरील कॉन्ट्रॅक्ट्ससाठी उपलब्ध नसते.
# @dev जेव्हा कोणत्याही NFT ची मालकी कोणत्याही यंत्रणेद्वारे बदलते तेव्हा उत्सर्जित होते. ही घटना तेव्हा उत्सर्जित होते जेव्हा NFTs
# तयार केले जातात (`from` == 0) आणि नष्ट केले जातात (`to` == 0). अपवाद: कॉन्ट्रॅक्ट तयार करताना, कोणतेही
# कितीही NFTs तयार केले जाऊ शकतात आणि Transfer उत्सर्जित न करता नियुक्त केले जाऊ शकतात. कोणत्याही
# हस्तांतरण वेळी, त्या NFT साठी मंजूर पत्ता (असल्यास) काहीही नाही (none) वर रीसेट केला जातो.
# @param _from NFT पाठवणारा (जर पत्ता शून्य पत्ता असेल तर ते टोकन निर्मिती दर्शवते).
# @param _to NFT प्राप्तकर्ता (जर पत्ता शून्य पत्ता असेल तर ते टोकन नष्ट होणे दर्शवते).
# @param _tokenId हस्तांतरित झालेला NFT.
event Transfer:
sender: indexed(address)
receiver: indexed(address)
tokenId: indexed(uint256)
हे ERC-20 Transfer घटनेसारखेच आहे, फक्त आपण रकमेऐवजी tokenId नोंदवतो. शून्य पत्ता कोणाच्याही मालकीचा नसतो, त्यामुळे प्रथेनुसार आपण त्याचा वापर टोकन्सची निर्मिती आणि नष्ट झाल्याची नोंद करण्यासाठी करतो.
# @dev जेव्हा NFT साठी मंजूर पत्ता बदलला जातो किंवा पुन्हा निश्चित केला जातो तेव्हा हे उत्सर्जित होते. शून्य
# पत्ता दर्शवतो की कोणताही मंजूर पत्ता नाही. जेव्हा Transfer घटना उत्सर्जित होते, तेव्हा हे देखील
# दर्शवते की त्या NFT साठी मंजूर पत्ता (असल्यास) काहीही नाही (none) वर रीसेट केला आहे.
# @param _owner NFT चा मालक.
# @param _approved पत्ता ज्याला आपण मंजुरी देत आहोत.
# @param _tokenId NFT ज्याला आपण मंजुरी देत आहोत.
event Approval:
owner: indexed(address)
approved: indexed(address)
tokenId: indexed(uint256)
ERC-721 मंजुरी ही ERC-20 मंजुरीसारखीच असते. एका विशिष्ट पत्त्याला विशिष्ट टोकन हस्तांतरित करण्याची परवानगी असते. हे कॉन्ट्रॅक्ट्सना टोकन स्वीकारताना प्रतिसाद देण्यासाठी एक यंत्रणा देते. कॉन्ट्रॅक्ट्स घटना ऐकू शकत नाहीत, त्यामुळे जर तुम्ही त्यांना फक्त टोकन हस्तांतरित केले तर त्यांना त्याबद्दल "माहित" नसते. अशा प्रकारे मालक प्रथम मंजुरी सबमिट करतो आणि नंतर कॉन्ट्रॅक्टला विनंती पाठवतो: "मी तुम्हाला टोकन X हस्तांतरित करण्यास मंजुरी दिली आहे, कृपया ... करा".
ERC-721 स्टँडर्डला ERC-20 स्टँडर्डसारखे बनवण्यासाठी ही एक डिझाइन निवड आहे. कारण ERC-721 टोकन फंजिबल नसतात, एखादे कॉन्ट्रॅक्ट टोकनच्या मालकीकडे पाहून त्याला विशिष्ट टोकन मिळाले आहे हे देखील ओळखू शकते.
# @dev जेव्हा मालकासाठी ऑपरेटर सक्षम किंवा अक्षम केला जातो तेव्हा हे उत्सर्जित होते. ऑपरेटर मालकाचे
# सर्व NFTs व्यवस्थापित करू शकतो.
# @param _owner NFT चा मालक.
# @param _operator पत्ता ज्यावर आपण ऑपरेटर अधिकार सेट करत आहोत.
# @param _approved ऑपरेटर अधिकारांची स्थिती (ऑपरेटर अधिकार दिले असल्यास true आणि
# रद्द केले असल्यास false).
event ApprovalForAll:
owner: indexed(address)
operator: indexed(address)
approved: bool
कधीकधी असा ऑपरेटर असणे उपयुक्त ठरते जो खात्याच्या विशिष्ट प्रकारच्या सर्व टोकन्सचे व्यवस्थापन करू शकतो (जे विशिष्ट कॉन्ट्रॅक्टद्वारे व्यवस्थापित केले जातात), पॉवर ऑफ ॲटर्नी प्रमाणे. उदाहरणार्थ, मला अशी शक्ती अशा कॉन्ट्रॅक्टला द्यायची असेल जे मी सहा महिने त्याच्याशी संपर्क साधला नाही हे तपासते आणि तसे असल्यास माझी मालमत्ता माझ्या वारसांना वितरीत करते (जर त्यांच्यापैकी कोणी त्याची मागणी केली तर, व्यवहाराद्वारे कॉल केल्याशिवाय कॉन्ट्रॅक्ट्स काहीही करू शकत नाहीत). ERC-20 मध्ये आपण वारसा कॉन्ट्रॅक्टला फक्त उच्च मंजुरी देऊ शकतो, परंतु ते ERC-721 साठी कार्य करत नाही कारण टोकन फंजिबल नसतात. हा त्याचा समतुल्य पर्याय आहे.
approved मूल्य आपल्याला सांगते की घटना मंजुरीसाठी आहे की मंजुरी मागे घेण्यासाठी आहे.
स्थिती व्हेरिएबल्स
या व्हेरिएबल्समध्ये टोकन्सची वर्तमान स्थिती असते: कोणते उपलब्ध आहेत आणि त्यांच्या मालकीचे कोण आहेत. यापैकी बहुतांश
HashMap ऑब्जेक्ट्स आहेत, दोन प्रकारांमध्ये अस्तित्वात असलेले युनिडायरेक्शनल मॅपिंग्ज (opens in a new tab).
# @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 इंटरफेस आयडीचे ते समर्थित आहे की नाही याबद्दल बूल (bool) वर मॅपिंग
supportedInterfaces: HashMap[bytes32, bool]
# @dev ERC165 चा ERC165 इंटरफेस आयडी
ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
# @dev ERC721 चा ERC165 इंटरफेस आयडी
ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd
ERC-165 (opens in a new tab) कॉन्ट्रॅक्टसाठी ॲप्लिकेशन्स त्याच्याशी कसा संवाद साधू शकतात, ते कोणत्या ERC चे पालन करते हे उघड करण्यासाठी एक यंत्रणा निर्दिष्ट करते. या प्रकरणात, कॉन्ट्रॅक्ट ERC-165 आणि ERC-721 चे पालन करते.
फंक्शन्स
ही ती फंक्शन्स आहेत जी प्रत्यक्षात ERC-721 ची अंमलबजावणी करतात.
कन्स्ट्रक्टर
@external
def __init__():
Vyper मध्ये, Python प्रमाणेच, कन्स्ट्रक्टर फंक्शनला __init__ म्हटले जाते.
"""
@dev कॉन्ट्रॅक्ट कन्स्ट्रक्टर.
"""
Python मध्ये आणि Vyper मध्ये, तुम्ही मल्टी-लाइन स्ट्रिंग (जी """ ने सुरू होते आणि संपते) निर्दिष्ट करून आणि तिचा कोणत्याही प्रकारे वापर न करता टिप्पणी देखील तयार करू शकता. या टिप्पण्यांमध्ये
NatSpec (opens in a new tab) देखील समाविष्ट असू शकते.
self.supportedInterfaces[ERC165_INTERFACE_ID] = True
self.supportedInterfaces[ERC721_INTERFACE_ID] = True
self.minter = msg.sender
स्थिती व्हेरिएबल्स ॲक्सेस करण्यासाठी तुम्ही self.<variable name> वापरता (पुन्हा, Python प्रमाणेच).
व्ह्यू फंक्शन्स
ही अशी फंक्शन्स आहेत जी ब्लॉकचेनची स्थिती बदलत नाहीत आणि म्हणून जर त्यांना बाह्यरित्या कॉल केले तर ती विनामूल्य कार्यान्वित केली जाऊ शकतात. जर व्ह्यू फंक्शन्सना कॉन्ट्रॅक्टद्वारे कॉल केले गेले तर त्यांना अद्याप प्रत्येक नोडवर कार्यान्वित करावे लागते आणि त्यामुळे गॅस खर्च होतो.
@view
@external
फंक्शन व्याख्येच्या आधीचे हे कीवर्ड जे ॲट चिन्हाने (@) सुरू होतात त्यांना डेकोरेशन्स म्हणतात. ते
अशा परिस्थिती निर्दिष्ट करतात ज्यामध्ये फंक्शनला कॉल केले जाऊ शकते.
@viewनिर्दिष्ट करते की हे फंक्शन एक व्ह्यू आहे.@externalनिर्दिष्ट करते की या विशिष्ट फंक्शनला व्यवहारांद्वारे आणि इतर कॉन्ट्रॅक्ट्सद्वारे कॉल केले जाऊ शकते.
def supportsInterface(_interfaceID: bytes32) -> bool:
Python च्या विपरीत, Vyper ही एक स्टॅटिक टाइप केलेली भाषा (opens in a new tab) आहे.
तुम्ही डेटा प्रकार (opens in a new tab) ओळखल्याशिवाय व्हेरिएबल किंवा फंक्शन पॅरामीटर घोषित करू शकत नाही. या प्रकरणात इनपुट पॅरामीटर bytes32 आहे, जे 256-बिट मूल्य आहे
(256 बिट्स हा इथेरियम व्हर्च्युअल मशीनचा मूळ शब्द आकार आहे). आउटपुट एक बुलियन मूल्य आहे. प्रथेनुसार, फंक्शन पॅरामीटर्सची नावे अंडरस्कोर (_) ने सुरू होतात.
"""
@dev इंटरफेस ओळख ERC-165 मध्ये निर्दिष्ट केली आहे.
@param _interfaceID इंटरफेसचा आयडी
"""
return self.supportedInterfaces[_interfaceID]
self.supportedInterfaces HashMap मधून मूल्य परत करा, जे कन्स्ट्रक्टर (__init__) मध्ये सेट केले आहे.
### व्ह्यू फंक्शन्स ###
ही व्ह्यू फंक्शन्स आहेत जी वापरकर्त्यांना आणि इतर कॉन्ट्रॅक्ट्सना टोकन्सबद्दल माहिती उपलब्ध करून देतात.
@view
@external
def balanceOf(_owner: address) -> uint256:
"""
@dev `_owner` च्या मालकीच्या NFTs ची संख्या परत करते.
जर `_owner` शून्य पत्ता असेल तर थ्रो (Throws) करते. शून्य पत्त्यावर नियुक्त केलेले NFTs अवैध मानले जातात.
@param _owner पत्ता ज्याच्यासाठी शिल्लक तपासायची आहे.
"""
assert _owner != ZERO_ADDRESS
ही ओळ असेर्ट करते (opens in a new tab) की _owner शून्य नाही. जर ते असेल, तर त्रुटी येते आणि ऑपरेशन पूर्ववत केले जाते.
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 != ZERO_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] != ZERO_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 दिलेला स्पेन्डर दिलेला टोकन आयडी हस्तांतरित करू शकतो की नाही हे परत करते
@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] == ZERO_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] = ZERO_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] != ZERO_ADDRESS:
# मंजुरी रीसेट करा
self.idToApprovals[_tokenId] = ZERO_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 != ZERO_ADDRESS
# मंजुरी साफ करा. जर `_from` सध्याचा मालक नसेल तर थ्रो (Throws) करते
self._clearApproval(_from, _tokenId)
# NFT काढा. जर `_tokenId` वैध NFT नसेल तर थ्रो (Throws) करते
self._removeTokenFrom(_from, _tokenId)
# NFT जोडा
self._addTokenTo(_to, _tokenId)
# हस्तांतरण लॉग करा
log Transfer(_from, _to, _tokenId)
Vyper मध्ये घटना उत्सर्जित करण्यासाठी तुम्ही log स्टेटमेंट वापरता (अधिक तपशीलांसाठी येथे पहा (opens in a new tab)).
हस्तांतरण फंक्शन्स
### हस्तांतरण फंक्शन्स ###
@external
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)
हे फंक्शन तुम्हाला अनियंत्रित पत्त्यावर हस्तांतरित करू देते. जोपर्यंत पत्ता वापरकर्ता नाही, किंवा टोकन कसे हस्तांतरित करायचे हे माहित असलेले कॉन्ट्रॅक्ट नाही, तोपर्यंत तुम्ही हस्तांतरित केलेले कोणतेही टोकन त्या पत्त्यावर अडकेल आणि निरुपयोगी होईल.
@external
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` कॉल करते आणि जर
परत केलेले मूल्य `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` नसेल तर थ्रो (Throws) करते.
टीप: bytes4 हे पॅडिंगसह bytes32 द्वारे दर्शविले जाते
@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: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)
लक्ष्य कॉन्ट्रॅक्टला कॉल करून ते ERC-721 टोकन प्राप्त करू शकते की नाही ते पहा.
# जर हस्तांतरण गंतव्यस्थान असे कॉन्ट्रॅक्ट असेल जे 'onERC721Received' ची अंमलबजावणी करत नसेल तर थ्रो (Throws) करते
assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32)
जर गंतव्यस्थान कॉन्ट्रॅक्ट असेल, परंतु जे ERC-721 टोकन स्वीकारत नाही (किंवा ज्याने हे विशिष्ट हस्तांतरण न स्वीकारण्याचे ठरवले आहे), तर पूर्ववत करा.
@external
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 != ZERO_ADDRESS
# जर `_approved` सध्याचा मालक असेल तर थ्रो (Throws) करते
assert _approved != owner
प्रथेनुसार जर तुम्हाला मंजूरकर्ता नको असेल तर तुम्ही स्वतःला नाही, तर शून्य पत्ता नियुक्त करता.
# आवश्यकता तपासा
senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender
senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]
assert (senderIsOwner or senderIsApprovedForAll)
मंजुरी सेट करण्यासाठी तुम्ही एकतर मालक असू शकता, किंवा मालकाद्वारे अधिकृत ऑपरेटर असू शकता.
# मंजुरी सेट करा
self.idToApprovals[_tokenId] = _approved
log Approval(owner, _approved, _tokenId)
@external
def setApprovalForAll(_operator: address, _approved: bool):
"""
@dev तृतीय पक्षाला ("ऑपरेटर") `msg.sender` च्या सर्व मालमत्ता व्यवस्थापित करण्यासाठी मंजुरी सक्षम किंवा अक्षम करते. हे ApprovalForAll घटना देखील उत्सर्जित करते.
जर `_operator` हा `msg.sender` असेल तर थ्रो (Throws) करते. (टीप: हे EIP मध्ये लिहिलेले नाही)
@notice प्रेषकाकडे त्यावेळी कोणतेही टोकन नसले तरीही हे कार्य करते.
@param _operator अधिकृत ऑपरेटर्सच्या संचामध्ये जोडण्यासाठी पत्ता.
@param _approved ऑपरेटर मंजूर असल्यास True, मंजुरी रद्द करण्यासाठी false.
"""
# जर `_operator` हा `msg.sender` असेल तर थ्रो (Throws) करते
assert _operator != msg.sender
self.ownerToOperators[msg.sender][_operator] = _approved
log ApprovalForAll(msg.sender, _operator, _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 कॉन्ट्रॅक्ट तयार करण्यासाठी तुम्ही हे कॉन्ट्रॅक्ट (opens in a new tab) घ्या आणि तुम्हाला हवे असलेले व्यवसाय लॉजिक लागू करण्यासाठी त्यात बदल करा.
निष्कर्ष
पुनरावलोकनासाठी, या कॉन्ट्रॅक्टमधील काही सर्वात महत्त्वाच्या कल्पना येथे आहेत:
- सुरक्षित हस्तांतरणासह ERC-721 टोकन प्राप्त करण्यासाठी, कॉन्ट्रॅक्ट्सना
ERC721Receiverइंटरफेसची अंमलबजावणी करावी लागते. - जरी तुम्ही सुरक्षित हस्तांतरण वापरत असलात तरी, जर तुम्ही टोकन्स अशा पत्त्यावर पाठवले ज्याची खाजगी की अज्ञात आहे, तर ते अद्याप अडकू शकतात.
- जेव्हा एखाद्या ऑपरेशनमध्ये समस्या असते तेव्हा केवळ अपयश मूल्य परत करण्याऐवजी कॉल
revertकरणे ही एक चांगली कल्पना आहे. - ERC-721 टोकन तेव्हा अस्तित्वात असतात जेव्हा त्यांचा मालक असतो.
- NFT हस्तांतरित करण्यासाठी अधिकृत होण्याचे तीन मार्ग आहेत. तुम्ही मालक असू शकता, विशिष्ट टोकनसाठी मंजूर असू शकता, किंवा मालकाच्या सर्व टोकन्ससाठी ऑपरेटर असू शकता.
- मागील घटना केवळ ब्लॉकचेनच्या बाहेर दृश्यमान असतात. ब्लॉकचेनच्या आत चालणारा कोड त्या पाहू शकत नाही.
आता जा आणि सुरक्षित Vyper कॉन्ट्रॅक्ट्सची अंमलबजावणी करा.