ERC721 (या ERC-721) नॉन-फंजिबल टोकन (nonfungible tokens) के लिए सबसे व्यापक रूप से उपयोग किया जाने वाला Ethereum स्टैण्डर्ड है। यह एक Ethereum एड्रेस के साथ एक यूनिक नंबर जोड़ता है, जिससे यह पता चलता है कि उस एड्रेस के पास वह यूनिक नंबर – यानी NFT है।
वास्तव में इस प्रसिद्ध टोकन डिज़ाइन को कवर करने वाले ट्यूटोरियल्स की कोई कमी नहीं है, हालाँकि, हमने पाया है कि कई डेवलपर्स, यहाँ तक कि अनुभवी लोगों को भी, विशिष्टताओं (specifications) — और कभी-कभी सिक्योरिटी समस्याओं की पूरी समझ नहीं होती है। इसलिए, हमने यहाँ इस स्टैण्डर्ड को उन क्षेत्रों पर जोर देते हुए डॉक्यूमेंट किया है जिन्हें अधिक अनुभवी डेवलपर्स मिस कर देते हैं।
कम ज्ञात कॉर्नर केसेस (corner cases) का परीक्षण करने के लिए अंत में प्रैक्टिस प्रॉब्लम्स (Practice problems) दिए गए हैं।
विषय सूची (Table of Contents)
- NFTs को यूनिक क्या बनाता है?
- ओनरशिप (Ownership) और
ownerOfफंक्शन - मिंटिंग प्रोसेस (Minting process)
transferFromके साथ NFTs ट्रांसफर करनाbalanceOfफंक्शन को समझना- अनलिमिटेड अप्रूवल्स:
setApprovalForAllऔरisApprovedForAllफंक्शन्स approveऔरgetApprovedफंक्शन्स के साथ विशिष्ट अप्रूवल्स- एन्यूमेरेबल एक्सटेंशन (enumerable extension) के बिना ओन्ड (owned) NFTs की पहचान करना
- सुरक्षित ट्रांसफर्स (Safe Transfers):
safeTransferFrom,_safeMint, औरonERC721Receivedफंक्शन - डेटा के साथ
safeTransferFromऔर यह क्यों मौजूद है - व्यावहारिक उपयोग के मामले और एफिशिएंसी (Efficiency) _mintऔरtransferFromकी तुलना में_safeMintऔरsafeTransferFromके लिए गैस संबंधी विचारburnफंक्शन और NFT डिस्ट्रक्शन (Destruction)- ERC721 इम्प्लीमेंटेशन्स (Implementations)
- अपने ज्ञान का परीक्षण करें
NFTs को यूनिक क्या बनाता है?
NFTs तीन वैल्युज (chain id, contract address, id) द्वारा विशिष्ट रूप से पहचाने जाते हैं।
एक NFT को ओन (own) करने का अर्थ है किसी विशेष EVM चेन पर ERC721 कॉन्ट्रैक्ट में स्टोर किए गए एक uint256 को ओन करना।
हम उन फंक्शन्स में गहराई से जानेंगे जो ERC721 स्पेसिफिकेशन बनाते हैं और इसके व्यवहार को सुविधाजनक बनाते हैं, जिसमें कोर (core) और ऑक्ज़िलरी (auxiliary) फंक्शन्स शामिल हैं। वे इस प्रकार हैं:
ownerOf: ओनरशिप मैपिंग- mint: टोकन क्रिएशन (Creation)
transferFrom: ओनरशिप ट्रांसफर करनाbalanceOf: ओनरशिप काउंट (Count)setApprovalForAll&isApprovedForAll: ट्रांसफर राइट्स (Rights) को डेलीगेट (Delegating) करनाapprove&getApproved: सिंगल NFT अप्रूवल मैकेनिज्मsafeTransferFrom&_safeMint: सुरक्षित ट्रांसफर फंक्शन्सburn: NFT डिस्ट्रक्शन (Destruction)
ओनरशिप और ERC721 ownerOf फंक्शन
ओनरशिप सिर्फ एक मैपिंग है: ownerOf(uint256 id)
मूल रूप से, एक ERC721 सिर्फ एक uint256 (NFT की id) से ओनर (owner) के एड्रेस तक की एक मैपिंग (mapping) है। NFTs के बारे में इतनी हाइप (hype) के बावजूद, वे केवल ग्लोरिफाइड हैश मैप्स (glorified hash maps) हैं। एक NFT को “ओन” करने का मतलब है कि एक मैपिंग मौजूद है जिसमें एक निश्चित id की (key) के रूप में है और आपका एड्रेस वैल्यू (value) के रूप में है। बस इतना ही।
स्पेसिफिकेशन के लिए एक पब्लिक (public) फंक्शन की आवश्यकता होती है, जो एक id दिए जाने पर, ओनर का एड्रेस रिटर्न करता है।
सरलता के लिए, हम एक पब्लिक फंक्शन के बजाय एक पब्लिक वेरिएबल का उपयोग करेंगे। बाहरी रूप से, इंटरेक्शन (interaction) बिल्कुल समान है।
contract ERC721 {
mapping(uint256 => address) public ownerOf;
}
फंक्शन (या पब्लिक मैपिंग) ownerOf NFT की id लेता है और उसे ओन करने वाले एड्रेस को रिटर्न करता है।
mint फंक्शन के साथ मिंटिंग प्रोसेस
चूँकि मैपिंग की डिफ़ॉल्ट वैल्यू 0 होती है, डिफ़ॉल्ट रूप से, जीरो एड्रेस सभी NFTs को “ओन” करता है, लेकिन हम आमतौर पर इसकी व्याख्या इस तरह नहीं करते हैं। यदि ownerOf जीरो एड्रेस रिटर्न करता है, तो हम कहते हैं कि NFT अस्तित्व में नहीं है। मिंटिंग वह प्रक्रिया है जिससे टोकन अस्तित्व में आते हैं।
Mint ERC721 स्पेसिफिकेशन का हिस्सा नहीं है, यह यूजर पर निर्भर छोड़ दिया जाता है कि वे यह परिभाषित करें कि NFTs कैसे मिंट होंगे। ऐसी कोई आवश्यकता नहीं है कि NFTs को क्रम 0,1,2,3 आदि में मिंट किया जाए। हम किसी व्यक्ति को उनके एड्रेस या ऐसी ही किसी चीज़ के साथ हैश किए गए ब्लॉक नंबर के आधार पर NFT मिंट कर सकते हैं। निम्नलिखित इम्प्लीमेंटेशन में, कोई भी किसी भी id को मिंट कर सकता है जब तक कि उसे पहले मिंट न किया गया हो।
contract ERC721 {
mapping(uint256 id => address owner) public ownerOf;
event Transfer(address indexed from, address indexed to, uint256 indexed id);
function mint(address recipient, uint256 id) public {
require(ownerOf[id] == address(0), "already minted");
ownerOf[id] = recipient;
emit Transfer(address(0), recipient, id);
}
}
यह अजीब लग सकता है कि एक Transfer इवेंट address(0) से प्राप्तकर्ता (recipient) तक जा रहा है, लेकिन यही स्पेसिफिकेशन है।
ERC721 transferFrom के साथ NFTs ट्रांसफर करना
स्वाभाविक रूप से, हम अपने NFT को किसी अन्य एड्रेस पर ले जाने का एक तरीका चाहते हैं। transferFrom फंक्शन इसे पूरा करता है।
contract ERC721 {
mapping(uint256 id => address owner) public ownerOf;
event Transfer(address indexed from, address indexed to, uint256 indexed id);
// mint hidden for readability
function transferFrom(address from, address to, uint256 id) external payable {
require(ownerOf[id] == msg.sender, "not allowed to transfer");
ownerOf[id] = to;
emit Transfer(from, to, id);
}
}
यह अजीब लग सकता है कि transferFrom payable है, लेकिन EIP 721 स्पेसिफिकेशन ऐसा ही कहता है। संभवतः, यह उन एप्लिकेशन्स की अनुमति देने के लिए है जिन्हें पहले से ही मिंट किए गए NFT को प्राप्त करने के लिए ईथर (Ether) के भुगतान की आवश्यकता होती है। कई इम्प्लीमेंटेशन्स स्पेसिफिकेशन के इस हिस्से का पालन नहीं करते हैं, और इस फीचर का बहुत कम उपयोग किया जाता है।
साथ ही, हमारे पास एक from फ़ील्ड क्यों है अगर हम केवल msg.sender को ही from होने की अनुमति देते हैं? जब हम अप्रूवल्स (approvals) के बारे में बात करेंगे तो हम इस पर आएंगे। अभी के लिए, यह स्पष्ट होना चाहिए कि ओनर को वह id ट्रांसफर करने में सक्षम होना चाहिए जिसे वे ओन करते हैं।
ERC721 balanceOf फंक्शन को समझना
ERC721 स्पेसिफिकेशन के लिए हमें यह ट्रैक करना आवश्यक है कि एक एड्रेस प्रति कॉन्ट्रैक्ट कितने NFTs ओन करता है।
ERC721 एक मैपिंग mapping(address owner => uint256 balances) balanceOf रखता है।
हमारे मिनिमल (minimal) NFT में अब नीचे दिए गए कोड में दिखाई गई कार्यक्षमता है।
इस बात पर जोर दिया जाना चाहिए कि balanceOf केवल यह बताता है कि एक एड्रेस कितने NFTs ओन करता है, यह यह नहीं बताता कि कौन से। हमें उन फंक्शन्स को अपडेट करने की आवश्यकता है जहाँ बैलेंस बदल सकता है, जो निश्चित रूप से mint और transfer हैं। जिन स्थानों पर हमने उन फंक्शन्स को अपडेट किया है, उन्हें हाइलाइट किया गया है

