मुख्य सामग्री पर जाएं

ERC-20 कॉन्ट्रैक्ट वॉक-थ्रू

Solidity
erc-20
शुरआती
ओरी पोमेरेंट्ज़
9 मार्च 2021
32 मिनट का पठन

परिचय

एथेरियम के लिए सबसे आम उपयोगों में से एक समूह के लिए एक व्यापार योग्य टोकन बनाना है, एक अर्थ में उनकी अपनी मुद्रा। ये टोकन आमतौर पर एक मानक का पालन करते हैं, ERC-20। यह मानक लिक्विडिटी पूल और वॉलेट जैसे उपकरण लिखना संभव बनाता है, जो सभी ERC-20 टोकन के साथ काम करते हैं। इस लेख में हम ओपनज़ेपेलिन सॉलिडिटी ERC20 इम्प्लीमेंटेशन (opens in a new tab), और इंटरफ़ेस परिभाषा (opens in a new tab) का विश्लेषण करेंगे।

यह एनोटेटेड सोर्स कोड है। अगर आप ERC-20 लागू करना चाहते हैं, तो यह ट्यूटोरियल पढ़ें (opens in a new tab)

इंटरफ़ेस

ERC-20 जैसे मानक का उद्देश्य कई टोकन कार्यान्वयनों को अनुमति देना है जो वॉलेट और विकेन्द्रीकृत एक्सचेंजों जैसे अनुप्रयोगों में इंटरऑपरेबल हैं। इसे प्राप्त करने के लिए, हम एक इंटरफ़ेस (opens in a new tab) बनाते हैं। कोई भी कोड जिसे टोकन कॉन्ट्रैक्ट का उपयोग करने की आवश्यकता है, वह इंटरफ़ेस में समान परिभाषाओं का उपयोग कर सकता है और इसका उपयोग करने वाले सभी टोकन कॉन्ट्रैक्ट के साथ संगत हो सकता है, चाहे वह मेटामास्क जैसा वॉलेट हो, etherscan.io जैसा डैप हो, या लिक्विडिटी पूल जैसा कोई अलग कॉन्ट्रैक्ट हो।

ERC-20 इंटरफ़ेस का चित्रण

यदि आप एक अनुभवी प्रोग्रामर हैं, तो आपको शायद जावा (opens in a new tab) या यहां तक कि C हेडर फ़ाइलों (opens in a new tab) में भी समान संरचनाएं देखने को याद होंगी।

यह ओपनज़ेपेलिन से ERC-20 इंटरफेस (opens in a new tab) की एक परिभाषा है। यह मानव पठनीय मानक (opens in a new tab) का सॉलिडिटी कोड में अनुवाद है। बेशक, इंटरफ़ेस स्वयं यह परिभाषित नहीं करता है कि कुछ कैसे करना है। यह नीचे दिए गए कॉन्ट्रैक्ट सोर्स कोड में समझाया गया है।

 

// SPDX-License-Identifier: MIT

सॉलिडिटी फ़ाइलों में एक लाइसेंस पहचानकर्ता शामिल होना चाहिए। आप यहां लाइसेंसों की सूची देख सकते हैं (opens in a new tab)। यदि आपको एक अलग लाइसेंस की आवश्यकता है, तो बस इसे कमेंट में समझाएं।

 

pragma solidity >=0.6.0 <0.8.0;

सॉलिडिटी भाषा अभी भी तेजी से विकसित हो रही है, और नए संस्करण पुराने कोड के साथ संगत नहीं हो सकते हैं (यहां देखें (opens in a new tab))। इसलिए, यह एक अच्छा विचार है कि न केवल भाषा का न्यूनतम संस्करण निर्दिष्ट किया जाए, बल्कि एक अधिकतम संस्करण भी, नवीनतम जिसके साथ आपने कोड का परीक्षण किया है।

 

/**
 * @dev EIP में परिभाषित ERC20 मानक का इंटरफ़ेस।
 */

कमेंट में @dev नैटस्पेक फॉर्मेट (opens in a new tab) का हिस्सा है, जिसका उपयोग सोर्स कोड से प्रलेखन बनाने के लिए किया जाता है।

 

interface IERC20 {

परंपरा के अनुसार, इंटरफ़ेस के नाम I से शुरू होते हैं।

 

