ERC-20 ஒப்பந்தம் குறித்த வழிகாட்டி
அறிமுகம்
எத்திரியம் பயன்பாடுகளில் மிகவும் பொதுவான ஒன்று, ஒரு குழு வர்த்தகம் செய்யக்கூடிய ஒரு வில்லையை (token), அதாவது அவர்களின் சொந்த நாணயத்தை உருவாக்குவதாகும். இந்த வில்லைகள் பொதுவாக ERC-20 என்ற தரநிலையைப் பின்பற்றுகின்றன. இந்தத் தரநிலையானது, அனைத்து ERC-20 வில்லைகளுடனும் வேலை செய்யும் நீர்மைத் தொகுப்புகள் (liquidity pools) மற்றும் பணப்பைகள் (wallets) போன்ற கருவிகளை எழுதச் சாத்தியமாக்குகிறது. இந்தக் கட்டுரையில், ஓப்பன்செப்பெலின் 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) உருவாக்குகிறோம். வில்லை ஒப்பந்தத்தைப் பயன்படுத்த வேண்டிய எந்தவொரு குறியீடும் இடைமுகத்தில் உள்ள அதே வரையறைகளைப் பயன்படுத்தலாம் மற்றும் அதைப் பயன்படுத்தும் அனைத்து வில்லை ஒப்பந்தங்களுடனும் இணக்கமாக இருக்கும், அது மெட்டாமேஸ்க் போன்ற பணப்பையாக இருந்தாலும், etherscan.io போன்ற பரவலாக்கப்பட்ட செயலியாக (dapp) இருந்தாலும் அல்லது நீர்மைத் தொகுப்பு போன்ற வேறுபட்ட ஒப்பந்தமாக இருந்தாலும் சரி.
நீங்கள் ஒரு அனுபவமிக்க நிரலாளராக இருந்தால், Java (opens in a new tab) அல்லது C ஹெடர் கோப்புகளில் (opens in a new tab) இதே போன்ற அமைப்புகளைப் பார்த்தது உங்களுக்கு நினைவிருக்கலாம்.
இது ஓப்பன்செப்பெலினில் இருந்து பெறப்பட்ட ERC-20 இடைமுகத்தின் (opens in a new tab) வரையறையாகும். இது மனிதர்கள் படிக்கக்கூடிய தரநிலையை (opens in a new tab) Solidity குறியீடாக மொழிபெயர்த்ததாகும். நிச்சயமாக, இடைமுகமே எதையும் எப்படி செய்வது என்று வரையறுக்காது. அது கீழே உள்ள ஒப்பந்த மூலக் குறியீட்டில் விளக்கப்பட்டுள்ளது.
// SPDX-License-Identifier: MIT
Solidity கோப்புகளில் உரிம அடையாளங்காட்டி இருக்க வேண்டும். உரிமங்களின் பட்டியலை நீங்கள் இங்கே காணலாம் (opens in a new tab). உங்களுக்கு வேறு உரிமம் தேவைப்பட்டால், அதை கருத்துகளில் (comments) விளக்குங்கள்.
pragma solidity >=0.6.0 <0.8.0;
Solidity மொழி இன்னும் விரைவாக உருவாகி வருகிறது, மேலும் புதிய பதிப்புகள் பழைய குறியீட்டுடன் இணக்கமாக இருக்காது (இங்கே பார்க்கவும் (opens in a new tab)). எனவே, மொழியின் குறைந்தபட்ச பதிப்பை மட்டும் குறிப்பிடாமல், நீங்கள் குறியீட்டைச் சோதித்த சமீபத்திய அதிகபட்ச பதிப்பையும் குறிப்பிடுவது நல்லது.
/**
* @dev EIP-இல் வரையறுக்கப்பட்டுள்ளபடி ERC-20 தரநிலையின் இடைமுகம்.
*/
கருத்தில் உள்ள @dev என்பது NatSpec வடிவமைப்பின் (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 ஆகவும் உள்ளது, அதாவது இது நிலையை மாற்றாது, எனவே தொகுதிச்சங்கிலியில் உள்ள ஒவ்வொரு கணுவும் (node) இதை இயக்குவதற்குப் பதிலாக ஒரு கணுவில் இதை இயக்க முடியும். இந்த வகையான செயல்பாடு ஒரு பரிவர்த்தனையை உருவாக்காது மற்றும் இதற்கு எரிவாயு செலவாகாது.
குறிப்பு: கோட்பாட்டளவில், ஒரு ஒப்பந்தத்தை உருவாக்கியவர் உண்மையான மதிப்பை விடச் சிறிய மொத்த விநியோகத்தை வழங்குவதன் மூலம் ஏமாற்றலாம் என்று தோன்றலாம், இதனால் ஒவ்வொரு வில்லையும் உண்மையில் இருப்பதை விட அதிக மதிப்புடையதாகத் தோன்றும். இருப்பினும், அந்த அச்சம் தொகுதிச்சங்கிலியின் உண்மையான தன்மையைப் புறக்கணிக்கிறது. தொகுதிச்சங்கிலியில் நடக்கும் அனைத்தையும் ஒவ்வொரு கணுவாலும் சரிபார்க்க முடியும். இதை அடைய, ஒவ்வொரு ஒப்பந்தத்தின் இயந்திர மொழிக் குறியீடு மற்றும் சேமிப்பகம் ஒவ்வொரு கணுவிலும் கிடைக்கிறது. உங்கள் ஒப்பந்தத்திற்கான Solidity குறியீட்டை நீங்கள் வெளியிட வேண்டிய அவசியமில்லை என்றாலும், நீங்கள் மூலக் குறியீட்டையும் அது தொகுக்கப்பட்ட Solidity பதிப்பையும் வெளியிட்டால் ஒழிய யாரும் உங்களை நம்ப மாட்டார்கள், எனவே நீங்கள் வழங்கிய இயந்திர மொழிக் குறியீட்டிற்கு எதிராக அதைச் சரிபார்க்க முடியும். உதாரணமாக, இந்த ஒப்பந்தத்தைப் (opens in a new tab) பார்க்கவும்.
/**
* @dev `account` கணக்கிற்குச் சொந்தமான வில்லைகளின் அளவை வழங்குகிறது.
*/
function balanceOf(address account) external view returns (uint256);
பெயர் குறிப்பிடுவது போல, balanceOf ஒரு கணக்கின் இருப்பை வழங்குகிறது. எத்திரியம் கணக்குகள் Solidity இல் address வகையைப் பயன்படுத்தி அடையாளம் காணப்படுகின்றன, இது 160 பிட்களைக் கொண்டுள்ளது.
இது external மற்றும் view ஆகவும் உள்ளது.
/**
* @dev அழைப்பாளரின் கணக்கிலிருந்து `recipient` முகவரிக்கு `amount` வில்லைகளை நகர்த்துகிறது.
*
* செயல்பாடு வெற்றிகரமாக நடந்ததா என்பதைக் குறிக்கும் பூலியன் மதிப்பை வழங்குகிறது.
*
* ஒரு {Transfer} நிகழ்வை வெளியிடுகிறது.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
transfer செயல்பாடு அழைப்பாளரிடமிருந்து வேறு முகவரிக்கு வில்லைகளைப் பரிமாற்றம் செய்கிறது. இது நிலையின் மாற்றத்தை உள்ளடக்கியது, எனவே இது ஒரு view அல்ல.
ஒரு பயனர் இந்தச் செயல்பாட்டை அழைக்கும் போது அது ஒரு பரிவர்த்தனையை உருவாக்குகிறது மற்றும் எரிவாயு செலவாகிறது. இது தொகுதிச்சங்கிலியில் உள்ள அனைவருக்கும் நிகழ்வைத் தெரிவிக்க, Transfer என்ற நிகழ்வையும் வெளியிடுகிறது.
இந்தச் செயல்பாடு இரண்டு வெவ்வேறு வகையான அழைப்பாளர்களுக்கு இரண்டு வகையான வெளியீட்டைக் கொண்டுள்ளது:
- பயனர் இடைமுகத்திலிருந்து நேரடியாகச் செயல்பாட்டை அழைக்கும் பயனர்கள். பொதுவாகப் பயனர் ஒரு பரிவர்த்தனையைச் சமர்ப்பிக்கிறார் மற்றும் பதிலுக்காகக் காத்திருக்க மாட்டார், இதற்கு எவ்வளவு நேரம் ஆகும் என்று உறுதியாகக் கூற முடியாது. பரிவர்த்தனை ரசீதைத் தேடுவதன் மூலம் (இது பரிவர்த்தனை ஹாஷ் மூலம் அடையாளம் காணப்படுகிறது) அல்லது
Transferநிகழ்வைத் தேடுவதன் மூலம் பயனர் என்ன நடந்தது என்பதைப் பார்க்க முடியும். - ஒட்டுமொத்த பரிவர்த்தனையின் ஒரு பகுதியாகச் செயல்பாட்டை அழைக்கும் பிற ஒப்பந்தங்கள். அந்த ஒப்பந்தங்கள் முடிவை உடனடியாகப் பெறுகின்றன, ஏனெனில் அவை ஒரே பரிவர்த்தனையில் இயங்குகின்றன, எனவே அவை செயல்பாட்டின் திரும்பும் மதிப்பைப் பயன்படுத்தலாம்.
ஒப்பந்தத்தின் நிலையை மாற்றும் பிற செயல்பாடுகளாலும் இதே வகையான வெளியீடு உருவாக்கப்படுகிறது.
அனுமதித்தொகைகள் ஒரு கணக்கை வேறு உரிமையாளருக்குச் சொந்தமான சில வில்லைகளைச் செலவிட அனுமதிக்கின்றன. உதாரணமாக, விற்பனையாளர்களாகச் செயல்படும் ஒப்பந்தங்களுக்கு இது பயனுள்ளதாக இருக்கும். ஒப்பந்தங்களால் நிகழ்வுகளைக் கண்காணிக்க முடியாது, எனவே வாங்குபவர் வில்லைகளை நேரடியாக விற்பனையாளர் ஒப்பந்தத்திற்குப் பரிமாற்றம் செய்தால், அந்த ஒப்பந்தத்திற்குப் பணம் செலுத்தப்பட்டது தெரியாது. அதற்குப் பதிலாக, வாங்குபவர் விற்பனையாளர் ஒப்பந்தத்தை ஒரு குறிப்பிட்ட தொகையைச் செலவிட அனுமதிக்கிறார், மேலும் விற்பனையாளர் அந்தத் தொகையைப் பரிமாற்றம் செய்கிறார். விற்பனையாளர் ஒப்பந்தம் அழைக்கும் ஒரு செயல்பாட்டின் மூலம் இது செய்யப்படுகிறது, எனவே விற்பனையாளர் ஒப்பந்தம் அது வெற்றிகரமாக இருந்ததா என்பதை அறிய முடியும்.
/**
* @dev `owner` சார்பாக {transferFrom} மூலம் செலவிட `spender` அனுமதிக்கப்படும் மீதமுள்ள வில்லைகளின் எண்ணிக்கையை வழங்குகிறது. இது இயல்பாகவே சுழியாகும்.
*
* {approve} அல்லது {transferFrom} அழைக்கப்படும்போது இந்த மதிப்பு மாறுகிறது.
*/
function allowance(address owner, address spender) external view returns (uint256);
allowance செயல்பாடு, ஒரு முகவரி (owner) மற்றொரு முகவரியை (spender) எவ்வளவு செலவிட அனுமதிக்கிறது என்பதை யார் வேண்டுமானாலும் வினவ அனுமதிக்கிறது.
/**
* @dev அழைப்பாளரின் வில்லைகள் மீது `spender` இன் அனுமதித்தொகையாக `amount` ஐ அமைக்கிறது.
*
* செயல்பாடு வெற்றிகரமாக நடந்ததா என்பதைக் குறிக்கும் பூலியன் மதிப்பை வழங்குகிறது.
*
* முக்கியமானது: இந்த முறையின் மூலம் அனுமதித்தொகையை மாற்றுவது, துரதிர்ஷ்டவசமான பரிவர்த்தனை வரிசைப்படுத்தலால் பழைய மற்றும் புதிய அனுமதித்தொகை இரண்டையும் யாராவது பயன்படுத்தக்கூடும் என்ற அபாயத்தைக் கொண்டுவருகிறது என்பதை நினைவில் கொள்க. இந்தப் போட்டி நிலையைத் தணிக்க ஒரு சாத்தியமான தீர்வு, முதலில் செலவிடுபவரின் அனுமதித்தொகையை 0 ஆகக் குறைத்து, பின்னர் விரும்பிய மதிப்பை அமைப்பதாகும்:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* ஒரு {Approval} நிகழ்வை வெளியிடுகிறது.
*/
function approve(address spender, uint256 amount) external returns (bool);
approve செயல்பாடு ஒரு அனுமதித்தொகையை உருவாக்குகிறது. இது எவ்வாறு தவறாகப்
பயன்படுத்தப்படலாம் என்பது பற்றிய செய்தியைப் படிக்க மறக்காதீர்கள். எத்திரியத்தில் உங்கள் சொந்தப்
பரிவர்த்தனைகளின் வரிசையை நீங்கள் கட்டுப்படுத்துகிறீர்கள், ஆனால் மற்றவர்களின் பரிவர்த்தனைகள் எந்த
வரிசையில் செயல்படுத்தப்படும் என்பதை உங்களால் கட்டுப்படுத்த முடியாது, மற்ற தரப்பினரின் பரிவர்த்தனை
நடந்ததை நீங்கள் பார்க்கும் வரை உங்கள் சொந்தப் பரிவர்த்தனையை நீங்கள் சமர்ப்பிக்காவிட்டால் ஒழிய.
/**
* @dev அனுமதித்தொகை பொறிமுறையைப் பயன்படுத்தி `sender` இடமிருந்து `recipient` முகவரிக்கு `amount` வில்லைகளை நகர்த்துகிறது. பின்னர் அழைப்பாளரின் அனுமதித்தொகையிலிருந்து `amount` கழிக்கப்படுகிறது.
*
* செயல்பாடு வெற்றிகரமாக நடந்ததா என்பதைக் குறிக்கும் பூலியன் மதிப்பை வழங்குகிறது.
*
* ஒரு {Transfer} நிகழ்வை வெளியிடுகிறது.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
இறுதியாக, அனுமதித்தொகையை உண்மையில் செலவிடச் செலவிடுபவரால் transferFrom பயன்படுத்தப்படுகிறது.
/**
* @dev ஒரு கணக்கிலிருந்து (`from`) மற்றொரு கணக்கிற்கு (`to`) `value` வில்லைகள் நகர்த்தப்படும்போது வெளியிடப்படுகிறது.
*
* `value` சுழியாக இருக்கலாம் என்பதை நினைவில் கொள்க.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev {approve} அழைப்பின் மூலம் `owner` க்கான `spender` இன் அனுமதித்தொகை அமைக்கப்படும்போது வெளியிடப்படுகிறது. `value` என்பது புதிய அனுமதித்தொகை ஆகும்.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
ERC-20 ஒப்பந்தத்தின் நிலை மாறும் போது இந்த நிகழ்வுகள் வெளியிடப்படுகின்றன.
உண்மையான ஒப்பந்தம்
இது ERC-20 தரநிலையைச் செயல்படுத்தும் உண்மையான ஒப்பந்தமாகும், இங்கிருந்து எடுக்கப்பட்டது (opens in a new tab). இது அப்படியே பயன்படுத்தப்படுவதற்காக அல்ல, ஆனால் நீங்கள் அதிலிருந்து மரபுரிமையாகப் பெற்று (inherit) (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என்பது ஈதர் இல்லாத பயனர்களைத் தொகுதிச்சங்கிலியைப் பயன்படுத்த அனுமதிக்கும் அமைப்பான OpenGSN (opens in a new tab) ஐப் பயன்படுத்தத் தேவையான வரையறைகளாகும். இது ஒரு பழைய பதிப்பு என்பதை நினைவில் கொள்க, நீங்கள் OpenGSN உடன் ஒருங்கிணைக்க விரும்பினால் இந்த வழிகாட்டுதலைப் பயன்படுத்தவும் (opens in a new tab).- SafeMath நிரலகம் (opens in a new tab), இது Solidity பதிப்புகள் <0.8.0 க்கான எண்கணித அளவுமீறல்களைத் (overflows/underflows) தடுக்கிறது. Solidity ≥0.8.0 இல், எண்கணிதச் செயல்பாடுகள் அளவுமீறலின் போது தானாகவே மீளமைக்கப்படும் (revert), இதனால் SafeMath தேவையற்றதாகிறது. பழைய தொகுப்பி (compiler) பதிப்புகளுடனான பின்தங்கிய இணக்கத்தன்மைக்காக இந்த ஒப்பந்தம் SafeMath ஐப் பயன்படுத்துகிறது.
இந்தக் கருத்து ஒப்பந்தத்தின் நோக்கத்தை விளக்குகிறது.
/**
* @dev {IERC20} இடைமுகத்தின் செயலாக்கம்.
*
* இந்த செயலாக்கம் வில்லைகள் உருவாக்கப்படும் முறையைப் பொருட்படுத்தாது. அதாவது {_mint} ஐப் பயன்படுத்தி பெறப்பட்ட ஒப்பந்தத்தில் ஒரு வழங்கல் பொறிமுறையைச் சேர்க்க வேண்டும்.
* பொதுவான பொறிமுறைக்கு {ERC20PresetMinterPauser} ஐப் பார்க்கவும்.
*
* குறிப்பு: விரிவான விளக்கத்திற்கு எங்கள் வழிகாட்டியைப் பார்க்கவும்
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* நாங்கள் பொதுவான ஓப்பன்செப்பெலின் வழிகாட்டுதல்களைப் பின்பற்றியுள்ளோம்: தோல்வியடையும் போது `false` ஐ வழங்குவதற்குப் பதிலாக செயல்பாடுகள் திரும்பப் பெறப்படும் (revert). இந்த நடத்தை வழக்கமானது மற்றும் ERC-20 பயன்பாடுகளின் எதிர்பார்ப்புகளுடன் முரண்படாது.
*
* கூடுதலாக, {transferFrom} அழைப்புகளில் ஒரு {Approval} நிகழ்வு வெளியிடப்படுகிறது.
* இது பயன்பாடுகள் குறிப்பிட்ட நிகழ்வுகளைக் கேட்பதன் மூலம் அனைத்து கணக்குகளுக்குமான அனுமதித்தொகையை மறுகட்டமைக்க அனுமதிக்கிறது. EIP இன் பிற செயலாக்கங்கள் இந்த நிகழ்வுகளை வெளியிடாமல் இருக்கலாம், ஏனெனில் இது விவரக்குறிப்பால் கோரப்படவில்லை.
*
* இறுதியாக, அனுமதித்தொகைகளை அமைப்பதில் உள்ள நன்கு அறியப்பட்ட சிக்கல்களைத் தணிக்க தரமற்ற {decreaseAllowance} மற்றும் {increaseAllowance}
* செயல்பாடுகள் சேர்க்கப்பட்டுள்ளன. {IERC20-approve} ஐப் பார்க்கவும்.
*/
ஒப்பந்த வரையறை
contract ERC20 is Context, IERC20 {
இந்த வரி மரபுரிமையைக் குறிப்பிடுகிறது, இந்த நிலையில் மேலே உள்ள IERC20 மற்றும் OpenGSN க்கான Context ஆகியவற்றிலிருந்து.
using SafeMath for uint256;
இந்த வரி SafeMath நிரலகத்தை uint256 வகையுடன் இணைக்கிறது. இந்த நிரலகத்தை நீங்கள்
இங்கே (opens in a new tab) காணலாம்.
மாறி வரையறைகள்
இந்த வரையறைகள் ஒப்பந்தத்தின் நிலை மாறிகளைக் குறிப்பிடுகின்றன. இந்த மாறிகள் private என அறிவிக்கப்படுகின்றன, ஆனால்
தொகுதிச்சங்கிலியில் உள்ள பிற ஒப்பந்தங்களால் அவற்றைப் படிக்க முடியாது என்பதை மட்டுமே இது குறிக்கிறது. தொகுதிச்சங்கிலியில் எந்த
ரகசியங்களும் இல்லை, ஒவ்வொரு கணுவிலும் உள்ள மென்பொருள் ஒவ்வொரு தொகுதியிலும் ஒவ்வொரு ஒப்பந்தத்தின் நிலையைக் கொண்டுள்ளது.
வழக்கமாக, நிலை மாறிகள் _<something> எனப் பெயரிடப்படுகின்றன.
முதல் இரண்டு மாறிகள் மேப்பிங்குகள் (mappings) (opens in a new tab) ஆகும், அதாவது விசைகள் எண் மதிப்புகளாக இருப்பதைத் தவிர, அவை தோராயமாக தொடர்புடைய அணிவரிசைகளைப் (associative arrays) (opens in a new tab) போலவே செயல்படுகின்றன. இயல்புநிலையிலிருந்து (சுழி) வேறுபட்ட மதிப்புகளைக் கொண்ட உள்ளீடுகளுக்கு மட்டுமே சேமிப்பகம் ஒதுக்கப்படுகிறது.
mapping (address => uint256) private _balances;
முதல் மேப்பிங், _balances, முகவரிகள் மற்றும் இந்த வில்லையின் அவற்றின் தொடர்புடைய இருப்புகள் ஆகும். இருப்பை அணுக,
இந்தத் தொடரியலைப் பயன்படுத்தவும்: _balances[<address>].
mapping (address => mapping (address => uint256)) private _allowances;
இந்த மாறி, _allowances, முன்பு விளக்கப்பட்ட அனுமதித்தொகைகளைச் சேமிக்கிறது. முதல் குறியீடு வில்லைகளின் உரிமையாளர்,
மற்றும் இரண்டாவது அனுமதித்தொகையைக் கொண்ட ஒப்பந்தம். முகவரி B இன் கணக்கிலிருந்து முகவரி A செலவிடக்கூடிய தொகையை அணுக,
_allowances[B][A] ஐப் பயன்படுத்தவும்.
uint256 private _totalSupply;
பெயர் குறிப்பிடுவது போல, இந்த மாறி வில்லைகளின் மொத்த விநியோகத்தைக் கண்காணிக்கிறது.
string private _name;
string private _symbol;
uint8 private _decimals;
இந்த மூன்று மாறிகள் வாசிப்புத்திறனை மேம்படுத்தப் பயன்படுத்தப்படுகின்றன. முதல் இரண்டு தாமாகவே விளங்குகின்றன, ஆனால் _decimals
அப்படி இல்லை.
ஒருபுறம், எத்திரியத்தில் மிதவைப் புள்ளி (floating point) அல்லது பின்ன மாறிகள் இல்லை. மறுபுறம், மனிதர்கள் வில்லைகளைப் பிரிக்க விரும்புவார்கள். நாணயத்திற்காக மக்கள் தங்கத்தைத் தேர்ந்தெடுத்ததற்கு ஒரு காரணம், யாராவது ஒரு வாத்தின் மதிப்பிற்குப் பசுவை வாங்க விரும்பியபோது சில்லறை மாற்றுவது கடினமாக இருந்தது.
இதற்கான தீர்வு முழு எண்களைக் கண்காணிப்பதாகும், ஆனால் உண்மையான வில்லைக்குப் பதிலாக கிட்டத்தட்ட மதிப்பற்ற ஒரு பின்ன வில்லையை எண்ணுவதாகும். ஈதரைப் பொறுத்தவரை, பின்ன வில்லை Wei என்று அழைக்கப்படுகிறது, மேலும் 10^18 Wei என்பது ஒரு ETH க்குச் சமம். இதை எழுதும் போது, 10,000,000,000,000 Wei என்பது தோராயமாக ஒரு US அல்லது யூரோ சென்ட் ஆகும்.
பயன்பாடுகள் வில்லை இருப்பை எப்படிக் காண்பிப்பது என்பதை அறிய வேண்டும். ஒரு பயனரிடம் 3,141,000,000,000,000,000 Wei இருந்தால், அது
3.14 ETH ஆ? 31.41 ETH ஆ? 3,141 ETH ஆ? ஈதரைப் பொறுத்தவரை இது ETH க்கு 10^18 Wei என வரையறுக்கப்பட்டுள்ளது, ஆனால் உங்கள்
வில்லைக்கு நீங்கள் வேறு மதிப்பைத் தேர்ந்தெடுக்கலாம். வில்லையைப் பிரிப்பதில் அர்த்தமில்லை என்றால், நீங்கள் ஒரு
_decimals மதிப்பைச் சுழியாகப் பயன்படுத்தலாம். நீங்கள் ETH போன்ற அதே தரநிலையைப் பயன்படுத்த விரும்பினால், 18 என்ற மதிப்பைப் பயன்படுத்தவும்.
ஆக்கி (Constructor)
/**
* @dev {name} மற்றும் {symbol} க்கான மதிப்புகளை அமைக்கிறது, {decimals} ஐ
* 18 என்ற இயல்புநிலை மதிப்புடன் துவக்குகிறது.
*
* {decimals} க்கு வேறு மதிப்பைத் தேர்ந்தெடுக்க, {_setupDecimals} ஐப் பயன்படுத்தவும்.
*
* இந்த மூன்று மதிப்புகளும் மாற்ற முடியாதவை: ஆக்கி மூலம் உருவாக்கப்படும் போது ஒரு முறை மட்டுமே இவற்றை அமைக்க முடியும்.
*/
constructor (string memory name_, string memory symbol_) public {
// Solidity ≥0.7.0 இல், 'public' என்பது மறைமுகமானது மற்றும் தவிர்க்கப்படலாம்.
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
ஒப்பந்தம் முதன்முதலில் உருவாக்கப்படும் போது ஆக்கி அழைக்கப்படுகிறது. வழக்கமாக, செயல்பாட்டு அளவுருக்கள் <something>_ எனப் பெயரிடப்படுகின்றன.
பயனர் இடைமுகச் செயல்பாடுகள்
/**
* @dev வில்லையின் பெயரை வழங்குகிறது.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev வில்லையின் குறியீட்டை வழங்குகிறது, இது பொதுவாக பெயரின் சுருக்கமான வடிவமாகும்.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev அதன் பயனர் பிரதிநிதித்துவத்தைப் பெறப் பயன்படுத்தப்படும் தசமங்களின் எண்ணிக்கையை வழங்குகிறது.
* எடுத்துக்காட்டாக, `decimals` `2` க்கு சமமாக இருந்தால், `505` வில்லைகளின் இருப்பு
* பயனருக்கு `5,05` (`505 / 10 ** 2`) எனக் காட்டப்பட வேண்டும்.
*
* ஈதர் மற்றும் Wei இடையேயான உறவைப் பின்பற்றி, வில்லைகள் பொதுவாக 18 என்ற மதிப்பைத் தேர்ந்தெடுக்கின்றன. {_setupDecimals} அழைக்கப்படாவிட்டால், {ERC20} பயன்படுத்தும் மதிப்பு இதுவாகும்.
*
* குறிப்பு: இந்தத் தகவல் _காட்சி_ நோக்கங்களுக்காக மட்டுமே பயன்படுத்தப்படுகிறது: இது
* {IERC20-balanceOf} மற்றும் {IERC20-transfer} உட்பட ஒப்பந்தத்தின் எந்தவொரு எண்கணிதத்தையும் எந்த வகையிலும் பாதிக்காது.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
இந்தச் செயல்பாடுகள், name, symbol மற்றும் decimals ஆகியவை பயனர் இடைமுகங்கள் உங்கள் ஒப்பந்தத்தைப் பற்றி அறிய உதவுகின்றன, இதனால் அவை அதைச் சரியாகக் காண்பிக்க முடியும்.
திரும்பும் வகை string memory ஆகும், அதாவது நினைவகத்தில் சேமிக்கப்பட்டுள்ள ஒரு சரத்தை (string) வழங்குகிறது. மாறிகள், அதாவது
சரங்கள் போன்றவற்றை மூன்று இடங்களில் சேமிக்கலாம்:
| வாழ்நாள் | ஒப்பந்த அணுகல் | எரிவாயு செலவு | |
|---|---|---|---|
| நினைவகம் | செயல்பாட்டு அழைப்பு | படிக்க/எழுத | பத்துகள் அல்லது நூறுகள் (உயர்ந்த இடங்களுக்கு அதிகம்) |
| அழைப்புத் தரவு | செயல்பாட்டு அழைப்பு | படிக்க மட்டும் | திரும்பும் வகையாகப் பயன்படுத்த முடியாது, செயல்பாட்டு அளவுரு வகையாக மட்டுமே |
| சேமிப்பகம் | மாற்றப்படும் வரை | படிக்க/எழுத | அதிகம் (படிக்க 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];
}
ஒரு கணக்கின் இருப்பைப் படிக்கவும். வேறு யாருடைய கணக்கு இருப்பையும் பெற எவரும் அனுமதிக்கப்படுகிறார்கள் என்பதை நினைவில் கொள்க. இந்தத் தகவலை மறைக்க முயற்சிப்பதில் எந்த அர்த்தமும் இல்லை, ஏனெனில் இது எப்படியும் ஒவ்வொரு கணுவிலும் கிடைக்கிறது. தொகுதிச்சங்கிலியில் எந்த ரகசியங்களும் இல்லை.
வில்லைகளைப் பரிமாற்றம் செய்யவும்
/**
* @dev {IERC20-transfer} ஐப் பார்க்கவும்.
*
* தேவைகள்:
*
* - `recipient` சுழி முகவரியாக இருக்க முடியாது.
* - அழைப்பாளரிடம் குறைந்தபட்சம் `amount` இருப்பு இருக்க வேண்டும்.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
அனுப்புநரின் கணக்கிலிருந்து வேறு கணக்கிற்கு வில்லைகளைப் பரிமாற்றம் செய்ய transfer செயல்பாடு அழைக்கப்படுகிறது. இது
ஒரு பூலியன் மதிப்பை வழங்கினாலும், அந்த மதிப்பு எப்போதும் உண்மை (true) ஆக இருக்கும் என்பதை நினைவில் கொள்க. பரிமாற்றம்
தோல்வியுற்றால் ஒப்பந்தம் அழைப்பை மீளமைக்கிறது.
_transfer(_msgSender(), recipient, amount);
return true;
}
_transfer செயல்பாடு உண்மையான வேலையைச் செய்கிறது. இது ஒரு தனிப்பட்ட செயல்பாடாகும், இது பிற
ஒப்பந்தச் செயல்பாடுகளால் மட்டுமே அழைக்கப்பட முடியும். வழக்கமாகத் தனிப்பட்ட செயல்பாடுகள் நிலை
மாறிகளைப் போலவே _<something> எனப் பெயரிடப்படுகின்றன.
பொதுவாக Solidity இல் செய்தி அனுப்புநருக்கு msg.sender ஐப் பயன்படுத்துகிறோம். இருப்பினும், அது
OpenGSN (opens in a new tab) ஐ உடைக்கிறது. எங்கள் வில்லையுடன் ஈதர் இல்லாத பரிவர்த்தனைகளை அனுமதிக்க விரும்பினால், நாங்கள்
_msgSender() ஐப் பயன்படுத்த வேண்டும். இது சாதாரணப் பரிவர்த்தனைகளுக்கு msg.sender ஐ வழங்குகிறது, ஆனால் ஈதர் இல்லாத பரிவர்த்தனைகளுக்குச்
செய்தியை அனுப்பிய ஒப்பந்தத்தை அல்லாமல் அசல் கையொப்பமிட்டவரை வழங்குகிறது.
அனுமதித்தொகைச் செயல்பாடுகள்
இவை அனுமதித்தொகைச் செயல்பாட்டைச் செயல்படுத்தும் செயல்பாடுகள்: allowance, approve, transferFrom,
மற்றும் _approve. கூடுதலாக, ஓப்பன்செப்பெலின் செயலாக்கம் பாதுகாப்பை மேம்படுத்தும் சில அம்சங்களைச் சேர்க்க அடிப்படைத் தரநிலைக்கு அப்பால் செல்கிறது: increaseAllowance மற்றும் decreaseAllowance.
allowance செயல்பாடு
/**
* @dev {IERC20-allowance} ஐப் பார்க்கவும்.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
allowance செயல்பாடு எந்தவொரு அனுமதித்தொகையையும் சரிபார்க்க அனைவரையும் அனுமதிக்கிறது.
approve செயல்பாடு
/**
* @dev {IERC20-approve} ஐப் பார்க்கவும்.
*
* தேவைகள்:
*
* - `spender` சுழி முகவரியாக இருக்க முடியாது.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
அனுமதித்தொகையை உருவாக்க இந்தச் செயல்பாடு அழைக்கப்படுகிறது. இது மேலே உள்ள transfer செயல்பாட்டைப் போன்றது:
- செயல்பாடு உண்மையான வேலையைச் செய்யும் ஒரு உள் செயல்பாட்டை (இந்த நிலையில்,
_approve) அழைக்கிறது. - செயல்பாடு
trueஐ வழங்குகிறது (வெற்றிகரமாக இருந்தால்) அல்லது மீளமைக்கிறது (இல்லையென்றால்).
_approve(_msgSender(), spender, amount);
return true;
}
நிலை மாற்றங்கள் நிகழும் இடங்களின் எண்ணிக்கையைக் குறைக்க உள் செயல்பாடுகளைப் பயன்படுத்துகிறோம். நிலையை மாற்றும் எந்தவொரு செயல்பாடும் பாதுகாப்பிற்காகத் தணிக்கை செய்யப்பட வேண்டிய சாத்தியமான பாதுகாப்பு அபாயமாகும். இதன் மூலம் நாம் தவறு செய்வதற்கான வாய்ப்புகள் குறைவு.
transferFrom செயல்பாடு
அனுமதித்தொகையைச் செலவிடச் செலவிடுபவர் அழைக்கும் செயல்பாடு இதுவாகும். இதற்கு இரண்டு செயல்பாடுகள் தேவை: செலவிடப்படும் தொகையைப் பரிமாற்றம் செய்வது மற்றும் அந்தத் தொகையால் அனுமதித்தொகையைக் குறைப்பது.
/**
* @dev {IERC20-transferFrom} ஐப் பார்க்கவும்.
*
* புதுப்பிக்கப்பட்ட அனுமதித்தொகையைக் குறிக்கும் ஒரு {Approval} நிகழ்வை வெளியிடுகிறது. இது
* EIP ஆல் கோரப்படவில்லை. {ERC20} இன் தொடக்கத்தில் உள்ள குறிப்பைப் பார்க்கவும்.
*
* தேவைகள்:
*
* - `sender` மற்றும் `recipient` சுழி முகவரியாக இருக்க முடியாது.
* - `sender` குறைந்தபட்சம் `amount` இருப்பைக் கொண்டிருக்க வேண்டும்.
* - அழைப்பாளர் ``sender`` இன் வில்லைகளுக்கு குறைந்தபட்சம் `amount` அனுமதித்தொகையைக் கொண்டிருக்க வேண்டும்.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual
override returns (bool) {
_transfer(sender, recipient, amount);
a.sub(b, "message") செயல்பாட்டு அழைப்பு இரண்டு விஷயங்களைச் செய்கிறது. முதலாவதாக, இது புதிய அனுமதித்தொகையான a-b ஐக் கணக்கிடுகிறது.
இரண்டாவதாக, இந்த முடிவு எதிர்மறையாக இல்லை என்பதைச் சரிபார்க்கிறது. அது எதிர்மறையாக இருந்தால், வழங்கப்பட்ட செய்தியுடன் அழைப்பு மீளமைக்கப்படும். ஒரு அழைப்பு மீளமைக்கப்படும் போது, அந்த அழைப்பின் போது முன்பு செய்யப்பட்ட எந்தவொரு செயலாக்கமும் புறக்கணிக்கப்படும் என்பதை நினைவில் கொள்க, எனவே நாம்
_transfer ஐச் செயல்தவிர்க்கத் தேவையில்லை.
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount,
"ERC20: transfer amount exceeds allowance"));
return true;
}
ஓப்பன்செப்பெலின் பாதுகாப்புச் சேர்த்தல்கள்
சுழியற்ற அனுமதித்தொகையை மற்றொரு சுழியற்ற மதிப்பிற்கு அமைப்பது ஆபத்தானது, ஏனெனில் நீங்கள் உங்கள் சொந்தப் பரிவர்த்தனைகளின் வரிசையை மட்டுமே கட்டுப்படுத்துகிறீர்கள், வேறு யாருடையதையும் அல்ல. உங்களிடம் இரண்டு பயனர்கள் இருப்பதாகக் கற்பனை செய்து பாருங்கள், ஆலிஸ் அப்பாவி மற்றும் பில் நேர்மையற்றவர். ஆலிஸ் பில்லிடமிருந்து சில சேவைகளை விரும்புகிறார், அதற்கு ஐந்து வில்லைகள் செலவாகும் என்று அவர் நினைக்கிறார் - எனவே அவர் பில்லுக்கு ஐந்து வில்லைகள் அனுமதித்தொகையை வழங்குகிறார்.
பின்னர் ஏதோ மாறுகிறது மற்றும் பில்லின் விலை பத்து வில்லைகளாக உயர்கிறது. இன்னும் சேவையை விரும்பும் ஆலிஸ், பில்லின் அனுமதித்தொகையைப் பத்தாக அமைக்கும் பரிவர்த்தனையை அனுப்புகிறார். பரிவர்த்தனைத் தொகுப்பில் இந்தப் புதிய பரிவர்த்தனையைப் பில் பார்த்தவுடன், அவர் ஆலிஸின் ஐந்து வில்லைகளைச் செலவழிக்கும் ஒரு பரிவர்த்தனையை அனுப்புகிறார், மேலும் அது மிக அதிக எரிவாயு விலையைக் கொண்டுள்ளது, எனவே அது வேகமாகச் செயலாக்கப்படும். அந்த வகையில் பில் முதலில் ஐந்து வில்லைகளைச் செலவிடலாம், பின்னர், ஆலிஸின் புதிய அனுமதித்தொகை செயலாக்கப்பட்டவுடன், ஆலிஸ் அங்கீகரிக்க நினைத்ததை விட அதிகமாக, மொத்தம் பதினைந்து வில்லைகளுக்கு மேலும் பத்தைச் செலவிடலாம். இந்த நுட்பம் முந்திச் செயல்படுதல் (front-running) (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) அனுமதித்தொகையை
ஒரு குறிப்பிட்ட தொகையால் மாற்ற உங்களை அனுமதிக்கின்றன. எனவே பில் ஏற்கனவே ஐந்து வில்லைகளைச் செலவழித்திருந்தால், அவரால் மேலும்
ஐந்து மட்டுமே செலவிட முடியும். நேரத்தைப் பொறுத்து, இது வேலை செய்ய இரண்டு வழிகள் உள்ளன, இவை
இரண்டும் பில்லுக்குப் பத்து வில்லைகள் மட்டுமே கிடைப்பதில் முடிகின்றன:
A:
| ஆலிஸ் பரிவர்த்தனை | ஆலிஸ் நான்ஸ் | பில் பரிவர்த்தனை | பில் நான்ஸ் | பில்லின் அனுமதித்தொகை | ஆலிஸிடமிருந்து பில்லின் மொத்த வருமானம் |
|---|---|---|---|---|---|
| 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 |
B:
| ஆலிஸ் பரிவர்த்தனை | ஆலிஸ் நான்ஸ் | பில் பரிவர்த்தனை | பில் நான்ஸ் | பில்லின் அனுமதித்தொகை | ஆலிஸிடமிருந்து பில்லின் மொத்த வருமானம் |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| increaseAllowance(Bill, 5) | 11 | 5+5 = 10 | 0 | ||
| transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 |
/**
* @dev அழைப்பாளரால் `spender` க்கு வழங்கப்பட்ட அனுமதித்தொகையை அணுரீதியாக (atomically) அதிகரிக்கிறது.
*
* இது {approve} க்கு ஒரு மாற்றாகும், இது {IERC20-approve} இல் விவரிக்கப்பட்டுள்ள
* சிக்கல்களுக்கான தணிப்பாகப் பயன்படுத்தப்படலாம்.
*
* புதுப்பிக்கப்பட்ட அனுமதித்தொகையைக் குறிக்கும் ஒரு {Approval} நிகழ்வை வெளியிடுகிறது.
*
* தேவைகள்:
*
* - `spender` சுழி முகவரியாக இருக்க முடியாது.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
a.add(b) செயல்பாடு ஒரு பாதுகாப்பான கூட்டல் ஆகும். a+b>=2^256 என்ற சாத்தியமில்லாத நிலையில், இது சாதாரணக் கூட்டல் செய்வது போல் சுற்றிக் கொள்ளாது (wrap around).
/**
* @dev அழைப்பாளரால் `spender` க்கு வழங்கப்பட்ட அனுமதித்தொகையை அணுரீதியாக (atomically) குறைக்கிறது.
*
* இது {approve} க்கு ஒரு மாற்றாகும், இது {IERC20-approve} இல் விவரிக்கப்பட்டுள்ள
* சிக்கல்களுக்கான தணிப்பாகப் பயன்படுத்தப்படலாம்.
*
* புதுப்பிக்கப்பட்ட அனுமதித்தொகையைக் குறிக்கும் ஒரு {Approval} நிகழ்வை வெளியிடுகிறது.
*
* தேவைகள்:
*
* - `spender` சுழி முகவரியாக இருக்க முடியாது.
* - `spender` அழைப்பாளருக்கு குறைந்தபட்சம் `subtractedValue` அனுமதித்தொகையைக் கொண்டிருக்க வேண்டும்.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue,
"ERC20: decreased allowance below zero"));
return true;
}
வில்லைத் தகவலை மாற்றும் செயல்பாடுகள்
உண்மையான வேலையைச் செய்யும் நான்கு செயல்பாடுகள் இவை: _transfer, _mint, _burn மற்றும் _approve.
_transfer செயல்பாடு
/**
* @dev `sender` இடமிருந்து `recipient` முகவரிக்கு `amount` வில்லைகளை நகர்த்துகிறது.
*
* இந்த உள்ளகச் செயல்பாடு {transfer} க்குச் சமமானது, மேலும் இது
* எ.கா., தானியங்கி வில்லைக் கட்டணங்கள், குறைப்பு (slashing) பொறிமுறைகள் போன்றவற்றைச் செயல்படுத்தப் பயன்படுத்தப்படலாம்.
*
* ஒரு {Transfer} நிகழ்வை வெளியிடுகிறது.
*
* தேவைகள்:
*
* - `sender` சுழி முகவரியாக இருக்க முடியாது.
* - `recipient` சுழி முகவரியாக இருக்க முடியாது.
* - `sender` குறைந்தபட்சம் `amount` இருப்பைக் கொண்டிருக்க வேண்டும்.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
இந்தச் செயல்பாடு, _transfer, ஒரு கணக்கிலிருந்து மற்றொரு கணக்கிற்கு வில்லைகளைப் பரிமாற்றம் செய்கிறது. இது transfer (அனுப்புநரின் சொந்தக் கணக்கிலிருந்து பரிமாற்றங்களுக்கு) மற்றும் transferFrom (வேறொருவரின் கணக்கிலிருந்து பரிமாற்றம் செய்ய அனுமதித்தொகைகளைப் பயன்படுத்துவதற்கு) ஆகிய இரண்டாலும் அழைக்கப்படுகிறது.
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
எத்திரியத்தில் சுழி முகவரியை (zero address) உண்மையில் யாரும் கொண்டிருக்கவில்லை (அதாவது, பொருந்தக்கூடிய பொது விசை சுழி முகவரியாக மாற்றப்படும் தனிப்பட்ட திறவுகோல் யாருக்கும் தெரியாது). மக்கள் அந்த முகவரியைப் பயன்படுத்தும் போது, அது பொதுவாக ஒரு மென்பொருள் பிழையாகும் - எனவே சுழி முகவரி அனுப்புநராகவோ அல்லது பெறுநராகவோ பயன்படுத்தப்பட்டால் நாங்கள் தோல்வியடைகிறோம்.
_beforeTokenTransfer(sender, recipient, amount);
இந்த ஒப்பந்தத்தைப் பயன்படுத்த இரண்டு வழிகள் உள்ளன:
- உங்கள் சொந்தக் குறியீட்டிற்கான வார்ப்புருவாக இதைப் பயன்படுத்தவும்
- இதிலிருந்து மரபுரிமையாகப் பெற்று (opens in a new tab), நீங்கள் மாற்ற வேண்டிய செயல்பாடுகளை மட்டும் மேலெழுதவும் (override)
இரண்டாவது முறை மிகவும் சிறந்தது, ஏனெனில் ஓப்பன்செப்பெலின் ERC-20 குறியீடு ஏற்கனவே தணிக்கை செய்யப்பட்டுப் பாதுகாப்பானது எனக் காட்டப்பட்டுள்ளது. நீங்கள் மரபுரிமையைப் பயன்படுத்தும் போது நீங்கள் மாற்றும் செயல்பாடுகள் என்ன என்பது தெளிவாகிறது, மேலும் உங்கள் ஒப்பந்தத்தை நம்புவதற்கு மக்கள் அந்த குறிப்பிட்ட செயல்பாடுகளை மட்டுமே தணிக்கை செய்ய வேண்டும்.
ஒவ்வொரு முறையும் வில்லைகள் கைமாறும் போது ஒரு செயல்பாட்டைச் செய்வது பெரும்பாலும் பயனுள்ளதாக இருக்கும். இருப்பினும், _transfer என்பது மிக முக்கியமான செயல்பாடாகும், மேலும் அதைப் பாதுகாப்பற்ற முறையில் எழுத முடியும் (கீழே காண்க), எனவே அதை மேலெழுதாமல் இருப்பது நல்லது. இதற்கான தீர்வு _beforeTokenTransfer ஆகும், இது ஒரு
ஹூக் செயல்பாடு (hook function) (opens in a new tab). நீங்கள் இந்தச் செயல்பாட்டை மேலெழுதலாம், மேலும் இது ஒவ்வொரு பரிமாற்றத்தின் போதும் அழைக்கப்படும்.
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
உண்மையில் பரிமாற்றத்தைச் செய்யும் வரிகள் இவை. அவற்றுக்கிடையே எதுவும் இல்லை என்பதையும், பெறுநரிடம் சேர்ப்பதற்கு முன் அனுப்புநரிடமிருந்து பரிமாற்றம் செய்யப்பட்ட தொகையைக் கழிக்கிறோம் என்பதையும் நினைவில் கொள்க. இது முக்கியமானது, ஏனென்றால் நடுவில் வேறு ஒப்பந்தத்திற்கு அழைப்பு இருந்தால், அது இந்த ஒப்பந்தத்தை ஏமாற்றப் பயன்படுத்தப்பட்டிருக்கலாம். இந்த வழியில் பரிமாற்றம் அணுவானது (atomic), அதன் நடுவில் எதுவும் நடக்க முடியாது.
emit Transfer(sender, recipient, amount);
}
இறுதியாக, ஒரு Transfer நிகழ்வை வெளியிடவும். நிகழ்வுகள் திறன் ஒப்பந்தங்களுக்கு அணுகக்கூடியவை அல்ல, ஆனால் தொகுதிச்சங்கிலிக்கு வெளியே இயங்கும் குறியீடு நிகழ்வுகளைக் கேட்கலாம் மற்றும் அவற்றுக்குப் பதிலளிக்கலாம். உதாரணமாக, உரிமையாளர் எப்போது அதிக வில்லைகளைப் பெறுகிறார் என்பதை ஒரு பணப்பை கண்காணிக்க முடியும்.
_mint மற்றும் _burn செயல்பாடுகள்
இந்த இரண்டு செயல்பாடுகளும் (_mint மற்றும் _burn) வில்லைகளின் மொத்த விநியோகத்தை மாற்றுகின்றன.
அவை அகச் செயல்பாடுகள் மற்றும் இந்த ஒப்பந்தத்தில் அவற்றை அழைக்கும் எந்தச் செயல்பாடும் இல்லை,
எனவே நீங்கள் ஒப்பந்தத்திலிருந்து மரபுரிமையாகப் பெற்று, எந்த நிபந்தனைகளின் கீழ் புதிய வில்லைகளை அச்சிடுவது (mint) அல்லது ஏற்கனவே உள்ளவற்றை எரிப்பது (burn) என்பதை முடிவு செய்ய உங்கள் சொந்த தர்க்கத்தைச் சேர்த்தால் மட்டுமே அவை பயனுள்ளதாக இருக்கும்.
குறிப்பு: ஒவ்வொரு ERC-20 வில்லையும் வில்லை நிர்வாகத்தைக் கட்டளையிடும் அதன் சொந்த வணிகத் தர்க்கத்தைக் கொண்டுள்ளது.
உதாரணமாக, ஒரு நிலையான விநியோக ஒப்பந்தம் ஆக்கியில் _mint ஐ மட்டுமே அழைக்கலாம்
மற்றும் _burn ஐ ஒருபோதும் அழைக்காது. வில்லைகளை விற்கும் ஒரு ஒப்பந்தம் பணம் செலுத்தப்படும் போது _mint ஐ அழைக்கும், மேலும் கட்டுப்பாடற்ற பணவீக்கத்தைத் தவிர்க்க ஒரு கட்டத்தில் _burn ஐ அழைக்கலாம்.
/** @dev `amount` வில்லைகளை உருவாக்கி அவற்றை `account` கணக்கிற்கு ஒதுக்குகிறது, மொத்த வழங்கலை
* அதிகரிக்கிறது.
*
* `from` சுழி முகவரிக்கு அமைக்கப்பட்டு ஒரு {Transfer} நிகழ்வை வெளியிடுகிறது.
*
* தேவைகள்:
*
* - `to` சுழி முகவரியாக இருக்க முடியாது.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
வில்லைகளின் மொத்த எண்ணிக்கை மாறும் போது _totalSupply ஐப் புதுப்பிக்க மறக்காதீர்கள்.
/**
* @dev `account` கணக்கிலிருந்து `amount` வில்லைகளை அழிக்கிறது, மொத்த
* வழங்கலைக் குறைக்கிறது.
*
* `to` சுழி முகவரிக்கு அமைக்கப்பட்டு ஒரு {Transfer} நிகழ்வை வெளியிடுகிறது.
*
* தேவைகள்:
*
* - `account` சுழி முகவரியாக இருக்க முடியாது.
* - `account` குறைந்தபட்சம் `amount` வில்லைகளைக் கொண்டிருக்க வேண்டும்.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
_burn செயல்பாடு _mint ஐப் போலவே இருக்கும், ஆனால் இது எதிர் திசையில் செல்கிறது.
_approve செயல்பாடு
அனுமதித்தொகைகளை உண்மையில் குறிப்பிடும் செயல்பாடு இதுவாகும். உரிமையாளரின் தற்போதைய இருப்பை விட அதிக அனுமதித்தொகையைக் குறிப்பிட இது உரிமையாளரை அனுமதிக்கிறது என்பதை நினைவில் கொள்க. இது பரவாயில்லை, ஏனென்றால் பரிமாற்றத்தின் போது இருப்பு சரிபார்க்கப்படுகிறது, அப்போது அது அனுமதித்தொகை உருவாக்கப்படும் போது இருந்த இருப்பிலிருந்து வேறுபட்டிருக்கலாம்.
/**
* @dev `owner` இன் வில்லைகள் மீது `spender` இன் அனுமதித்தொகையாக `amount` ஐ அமைக்கிறது.
*
* இந்த உள்ளகச் செயல்பாடு `approve` க்குச் சமமானது, மேலும் இது
* எ.கா., சில துணை அமைப்புகளுக்கு தானியங்கி அனுமதித்தொகைகளை அமைக்கப் பயன்படுத்தப்படலாம்.
*
* ஒரு {Approval} நிகழ்வை வெளியிடுகிறது.
*
* தேவைகள்:
*
* - `owner` சுழி முகவரியாக இருக்க முடியாது.
* - `spender` சுழி முகவரியாக இருக்க முடியாது.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
ஒரு Approval நிகழ்வை வெளியிடவும். பயன்பாடு எவ்வாறு எழுதப்பட்டுள்ளது என்பதைப் பொறுத்து, செலவிடுபவர் ஒப்பந்தத்திற்கு ஒப்புதல் பற்றி உரிமையாளரால் அல்லது இந்த நிகழ்வுகளைக் கேட்கும் சேவையகத்தால் தெரிவிக்கப்படலாம்.
emit Approval(owner, spender, amount);
}
Decimals மாறியை மாற்றவும்
/**
* @dev {decimals} ஐ 18 என்ற இயல்புநிலை மதிப்பைத் தவிர வேறு மதிப்பிற்கு அமைக்கிறது.
*
* எச்சரிக்கை: இந்தச் செயல்பாடு ஆக்கியிலிருந்து மட்டுமே அழைக்கப்பட வேண்டும். வில்லை ஒப்பந்தங்களுடன்
* தொடர்பு கொள்ளும் பெரும்பாலான பயன்பாடுகள் {decimals} எப்போதாவது மாறும் என்று எதிர்பார்க்காது, அவ்வாறு மாறினால் தவறாகச் செயல்படலாம்.
*/
function _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
இந்தச் செயல்பாடு _decimals மாறியை மாற்றுகிறது, இது தொகையை எவ்வாறு விளக்குவது என்று பயனர் இடைமுகங்களுக்குச் சொல்லப் பயன்படுகிறது.
நீங்கள் அதை ஆக்கியிலிருந்து அழைக்க வேண்டும். அதைத் தொடர்ந்து எந்த நேரத்திலும் அழைப்பது நேர்மையற்றதாக இருக்கும், மேலும் பயன்பாடுகள் அதைக் கையாளும் வகையில் வடிவமைக்கப்படவில்லை.
ஹூக்குகள் (Hooks)
/**
* @dev வில்லைகளின் எந்தவொரு பரிமாற்றத்திற்கும் முன் அழைக்கப்படும் கொக்கி (Hook). இதில்
* உருவாக்குதல் (minting) மற்றும் அழித்தல் (burning) ஆகியவை அடங்கும்.
*
* அழைப்பதற்கான நிபந்தனைகள்:
*
* - `from` மற்றும் `to` இரண்டும் சுழியாக இல்லாதபோது, ``from`` இன் வில்லைகளில் `amount` அளவு
* `to` முகவரிக்கு பரிமாற்றம் செய்யப்படும்.
* - `from` சுழியாக இருக்கும்போது, `to` முகவரிக்காக `amount` வில்லைகள் உருவாக்கப்படும்.
* - `to` சுழியாக இருக்கும்போது, ``from`` இன் வில்லைகளில் `amount` அளவு அழிக்கப்படும்.
* - `from` மற்றும் `to` இரண்டும் ஒருபோதும் சுழியாக இருக்காது.
*
* கொக்கிகள் பற்றி மேலும் அறிய, xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks] ஐப் பார்க்கவும்.
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
பரிமாற்றங்களின் போது அழைக்கப்பட வேண்டிய ஹூக் செயல்பாடு இதுவாகும். இது இங்கே காலியாக உள்ளது, ஆனால் அது ஏதாவது செய்ய வேண்டும் என நீங்கள் விரும்பினால், அதை மேலெழுதவும்.
முடிவுரை
மதிப்பாய்வுக்காக, இந்த ஒப்பந்தத்தில் உள்ள சில மிக முக்கியமான யோசனைகள் இங்கே (என் கருத்துப்படி, உங்களுடையது மாறுபடலாம்):
- தொகுதிச்சங்கிலியில் எந்த ரகசியங்களும் இல்லை. ஒரு திறன் ஒப்பந்தம் அணுகக்கூடிய எந்தத் தகவலும் உலகம் முழுவதற்கும் கிடைக்கும்.
- உங்கள் சொந்தப் பரிவர்த்தனைகளின் வரிசையை நீங்கள் கட்டுப்படுத்தலாம், ஆனால் மற்றவர்களின் பரிவர்த்தனை எப்போது நடக்கும் என்பதை அல்ல. அனுமதித்தொகையை மாற்றுவது ஆபத்தானதாக இருப்பதற்கு இதுவே காரணம், ஏனெனில் இது இரண்டு அனுமதித்தொகைகளின் கூட்டுத்தொகையையும் செலவிடச் செலவிடுபவரை அனுமதிக்கிறது.
uint256வகையின் மதிப்புகள் சுற்றிக் கொள்ளும் (wrap around). வேறு வார்த்தைகளில் கூறுவதானால், 0-1=2^256-1. அது விரும்பிய நடத்தை இல்லை என்றால், நீங்கள் அதைச் சரிபார்க்க வேண்டும் (அல்லது உங்களுக்காக அதைச் செய்யும் SafeMath நிரலகத்தைப் பயன்படுத்தவும்). இது Solidity 0.8.0 (opens in a new tab) இல் மாறியுள்ளது என்பதை நினைவில் கொள்க.- ஒரு குறிப்பிட்ட வகையின் அனைத்து நிலை மாற்றங்களையும் ஒரு குறிப்பிட்ட இடத்தில் செய்யுங்கள், ஏனெனில் இது தணிக்கையை எளிதாக்குகிறது. உதாரணமாக,
approve,transferFrom,increaseAllowanceமற்றும்decreaseAllowanceஆகியவற்றால் அழைக்கப்படும்_approveநம்மிடம் இருப்பதற்கு இதுவே காரணம். - நிலை மாற்றங்கள் அணுவாக (atomic) இருக்க வேண்டும், அவற்றின் நடுவில் வேறு எந்தச் செயலும் இல்லாமல் (நீங்கள்
_transferஇல் பார்ப்பது போல). ஏனென்றால் நிலை மாற்றத்தின் போது உங்களுக்கு ஒரு முரண்பாடான நிலை உள்ளது. உதாரணமாக, அனுப்புநரின் இருப்பிலிருந்து நீங்கள் கழிக்கும் நேரத்திற்கும் பெறுநரின் இருப்பில் நீங்கள் சேர்க்கும் நேரத்திற்கும் இடையில் இருக்க வேண்டியதை விடக் குறைவான வில்லைகள் உள்ளன. அவற்றுக்கிடையே செயல்பாடுகள் இருந்தால், குறிப்பாக வேறு ஒப்பந்தத்திற்கான அழைப்புகள் இருந்தால், இது தவறாகப் பயன்படுத்தப்படலாம்.
ஓப்பன்செப்பெலின் ERC-20 ஒப்பந்தம் எவ்வாறு எழுதப்பட்டுள்ளது என்பதையும், குறிப்பாக அது எவ்வாறு மிகவும் பாதுகாப்பானதாக மாற்றப்பட்டுள்ளது என்பதையும் இப்போது நீங்கள் பார்த்துள்ளீர்கள், சென்று உங்கள் சொந்தப் பாதுகாப்பான ஒப்பந்தங்கள் மற்றும் பயன்பாடுகளை எழுதுங்கள்.
எனது மேலும் பல பணிகளுக்கு இங்கே பார்க்கவும் (opens in a new tab).