यहाँ एक और चेतावनी दी गई है: एक ओनर अपनी इच्छा से NFTs ट्रांसफर कर सकता है, इसलिए स्मार्ट कॉन्ट्रैक्ट में निर्णय लेते समय balanceOf पर निर्भर रहने पर आपको बहुत सावधान रहना चाहिए। balanceOf() को एक स्टैटिक (static) वैल्यू की तरह न मानें क्योंकि यह ट्रांजैक्शन के दौरान बदल सकता है अगर ओनर किसी अन्य एड्रेस से खुद को NFT ट्रांसफर करता है, या NFT को किसी अन्य एड्रेस पर ट्रांसफर करता है जिसे वे ओन करते हैं, तो वे balanceOf() फंक्शन में हेरफेर कर सकते हैं।
अनलिमिटेड अप्रूवल्स: ERC721 setApprovalForAll और isApprovedForAll फंक्शन्स
ERC721 स्पेसिफिकेशन एक NFT ओनर को NFT को उन्हें ट्रांसफर किए बिना किसी अन्य एड्रेस को NFT का कण्ट्रोल (control) देने की अनुमति देता है। ऐसा करने का पहला मैकेनिज्म setApprovalForAll() फंक्शन के साथ है। जैसा कि नाम से ही स्पष्ट है, यह किसी अन्य एड्रेस को ओनर की ओर से NFTs ट्रांसफर करने की अनुमति देता है। यह एड्रेस द्वारा ओन किए गए किसी भी NFT पर लागू होता है। काउंटरपार्ट (counterpart) isApprovedForAll() यह जाँचता है कि क्या operator नामक किसी निश्चित एड्रेस को ओनर से अथॉरिटी (authority) डेलीगेट (delegate) की गई है।
एक owner के कई ऑपरेटर (operators) हो सकते हैं। यह एक ऐसा मैकेनिज्म है जिसके द्वारा एक ही NFT कई NFT मार्केटप्लेस (marketplaces) पर बिक्री के लिए हो सकता है। यदि मार्केटप्लेस ओनर के एड्रेस के लिए अप्रूव (approve) किए गए हैं, तो वे इसे एक खरीदार (buyer) को ट्रांसफर कर सकते हैं यदि खरीदार ईथर की सही राशि का भुगतान करता है।