    /**
     * @dev अस्तित्व में टोकन की मात्रा लौटाता है।
     */
    function totalSupply() external view returns (uint256);

यह फ़ंक्शन external है, जिसका अर्थ है इसे केवल अनुबंध के बाहर से कॉल किया जा सकता है (opens in a new tab)। यह कॉन्ट्रैक्ट में टोकन की कुल आपूर्ति लौटाता है। यह मान एथेरियम में सबसे सामान्य प्रकार, अनसाईंड 256 बिट्स का उपयोग करके लौटाया जाता है (256 बिट EVM का नेटिव शब्द आकार है)। यह फ़ंक्शन एक view भी है, जिसका अर्थ है कि यह स्टेट को नहीं बदलता है, इसलिए इसे ब्लॉकचेन के प्रत्येक नोड पर चलाने के बजाय एकल नोड पर निष्पादित किया जा सकता है। इस तरह का फ़ंक्शन लेनदेन उत्पन्न नहीं करता है और इसमें गैस नहीं लगती है।

ध्यान दें: सिद्धांत रूप में ऐसा लग सकता है कि किसी कॉन्ट्रैक्ट का निर्माता वास्तविक मूल्य से कम कुल आपूर्ति लौटाकर धोखा दे सकता है, जिससे प्रत्येक टोकन वास्तविक से अधिक मूल्यवान प्रतीत होता है। हालांकि, यह डर ब्लॉकचेन की वास्तविक प्रकृति को नजरअंदाज करता है। ब्लॉकचेन पर होने वाली हर चीज़ को हर नोड द्वारा सत्यापित किया जा सकता है। इसे प्राप्त करने के लिए, प्रत्येक कॉन्ट्रैक्ट का मशीन भाषा कोड और स्टोरेज हर नोड पर उपलब्ध है। हालांकि आपको अपने अनुबंध के लिए सॉलिडिटी कोड प्रकाशित करने की आवश्यकता नहीं है, लेकिन कोई भी आपको गंभीरता से नहीं लेगा जब तक कि आप स्रोत कोड और सॉलिडिटी का वह संस्करण प्रकाशित नहीं करते जिसके साथ इसे संकलित किया गया था, ताकि इसे आपके द्वारा प्रदान किए गए मशीन भाषा कोड के विरुद्ध सत्यापित किया जा सके। उदाहरण के लिए, यह अनुबंध देखें (opens in a new tab)

 

    /**
     * @dev `खाते` के स्वामित्व वाले टोकन की राशि लौटाता है।
     */
    function balanceOf(address account) external view returns (uint256);

जैसा कि नाम से पता चलता है, balanceOf एक खाते का बैलेंस लौटाता है। एथेरियम खातों को address प्रकार का उपयोग करके सॉलिडिटी में पहचाना जाता है, जो 160 बिट्स रखता है। यह external और view भी है।

 

