ERC-20 कॉन्ट्रॅक्ट वॉक-थ्रू
प्रस्तावना
Ethereum च्या सर्वात सामान्य उपयोगांपैकी एक म्हणजे एखाद्या गटाने एक ट्रेडेबल टोकन तयार करणे, एका अर्थाने त्यांचे स्वतःचे चलन. हे टोकन सामान्यतः एका मानकाचे पालन करतात, ERC-20. हे मानक लिक्विडिटी पूल आणि वॉलेटसारखी साधने लिहिणे शक्य करते, जे सर्व ERC-20 टोकनसोबत काम करतात. या लेखात आपण OpenZeppelin Solidity 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) तयार करतो. ज्या कोणत्याही कोडला टोकन कॉन्ट्रॅक्ट वापरण्याची आवश्यकता आहे, तो इंटरफेसमधील समान परिभाषा वापरू शकतो आणि ते वापरणाऱ्या सर्व टोकन कॉन्ट्रॅक्टसोबत सुसंगत असू शकतो, मग ते MetaMask सारखे वॉलेट असो, etherscan.io सारखे dapp असो, किंवा लिक्विडिटी पूल सारखे वेगळे कॉन्ट्रॅक्ट असो.
तुम्ही अनुभवी प्रोग्रामर असाल तर, तुम्हाला Java (opens in a new tab) मध्ये किंवा C हेडर फाइल्स (opens in a new tab) मध्ये समान रचना पाहिल्याचे आठवत असेल.
ही OpenZeppelin कडील ERC-20 इंटरफेस (opens in a new tab) ची परिभाषा आहे. हे मानव वाचनीय मानक (opens in a new tab) चे Solidity कोडमधील भाषांतर आहे. अर्थात, इंटरफेस स्वतः काहीही कसे करायचे हे परिभाषित करत नाही. ते खालील कॉन्ट्रॅक्ट सोर्स कोडमध्ये स्पष्ट केले आहे.
1// SPDX-License-Identifier: MITSolidity फाइल्समध्ये परवाना ओळखकर्ता समाविष्ट करणे अपेक्षित आहे. तुम्ही परवान्यांची यादी येथे पाहू शकता (opens in a new tab). तुम्हाला वेगळा परवाना हवा असल्यास, तो कमेंट्समध्ये स्पष्ट करा.
1pragma solidity >=0.6.0 <0.8.0;Solidity भाषा अजूनही वेगाने विकसित होत आहे, आणि नवीन आवृत्त्या जुन्या कोडसोबत सुसंगत असू शकत नाहीत (येथे पहा (opens in a new tab)). म्हणून, केवळ भाषेची किमान आवृत्तीच नव्हे, तर कमाल आवृत्ती, ज्या नवीनतम आवृत्तीसह तुम्ही कोडची चाचणी केली आहे, ती देखील निर्दिष्ट करणे एक चांगली कल्पना आहे.
1/**2 * @dev EIP मध्ये परिभाषित केल्यानुसार ERC20 मानकाचा इंटरफेस.3 */कमेंटमधील @dev हा NatSpec फॉरमॅट (opens in a new tab) चा भाग आहे, जो सोर्स कोडमधून
डॉक्युमेंटेशन तयार करण्यासाठी वापरला जातो.
1interface IERC20 {परंपरेनुसार, इंटरफेसची नावे I ने सुरू होतात.
1 /**2 * @dev अस्तित्वात असलेल्या टोकनची संख्या परत करते.3 */4 function totalSupply() external view returns (uint256);हे फंक्शन external आहे, म्हणजे ते फक्त कॉन्ट्रॅक्टच्या बाहेरूनच कॉल केले जाऊ शकते (opens in a new tab).
ते कॉन्ट्रॅक्टमधील टोकनचा एकूण पुरवठा परत करते. हे मूल्य Ethereum मधील सर्वात सामान्य प्रकार, unsigned 256 bits (256 bits हा EVM चा
नेटिव्ह वर्ड आकार आहे) वापरून परत केले जाते. हे फंक्शन view देखील आहे, ज्याचा अर्थ ते स्टेट बदलत नाही, त्यामुळे ते ब्लॉकचेनमधील प्रत्येक
नोडवर चालवण्याऐवजी एकाच नोडवर कार्यान्वित केले जाऊ शकते. या प्रकारचे फंक्शन व्यवहार तयार करत नाही आणि त्याला gas लागत नाही.
टीप: सैद्धांतिकदृष्ट्या असे दिसू शकते की कॉन्ट्रॅक्टचा निर्माता वास्तविक मूल्यापेक्षा कमी एकूण पुरवठा परत करून फसवणूक करू शकतो, ज्यामुळे प्रत्येक टोकन प्रत्यक्षात आहे त्यापेक्षा अधिक मौल्यवान दिसेल. तथापि, ती भीती ब्लॉकचेनच्या खऱ्या स्वरूपाकडे दुर्लक्ष करते. ब्लॉकचेनवर जे काही घडते ते प्रत्येक नोडद्वारे सत्यापित केले जाऊ शकते. हे साध्य करण्यासाठी, प्रत्येक कॉन्ट्रॅक्टचा मशीन लँग्वेज कोड आणि स्टोरेज प्रत्येक नोडवर उपलब्ध आहे. तुम्ही तुमच्या कॉन्ट्रॅक्टसाठी Solidity कोड प्रकाशित करणे आवश्यक नसले तरी, जोपर्यंत तुम्ही सोर्स कोड आणि ज्या Solidity आवृत्तीसह तो संकलित केला होता ती प्रकाशित करत नाही तोपर्यंत कोणीही तुम्हाला गांभीर्याने घेणार नाही, जेणेकरून तुम्ही प्रदान केलेल्या मशीन लँग्वेज कोडविरुद्ध ते सत्यापित केले जाऊ शकते. उदाहरणार्थ, हा कॉन्ट्रॅक्ट (opens in a new tab) पहा.
1 /**2 * @dev `account` च्या मालकीच्या टोकनची संख्या परत करते.3 */4 function balanceOf(address account) external view returns (uint256);नावाप्रमाणेच, balanceOf खात्याची शिल्लक परत करते. Ethereum खाती Solidity मध्ये address प्रकार वापरून ओळखली जातात, ज्यात 160 बिट्स असतात.
ते external आणि view देखील आहे.
1 /**2 * @dev कॉलरच्या खात्यातून `recipient` कडे `amount` टोकन हलवते.3 *4 * ऑपरेशन यशस्वी झाले की नाही हे दर्शवणारे बुलियन मूल्य परत करते.5 *6 * एक {Transfer} इव्हेंट उत्सर्जित करते.7 */8 function transfer(address recipient, uint256 amount) external returns (bool);transfer फंक्शन कॉलरकडून दुसऱ्या पत्त्यावर टोकन हस्तांतरित करते. यात स्टेटमध्ये बदल समाविष्ट आहे, म्हणून ते view नाही.
जेव्हा वापरकर्ता हे फंक्शन कॉल करतो तेव्हा ते एक व्यवहार तयार करते आणि त्याला गॅस लागतो. ते एक इव्हेंट, Transfer, देखील उत्सर्जित करते, ज्यामुळे ब्लॉकचेनवरील
सर्वांना या घटनेची माहिती मिळते.
फंक्शनमध्ये दोन वेगवेगळ्या प्रकारच्या कॉलर्ससाठी दोन प्रकारचे आउटपुट आहेत:
- जे वापरकर्ते थेट वापरकर्ता इंटरफेसवरून फंक्शन कॉल करतात. सामान्यतः वापरकर्ता एक व्यवहार
सादर करतो आणि प्रतिसादाची वाट पाहत नाही, ज्याला अनिश्चित कालावधी लागू शकतो. व्यवहाराची पावती (जी व्यवहाराच्या हॅशद्वारे ओळखली जाते) शोधून किंवा
Transferइव्हेंट शोधून वापरकर्ता काय झाले ते पाहू शकतो. - इतर कॉन्ट्रॅक्ट, जे एकूण व्यवहाराचा भाग म्हणून फंक्शन कॉल करतात. त्या कॉन्ट्रॅक्टना ताबडतोब निकाल मिळतो, कारण ते त्याच व्यवहारात चालतात, त्यामुळे ते फंक्शन रिटर्न व्हॅल्यू वापरू शकतात.
कॉन्ट्रॅक्टच्या स्टेटमध्ये बदल करणाऱ्या इतर फंक्शन्सद्वारे समान प्रकारचे आउटपुट तयार केले जाते.
अलाउन्स एका खात्याला दुसऱ्या मालकाच्या मालकीचे काही टोकन खर्च करण्याची परवानगी देतात. हे उपयुक्त आहे, उदाहरणार्थ, विक्रेते म्हणून काम करणाऱ्या कॉन्ट्रॅक्टसाठी. कॉन्ट्रॅक्ट इव्हेंट्ससाठी निरीक्षण करू शकत नाहीत, म्हणून जर खरेदीदाराने थेट विक्रेता कॉन्ट्रॅक्टला टोकन हस्तांतरित केले तर त्या कॉन्ट्रॅक्टला पैसे भरले गेले आहेत हे कळणार नाही. त्याऐवजी, खरेदीदार विक्रेता कॉन्ट्रॅक्टला एक निश्चित रक्कम खर्च करण्याची परवानगी देतो, आणि विक्रेता ती रक्कम हस्तांतरित करतो. हे विक्रेता कॉन्ट्रॅक्ट कॉल करत असलेल्या फंक्शनद्वारे केले जाते, जेणेकरून विक्रेता कॉन्ट्रॅक्टला ते यशस्वी झाले की नाही हे कळू शकेल.
1 /**2 * @dev `spender` ला {transferFrom} द्वारे `owner` च्या वतीने खर्च करण्याची परवानगी असलेल्या3 * टोकनची उर्वरित संख्या परत करते. हे4 * डीफॉल्टनुसार शून्य असते.5 *6 * जेव्हा {approve} किंवा {transferFrom} कॉल केले जाते तेव्हा हे मूल्य बदलते.7 */8 function allowance(address owner, address spender) external view returns (uint256);allowance फंक्शन कोणालाही हे तपासण्याची परवानगी देते की एक
पत्ता (owner) दुसऱ्या पत्त्याला (spender) किती खर्च करू देतो.
1 /**2 * @dev कॉलरच्या टोकनवर `spender` चा अलाउन्स म्हणून `amount` सेट करते.3 *4 * ऑपरेशन यशस्वी झाले की नाही हे दर्शवणारे बुलियन मूल्य परत करते.5 *6 * महत्त्वाचे: सावध रहा की या पद्धतीसह अलाउन्स बदलण्यामुळे7 * दुर्दैवी व्यवहार क्रमाने कोणीतरी जुने आणि नवीन दोन्ही अलाउन्स वापरण्याचा धोका8 * असतो. या रेस कंडीशनला कमी करण्याचा एक संभाव्य उपाय म्हणजे9 * प्रथम स्पेंडरचा अलाउन्स 0 पर्यंत कमी करणे आणि नंतर10 * इच्छित मूल्य सेट करणे:11 * https://github.com/ethereum/EIPs/issues/20#issuecomment-26352472912 *13 * एक {Approval} इव्हेंट उत्सर्जित करते.14 */15 function approve(address spender, uint256 amount) external returns (bool);approve फंक्शन अलाउन्स तयार करते. त्याचा गैरवापर कसा होऊ शकतो याबद्दलचा
संदेश वाचण्याची खात्री करा. Ethereum मध्ये तुम्ही तुमच्या स्वतःच्या व्यवहारांच्या क्रमावर नियंत्रण ठेवता,
परंतु तुम्ही इतर लोकांचे व्यवहार कोणत्या क्रमाने
पार पाडले जातील यावर नियंत्रण ठेवू शकत नाही, जोपर्यंत तुम्ही दुसऱ्या बाजूचा
व्यवहार झाल्याचे पाहत नाही तोपर्यंत तुम्ही तुमचा स्वतःचा व्यवहार सादर करत नाही.
1 /**2 * @dev अलाउन्स यंत्रणा वापरून `sender` कडून `recipient` कडे `amount` टोकन हलवते.3 * `amount` नंतर कॉलरच्या4 * अलाउन्स मधून वजा केली जाते.5 *6 * ऑपरेशन यशस्वी झाले की नाही हे दर्शवणारे बुलियन मूल्य परत करते.7 *8 * एक {Transfer} इव्हेंट उत्सर्जित करते.9 */10 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);शेवटी, transferFrom चा वापर स्पेंडरद्वारे अलाउन्स प्रत्यक्षात खर्च करण्यासाठी केला जातो.
1
2 /**3 * @dev जेव्हा `value` टोकन एका खात्यातून (`from`) दुसऱ्या खात्यात (`to`) हलवले जातात तेव्हा उत्सर्जित होते.4 *5 * लक्षात घ्या की `value` शून्य असू शकते.6 */7 event Transfer(address indexed from, address indexed to, uint256 value);8
9 /**10 * @dev जेव्हा {approve} ला कॉल करून `owner` साठी `spender` चा अलाउन्स सेट केला जातो तेव्हा उत्सर्जित होते. `value` हा नवीन अलाउन्स आहे.11 */12 event Approval(address indexed owner, address indexed spender, uint256 value);13}जेव्हा ERC-20 कॉन्ट्रॅक्टची स्टेट बदलते तेव्हा हे इव्हेंट्स उत्सर्जित होतात.
वास्तविक कॉन्ट्रॅक्ट
हा वास्तविक कॉन्ट्रॅक्ट आहे जो ERC-20 मानकाची अंमलबजावणी करतो, येथून घेतला आहे (opens in a new tab). तो आहे तसा वापरण्यासाठी नाही, परंतु तुम्ही त्याचा वापरण्यायोग्य गोष्टीत विस्तार करण्यासाठी त्यातून इनहेरिट (opens in a new tab) करू शकता.
1// SPDX-License-Identifier: MIT2pragma solidity >=0.6.0 <0.8.0;
इम्पोर्ट स्टेटमेंट
वरील इंटरफेस परिभाषांव्यतिरिक्त, कॉन्ट्रॅक्ट परिभाषा दोन इतर फाइल्स इम्पोर्ट करते:
1
2import "../../GSN/Context.sol";3import "./IERC20.sol";4import "../../math/SafeMath.sol";GSN/Context.solही OpenGSN (opens in a new tab) वापरण्यासाठी आवश्यक असलेली परिभाषा आहे, ही एक प्रणाली आहे जी इथर नसलेल्या वापरकर्त्यांना ब्लॉकचेन वापरण्याची परवानगी देते. लक्षात घ्या की ही एक जुनी आवृत्ती आहे, तुम्हाला OpenGSN सोबत एकत्रीकरण करायचे असल्यास हे ट्यूटोरियल वापरा (opens in a new tab).- SafeMath लायब्ररी (opens in a new tab), जी Solidity आवृत्त्या <0.8.0 साठी अंकगणित ओव्हरफ्लो/अंडरफ्लो प्रतिबंधित करते. Solidity ≥0.8.0 मध्ये, अंकगणितीय क्रिया ओव्हरफ्लो/अंडरफ्लोवर आपोआप रिव्हर्ट होतात, ज्यामुळे SafeMath अनावश्यक बनते. हा कॉन्ट्रॅक्ट जुन्या कंपाइलर आवृत्त्यांसह बॅकवर्ड कंपॅटिबिलिटीसाठी SafeMath वापरतो.
ही कमेंट कॉन्ट्रॅक्टचा उद्देश स्पष्ट करते.
1/**2 * @dev {IERC20} इंटरफेसची अंमलबजावणी.3 *4 * ही अंमलबजावणी टोकन कसे तयार केले जातात या बाबतीत अनभिज्ञ आहे. याचा अर्थ5 * {_mint} वापरून एका साधित कॉन्ट्रॅक्टमध्ये पुरवठा यंत्रणा जोडणे आवश्यक आहे.6 * एका सामान्य यंत्रणेसाठी {ERC20PresetMinterPauser} पहा.7 *8 * टीप: तपशीलवार लेखनासाठी आमचे मार्गदर्शक पहा9 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[पुरवठा10 * यंत्रणा कशी लागू करावी].11 *12 * आम्ही सामान्य OpenZeppelin मार्गदर्शक तत्त्वांचे पालन केले आहे: अयशस्वी झाल्यास `false` परत करण्याऐवजी13 * फंक्शन्स रिव्हर्ट होतात. तरीही हे वर्तन पारंपारिक14 * आहे आणि ERC20 ॲप्लिकेशन्सच्या अपेक्षांशी संघर्ष करत नाही.15 *16 * याव्यतिरिक्त, {transferFrom} ला कॉल केल्यावर एक {Approval} इव्हेंट उत्सर्जित होतो.17 * हे ॲप्लिकेशन्सना केवळ उक्त इव्हेंट्स ऐकून सर्व खात्यांसाठी18 * अलाउन्सची पुनर्रचना करण्यास अनुमती देते. EIP च्या इतर अंमलबजावणी19 * हे इव्हेंट्स उत्सर्जित करू शकत नाहीत, कारण स्पेसिफिकेशननुसार ते आवश्यक नाही.20 *21 * शेवटी, अलाउन्स सेट करण्याच्या22 * सुप्रसिद्ध समस्या कमी करण्यासाठी नॉन-स्टँडर्ड {decreaseAllowance} आणि {increaseAllowance}23 * फंक्शन्स जोडले गेले आहेत. {IERC20-approve} पहा.24 */25
कॉन्ट्रॅक्ट परिभाषा
1contract ERC20 is Context, IERC20 {ही ओळ इनहेरिटन्स निर्दिष्ट करते, या प्रकरणात वरील IERC20 आणि OpenGSN साठी Context मधून.
1
2 using SafeMath for uint256;3
ही ओळ SafeMath लायब्ररीला uint256 प्रकाराशी जोडते. तुम्ही ही लायब्ररी
येथे (opens in a new tab) शोधू शकता.
व्हेरिएबल परिभाषा
या परिभाषा कॉन्ट्रॅक्टचे स्टेट व्हेरिएबल्स निर्दिष्ट करतात. हे व्हेरिएबल्स private घोषित केले आहेत, परंतु
त्याचा अर्थ फक्त एवढाच आहे की ब्लॉकचेनवरील इतर कॉन्ट्रॅक्ट ते वाचू शकत नाहीत. ब्लॉकचेनवर कोणतीही
रहस्ये नाहीत, प्रत्येक नोडवरील सॉफ्टवेअरमध्ये प्रत्येक ब्लॉकवर प्रत्येक कॉन्ट्रॅक्टची
स्टेट असते. परंपरेनुसार, स्टेट व्हेरिएबल्सना _<something> असे नाव दिले जाते.
पहिले दोन व्हेरिएबल्स मॅपिंग्स (opens in a new tab) आहेत, म्हणजे ते साधारणपणे असोसिएटिव्ह ॲरे (opens in a new tab) सारखेच वागतात, फक्त कीज संख्यात्मक मूल्ये आहेत. स्टोरेज फक्त त्या नोंदींसाठी वाटप केले जाते ज्यांची मूल्ये डीफॉल्टपेक्षा (शून्य) वेगळी आहेत.
1 mapping (address => uint256) private _balances;पहिले मॅपिंग, _balances, हे पत्ते आणि त्यांचे या टोकनचे संबंधित शिल्लक आहेत. शिल्लक ऍक्सेस करण्यासाठी,
हे सिंटॅक्स वापरा: _balances[<address>].
1 mapping (address => mapping (address => uint256)) private _allowances;हा व्हेरिएबल, _allowances, पूर्वी स्पष्ट केलेले अलाउन्स संग्रहित करतो. पहिला इंडेक्स टोकनचा मालक
आहे, आणि दुसरा अलाउन्स असलेला कॉन्ट्रॅक्ट आहे. पत्ता A पत्ता B च्या खात्यातून किती रक्कम
खर्च करू शकतो हे ऍक्सेस करण्यासाठी, _allowances[B][A] वापरा.
1 uint256 private _totalSupply;नावाप्रमाणेच, हा व्हेरिएबल टोकनच्या एकूण पुरवठ्याचा मागोवा ठेवतो.
1 string private _name;2 string private _symbol;3 uint8 private _decimals;हे तीन व्हेरिएबल्स वाचनीयता सुधारण्यासाठी वापरले जातात. पहिले दोन स्व-स्पष्ट आहेत, परंतु _decimals
नाही.
एकीकडे, Ethereum मध्ये फ्लोटिंग पॉइंट किंवा अपूर्णांक व्हेरिएबल्स नाहीत. दुसरीकडे, मानवांना टोकन विभाजित करता येणे आवडते. लोकांनी चलनी म्हणून सोन्याचा स्वीकार करण्याचे एक कारण म्हणजे जेव्हा कोणी गाईच्या मूल्याचे बदक विकत घेऊ इच्छित असेल तेव्हा सुटे पैसे करणे कठीण होते.
यावर उपाय म्हणजे पूर्णांकांचा मागोवा ठेवणे, परंतु वास्तविक टोकनऐवजी जवळजवळ निरुपयोगी असलेल्या अपूर्णांक टोकनची गणना करणे. इथरच्या बाबतीत, अपूर्णांक टोकनला 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? इथरच्या बाबतीत ते 10^18 wei प्रति ETH असे परिभाषित केले आहे, परंतु तुमच्या
टोकनसाठी तुम्ही वेगळे मूल्य निवडू शकता. जर टोकनचे विभाजन करणे अर्थपूर्ण नसेल, तर तुम्ही
शून्य _decimals मूल्य वापरू शकता. तुम्हाला ETH प्रमाणेच मानक वापरायचे असल्यास, 18 हे मूल्य वापरा.
कन्स्ट्रक्टर
1 /**2 * @dev {name} आणि {symbol} साठी मूल्ये सेट करते, {decimals} ला3 * 18 च्या डीफॉल्ट मूल्याने सुरू करते.4 *5 * {decimals} साठी वेगळे मूल्य निवडण्यासाठी, {_setupDecimals} वापरा.6 *7 * ही तिन्ही मूल्ये अपरिवर्तनीय आहेत: ती फक्त एकदाच8 * बांधकामादरम्यान सेट केली जाऊ शकतात.9 */10 constructor (string memory name_, string memory symbol_) public {11 // Solidity ≥0.7.0 मध्ये, 'public' अंतर्निहित आहे आणि वगळले जाऊ शकते.12
13 _name = name_;14 _symbol = symbol_;15 _decimals = 18;16 }जेव्हा कॉन्ट्रॅक्ट प्रथम तयार केला जातो तेव्हा कन्स्ट्रक्टर कॉल केला जातो. परंपरेनुसार, फंक्शन पॅरामीटर्सना <something>_ असे नाव दिले जाते.
वापरकर्ता इंटरफेस फंक्शन्स
1 /**2 * @dev टोकनचे नाव परत करते.3 */4 function name() public view returns (string memory) {5 return _name;6 }7
8 /**9 * @dev टोकनचे चिन्ह परत करते, सहसा नावाचे एक छोटे रूप.10 */11 function symbol() public view returns (string memory) {12 return _symbol;13 }14
15 /**16 * @dev वापरकर्त्याचे प्रतिनिधित्व मिळविण्यासाठी वापरलेल्या दशांशांची संख्या परत करते.17 * उदाहरणार्थ, जर `decimals` `2` च्या बरोबर असेल, तर `505` टोकनची शिल्लक वापरकर्त्याला `5,05` (`505 / 10 ** 2`) म्हणून प्रदर्शित केली पाहिजे.18 *19 * टोकन सहसा 18 चे मूल्य निवडतात, जे इथर आणि wei यांच्यातील संबंधाचे अनुकरण करते.20 * हे मूल्य {ERC20} वापरते, जोपर्यंत {_setupDecimals} कॉल केले जात नाही.21 *22 * टीप: ही माहिती केवळ _प्रदर्शन_ हेतूंसाठी वापरली जाते: ती कोणत्याही प्रकारे कराराच्या अंकगणितावर परिणाम करत नाही,23 * {IERC20-balanceOf} आणि {IERC20-transfer} यासह.24 */25 function decimals() public view returns (uint8) {26 return _decimals;27 }ही फंक्शन्स, name, symbol, आणि decimals वापरकर्ता इंटरफेसना तुमच्या कॉन्ट्रॅक्टबद्दल माहिती देतात जेणेकरून ते ते योग्यरित्या प्रदर्शित करू शकतील.
रिटर्न प्रकार string memory आहे, म्हणजे मेमरीमध्ये संग्रहित केलेली स्ट्रिंग परत करणे. व्हेरिएबल्स, जसे की
स्ट्रिंग्स, तीन ठिकाणी संग्रहित केले जाऊ शकतात:
| आयुष्य | कॉन्ट्रॅक्ट ऍक्सेस | गॅस खर्च | |
|---|---|---|---|
| मेमरी | फंक्शन कॉल | वाचन/लेखन | दहा किंवा शेकडो (उच्च स्थानांसाठी अधिक) |
| कॉलडेटा | फंक्शन कॉल | केवळ वाचनीय | रिटर्न प्रकार म्हणून वापरता येत नाही, केवळ फंक्शन पॅरामीटर प्रकार |
| स्टोरेज | बदलल्या जाईपर्यंत | वाचन/लेखन | उच्च (वाचनासाठी 800, लेखनासाठी 20k) |
या प्रकरणात, memory हा सर्वोत्तम पर्याय आहे.
टोकन माहिती वाचा
ही फंक्शन्स आहेत जी टोकनबद्दल माहिती प्रदान करतात, एकतर एकूण पुरवठा किंवा खात्याची शिल्लक.
1 /**2 * @dev {IERC20-totalSupply} पहा.3 */4 function totalSupply() public view override returns (uint256) {5 return _totalSupply;6 }totalSupply फंक्शन टोकनचा एकूण पुरवठा परत करते.
1 /**2 * @dev {IERC20-balanceOf} पहा.3 */4 function balanceOf(address account) public view override returns (uint256) {5 return _balances[account];6 }खात्याची शिल्लक वाचा. लक्षात घ्या की कोणालाही दुसऱ्याच्या खात्याची शिल्लक मिळवण्याची परवानगी आहे. ही माहिती लपवण्याचा प्रयत्न करण्यात काहीच अर्थ नाही, कारण ती प्रत्येक नोडवर तरीही उपलब्ध आहे. ब्लॉकचेनवर कोणतीही रहस्ये नाहीत.
टोकन हस्तांतरित करा
1 /**2 * @dev {IERC20-transfer} पहा.3 *4 * आवश्यकता:5 *6 * - `recipient` शून्य पत्ता असू शकत नाही.7 * - कॉलरकडे किमान `amount` इतकी शिल्लक असणे आवश्यक आहे.8 */9 function transfer(address recipient, uint256 amount) public virtual override returns (bool) {transfer फंक्शन प्रेषकाच्या खात्यातून दुसऱ्या खात्यात टोकन हस्तांतरित करण्यासाठी कॉल केले जाते. लक्षात
घ्या की ते बुलियन मूल्य परत करत असले तरी, ते मूल्य नेहमी सत्य असते. जर हस्तांतरण
अयशस्वी झाले तर कॉन्ट्रॅक्ट कॉलला रिव्हर्ट करते.
1 _transfer(_msgSender(), recipient, amount);2 return true;3 }_transfer फंक्शन वास्तविक काम करते. हे एक खाजगी फंक्शन आहे जे केवळ
इतर कॉन्ट्रॅक्ट फंक्शन्सद्वारे कॉल केले जाऊ शकते. परंपरेनुसार खाजगी फंक्शन्सना _<something> असे नाव दिले जाते, जसे की स्टेट
व्हेरिएबल्स.
सामान्यतः Solidity मध्ये आपण संदेश प्रेषकासाठी msg.sender वापरतो. तथापि, ते
OpenGSN (opens in a new tab) तोडते. आपल्याला आपल्या टोकनसह इथरलेस व्यवहारांना परवानगी द्यायची असल्यास, आपल्याला
_msgSender() वापरण्याची आवश्यकता आहे. ते सामान्य व्यवहारांसाठी msg.sender परत करते, परंतु इथरलेस व्यवहारांसाठी
मूळ स्वाक्षरीकर्ता परत करते आणि संदेश रिले करणाऱ्या कॉन्ट्रॅक्टला नाही.
अलाउन्स फंक्शन्स
ही फंक्शन्स आहेत जी अलाउन्स कार्यक्षमता लागू करतात: allowance, approve, transferFrom,
आणि _approve. याव्यतिरिक्त, OpenZeppelin अंमलबजावणी मूलभूत मानकांच्या पलीकडे जाऊन सुरक्षा सुधारणारी काही वैशिष्ट्ये समाविष्ट करते: increaseAllowance आणि decreaseAllowance.
अलाउन्स फंक्शन
1 /**2 * @dev {IERC20-allowance} पहा.3 */4 function allowance(address owner, address spender) public view virtual override returns (uint256) {5 return _allowances[owner][spender];6 }allowance फंक्शन प्रत्येकाला कोणताही अलाउन्स तपासण्याची परवानगी देते.
अप्रूव्ह फंक्शन
1 /**2 * @dev {IERC20-approve} पहा.3 *4 * आवश्यकता:5 *6 * - `spender` शून्य पत्ता असू शकत नाही.7 */8 function approve(address spender, uint256 amount) public virtual override returns (bool) {हे फंक्शन अलाउन्स तयार करण्यासाठी कॉल केले जाते. हे वरील transfer फंक्शनसारखे आहे:
- हे फंक्शन फक्त एका अंतर्गत फंक्शनला (या प्रकरणात,
_approve) कॉल करते जे वास्तविक काम करते. - हे फंक्शन एकतर
true(यशस्वी झाल्यास) परत करते किंवा रिव्हर्ट होते (नसल्यास).
1 _approve(_msgSender(), spender, amount);2 return true;3 }स्टेट बदल होणाऱ्या ठिकाणांची संख्या कमी करण्यासाठी आम्ही अंतर्गत फंक्शन्स वापरतो. स्टेट बदलणारे कोणतेही फंक्शन एक संभाव्य सुरक्षा धोका आहे ज्याचे सुरक्षेसाठी ऑडिट करणे आवश्यक आहे. यामुळे आम्हाला चूक होण्याची शक्यता कमी होते.
transferFrom फंक्शन
हे फंक्शन आहे जे एक स्पेंडर अलाउन्स खर्च करण्यासाठी कॉल करतो. यासाठी दोन क्रिया आवश्यक आहेत: खर्च केलेली रक्कम हस्तांतरित करणे आणि त्या रकमेने अलाउन्स कमी करणे.
1 /**2 * @dev {IERC20-transferFrom} पहा.3 *4 * अद्यतनित अलाउन्स दर्शवणारा एक {Approval} इव्हेंट उत्सर्जित करते. हे5 * EIP द्वारे आवश्यक नाही. {ERC20} च्या सुरुवातीला टीप पहा.6 *7 * आवश्यकता:8 *9 * - `sender` आणि `recipient` शून्य पत्ता असू शकत नाहीत.10 * - `sender` कडे किमान `amount` इतकी शिल्लक असणे आवश्यक आहे.11 * - कॉलरकडे किमान12 * `amount` च्या ``sender``'च्या टोकनसाठी अलाउन्स असणे आवश्यक आहे.13 */14 function transferFrom(address sender, address recipient, uint256 amount) public virtual15 override returns (bool) {16 _transfer(sender, recipient, amount);
a.sub(b, "message") फंक्शन कॉल दोन गोष्टी करतो. प्रथम, ते a-b ची गणना करते, जो नवीन अलाउन्स आहे.
दुसरे, ते तपासते की हा परिणाम नकारात्मक नाही. जर तो नकारात्मक असेल तर कॉल प्रदान केलेल्या संदेशासह रिव्हर्ट होतो. लक्षात घ्या की जेव्हा एखादा कॉल रिव्हर्ट होतो तेव्हा त्या कॉलदरम्यान पूर्वी केलेले कोणतेही प्रक्रिया दुर्लक्षित केले जाते त्यामुळे आम्हाला _transfer
पूर्ववत करण्याची आवश्यकता नाही.
1 _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount,2 "ERC20: transfer amount exceeds allowance"));3 return true;4 }OpenZeppelin सुरक्षा जोडणी
शून्य-नसलेला अलाउन्स दुसऱ्या शून्य-नसलेल्या मूल्यावर सेट करणे धोकादायक आहे, कारण तुम्ही फक्त तुमच्या स्वतःच्या व्यवहारांच्या क्रमावर नियंत्रण ठेवता, इतर कोणाच्याही नाही. कल्पना करा की तुमच्याकडे दोन वापरकर्ते आहेत, एलिस जी भोळी आहे आणि बिल जो अप्रामाणिक आहे. एलिसला बिलकडून काही सेवा हवी आहे, जी तिला वाटते की पाच टोकन खर्च करते - म्हणून ती बिलला पाच टोकनचा अलाउन्स देते.
नंतर काहीतरी बदलते आणि बिलची किंमत दहा टोकनपर्यंत वाढते. एलिस, जिला अजूनही सेवा हवी आहे, एक व्यवहार पाठवते जो बिलचा अलाउन्स दहावर सेट करतो. ज्या क्षणी बिलला हा नवीन व्यवहार व्यवहार पूलमध्ये दिसतो, तो एलिसचे पाच टोकन खर्च करणारा एक व्यवहार पाठवतो आणि त्याची गॅस किंमत खूप जास्त असते जेणेकरून तो जलद माइन होईल. अशाप्रकारे बिल प्रथम पाच टोकन खर्च करू शकतो आणि नंतर, एकदा एलिसचा नवीन अलाउन्स माइन झाल्यावर, पंधरा टोकनच्या एकूण किंमतीसाठी आणखी दहा खर्च करू शकतो, जे एलिसने अधिकृत करू इच्छिलेल्या रकमेपेक्षा जास्त आहे. या तंत्राला फ्रंट-रनिंग (opens in a new tab) म्हणतात
| एलिस व्यवहार | एलिस नॉन्स | बिल व्यवहार | बिल नॉन्स | बिलचा अलाउन्स | एलिसकडून बिलचे एकूण उत्पन्न |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | ||
| approve(Bill, 10) | 11 | 10 | 5 | ||
| transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 |
ही समस्या टाळण्यासाठी, ही दोन फंक्शन्स (increaseAllowance आणि decreaseAllowance) तुम्हाला
एका विशिष्ट रकमेने अलाउन्समध्ये बदल करण्याची परवानगी देतात. त्यामुळे जर बिलने आधीच पाच टोकन खर्च केले असतील, तर तो फक्त
आणखी पाच खर्च करू शकेल. वेळेनुसार, हे काम करण्याचे दोन मार्ग आहेत, जे दोन्ही
बिलला फक्त दहा टोकन मिळवून संपतात:
अ:
| एलिस व्यवहार | एलिस नॉन्स | बिल व्यवहार | बिल नॉन्स | बिलचा अलाउन्स | एलिसकडून बिलचे एकूण उत्पन्न |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | ||
| increaseAllowance(Bill, 5) | 11 | 0+5 = 5 | 5 | ||
| transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 |
ब:
| एलिस व्यवहार | एलिस नॉन्स | बिल व्यवहार | बिल नॉन्स | बिलचा अलाउन्स | एलिसकडून बिलचे एकूण उत्पन्न |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| increaseAllowance(Bill, 5) | 11 | 5+5 = 10 | 0 | ||
| transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 |
1 /**2 * @dev कॉलरद्वारे `spender` ला दिलेला अलाउन्स अणुप्रायपणे वाढवते.3 *4 * हा {approve} चा एक पर्याय आहे जो {IERC20-approve} मध्ये वर्णन केलेल्या समस्यांसाठी5 * एक कमी करण्याच्या उपायांप्रमाणे वापरला जाऊ शकतो.6 *7 * अद्यतनित अलाउन्स दर्शवणारा एक {Approval} इव्हेंट उत्सर्जित करते.8 *9 * आवश्यकता:10 *11 * - `spender` शून्य पत्ता असू शकत नाही.12 */13 function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {14 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));15 return true;16 }a.add(b) फंक्शन एक सुरक्षित बेरीज आहे. असंभाव्य परिस्थितीत की a+b>=2^256 असेल तर ते सामान्य बेरीजप्रमाणे
रॅप अराउंड होत नाही.
1
2 /**3 * @dev कॉलरद्वारे `spender` ला दिलेला अलाउन्स अणुप्रायपणे कमी करते.4 *5 * हा {approve} चा एक पर्याय आहे जो {IERC20-approve} मध्ये वर्णन केलेल्या समस्यांसाठी6 * एक कमी करण्याच्या उपायांप्रमाणे वापरला जाऊ शकतो.7 *8 * अद्यतनित अलाउन्स दर्शवणारा एक {Approval} इव्हेंट उत्सर्जित करते.9 *10 * आवश्यकता:11 *12 * - `spender` शून्य पत्ता असू शकत नाही.13 * - `spender` कडे किमान `subtractedValue`14 * इतका कॉलरसाठी अलाउन्स असणे आवश्यक आहे.15 */16 function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {17 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue,18 "ERC20: decreased allowance below zero"));19 return true;20 }टोकन माहितीमध्ये बदल करणारी फंक्शन्स
ही चार फंक्शन्स आहेत जी वास्तविक काम करतात: _transfer, _mint, _burn, आणि _approve.
_transfer फंक्शन
1 /**2 * @dev `sender` कडून `recipient` कडे `amount` टोकन हलवते.3 *4 * हे अंतर्गत फंक्शन {transfer} च्या समकक्ष आहे, आणि उदाहरणार्थ, स्वयंचलित टोकन शुल्क, स्लॅशिंग यंत्रणा, इत्यादी लागू करण्यासाठी वापरले जाऊ शकते.5 *6 * एक {Transfer} इव्हेंट उत्सर्जित करते.7 *8 * आवश्यकता:9 *10 * - `sender` शून्य पत्ता असू शकत नाही.11 * - `recipient` शून्य पत्ता असू शकत नाही.12 * - `sender` कडे किमान `amount` इतकी शिल्लक असणे आवश्यक आहे.13 */14 function _transfer(address sender, address recipient, uint256 amount) internal virtual {हे फंक्शन, _transfer, एका खात्यातून दुसऱ्या खात्यात टोकन हस्तांतरित करते. ते transfer (प्रेषकाच्या स्वतःच्या खात्यातून हस्तांतरणासाठी) आणि transferFrom (दुसऱ्याच्या खात्यातून
हस्तांतरणासाठी अलाउन्स वापरण्यासाठी) दोन्हीद्वारे कॉल केले जाते.
1 require(sender != address(0), "ERC20: transfer from the zero address");2 require(recipient != address(0), "ERC20: transfer to the zero address");Ethereum मध्ये शून्य पत्त्याची मालकी कोणाकडेही नाही (म्हणजेच, कोणालाही अशी खाजगी की माहित नाही जिची जुळणारी सार्वजनिक की शून्य पत्त्यात रूपांतरित होते). जेव्हा लोक तो पत्ता वापरतात, तेव्हा ती सहसा एक सॉफ्टवेअर बग असते - म्हणून आम्ही शून्य पत्ता प्रेषक किंवा प्राप्तकर्ता म्हणून वापरल्यास अयशस्वी होतो.
1 _beforeTokenTransfer(sender, recipient, amount);2
हा कॉन्ट्रॅक्ट वापरण्याचे दोन मार्ग आहेत:
- तुमच्या स्वतःच्या कोडसाठी टेम्पलेट म्हणून वापरा
- त्यातून इनहेरिट करा (opens in a new tab), आणि फक्त त्या फंक्शन्सना ओव्हरराइड करा ज्यात तुम्हाला बदल करायचा आहे
दुसरी पद्धत खूपच चांगली आहे कारण OpenZeppelin ERC-20 कोडचे आधीच ऑडिट केले गेले आहे आणि ते सुरक्षित असल्याचे दर्शविले आहे. जेव्हा तुम्ही इनहेरिटन्स वापरता तेव्हा तुम्ही कोणत्या फंक्शन्समध्ये बदल करता हे स्पष्ट होते, आणि तुमच्या कॉन्ट्रॅक्टवर विश्वास ठेवण्यासाठी लोकांना फक्त त्या विशिष्ट फंक्शन्सचे ऑडिट करण्याची आवश्यकता असते.
जेव्हा जेव्हा टोकनची देवाणघेवाण होते तेव्हा प्रत्येक वेळी एक फंक्शन करणे अनेकदा उपयुक्त ठरते. तथापि, _transfer हे एक अत्यंत महत्त्वाचे फंक्शन आहे आणि ते
असुरक्षितपणे लिहिणे शक्य आहे (खाली पहा), म्हणून ते ओव्हरराइड न करणे सर्वोत्तम आहे. यावर उपाय म्हणजे _beforeTokenTransfer, एक
हुक फंक्शन (opens in a new tab). तुम्ही हे फंक्शन ओव्हरराइड करू शकता, आणि ते प्रत्येक हस्तांतरणावर कॉल केले जाईल.
1 _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");2 _balances[recipient] = _balances[recipient].add(amount);या ओळी आहेत ज्या प्रत्यक्षात हस्तांतरण करतात. लक्षात घ्या की त्यांच्यामध्ये काहीही नाही, आणि आम्ही हस्तांतरित रक्कम प्राप्तकर्त्याला जोडण्यापूर्वी प्रेषकाकडून वजा करतो. हे महत्त्वाचे आहे कारण जर मध्ये दुसऱ्या कॉन्ट्रॅक्टला कॉल असता, तर त्याचा वापर या कॉन्ट्रॅक्टला फसवण्यासाठी केला गेला असता. अशा प्रकारे हस्तांतरण अणुप्राय (atomic) आहे, त्याच्यामध्ये काहीही होऊ शकत नाही.
1 emit Transfer(sender, recipient, amount);2 }शेवटी, एक Transfer इव्हेंट उत्सर्जित करा. इव्हेंट्स स्मार्ट कॉन्ट्रॅक्ट्सना ऍक्सेस करता येत नाहीत, परंतु ब्लॉकचेनच्या
बाहेर चालणारा कोड इव्हेंट्स ऐकू शकतो आणि त्यावर प्रतिक्रिया देऊ शकतो. उदाहरणार्थ, वॉलेट मालकाला अधिक टोकन कधी मिळतात याचा मागोवा ठेवू शकते.
_mint आणि _burn फंक्शन्स
ही दोन फंक्शन्स (_mint आणि _burn) टोकनच्या एकूण पुरवठ्यात बदल करतात.
ती इंटरनल आहेत आणि या कॉन्ट्रॅक्टमध्ये त्यांना कॉल करणारे कोणतेही फंक्शन नाही,
त्यामुळे तुम्ही कॉन्ट्रॅक्टमधून इनहेरिट केल्यास आणि नवीन टोकन कोणत्या परिस्थितीत मिंट करायचे किंवा
अस्तित्वात असलेले बर्न करायचे हे ठरवण्यासाठी तुमचा स्वतःचा
लॉजिक जोडल्यासच ते उपयुक्त आहेत.
टीप: प्रत्येक ERC-20 टोकनचा स्वतःचा व्यवसाय लॉजिक असतो जो टोकन व्यवस्थापन ठरवतो.
उदाहरणार्थ, एक निश्चित पुरवठा कॉन्ट्रॅक्ट कदाचित फक्त कन्स्ट्रक्टरमध्ये _mint ला कॉल करेल आणि _burn ला कधीही कॉल करणार नाही. टोकन विकणारा कॉन्ट्रॅक्ट
पैसे भरल्यावर _mint ला कॉल करेल, आणि संभाव्यतः अनियंत्रित चलनवाढ टाळण्यासाठी काही टप्प्यावर _burn ला कॉल करेल.
1 /** @dev `amount` टोकन तयार करते आणि ते `account` ला नियुक्त करते, ज्यामुळे2 * एकूण पुरवठा वाढतो.3 *4 * `from` शून्य पत्त्यावर सेट करून एक {Transfer} इव्हेंट उत्सर्जित करते.5 *6 * आवश्यकता:7 *8 * - `to` शून्य पत्ता असू शकत नाही.9 */10 function _mint(address account, uint256 amount) internal virtual {11 require(account != address(0), "ERC20: mint to the zero address");12 _beforeTokenTransfer(address(0), account, amount);13 _totalSupply = _totalSupply.add(amount);14 _balances[account] = _balances[account].add(amount);15 emit Transfer(address(0), account, amount);16 }जेव्हा टोकनची एकूण संख्या बदलते तेव्हा _totalSupply अद्यतनित करण्याची खात्री करा.
1 /**2 * @dev `account` मधून `amount` टोकन नष्ट करते, ज्यामुळे3 * एकूण पुरवठा कमी होतो.4 *5 * `to` शून्य पत्त्यावर सेट करून एक {Transfer} इव्हेंट उत्सर्जित करते.6 *7 * आवश्यकता:8 *9 * - `account` शून्य पत्ता असू शकत नाही.10 * - `account` कडे किमान `amount` टोकन असणे आवश्यक आहे.11 */12 function _burn(address account, uint256 amount) internal virtual {13 require(account != address(0), "ERC20: burn from the zero address");14
15 _beforeTokenTransfer(account, address(0), amount);16
17 _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");18 _totalSupply = _totalSupply.sub(amount);19 emit Transfer(account, address(0), amount);20 }_burn फंक्शन जवळजवळ _mint सारखेच आहे, फक्त ते उलट दिशेने जाते.
_approve फंक्शन
हे फंक्शन आहे जे प्रत्यक्षात अलाउन्स निर्दिष्ट करते. लक्षात घ्या की ते एका मालकाला मालकाच्या वर्तमान शिलकेपेक्षा जास्त असलेला अलाउन्स निर्दिष्ट करण्याची परवानगी देते. हे ठीक आहे कारण शिल्लक हस्तांतरणाच्या वेळी तपासली जाते, जेव्हा ती अलाउन्स तयार केल्यावरच्या शिलकेपेक्षा वेगळी असू शकते.
1 /**2 * @dev `owner` च्या टोकनवर `spender` चा अलाउन्स म्हणून `amount` सेट करते.3 *4 * हे अंतर्गत फंक्शन `approve` च्या समकक्ष आहे, आणि उदाहरणार्थ, विशिष्ट सबसिस्टमसाठी स्वयंचलित अलाउन्स सेट करण्यासाठी वापरले जाऊ शकते.5 *6 * एक {Approval} इव्हेंट उत्सर्जित करते.7 *8 * आवश्यकता:9 *10 * - `owner` शून्य पत्ता असू शकत नाही.11 * - `spender` शून्य पत्ता असू शकत नाही.12 */13 function _approve(address owner, address spender, uint256 amount) internal virtual {14 require(owner != address(0), "ERC20: approve from the zero address");15 require(spender != address(0), "ERC20: approve to the zero address");16
17 _allowances[owner][spender] = amount;
एक Approval इव्हेंट उत्सर्जित करा. ॲप्लिकेशन कसे लिहिले आहे यावर अवलंबून, स्पेंडर कॉन्ट्रॅक्टला
अप्रूव्हलबद्दल मालकाद्वारे किंवा हे इव्हेंट्स ऐकणाऱ्या सर्व्हरद्वारे सांगितले जाऊ शकते.
1 emit Approval(owner, spender, amount);2 }3
दशांश व्हेरिएबलमध्ये बदल करा
1
2
3 /**4 * @dev {decimals} ला 18 च्या डीफॉल्ट मूल्याव्यतिरिक्त दुसऱ्या मूल्यावर सेट करते.5 *6 * चेतावणी: हे फंक्शन केवळ कन्स्ट्रक्टरमधून कॉल केले पाहिजे. टोकन7 * कॉन्ट्रॅक्टशी संवाद साधणारे बहुतेक ॲप्लिकेशन्स {decimals} कधीही बदलतील अशी8 * अपेक्षा करणार नाहीत, आणि जर बदलले तर ते चुकीच्या पद्धतीने काम करू शकतात.9 */10 function _setupDecimals(uint8 decimals_) internal {11 _decimals = decimals_;12 }हे फंक्शन _decimals व्हेरिएबलमध्ये बदल करते जे वापरकर्ता इंटरफेसना रक्कम कशी इंटरप्रेट करायची हे सांगण्यासाठी वापरले जाते.
तुम्ही ते कन्स्ट्रक्टरमधून कॉल केले पाहिजे. त्यानंतरच्या कोणत्याही टप्प्यावर ते कॉल करणे अप्रामाणिक ठरेल, आणि ॲप्लिकेशन्स
ते हाताळण्यासाठी डिझाइन केलेले नाहीत.
हुक्स
1
2 /**3 * @dev टोकनच्या कोणत्याही हस्तांतरणापूर्वी कॉल केला जाणारा हुक. यामध्ये4 * मिंटिंग आणि बर्निंगचा समावेश आहे.5 *6 * कॉलिंग अटी:7 *8 * - जेव्हा `from` आणि `to` दोन्ही शून्य-नसलेले असतात, तेव्हा ``from`` च्या टोकनची `amount`9 * `to` ला हस्तांतरित केली जाईल.10 * - जेव्हा `from` शून्य असेल, तेव्हा `to` साठी `amount` टोकन मिंट केले जातील.11 * - जेव्हा `to` शून्य असेल, तेव्हा ``from`` च्या टोकनची `amount` बर्न केली जाईल.12 * - `from` आणि `to` दोन्ही कधीही शून्य नसतात.13 *14 * हुकबद्दल अधिक जाणून घेण्यासाठी, xref:ROOT:extending-contracts.adoc#using-hooks[हुक वापरणे] येथे जा.15 */16 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }17}हे हस्तांतरणादरम्यान कॉल केले जाणारे हुक फंक्शन आहे. हे येथे रिकामे आहे, परंतु तुम्हाला त्याला काहीतरी करायला लावायचे असल्यास तुम्ही फक्त ते ओव्हरराइड करा.
निष्कर्ष
पुनरावलोकनासाठी, या कॉन्ट्रॅक्टमधील काही सर्वात महत्त्वाच्या कल्पना येथे आहेत (माझ्या मते, तुमची मते भिन्न असू शकतात):
- ब्लॉकचेनवर कोणतीही गुपिते नाहीत. स्मार्ट कॉन्ट्रॅक्ट ऍक्सेस करू शकणारी कोणतीही माहिती संपूर्ण जगासाठी उपलब्ध आहे.
- तुम्ही तुमच्या स्वतःच्या व्यवहारांच्या क्रमावर नियंत्रण ठेवू शकता, परंतु इतर लोकांचे व्यवहार कधी होतात यावर नाही. हेच कारण आहे की अलाउन्स बदलणे धोकादायक असू शकते, कारण ते स्पेंडरला दोन्ही अलाउन्सची बेरीज खर्च करू देते.
uint256प्रकाराची मूल्ये रॅप अराउंड होतात. दुसऱ्या शब्दांत, 0-1=2^256-1. जर ते इच्छित वर्तन नसेल, तर तुम्हाला ते तपासावे लागेल (किंवा SafeMath लायब्ररी वापरा जी तुमच्यासाठी ते करते). लक्षात घ्या की हे Solidity 0.8.0 (opens in a new tab) मध्ये बदलले आहे.- एका विशिष्ट प्रकारातील सर्व स्टेट बदल एका विशिष्ट ठिकाणी करा, कारण यामुळे ऑडिटिंग सोपे होते.
हेच कारण आहे की आमच्याकडे, उदाहरणार्थ,
_approveआहे, जेapprove,transferFrom,increaseAllowance, आणिdecreaseAllowanceद्वारे कॉल केले जाते - स्टेट बदल अणुप्राय (atomic) असावेत, त्यांच्यामध्ये इतर कोणतीही क्रिया नसावी (जसे तुम्ही
_transferमध्ये पाहू शकता). याचे कारण असे की स्टेट बदलादरम्यान तुमच्याकडे एक विसंगत स्टेट असते. उदाहरणार्थ, तुम्ही प्रेषकाच्या शिलकीतून वजा करता आणि प्राप्तकर्त्याच्या शिलकीत जोडता या वेळेदरम्यान अस्तित्त्वात असलेले टोकन आवश्यकतेपेक्षा कमी असतात. जर त्यांच्यामध्ये ऑपरेशन्स असतील तर याचा संभाव्य गैरवापर होऊ शकतो, विशेषतः वेगळ्या कॉन्ट्रॅक्टला केलेले कॉल.
आता तुम्ही पाहिले आहे की OpenZeppelin ERC-20 कॉन्ट्रॅक्ट कसे लिहिले आहे, आणि विशेषतः ते कसे अधिक सुरक्षित बनवले आहे, आता जा आणि तुमचे स्वतःचे सुरक्षित कॉन्ट्रॅक्ट आणि ॲप्लिकेशन्स लिहा.
माझ्या कामाबद्दल अधिक माहितीसाठी येथे पहा (opens in a new tab).
पृष्ठ अखेरचे अद्यतन: 3 मार्च, 2026