TransferFrom अब ओनर और एक ऐसे एड्रेस दोनों को टोकन ट्रांसफर करने की अनुमति देता है जो _approvedForAll है।
ERC721 approve और getApproved फंक्शन्स के साथ विशिष्ट टोकन id अप्रूवल्स
किसी अन्य एड्रेस को आपके द्वारा ओन किए गए हर NFT को ट्रांसफर करने की अनुमति देने के बजाय, आप उन्हें एक सिंगल id के लिए अप्रूव (approve) कर सकते हैं, जो आम तौर पर अधिक सुरक्षित होता है। इसे पब्लिक मैपिंग getApproved() में डाल दिया जाता है।
isApprovedForAll के विपरीत, NFT के लिए अप्रूव होने का ओनर के एड्रेस से कोई लेना-देना नहीं है, यह केवल id से जुड़ा है।
एक ट्रांसफर के बाद, नया ओनर संभवतः यह नहीं चाहेगा कि किसी और के पास उस id पर अप्रूवल हो। इसलिए, उस अप्रूवल को क्लियर (clear) करने के लिए transferFrom फंक्शन को अपडेट करने की आवश्यकता है।
approve की एक सीमा यह है कि प्रति id केवल एक ही एड्रेस को अप्रूव किया जा सकता है। यदि हम कई एड्रेस को अप्रूव करना चाहते हैं, तो ट्रांसफर के दौरान उन सभी को डिलीट (delete) करना बहुत महंगा (expensive) होगा।
ध्यान दें कि यदि कोई एड्रेस approvedForAll है, तो वह उस एड्रेस द्वारा ओन की गई ids के लिए किसी अन्य एड्रेस को approve करने में सक्षम है जिसका वह ऑपरेटर है। setApprovalForAll() फंक्शन में कुछ भी नहीं बदला है।