    /**
     * @dev कॉलर के खाते से `recipient` को `राशि` टोकन ले जाता है।
     *
     * एक बूलियन मान लौटाता है जो यह दर्शाता है कि ऑपरेशन सफल हुआ या नहीं।
     *
     * {ट्रांसफर} इवेंट उत्सर्जित करता है।
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

transfer फ़ंक्शन कॉलर से एक अलग पते पर टोकन स्थानांतरित करता है। इसमें स्टेट में बदलाव शामिल है, इसलिए यह view नहीं है। जब कोई यूज़र इस फ़ंक्शन को कॉल करता है तो यह एक लेनदेन बनाता है और इसमें गैस लगती है। यह एक इवेंट, Transfer भी उत्सर्जित करता है, ताकि ब्लॉकचेन पर सभी को इवेंट की सूचना दी जा सके।

फ़ंक्शन में दो अलग-अलग प्रकार के कॉलर्स के लिए दो प्रकार के आउटपुट होते हैं:

  • उपयोगकर्ता जो सीधे उपयोगकर्ता इंटरफ़ेस से फ़ंक्शन को कॉल करते हैं। आमतौर पर उपयोगकर्ता एक लेनदेन सबमिट करता है और प्रतिक्रिया की प्रतीक्षा नहीं करता है, जिसमें अनिश्चित काल तक का समय लग सकता है। उपयोगकर्ता यह देख सकता है कि क्या हुआ लेनदेन रसीद (जिसे लेनदेन हैश द्वारा पहचाना जाता है) को देखकर या ट्रांसफर इवेंट को देखकर।
  • अन्य अनुबंध, जो समग्र लेनदेन के हिस्से के रूप में फ़ंक्शन को कॉल करते हैं। उन अनुबंधों को तुरंत परिणाम मिलता है, क्योंकि वे एक ही लेनदेन में चलते हैं, इसलिए वे फ़ंक्शन वापसी मान का उपयोग कर सकते हैं।

अनुबंध की स्थिति को बदलने वाले अन्य कार्यों द्वारा समान प्रकार का आउटपुट बनाया जाता है।

 

अलाउंस एक खाते को कुछ ऐसे टोकन खर्च करने की अनुमति देते हैं जो एक अलग मालिक के हैं। यह उपयोगी है, उदाहरण के लिए, उन अनुबंधों के लिए जो विक्रेताओं के रूप में कार्य करते हैं। अनुबंध इवेंट के लिए मॉनिटर नहीं कर सकते हैं, इसलिए यदि कोई खरीदार सीधे विक्रेता अनुबंध को टोकन स्थानांतरित करता है तो उस अनुबंध को यह नहीं पता होगा कि इसका भुगतान किया गया था। इसके बजाय, खरीदार विक्रेता अनुबंध को एक निश्चित राशि खर्च करने की अनुमति देता है, और विक्रेता उस राशि को स्थानांतरित करता है। यह एक फ़ंक्शन के माध्यम से किया जाता है जिसे विक्रेता अनुबंध कॉल करता है, इसलिए विक्रेता अनुबंध जान सकता है कि यह सफल था या नहीं।

    /**
     * @dev टोकन की शेष संख्या लौटाता है जिसे `spender` को
     * {transferFrom} के माध्यम से `owner` की ओर से खर्च करने की अनुमति दी जाएगी। यह
     * डिफ़ॉल्ट रूप से शून्य है।
     *
     * जब {approve} या {transferFrom} को कॉल किया जाता है तो यह मान बदल जाता है।
     */
    function allowance(address owner, address spender) external view returns (uint256);

allowance फ़ंक्शन किसी को भी यह देखने के लिए क्वेरी करने देता है कि एक पता (owner) दूसरे पते (spender) को कितना खर्च करने देता है।

 

approve फ़ंक्शन एक अलाउंस बनाता है। इसके दुरुपयोग के बारे में संदेश पढ़ना सुनिश्चित करें। एथेरियम में आप अपने स्वयं के लेनदेन के क्रम को नियंत्रित करते हैं, लेकिन आप उस क्रम को नियंत्रित नहीं कर सकते जिसमें अन्य लोगों के लेनदेन निष्पादित होंगे, जब तक कि आप अपना स्वयं का लेनदेन तब तक सबमिट नहीं करते जब तक आप यह नहीं देखते कि दूसरे पक्ष का लेनदेन हो गया है।

 

अंत में, transferFrom का उपयोग खर्च करने वाले द्वारा वास्तव में भत्ता खर्च करने के लिए किया जाता है।

 

ये इवेंट तब उत्सर्जित होते हैं जब ERC-20 अनुबंध की स्थिति बदल जाती है।

वास्तविक अनुबंध

यह वास्तविक अनुबंध है जो ERC-20 मानक को लागू करता है, यहां से लिया गया (opens in a new tab)। इसका उपयोग जैसा है वैसा करने के लिए नहीं है, लेकिन आप इसे कुछ प्रयोग करने योग्य बनाने के लिए इससे इनहेरिट (opens in a new tab) कर सकते हैं।

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

 

इंपोर्ट स्टेटमेंट

ऊपर दी गई इंटरफ़ेस परिभाषाओं के अलावा, अनुबंध परिभाषा दो अन्य फ़ाइलों को आयात करती है:


import "../../GSN/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
  • GSN/Context.sol ओपनजीएसएन (opens in a new tab) का उपयोग करने के लिए आवश्यक परिभाषाएं हैं, एक ऐसी प्रणाली जो ईथर के बिना उपयोगकर्ताओं को ब्लॉकचेन का उपयोग करने की अनुमति देती है। ध्यान दें कि यह एक पुराना संस्करण है, यदि आप ओपनजीएसएन के साथ एकीकृत करना चाहते हैं इस ट्यूटोरियल का उपयोग करें (opens in a new tab)
  • SafeMath लाइब्रेरी (opens in a new tab), जो सॉलिडिटी संस्करणों <0.8.0 के लिए अरिथमैटिक ओवरफ़्लो/अंडरफ़्लो को रोकती है। सॉलिडिटी ≥0.8.0 में, अंकगणितीय संचालन स्वचालित रूप से ओवरफ़्लो/अंडरफ़्लो पर वापस आ जाते हैं, जिससे SafeMath अनावश्यक हो जाता है। यह अनुबंध पुराने संकलक संस्करणों के साथ पश्चगामी संगतता के लिए SafeMath का उपयोग करता है।

 

यह टिप्पणी अनुबंध के उद्देश्य को बताती है।

अनुबंध परिभाषा

contract ERC20 is Context, IERC20 {

यह पंक्ति इनहेरिटेंस को निर्दिष्ट करती है, इस मामले में ऊपर से IERC20 और Context से, ओपनजीएसएन के लिए।

 


    using SafeMath for uint256;

यह पंक्ति SafeMath लाइब्रेरी को uint256 प्रकार से जोड़ती है। आप इस लाइब्रेरी को यहां (opens in a new tab) पा सकते हैं।

वैरिएबल परिभाषाएं

ये परिभाषाएँ अनुबंध के स्टेट वैरिएबल को निर्दिष्ट करती हैं। इन वैरिएबल को private घोषित किया गया है, लेकिन इसका केवल यह मतलब है कि ब्लॉकचेन पर अन्य अनुबंध उन्हें नहीं पढ़ सकते हैं। ब्लॉकचेन पर कोई राज नहीं है, हर नोड पर सॉफ्टवेयर में हर ब्लॉक पर हर अनुबंध की स्टेट होती है। परंपरा के अनुसार, स्टेट वैरिएबल का नाम _<something> होता है।

पहले दो वैरिएबल मैपिंग (opens in a new tab) हैं, मतलब वे मोटे तौर पर एसोसिएटिव एरे (opens in a new tab) के समान व्यवहार करते हैं, सिवाय इसके कि कुंजी संख्यात्मक मान हैं। भंडारण केवल उन प्रविष्टियों के लिए आवंटित किया जाता है जिनके मान डिफ़ॉल्ट (शून्य) से अलग हैं।

    mapping (address => uint256) private _balances;

पहली मैपिंग, _balances, इस टोकन के पते और उनके संबंधित बैलेंस हैं। बैलेंस तक पहुँचने के लिए, इस सिंटैक्स का उपयोग करें: _balances[<address>]

 

    mapping (address => mapping (address => uint256)) private _allowances;

यह वैरिएबल, _allowances, पहले बताए गए अलाउंस को स्टोर करता है। पहला इंडेक्स टोकन का मालिक है, और दूसरा अलाउंस के साथ अनुबंध है। पता A पते B के खाते से कितनी राशि खर्च कर सकता है, यह जानने के लिए, _allowances[B][A] का उपयोग करें।

 

    uint256 private _totalSupply;

जैसा कि नाम से पता चलता है, यह वैरिएबल टोकन की कुल आपूर्ति का ट्रैक रखता है।

 

    string private _name;
    string private _symbol;
    uint8 private _decimals;

इन तीन वैरिएबल का उपयोग पठनीयता में सुधार के लिए किया जाता है। पहले दो स्व-व्याख्यात्मक हैं, लेकिन _decimals नहीं है।

एक तरफ, एथेरियम में फ्लोटिंग पॉइंट या भिन्नात्मक वैरिएबल नहीं हैं। दूसरी ओर, मनुष्य टोकन को विभाजित करने में सक्षम होना पसंद करते हैं। लोगों द्वारा मुद्रा के लिए सोने को अपनाने का एक कारण यह था कि जब कोई गाय के मूल्य की बत्तख खरीदना चाहता था तो छुट्टे बनाना मुश्किल था।

समाधान पूर्णांकों का ट्रैक रखना है, लेकिन वास्तविक टोकन के बजाय एक भिन्नात्मक टोकन की गणना करना है जो लगभग बेकार है। ईथर के मामले में, भिन्नात्मक टोकन को wei कहा जाता है, और 10^18 wei एक ETH के बराबर होता है। लिखते समय, 10,000,000,000,000 wei लगभग एक अमेरिकी या यूरो सेंट के बराबर है।

एप्लिकेशन को यह जानना होगा कि टोकन बैलेंस कैसे प्रदर्शित करें। यदि किसी उपयोगकर्ता के पास 3,141,000,000,000,000,000 wei है, तो क्या वह 3.14 ETH है? 31.41 ETH? 3,141 ETH? ईथर के मामले में इसे ETH के लिए 10^18 wei परिभाषित किया गया है, लेकिन अपने टोकन के लिए आप एक अलग मान चुन सकते हैं। यदि टोकन को विभाजित करने का कोई मतलब नहीं है, तो आप शून्य के _decimals मान का उपयोग कर सकते हैं। यदि आप ETH के समान मानक का उपयोग करना चाहते हैं, तो मान 18 का उपयोग करें।

कंस्ट्रक्टर

कंस्ट्रक्टर को तब कॉल किया जाता है जब अनुबंध पहली बार बनाया जाता है। परंपरा के अनुसार, फ़ंक्शन पैरामीटर का नाम <something>_ होता है।

यूज़र इंटरफ़ेस फ़ंक्शंस

ये फ़ंक्शन, name, symbol, और decimals उपयोगकर्ता इंटरफ़ेस को आपके अनुबंध के बारे में जानने में मदद करते हैं ताकि वे इसे ठीक से प्रदर्शित कर सकें।

रिटर्न प्रकार string memory है, जिसका अर्थ है मेमोरी में संग्रहीत एक स्ट्रिंग लौटाना। वैरिएबल, जैसे स्ट्रिंग्स, तीन स्थानों पर संग्रहीत किए जा सकते हैं:

जीवनकालअनुबंध एक्सेसगैस लागत
मेमोरीफ़ंक्शन कॉलपढ़ें/लिखेंदसियों या सैकड़ों (उच्च स्थानों के लिए उच्च)
Calldataफ़ंक्शन कॉलकेवल पढ़ेंरिटर्न प्रकार के रूप में उपयोग नहीं किया जा सकता है, केवल एक फ़ंक्शन पैरामीटर प्रकार
स्टोरेजबदलने तकपढ़ें/लिखेंउच्च (पढ़ने के लिए 800, लिखने के लिए 20k)

इस मामले में, memory सबसे अच्छा विकल्प है।

टोकन जानकारी पढ़ें

ये फ़ंक्शन हैं जो टोकन के बारे में जानकारी प्रदान करते हैं, या तो कुल आपूर्ति या किसी खाते का बैलेंस।

    /**
     * @dev {IERC20-totalSupply} देखें।
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

totalSupply फ़ंक्शन टोकन की कुल आपूर्ति लौटाता है।

 

    /**
     * @dev {IERC20-balanceOf} देखें।
     */
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

किसी खाते का बैलेंस पढ़ें। ध्यान दें कि किसी को भी किसी और के खाते का बैलेंस प्राप्त करने की अनुमति है। इस जानकारी को छिपाने की कोशिश करने का कोई मतलब नहीं है, क्योंकि यह वैसे भी हर नोड पर उपलब्ध है। ब्लॉकचेन पर कोई रहस्य नहीं है।

टोकन स्थानांतरित करें

transfer फ़ंक्शन को प्रेषक के खाते से किसी भिन्न खाते में टोकन स्थानांतरित करने के लिए कहा जाता है। ध्यान दें कि भले ही यह एक बूलियन मान लौटाता है, वह मान हमेशा सत्य होता है। यदि ट्रांसफर विफल हो जाता है तो अनुबंध कॉल को वापस कर देता है।

 

        _transfer(_msgSender(), recipient, amount);
        return true;
    }

_transfer फ़ंक्शन वास्तविक कार्य करता है। यह एक निजी फ़ंक्शन है जिसे केवल अन्य अनुबंध कार्यों द्वारा ही कॉल किया जा सकता है। परंपरा के अनुसार निजी कार्यों का नाम _<something> होता है, जो स्टेट वैरिएबल के समान है।

आम तौर पर सॉलिडिटी में हम संदेश भेजने वाले के लिए msg.sender का उपयोग करते हैं। हालांकि, यह ओपनजीएसएन (opens in a new tab) को तोड़ता है। यदि हम अपने टोकन के साथ ईथरलेस लेनदेन की अनुमति देना चाहते हैं, तो हमें _msgSender() का उपयोग करने की आवश्यकता है। यह सामान्य लेनदेन के लिए msg.sender लौटाता है, लेकिन ईथरलेस के लिए मूल हस्ताक्षरकर्ता लौटाता है न कि उस अनुबंध को जिसने संदेश रिले किया था।

अलाउंस फ़ंक्शंस

ये फ़ंक्शन हैं जो अलाउंस की कार्यक्षमता को लागू करते हैं: allowance, approve, transferFrom, और _approve। इसके अतिरिक्त, ओपनज़ेपेलिन कार्यान्वयन सुरक्षा में सुधार करने वाली कुछ सुविधाओं को शामिल करने के लिए मूल मानक से परे जाता है: increaseAllowance, और decreaseAllowance

अलाउंस फ़ंक्शन