ट्रांसफर के बाद, अप्रूवल्स क्लियर कर दिए जाते हैं क्योंकि नया ओनर सामान्य रूप से यह नहीं चाहेगा कि पिछले एड्रेस के पास id पर अप्रूवल हो।
हमने ERC721 स्पेसिफिकेशन द्वारा आवश्यक प्रत्येक फंक्शन को लगभग पूरी तरह से इम्प्लीमेंट कर लिया है। बचे हुए फंक्शन्स के लिए काफी अधिक डॉक्यूमेंटेशन की आवश्यकता है।
एन्यूमेरेबल एक्सटेंशन (enumerable extension) के बिना ओन्ड (owned) NFTs की पहचान करना
ओन्ड (owned) ids की एक सूची निर्धारित करना
उपरोक्त विधियों का उपयोग करते हुए, क्या यह निर्धारित करने का कोई कुशल तरीका है कि एक एड्रेस के पास कौन से NFTs हैं?
कोई तरीका नहीं है।
फंक्शन balanceOf हमें केवल यह बताता है कि एक एड्रेस कितने NFTs ओन करता है, और ownerOf केवल हमें यह बताता है कि किसी विशेष id को कौन ओन करता है। सिद्धांत रूप में, हम यह पता लगाने के लिए सभी ids के माध्यम से लूप (loop) कर सकते हैं कि एक विशेष एड्रेस कौन से ओन करता है, लेकिन यह कुशल (efficient) नहीं है।
एन्यूमेरेबल एक्सटेंशन (enumerable extension) के बिना, केवल ऑन-चेन (on-chain) यह निर्धारित करने का कोई कुशल तरीका नहीं है कि कौन से NFTs एक एड्रेस के पास हैं।
हम बाद में एन्यूमेरेबल एक्सटेंशन पर आएँगे, लेकिन हम इसके बिना कैसे आगे बढ़ें?
यदि किसी कॉन्ट्रैक्ट को यह जानने की आवश्यकता है कि 0xc0ffee… के पास ids 5, 7, और 21 हैं, तो समाधान यह है कि कॉन्ट्रैक्ट को बताएं कि 0xc0ffee… के पास वे ids हैं, फिर कॉन्ट्रैक्ट इसे वेरिफाई (verifies) करता है कि यह वास्तव में सच है।
function checkOwnership(uint256[] calldata ids, address claimedOwner) public {
for (uint256 i = 0; i < ids.length; i++) {
require(nft.ownerOf(ids[i]) == claimedOwner, "not the claimed owner");
}
// rest of the logic
}
लेकिन हम कुशलतापूर्वक (efficiently) ऑफ चेन (off chain) कैसे निर्धारित करते हैं कि 0xc0ffee… 5, 7, और 21 को ओन करता है? हम सभी ids के माध्यम से लूप कर सकते हैं और ownerOf() को कॉल कर सकते हैं, लेकिन इससे हमारा RPC प्रोवाइडर अमीर बन जाएगा।
ERC721 इवेंट्स (Events) को पार्स (Parsing) करना
यहाँ web3 js का उपयोग करके कुछ सैंपल कोड (sample code) दिए गए हैं जो यह ट्रैक करते हैं कि कौन से NFTs एक एड्रेस द्वारा ओन किए गए हैं। ध्यान रखें कि कोड 0वें ब्लॉक से इवेंट्स को स्कैन (scan) करता है, जो कुशल नहीं है। आपको अधिक समझदारी वाली हाल की वैल्यू (recent value) चुननी चाहिए।
gist.github.com/RareSkills/5d60ad42cdd81b6e136605a832ba59ee
सुरक्षित ट्रांसफर्स (Safe Transfers): safeTransferFrom, _safeMint और onERC721Received फंक्शन
safeTransferFrom और _safeMint का उद्देश्य कॉन्ट्रैक्ट में फँसने वाले NFTs को हैंडल (handle) करना है। यदि कोई NFT ऐसे कॉन्ट्रैक्ट में ट्रांसफर किया जाता है जिसमें स्वयं transferFrom को कॉल करने की क्षमता नहीं है, तो NFT कॉन्ट्रैक्ट में “लॉक (locked in)” हो जाएगा, और प्रभावी रूप से नष्ट हो जाएगा।
ऐसा होने से रोकने के लिए, ERC-721 केवल उन कॉन्ट्रैक्ट्स में ट्रांसफर करना चाहता है जिनमें बाद में NFT को ट्रांसफर करने में सक्षम होने का मैकेनिज्म (mechanism) हो। एक कॉन्ट्रैक्ट को NFTs को “हैंडल” करने में सक्षम के रूप में चिह्नित किया जाता है यदि इसमें एक फंक्शन onERC721Received() है जो मैजिक बाइट्स4 (magic bytes4) वैल्यू 0x150b7a02 रिटर्न करता है। यह नीचे दिखाए गए onERC721Received() का फंक्शन सेलेक्टर (function selector) है। (एक फंक्शन सेलेक्टर Solidity का फंक्शन्स के लिए इंटरनल आइडेंटिफायर है)।
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
यहाँ उस इंटरफ़ेस का उपयोग करने वाले कॉन्ट्रैक्ट का एक मिनिमल (minimal) उदाहरण दिया गया है:
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
contract MinimaExample is IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4) {
return IERC721Receiver.onERC721Received.selector; // returns 0x150b7a02
}
}
safeTransferFrom बिल्कुल transferFrom की तरह व्यवहार करता है। बैकग्राउंड (Under the hood) में यह transferFrom को कॉल करता है और फिर जाँचता है कि क्या रिसीविंग एड्रेस एक स्मार्ट कॉन्ट्रैक्ट है।
- यदि यह नहीं है, तो कोई अतिरिक्त कदम न उठाएं
- यदि यह है
- यह NFT प्राप्त करने वाले कॉन्ट्रैक्ट पर उपरोक्त तर्कों (arguments) के साथ
onERC721Received()फंक्शन को कॉल करने का प्रयास करता है - यदि फंक्शन कॉल रिवर्ट (revert) हो जाता है या 0x150b7a02 रिटर्न नहीं किया जाता है, तो यह रिवर्ट हो जाता है
- यह NFT प्राप्त करने वाले कॉन्ट्रैक्ट पर उपरोक्त तर्कों (arguments) के साथ
फंक्शन सेलेक्टर की जाँच क्यों करें?
यह जाँचना कि क्या onERC721Received() रिवर्ट नहीं हुआ है, यह निर्धारित करने के लिए पर्याप्त नहीं है कि क्या कोई कॉन्ट्रैक्ट ठीक से ERC721 टोकन को हैंडल कर सकता है।
यदि कोई NFT किसी ऐसे स्मार्ट कॉन्ट्रैक्ट में ट्रांसफर किया जाता है जिसमें एक फॉलबैक (fallback) फंक्शन है, और रिटर्न वैल्यू (return value) की जाँच नहीं की जाती है, तो ट्रांजैक्शन रिवर्ट नहीं होगा। हालाँकि, कॉन्ट्रैक्ट में संभवतः NFTs प्राप्त करने को हैंडल करने का कोई मैकेनिज्म नहीं है, सिर्फ इसलिए कि इसमें एक फॉलबैक फंक्शन है।
onERC721Received के फंक्शन आर्गुमेंट्स (arguments)
जब onERC721Received को कॉल किया जाता है, तो इसके लिए निम्नलिखित आर्गुमेंट्स पास किए जाते हैं, जिनका वर्णन नीचे किया गया है
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
operator:
ऑपरेटर (Operator) safeTransfer के नजरिए से msg.sender है। यह NFT ओनर या उस NFT को ट्रांसफर करने के लिए अप्रूव (approved) एड्रेस हो सकता है।
from:
From NFT का ओनर है। पैरामीटर्स from और operator बराबर होंगे यदि ओनर ही ट्रांसफर कॉल करने वाला है।
tokenId:
ट्रांसफर किए जा रहे NFT की id।
data:
यदि safeTransferFrom को data के साथ कॉल किया गया था, तो इसे रिसीविंग कॉन्ट्रैक्ट में फॉरवर्ड (forward) कर दिया जाता है। data पैरामीटर पर बाद के सेक्शन में चर्चा की जाएगी।
onERC721Received सिक्योरिटी (security) संबंधी विचार
onERC721Received में हमेशा msg.sender की जाँच करें
डिफ़ॉल्ट रूप से, कोई भी मनमाने (arbitrary) पैरामीटर्स के साथ onERC721Received() को कॉल कर सकता है, कॉन्ट्रैक्ट को यह सोचने पर मूर्ख बना सकता है कि उसे एक NFT प्राप्त हुआ है जो उसके पास नहीं है। यदि आपका कॉन्ट्रैक्ट onERC721Received() का उपयोग करता है, तो आपको यह जाँचना होगा कि msg.sender वही NFT कॉन्ट्रैक्ट है जिसकी आप अपेक्षा करते हैं!
safeTransfer रीएंट्रेंसी (reentrancy)
SafeTransfer और _safeMint किसी बाहरी कॉन्ट्रैक्ट को एग्जीक्यूशन कण्ट्रोल (execution control) सौंपते हैं। किसी मनमाने एड्रेस पर NFT भेजने के लिए safeTransfer का उपयोग करते समय सावधान रहें, रिसीवर onERC721Received() फंक्शन में अपनी पसंद का कोई भी लॉजिक डाल सकता है, जिससे संभवतः रीएंट्रेंसी हो सकती है। यदि आप ठीक से रीएंट्रेंसी अटैक्स (reentrancy attacks) के खिलाफ बचाव करते हैं, तो इसके बारे में चिंता करने की कोई आवश्यकता नहीं है।
safeTransfer डिनायल ऑफ़ सर्विस (denial of service)
एक दुर्भावनापूर्ण रिसीवर (malicious receiver) onERC721Received() के अंदर रिवर्ट (revert) करके या सारी गैस की खपत करने के लिए एक लूप का उपयोग करके जबरन ट्रांजैक्शन्स को रिवर्ट कर सकता है। आपको यह नहीं मान लेना चाहिए कि किसी मनमाने एड्रेस पर safeTransferFrom सफल होगा।
डेटा के साथ safeTransferFrom और यह क्यों मौजूद है - व्यावहारिक उपयोग के मामले और एफिशिएंसी (Efficiency)
ERC721 निर्दिष्ट करता है कि दो safeTransferFrom फंक्शन्स मौजूद हैं:
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
दूसरे वाले में एक अतिरिक्त data पैरामीटर होता है। निम्नलिखित उदाहरण onERC721Received() के साथ डेटा पैरामीटर का उपयोग करना प्रदर्शित करेगा।
गैस कुशल स्टैकिंग (Gas efficient staking), अप्रूवल को बायपास (bypassing) करना
एक बहुत ही सामान्य पैटर्न स्टैकिंग (staking) के उद्देश्य से किसी कॉन्ट्रैक्ट में NFT जमा (deposit) करना है। बेशक NFT स्मार्ट कॉन्ट्रैक्ट के “अंदर” नहीं होता है, बल्कि उस विशेष id का ownerOf स्टैकिंग कॉन्ट्रैक्ट होता है, और स्टैकिंग कॉन्ट्रैक्ट के पास ओरिजिनल ओनर (original owner) का ट्रैक रखने के लिए कुछ बुक कीपिंग (book keeping) होती है।
इसे करने का एक सामान्य, लेकिन अकुशल (inefficient) तरीका निम्नलिखित कोड स्निपेट (snippet) में दिखाया गया है। यह अकुशल होने का कारण यह है कि इसके लिए यूजर को deposit() कॉल करने से पहले स्टैकिंग (Staking) को approve करने की आवश्यकता होती है। हमने ट्रांसफर के दौरान पैरामीटर्स जोड़ने के उदाहरण के रूप में स्टैकिंग करते समय वोट देने का विकल्प जोड़ा है।
contract Staking {
struct Stake {
uint8 voteId;
address originalOwner;
}
mapping(uint256 id => Stake stake) public stakes;
function deposit(uint256 id, uint8 _voteId) external {
stakes[id] = Stake({voteId: _voteId, originalOwner: msg.sender});
// user must approve Staking contract first
nft.transferFrom(msg.sender, address(this), id);
}
function withdraw(uint256 id) external {
require(msg.sender == staked[id].originalOwner, "not original owner");
delete stakes[id];
nft.transferFrom(address(this), msg.sender, id);
}
}
एक अधिक गैस कुशल (gas efficient) विकल्प बस एसेट (asset) को अंदर ले जाने के लिए safeTransfer करना है। यह यूजर को approve स्टेप को छोड़ने (skip) की अनुमति देता है। यूजर एरर्स (errors) को कम करने के लिए फ्रंटएंड (frontend) एप्लीकेशन द्वारा निश्चित रूप से इसे हैंडल (handle) करने की आवश्यकता है। ध्यान दें कि vote पैरामीटर अब data आर्गुमेंट में समाहित है।
contract ImprovedStaking is IERC721Receiver {
struct Stake {
uint8 voteId;
address originalOwner;
}
mapping(uint256 id => Stake stake) public stakes;
function onERC721Received(address operator, address from, uint256 id, bytes calldata data) external {
// important safety to check only allow calls from our intended NFT
require(msg.sender == address(nft), "wrong NFT");
uint8 voteId = abi.decode(data, (uint8));
originalOwners[id] = from; // from is the original owner
}
function withdraw(uint256 id) external {
address originalOwner = stakes[id].originalOwner;
require(msg.sender == originalOwner, "not owner");
delete stakes[id];
nft.transferFrom(address(this), msg.sender, id);
}
}
फिर से, यह लागू करना अत्यंत महत्वपूर्ण है कि onERC721Received में msg.sender NFT कॉन्ट्रैक्ट है अन्यथा कोई भी फंक्शन को कॉल कर सकता है और इसे दुर्भावनापूर्ण (malicious) डेटा प्रदान कर सकता है।
उपरोक्त उदाहरण स्पष्ट करता है कि डेटा पैरामीटर कैसे उपयोगी हो सकता है। bytes calldata data पैरामीटर हमें किसी भी डेटा को एन्कोड (encode) करने की सुविधा (flexibility) देता है जिसकी हमें परवाह है। हमने केवल uint8 voteId शामिल किया है, लेकिन अगर हम intendedDuration, delegate, और अन्य पैरामीटर्स भी जोड़ना चाहते हैं, तो हम
(voteId, intendedDuration, delegate) = abi.decode(data, (uint8, uint256, address) कर सकते हैं।
_mint और transferFrom की तुलना में _safeMint और safeTransferFrom के लिए गैस संबंधी विचार
यदि आप उम्मीद करते हैं कि रिसीवर (receiver) एक EOA होगा, तो transferFrom या _mint का उपयोग करना बेहतर है क्योंकि यह जाँचना कि क्या वे एक कॉन्ट्रैक्ट हैं (जो _safeMint और safeTransferFrom करते हैं) गैस की बर्बादी होगी।
burn फंक्शन और NFT डिस्ट्रक्शन (Destruction)
एक NFT को जीरो एड्रेस पर ट्रांसफर करके बर्न (burn) किया जा सकता है। NFTs को बर्न (burn) करने में सक्षम होना आधिकारिक तौर पर ERC स्पेसिफिकेशन का हिस्सा नहीं है, इसलिए कॉन्ट्रैक्ट्स को इस ऑपरेशन (operation) का समर्थन करने की आवश्यकता नहीं है।
ERC721 इम्प्लीमेंटेशन्स (Implementations)
OpenZeppelin इम्प्लीमेंटेशन डेवलपर्स के लिए सबसे अधिक बिगिनर-फ्रेंडली (beginner friendly) लाइब्रेरी है और यदि इसे बाकी अपग्रेडेबल (upgradeable) कॉन्ट्रैक्ट्स के साथ उपयोग किया जाता है तो यह आदर्श है। अधिक अनुभवी डेवलपर्स को Solady ERC721 इम्प्लीमेंटेशन पर विचार करना चाहिए क्योंकि यह गैस की काफी बचत की पेशकश करेगा।
अपने ज्ञान का परीक्षण करें
चूँकि ERC721 बहुत सर्वव्यापी (ubiquitous) है, गंभीर Solidity डेवलपर्स को प्रोटोकॉल को पूरी तरह से समझना चाहिए और मेमोरी से स्क्रैच (scratch) से एक को इम्प्लीमेंट करने में सक्षम होना चाहिए। यह देखने के लिए कि क्या आप सब कुछ समझ गए हैं, ERC721 के लिए निम्नलिखित सिक्योरिटी एक्सरसाइज को हल करने का प्रयास करें:
Overmint 1 (RareSkills Riddles)
Overmint 2 (RareSkills Riddles)
Diamond Hands (RareSkills Riddles)
Jpeg Sniper (Mr Steal Yo Crypto)
सीखते रहें: ERC721 Enumerable
ERC721 का Enumerable एक्सटेंशन एक स्मार्ट कॉन्ट्रैक्ट को एक एड्रेस द्वारा ओन (own) किए गए सभी NFTs की सूची बनाने की अनुमति देता है। सीखना जारी रखने के लिए ERC721 Enumerable के बारे में हमारा लेख देखें।
RareSkills के साथ और जानें
प्रोग्राम के बारे में अधिक जानने के लिए कृपया हमारा उद्योग में अग्रणी Solidity bootcamp देखें।
मूल रूप से 8 नवंबर, 2023 को प्रकाशित