    /**
     * @dev {IERC20-अलाउंस} देखें।
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

allowance फ़ंक्शन सभी को किसी भी अलाउंस की जांच करने की अनुमति देता है।

अप्रूव फ़ंक्शन

    /**
     * @dev {IERC20-अनुमोदन} देखें।
     *
     * आवश्यकताएं:
     *
     * - `खर्च करने वाला` शून्य पता नहीं हो सकता।
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {

इस फ़ंक्शन को अलाउंस बनाने के लिए कहा जाता है। यह ऊपर दिए गए transfer फ़ंक्शन के समान है:

  • फ़ंक्शन बस एक आंतरिक फ़ंक्शन (इस मामले में, _approve) को कॉल करता है जो वास्तविक कार्य करता है।
  • फ़ंक्शन या तो true (यदि सफल हो) लौटाता है या रिवर्ट करता है (यदि नहीं)।

 

        _approve(_msgSender(), spender, amount);
        return true;
    }

हम उन जगहों की संख्या को कम करने के लिए आंतरिक कार्यों का उपयोग करते हैं जहां स्टेट परिवर्तन होते हैं। कोई भी फ़ंक्शन जो स्टेट को बदलता है, एक संभावित सुरक्षा जोखिम है जिसकी सुरक्षा के लिए ऑडिट करने की आवश्यकता है। इस तरह हमारे गलत होने की संभावना कम होती है।

transferFrom फ़ंक्शन

यह वह फ़ंक्शन है जिसे एक स्पेंडर अलाउंस खर्च करने के लिए कॉल करता है। इसके लिए दो संचालन की आवश्यकता होती है: खर्च की जा रही राशि को स्थानांतरित करें और उस राशि से अलाउंस कम करें।

 

a.sub(b, "संदेश") फ़ंक्शन कॉल दो काम करता है। सबसे पहले, यह a-b की गणना करता है, जो नया अलाउंस है। दूसरा, यह जांचता है कि यह परिणाम नकारात्मक नहीं है। यदि यह नकारात्मक है तो कॉल दिए गए संदेश के साथ रिवर्ट हो जाता है। ध्यान दें कि जब कोई कॉल रिवर्ट होता है तो उस कॉल के दौरान पहले की गई कोई भी प्रोसेसिंग अनदेखा कर दी जाती है इसलिए हमें _transfer को पूर्ववत करने की आवश्यकता नहीं है।

        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount,
             "ERC20: transfer amount exceeds allowance"));
        return true;
    }

ओपनज़ेपेलिन सुरक्षा परिवर्धन

गैर-शून्य भत्ते को किसी अन्य गैर-शून्य मान पर सेट करना खतरनाक है, क्योंकि आप केवल अपने स्वयं के लेनदेन के क्रम को नियंत्रित करते हैं, किसी और के नहीं। कल्पना कीजिए कि आपके पास दो उपयोगकर्ता हैं, ऐलिस जो भोली है और बिल जो बेईमान है। ऐलिस बिल से कुछ सेवा चाहती है, जिसकी उसे लगता है कि कीमत पांच टोकन है - इसलिए वह बिल को पांच टोकन का भत्ता देती है।

फिर कुछ बदलता है और बिल की कीमत दस टोकन तक बढ़ जाती है। ऐलिस, जो अभी भी सेवा चाहती है, एक लेनदेन भेजती है जो बिल के भत्ते को दस पर सेट करती है। जिस क्षण बिल इस नए लेनदेन को लेनदेन पूल में देखता है, वह एक लेनदेन भेजता है जो ऐलिस के पांच टोकन खर्च करता है और इसकी गैस कीमत बहुत अधिक होती है ताकि इसे तेजी से माइन किया जा सके। इस तरह बिल पहले पांच टोकन खर्च कर सकता है और फिर, एक बार जब ऐलिस का नया भत्ता माइन हो जाता है, तो पंद्रह टोकन की कुल कीमत के लिए दस और खर्च कर सकता है, जो ऐलिस द्वारा अधिकृत करने का इरादा था। इस तकनीक को फ्रंट-रनिंग (opens in a new tab) कहा जाता है

ऐलिस लेनदेनऐलिस नॉन्सबिल लेनदेनबिल नॉन्सबिल का भत्ताऐलिस से बिल की कुल आय
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
approve(Bill, 10)11105
transferFrom(Alice, Bill, 10)10,124015

इस समस्या से बचने के लिए, ये दो फ़ंक्शन (increaseAllowance और decreaseAllowance) आपको एक विशिष्ट राशि से भत्ते को संशोधित करने की अनुमति देते हैं। तो अगर बिल ने पहले ही पांच टोकन खर्च कर दिए थे, तो वह सिर्फ पांच और खर्च कर पाएगा। समय के आधार पर, यह दो तरीकों से काम कर सकता है, दोनों में बिल को केवल दस टोकन मिलते हैं:

A:

ऐलिस लेनदेनऐलिस नॉन्सबिल लेनदेनबिल नॉन्सबिल का भत्ताऐलिस से बिल की कुल आय
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
increaseAllowance(Bill, 5)110+5 = 55
transferFrom(Alice, Bill, 5)10,124010

B:

ऐलिस लेनदेनऐलिस नॉन्सबिल लेनदेनबिल नॉन्सबिल का भत्ताऐलिस से बिल की कुल आय
approve(Bill, 5)1050
increaseAllowance(Bill, 5)115+5 = 100
transferFrom(Alice, Bill, 10)10,124010

a.add(b) फ़ंक्शन एक सुरक्षित जोड़ है। इस असंभावित मामले में कि a+b>=2^256 यह उस तरह से लपेटता नहीं है जिस तरह से सामान्य जोड़ होता है।

टोकन जानकारी को संशोधित करने वाले फ़ंक्शंस

ये चार फ़ंक्शन हैं जो वास्तविक कार्य करते हैं: _transfer, _mint, _burn, और _approve

_transfer फ़ंक्शन

यह फ़ंक्शन, _transfer, एक खाते से दूसरे खाते में टोकन स्थानांतरित करता है। इसे दोनों transfer (प्रेषक के अपने खाते से स्थानांतरण के लिए) और transferFrom (किसी और के खाते से स्थानांतरण के लिए भत्ते का उपयोग करने के लिए) द्वारा कॉल किया जाता है।

 

        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

एथेरियम में कोई भी वास्तव में शून्य पते का मालिक नहीं है (यानी, कोई भी ऐसी निजी कुंजी नहीं जानता है जिसकी मिलान सार्वजनिक कुंजी शून्य पते में बदल जाती है)। जब लोग उस पते का उपयोग करते हैं, तो यह आमतौर पर एक सॉफ्टवेयर बग होता है - इसलिए हम विफल हो जाते हैं यदि शून्य पते का उपयोग प्रेषक या प्राप्तकर्ता के रूप में किया जाता है।

 

        _beforeTokenTransfer(sender, recipient, amount);

इस अनुबंध का उपयोग करने के दो तरीके हैं:

  1. इसे अपने स्वयं के कोड के लिए एक टेम्पलेट के रूप में उपयोग करें
  2. इससे इनहेरिट करें (opens in a new tab), और केवल उन फ़ंक्शंस को ओवरराइड करें जिन्हें आपको संशोधित करने की आवश्यकता है

दूसरा तरीका बहुत बेहतर है क्योंकि ओपनज़ेपेलिन ERC-20 कोड का पहले ही ऑडिट किया जा चुका है और इसे सुरक्षित दिखाया गया है। जब आप इनहेरिटेंस का उपयोग करते हैं तो यह स्पष्ट होता है कि आप किन फ़ंक्शंस को संशोधित करते हैं, और आपके अनुबंध पर भरोसा करने के लिए लोगों को केवल उन विशिष्ट फ़ंक्शंस का ऑडिट करने की आवश्यकता होती है।

हर बार जब टोकन का आदान-प्रदान होता है तो एक फ़ंक्शन करना अक्सर उपयोगी होता है। हालांकि, _transfer एक बहुत ही महत्वपूर्ण फ़ंक्शन है और इसे असुरक्षित रूप से लिखना संभव है (नीचे देखें), इसलिए इसे ओवरराइड न करना सबसे अच्छा है। समाधान _beforeTokenTransfer है, एक हुक फ़ंक्शन (opens in a new tab)। आप इस फ़ंक्शन को ओवरराइड कर सकते हैं, और इसे प्रत्येक ट्रांसफर पर कॉल किया जाएगा।

 

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);

ये वे पंक्तियाँ हैं जो वास्तव में स्थानांतरण करती हैं। ध्यान दें कि उनके बीच कुछ भी नहीं है, और हम प्राप्तकर्ता को जोड़ने से पहले प्रेषक से स्थानांतरित राशि घटाते हैं। यह महत्वपूर्ण है क्योंकि अगर बीच में किसी अलग अनुबंध को कॉल किया गया होता, तो इसका उपयोग इस अनुबंध को धोखा देने के लिए किया जा सकता था। इस तरह ट्रांसफर परमाणु होता है, इसके बीच में कुछ भी नहीं हो सकता है।

 

        emit Transfer(sender, recipient, amount);
    }

अंत में, एक ट्रांसफर इवेंट उत्सर्जित करें। इवेंट स्मार्ट अनुबंधों के लिए सुलभ नहीं हैं, लेकिन ब्लॉकचेन के बाहर चलने वाला कोड इवेंट को सुन सकता है और उन पर प्रतिक्रिया कर सकता है। उदाहरण के लिए, एक वॉलेट ट्रैक रख सकता है कि मालिक को अधिक टोकन कब मिलते हैं।

_mint और _burn फ़ंक्शंस

ये दो फ़ंक्शन (_mint और _burn) टोकन की कुल आपूर्ति को संशोधित करते हैं। वे आंतरिक हैं और इस अनुबंध में उन्हें कॉल करने वाला कोई फ़ंक्शन नहीं है, इसलिए वे केवल तभी उपयोगी होते हैं जब आप अनुबंध से इनहेरिट करते हैं और यह तय करने के लिए अपना स्वयं का तर्क जोड़ते हैं कि किन परिस्थितियों में नए टोकन मिंट करने हैं या मौजूदा को बर्न करना है।

ध्यान दें: प्रत्येक ERC-20 टोकन का अपना व्यावसायिक तर्क होता है जो टोकन प्रबंधन को निर्धारित करता है। उदाहरण के लिए, एक निश्चित आपूर्ति अनुबंध केवल कंस्ट्रक्टर में _mint को कॉल कर सकता है और कभी भी _burn को कॉल नहीं कर सकता है। एक अनुबंध जो टोकन बेचता है, जब इसका भुगतान किया जाता है तो _mint को कॉल करेगा, और संभवतः किसी बिंदु पर _burn को कॉल करेगा ताकि भगोड़ा मुद्रास्फीति से बचा जा सके।

जब टोकन की कुल संख्या बदलती है तो _totalSupply को अपडेट करना सुनिश्चित करें।

 

_burn फ़ंक्शन लगभग _mint के समान है, सिवाय इसके कि यह दूसरी दिशा में जाता है।

_approve फ़ंक्शन

यह वह फ़ंक्शन है जो वास्तव में अलाउंस निर्दिष्ट करता है। ध्यान दें कि यह एक मालिक को एक भत्ता निर्दिष्ट करने की अनुमति देता है जो मालिक के वर्तमान बैलेंस से अधिक है। यह ठीक है क्योंकि बैलेंस ट्रांसफर के समय जांचा जाता है, जब यह अलाउंस बनाए जाने पर बैलेंस से अलग हो सकता है।

 

अनुमोदन इवेंट उत्सर्जित करें। एप्लिकेशन कैसे लिखा जाता है, इसके आधार पर, खर्च करने वाले अनुबंध को अनुमोदन के बारे में या तो मालिक द्वारा या इन घटनाओं को सुनने वाले सर्वर द्वारा बताया जा सकता है।

        emit Approval(owner, spender, amount);
    }

दशमलव वैरिएबल को संशोधित करें

यह फ़ंक्शन _decimals वैरिएबल को संशोधित करता है जिसका उपयोग उपयोगकर्ता इंटरफ़ेस को यह बताने के लिए किया जाता है कि राशि की व्याख्या कैसे करें। आपको इसे कंस्ट्रक्टर से कॉल करना चाहिए। किसी भी बाद के बिंदु पर इसे कॉल करना बेईमानी होगी, और एप्लिकेशन इसे संभालने के लिए डिज़ाइन नहीं किए गए हैं।

हुक्स

यह ट्रांसफर के दौरान कॉल किया जाने वाला हुक फ़ंक्शन है। यह यहाँ खाली है, लेकिन अगर आपको इसकी आवश्यकता है तो आप इसे बस ओवरराइड करें।

निष्कर्ष

समीक्षा के लिए, यहां इस अनुबंध में कुछ सबसे महत्वपूर्ण विचार दिए गए हैं (मेरी राय में, आपका अलग होने की संभावना है):

  • यह कई मामलों में महत्वपूर्ण है, लेकिन यहां नहीं।ब्लॉकचेन पर कोई रहस्य नहीं हैं। कोई भी जानकारी जिसे एक स्मार्ट अनुबंध एक्सेस कर सकता है, पूरी दुनिया के लिए उपलब्ध है।
  • आप अपने स्वयं के लेनदेन के क्रम को नियंत्रित कर सकते हैं, लेकिन यह नहीं कि दूसरे लोगों का लेनदेन कब होता है। यही कारण है कि एक भत्ता बदलना खतरनाक हो सकता है, क्योंकि यह खर्च करने वाले को दोनों भत्तों का योग खर्च करने देता है।
  • uint256 प्रकार के मान रैप अराउंड होते हैं। दूसरे शब्दों में, 0-1=2^256-1। यदि वह वांछित व्यवहार नहीं है, तो आपको इसकी जांच करनी होगी (या SafeMath लाइब्रेरी का उपयोग करें जो आपके लिए ऐसा करती है)। ध्यान दें कि यह सॉलिडिटी 0.8.0 (opens in a new tab) में बदल गया।
  • एक विशिष्ट प्रकार के सभी स्टेट परिवर्तन एक विशिष्ट स्थान पर करें, क्योंकि इससे ऑडिटिंग आसान हो जाती है। यही कारण है कि हमारे पास, उदाहरण के लिए, _approve है, जिसे approve, transferFrom, increaseAllowance, और decreaseAllowance द्वारा कॉल किया जाता है।
  • स्टेट परिवर्तन परमाणु होने चाहिए, उनके बीच कोई अन्य क्रिया नहीं होनी चाहिए (जैसा कि आप _transfer में देख सकते हैं)। ऐसा इसलिए है क्योंकि स्टेट परिवर्तन के दौरान आपकी एक असंगत स्टेट होती है। उदाहरण के लिए, प्रेषक के बैलेंस से कटौती करने और प्राप्तकर्ता के बैलेंस में जोड़ने के समय के बीच, अस्तित्व में होने चाहिए उससे कम टोकन होते हैं। इसका संभावित रूप से दुरुपयोग किया जा सकता है यदि उनके बीच संचालन हों, विशेष रूप से एक अलग अनुबंध को कॉल।

अब जब आपने देख लिया है कि ओपनज़ेपेलिन ERC-20 अनुबंध कैसे लिखा जाता है, और विशेष रूप से इसे कैसे अधिक सुरक्षित बनाया जाता है, तो जाएं और अपने स्वयं के सुरक्षित अनुबंध और एप्लिकेशन लिखें।

मेरे और काम के लिए यहाँ देखें (opens in a new tab)

पेज का अंतिम अपडेट: 15 अप्रैल 2026

क्या यह ट्यूटोरियल उपयोगी था?