முக்கிய உள்ளடக்கத்திற்குச் செல்லவும்

ERC-20 ஒப்பந்தத்தின் வழிகாட்டி

Solidity
erc-20
தொடக்கநிலையாளர்
ஓரி பொமரன்ட்ஸ்
9 மார்ச், 2021
23 நிமிட வாசிப்பு

அறிமுகம்

Ethereum-இன் மிகவும் பொதுவான பயன்பாடுகளில் ஒன்று, ஒரு குழு வர்த்தகம் செய்யக்கூடிய டோக்கனை உருவாக்குவதாகும், ஒரு வகையில் இது அவர்களின் சொந்த நாணயமாகும். இந்த டோக்கன்கள் பொதுவாக ERC-20 என்ற தரநிலையைப் பின்பற்றுகின்றன. இந்தத் தரநிலையானது, அனைத்து ERC-20 டோக்கன்களுடனும் வேலை செய்யும் லிக்விடிட்டி பூல்கள் (liquidity pools) மற்றும் வாலெட்டுகள் போன்ற கருவிகளை உருவாக்குவதை சாத்தியமாக்குகிறது. இந்தக் கட்டுரையில், OpenZeppelin Solidity ERC20 செயலாக்கம் (opens in a new tab) மற்றும் இடைமுக வரையறை (interface definition) (opens in a new tab) ஆகியவற்றை பகுப்பாய்வு செய்வோம்.

இது குறிப்புகளுடன் கூடிய மூலக் குறியீடு (annotated source code). நீங்கள் ERC-20-ஐ செயல்படுத்த விரும்பினால், இந்த வழிகாட்டியைப் படிக்கவும் (opens in a new tab).

இடைமுகம் (The Interface)

ERC-20 போன்ற ஒரு தரநிலையின் நோக்கம், வாலெட்டுகள் மற்றும் பரவலாக்கப்பட்ட பரிமாற்றங்கள் (decentralized exchanges) போன்ற பயன்பாடுகளில் இயங்கக்கூடிய பல டோக்கன் செயலாக்கங்களை அனுமதிப்பதாகும். அதை அடைய, நாங்கள் ஒரு இடைமுகத்தை (interface) (opens in a new tab) உருவாக்குகிறோம். டோக்கன் ஒப்பந்தத்தைப் பயன்படுத்த வேண்டிய எந்தவொரு குறியீடும் இடைமுகத்தில் உள்ள அதே வரையறைகளைப் பயன்படுத்தலாம் மற்றும் அதைப் பயன்படுத்தும் அனைத்து டோக்கன் ஒப்பந்தங்களுடனும் இணக்கமாக இருக்கும், அது MetaMask போன்ற வாலெட்டாக இருந்தாலும், etherscan.io போன்ற dapp ஆக இருந்தாலும் அல்லது லிக்விடிட்டி பூல் போன்ற வேறுபட்ட ஒப்பந்தமாக இருந்தாலும் சரி.

ERC-20 இடைமுகத்தின் விளக்கம்

நீங்கள் ஒரு அனுபவமிக்க புரோகிராமராக இருந்தால், Java (opens in a new tab) அல்லது C ஹெடர் கோப்புகளில் (C header files) (opens in a new tab) இதே போன்ற கட்டமைப்புகளைப் பார்த்தது உங்களுக்கு நினைவிருக்கலாம்.

இது OpenZeppelin-இலிருந்து ERC-20 இடைமுகத்தின் (opens in a new tab) வரையறையாகும். இது மனிதர்கள் படிக்கக்கூடிய தரநிலையை (opens in a new tab) Solidity குறியீடாக மொழிபெயர்த்ததாகும். நிச்சயமாக, இடைமுகமே எதையும் எப்படி செய்வது என்று வரையறுக்கவில்லை. அது கீழே உள்ள ஒப்பந்த மூலக் குறியீட்டில் விளக்கப்பட்டுள்ளது.

 

1// SPDX-License-Identifier: MIT

Solidity கோப்புகளில் உரிம அடையாளங்காட்டி (license identifier) இருக்க வேண்டும். உரிமங்களின் பட்டியலை இங்கே காணலாம் (opens in a new tab). உங்களுக்கு வேறு உரிமம் தேவைப்பட்டால், அதை கருத்துகளில் (comments) விளக்குங்கள்.

 

1pragma solidity >=0.6.0 <0.8.0;

Solidity மொழி இன்னும் வேகமாக வளர்ந்து வருகிறது, மேலும் புதிய பதிப்புகள் பழைய குறியீட்டுடன் இணக்கமாக இருக்காது (இங்கே பார்க்கவும் (opens in a new tab)). எனவே, மொழியின் குறைந்தபட்ச பதிப்பை மட்டும் குறிப்பிடாமல், நீங்கள் குறியீட்டைச் சோதித்த சமீபத்திய அதிகபட்ச பதிப்பையும் குறிப்பிடுவது நல்லது.

 

1/* *
2 * @dev EIP-இல் வரையறுக்கப்பட்டுள்ளபடி ERC20 தரநிலையின் இடைமுகம் (Interface). */

கருத்தில் உள்ள @dev என்பது NatSpec வடிவத்தின் (opens in a new tab) ஒரு பகுதியாகும், இது மூலக் குறியீட்டிலிருந்து ஆவணங்களை உருவாக்கப் பயன்படுகிறது.

 

1interface IERC20 {

வழக்கமாக, இடைமுகப் பெயர்கள் I உடன் தொடங்கும்.

 

1 /* *
2 * @dev இருப்பில் உள்ள டோக்கன்களின் அளவை வழங்குகிறது. */
3 function totalSupply() external view returns (uint256);

இந்தச் செயல்பாடு external ஆகும், அதாவது இதை ஒப்பந்தத்திற்கு வெளியிலிருந்து மட்டுமே அழைக்க முடியும் (opens in a new tab). இது ஒப்பந்தத்தில் உள்ள டோக்கன்களின் மொத்த விநியோகத்தை (total supply) வழங்குகிறது. இந்த மதிப்பு Ethereum-இல் மிகவும் பொதுவான வகையான unsigned 256 பிட்களைப் பயன்படுத்தி வழங்கப்படுகிறது (256 பிட்கள் என்பது EVM-இன் இயல்பான சொல் அளவு). இந்தச் செயல்பாடு ஒரு view ஆகவும் உள்ளது, அதாவது இது நிலையை (state) மாற்றாது, எனவே பிளாக்செயினில் உள்ள ஒவ்வொரு நோடும் இதை இயக்குவதற்குப் பதிலாக ஒரு ஒற்றை நோடில் இதை இயக்க முடியும். இந்த வகையான செயல்பாடு ஒரு பரிவர்த்தனையை உருவாக்காது மற்றும் கேஸ் (gas) செலவாகாது.

குறிப்பு: கோட்பாட்டளவில், ஒரு ஒப்பந்தத்தை உருவாக்கியவர் உண்மையான மதிப்பை விட சிறிய மொத்த விநியோகத்தை வழங்குவதன் மூலம் ஏமாற்றலாம் என்று தோன்றலாம், இதனால் ஒவ்வொரு டோக்கனும் உண்மையில் இருப்பதை விட அதிக மதிப்புடையதாகத் தோன்றும். இருப்பினும், அந்த பயம் பிளாக்செயினின் உண்மையான தன்மையைப் புறக்கணிக்கிறது. பிளாக்செயினில் நடக்கும் அனைத்தையும் ஒவ்வொரு நோடும் சரிபார்க்க முடியும். இதை அடைய, ஒவ்வொரு ஒப்பந்தத்தின் இயந்திர மொழிக் குறியீடு மற்றும் சேமிப்பகம் ஒவ்வொரு நோடிலும் கிடைக்கிறது. உங்கள் ஒப்பந்தத்திற்கான Solidity குறியீட்டை நீங்கள் வெளியிட வேண்டிய அவசியமில்லை என்றாலும், நீங்கள் மூலக் குறியீட்டையும் அது தொகுக்கப்பட்ட Solidity பதிப்பையும் வெளியிட்டால் ஒழிய யாரும் உங்களை தீவிரமாக எடுத்துக் கொள்ள மாட்டார்கள், எனவே நீங்கள் வழங்கிய இயந்திர மொழிக் குறியீட்டிற்கு எதிராக அதைச் சரிபார்க்க முடியும். உதாரணமாக, இந்த ஒப்பந்தத்தைப் பார்க்கவும் (opens in a new tab).

 

1 /* *
2 * @dev `account` கணக்கிற்குச் சொந்தமான டோக்கன்களின் அளவை வழங்குகிறது. */
3 function balanceOf(address account) external view returns (uint256);

பெயர் குறிப்பிடுவது போல, balanceOf ஒரு கணக்கின் இருப்பை (balance) வழங்குகிறது. Ethereum கணக்குகள் Solidity-இல் address வகையைப் பயன்படுத்தி அடையாளம் காணப்படுகின்றன, இது 160 பிட்களைக் கொண்டுள்ளது. இது external மற்றும் view ஆகவும் உள்ளது.

 

1 /* *
2 * @dev அழைப்பாளரின் கணக்கிலிருந்து `recipient` (பெறுநருக்கு) `amount` டோக்கன்களை மாற்றுகிறது.
3 *
4 * செயல்பாடு வெற்றிகரமாக நடந்ததா என்பதைக் குறிக்கும் பூலியன் மதிப்பை வழங்குகிறது.
5 *
6 * {Transfer} நிகழ்வை வெளியிடுகிறது. */
7 function transfer(address recipient, uint256 amount) external returns (bool);

transfer செயல்பாடு அழைப்பாளரிடமிருந்து (caller) வேறு முகவரிக்கு டோக்கன்களை மாற்றுகிறது. இது நிலையில் மாற்றத்தை உள்ளடக்கியது, எனவே இது view அல்ல. ஒரு பயனர் இந்தச் செயல்பாட்டை அழைக்கும் போது அது ஒரு பரிவர்த்தனையை உருவாக்குகிறது மற்றும் கேஸ் செலவாகிறது. இது பிளாக்செயினில் உள்ள அனைவருக்கும் நிகழ்வைத் தெரிவிக்க Transfer என்ற நிகழ்வையும் (event) வெளியிடுகிறது.

இந்தச் செயல்பாடு இரண்டு வெவ்வேறு வகையான அழைப்பாளர்களுக்கு இரண்டு வகையான வெளியீட்டைக் கொண்டுள்ளது:

  • பயனர் இடைமுகத்திலிருந்து நேரடியாகச் செயல்பாட்டை அழைக்கும் பயனர்கள். பொதுவாக பயனர் ஒரு பரிவர்த்தனையைச் சமர்ப்பிக்கிறார் மற்றும் பதிலுக்காகக் காத்திருக்க மாட்டார், இதற்கு காலவரையற்ற நேரம் ஆகலாம். பரிவர்த்தனை ரசீதைத் தேடுவதன் மூலம் (இது பரிவர்த்தனை ஹாஷ் மூலம் அடையாளம் காணப்படுகிறது) அல்லது Transfer நிகழ்வைத் தேடுவதன் மூலம் பயனர் என்ன நடந்தது என்பதைப் பார்க்கலாம்.
  • ஒட்டுமொத்த பரிவர்த்தனையின் ஒரு பகுதியாகச் செயல்பாட்டை அழைக்கும் பிற ஒப்பந்தங்கள். அந்த ஒப்பந்தங்கள் உடனடியாக முடிவைப் பெறுகின்றன, ஏனெனில் அவை ஒரே பரிவர்த்தனையில் இயங்குகின்றன, எனவே அவை செயல்பாட்டின் திரும்பப் பெறும் மதிப்பைப் (return value) பயன்படுத்தலாம்.

ஒப்பந்தத்தின் நிலையை மாற்றும் பிற செயல்பாடுகளாலும் இதே வகையான வெளியீடு உருவாக்கப்படுகிறது.

 

அலவன்ஸ்கள் (Allowances) ஒரு கணக்கை வேறு உரிமையாளருக்குச் சொந்தமான சில டோக்கன்களைச் செலவிட அனுமதிக்கின்றன. உதாரணமாக, விற்பனையாளர்களாகச் செயல்படும் ஒப்பந்தங்களுக்கு இது பயனுள்ளதாக இருக்கும். ஒப்பந்தங்களால் நிகழ்வுகளைக் கண்காணிக்க முடியாது, எனவே வாங்குபவர் டோக்கன்களை நேரடியாக விற்பனையாளர் ஒப்பந்தத்திற்கு மாற்றினால், அந்த ஒப்பந்தத்திற்கு பணம் செலுத்தப்பட்டது தெரியாது. அதற்குப் பதிலாக, வாங்குபவர் விற்பனையாளர் ஒப்பந்தத்தை ஒரு குறிப்பிட்ட தொகையைச் செலவிட அனுமதிக்கிறார், மேலும் விற்பனையாளர் அந்தத் தொகையை மாற்றுகிறார். விற்பனையாளர் ஒப்பந்தம் அழைக்கும் ஒரு செயல்பாட்டின் மூலம் இது செய்யப்படுகிறது, எனவே விற்பனையாளர் ஒப்பந்தம் அது வெற்றிகரமாக இருந்ததா என்பதை அறிய முடியும்.

1 /* *
2 * @dev {transferFrom} மூலம் `owner` சார்பாக `spender` செலவிட அனுமதிக்கப்படும் மீதமுள்ள டோக்கன்களின் எண்ணிக்கையை வழங்குகிறது. இது இயல்பாகவே பூஜ்ஜியமாக இருக்கும்.
3 *
4 * {approve} அல்லது {transferFrom} அழைக்கப்படும்போது இந்த மதிப்பு மாறும். */
5 function allowance(address owner, address spender) external view returns (uint256);

allowance செயல்பாடு, ஒரு முகவரி (owner) மற்றொரு முகவரியை (spender) செலவிட அனுமதிக்கும் அலவன்ஸ் என்ன என்பதை எவரும் வினவ அனுமதிக்கிறது.

 

1 /* *
2 * @dev அழைப்பாளரின் டோக்கன்களில் `spender` க்கான அனுமதியாக `amount` ஐ அமைக்கிறது.
3 *
4 * செயல்பாடு வெற்றிகரமாக நடந்ததா என்பதைக் குறிக்கும் பூலியன் மதிப்பை வழங்குகிறது.
5 *
6 * முக்கியமானது: இந்த முறையின் மூலம் அனுமதியை மாற்றுவது, துரதிர்ஷ்டவசமான பரிவர்த்தனை வரிசையால் பழைய மற்றும் புதிய அனுமதி இரண்டையும் யாராவது பயன்படுத்தக்கூடும் என்ற அபாயத்தைக் கொண்டுவருகிறது. இந்த ரேஸ் கண்டிஷனை (race condition) தணிக்க ஒரு சாத்தியமான தீர்வு, முதலில் செலவிடுபவரின் அனுமதியை 0 ஆகக் குறைத்து, பின்னர் விரும்பிய மதிப்பை அமைப்பதாகும்:
7 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
8 *
9 * {Approval} நிகழ்வை வெளியிடுகிறது. */
10 function approve(address spender, uint256 amount) external returns (bool);

approve செயல்பாடு ஒரு அலவன்ஸை உருவாக்குகிறது. அது எவ்வாறு தவறாகப் பயன்படுத்தப்படலாம் என்பது பற்றிய செய்தியைப் படிக்க மறக்காதீர்கள். Ethereum-இல் உங்கள் சொந்த பரிவர்த்தனைகளின் வரிசையை நீங்கள் கட்டுப்படுத்துகிறீர்கள், ஆனால் மற்றவர்களின் பரிவர்த்தனைகள் எந்த வரிசையில் செயல்படுத்தப்படும் என்பதை உங்களால் கட்டுப்படுத்த முடியாது, மற்ற தரப்பினரின் பரிவர்த்தனை நடந்ததை நீங்கள் பார்க்கும் வரை உங்கள் சொந்த பரிவர்த்தனையை நீங்கள் சமர்ப்பிக்காவிட்டால் ஒழிய.

 

1 /* *
2 * @dev அனுமதி பொறிமுறையைப் பயன்படுத்தி `sender` இடமிருந்து `recipient` க்கு `amount` டோக்கன்களை மாற்றுகிறது. பின்னர் அழைப்பாளரின் அனுமதியிலிருந்து `amount` கழிக்கப்படும்.
3 *
4 * செயல்பாடு வெற்றிகரமாக நடந்ததா என்பதைக் குறிக்கும் பூலியன் மதிப்பை வழங்குகிறது.
5 *
6 * {Transfer} நிகழ்வை வெளியிடுகிறது. */
7 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

இறுதியாக, அலவன்ஸை உண்மையில் செலவிட ஸ்பெண்டரால் (spender) transferFrom பயன்படுத்தப்படுகிறது.

 

1
2 /* *
3 * @dev ஒரு கணக்கிலிருந்து (`from`) மற்றொரு கணக்கிற்கு (`to`) `value` டோக்கன்கள் மாற்றப்படும்போது வெளியிடப்படுகிறது.
4 *
5 * `value` பூஜ்ஜியமாக இருக்கலாம் என்பதை நினைவில் கொள்க. */
6 event Transfer(address indexed from, address indexed to, uint256 value);
7
8 /* *
9 * @dev {approve} அழைப்பின் மூலம் `owner` க்கான `spender` இன் அனுமதி அமைக்கப்படும்போது வெளியிடப்படுகிறது. `value` என்பது புதிய அனுமதி ஆகும். */
10 event Approval(address indexed owner, address indexed spender, uint256 value);
11}

ERC-20 ஒப்பந்தத்தின் நிலை மாறும் போது இந்த நிகழ்வுகள் வெளியிடப்படுகின்றன.

உண்மையான ஒப்பந்தம்

இது ERC-20 தரநிலையைச் செயல்படுத்தும் உண்மையான ஒப்பந்தமாகும், இங்கிருந்து எடுக்கப்பட்டது (opens in a new tab). இது அப்படியே பயன்படுத்தப்படுவதற்காக அல்ல, ஆனால் நீங்கள் அதிலிருந்து மரபுரிமையாகப் பெற்று (inherit) (opens in a new tab) அதைப் பயன்படுத்தக்கூடிய ஒன்றாக நீட்டிக்கலாம்.

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

 

இறக்குமதி அறிக்கைகள் (Import Statements)

மேலே உள்ள இடைமுக வரையறைகளுக்கு மேலதிகமாக, ஒப்பந்த வரையறை வேறு இரண்டு கோப்புகளை இறக்குமதி செய்கிறது:

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-க்கான எண்கணித ஓவர்ஃப்ளோக்கள்/அண்டர்ஃப்ளோக்களைத் (overflows/underflows) தடுக்கிறது. Solidity ≥0.8.0-இல், எண்கணித செயல்பாடுகள் தானாகவே ஓவர்ஃப்ளோ/அண்டர்ஃப்ளோவில் திரும்பப் பெறப்படும், இதனால் SafeMath தேவையற்றதாகிறது. பழைய கம்பைலர் பதிப்புகளுடன் பின்தங்கிய இணக்கத்தன்மைக்காக (backward compatibility) இந்த ஒப்பந்தம் SafeMath-ஐப் பயன்படுத்துகிறது.

 

இந்தக் கருத்து ஒப்பந்தத்தின் நோக்கத்தை விளக்குகிறது.

1/* *
2 * @dev {IERC20} இடைமுகத்தின் செயலாக்கம்.
3 *
4 * இந்த செயலாக்கம் டோக்கன்கள் உருவாக்கப்படும் முறையைப் பொருட்படுத்தாது. அதாவது {_mint} ஐப் பயன்படுத்தி பெறப்பட்ட ஒப்பந்தத்தில் விநியோக பொறிமுறை சேர்க்கப்பட வேண்டும்.
5 * பொதுவான பொறிமுறைக்கு {ERC20PresetMinterPauser} ஐப் பார்க்கவும்.
6 *
7 * குறிப்பு: விரிவான விளக்கத்திற்கு எங்கள் வழிகாட்டியைப் பார்க்கவும்
8 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[விநியோக பொறிமுறைகளை எவ்வாறு
9 * செயல்படுத்துவது].
10 *
11 * நாங்கள் பொதுவான OpenZeppelin வழிகாட்டுதல்களைப் பின்பற்றியுள்ளோம்: தோல்வியடையும் போது `false` ஐ வழங்குவதற்குப் பதிலாக செயல்பாடுகள் நிராகரிக்கப்படும் (revert). இந்த நடத்தை வழக்கமானது மற்றும் ERC20 பயன்பாடுகளின் எதிர்பார்ப்புகளுடன் முரண்படாது.
12 *
13 * கூடுதலாக, {transferFrom} க்கான அழைப்புகளில் {Approval} நிகழ்வு வெளியிடப்படுகிறது.
14 * இது பயன்பாடுகள் குறிப்பிட்ட நிகழ்வுகளைக் கேட்பதன் மூலம் அனைத்து கணக்குகளுக்குமான அனுமதியை மறுகட்டமைக்க அனுமதிக்கிறது. EIP இன் பிற செயலாக்கங்கள் இந்த நிகழ்வுகளை வெளியிடாமல் இருக்கலாம், ஏனெனில் இது விவரக்குறிப்பால் கோரப்படவில்லை.
15 *
16 * இறுதியாக, அனுமதிகளை அமைப்பதில் உள்ள நன்கு அறியப்பட்ட சிக்கல்களைத் தணிக்க, தரமற்ற {decreaseAllowance} மற்றும் {increaseAllowance}
17 * செயல்பாடுகள் சேர்க்கப்பட்டுள்ளன. {IERC20-approve} ஐப் பார்க்கவும். */
18

ஒப்பந்த வரையறை

1contract ERC20 is Context, IERC20 {

இந்த வரி மரபுரிமையைக் (inheritance) குறிப்பிடுகிறது, இந்த விஷயத்தில் மேலேயுள்ள IERC20 மற்றும் OpenGSN-க்கான Context-இலிருந்து.

 

1
2 using SafeMath for uint256;
3

இந்த வரி SafeMath லைப்ரரியை uint256 வகையுடன் இணைக்கிறது. இந்த லைப்ரரியை நீங்கள் இங்கே (opens in a new tab) காணலாம்.

மாறி வரையறைகள் (Variable Definitions)

இந்த வரையறைகள் ஒப்பந்தத்தின் நிலை மாறிகளைக் (state variables) குறிப்பிடுகின்றன. இந்த மாறிகள் private என அறிவிக்கப்படுகின்றன, ஆனால் பிளாக்செயினில் உள்ள பிற ஒப்பந்தங்களால் அவற்றைப் படிக்க முடியாது என்பதை மட்டுமே இது குறிக்கிறது. பிளாக்செயினில் எந்த ரகசியங்களும் இல்லை, ஒவ்வொரு நோடிலும் உள்ள மென்பொருள் ஒவ்வொரு பிளாக்கிலும் ஒவ்வொரு ஒப்பந்தத்தின் நிலையைக் கொண்டுள்ளது. வழக்கமாக, நிலை மாறிகள் _<something> எனப் பெயரிடப்படுகின்றன.

முதல் இரண்டு மாறிகள் மேப்பிங்குகள் (mappings) (opens in a new tab) ஆகும், அதாவது விசைகள் (keys) எண் மதிப்புகளாக இருப்பதைத் தவிர, அவை தோராயமாக அசோசியேட்டிவ் அரேக்கள் (associative arrays) (opens in a new tab) போலவே செயல்படுகின்றன. இயல்புநிலையிலிருந்து (பூஜ்ஜியம்) வேறுபட்ட மதிப்புகளைக் கொண்ட உள்ளீடுகளுக்கு மட்டுமே சேமிப்பகம் ஒதுக்கப்படுகிறது.

1 mapping (address => uint256) private _balances;

முதல் மேப்பிங், _balances, முகவரிகள் மற்றும் இந்த டோக்கனின் அந்தந்த இருப்புகள் ஆகும். இருப்பை அணுக, இந்த தொடரியலைப் பயன்படுத்தவும்: _balances[<address>].

 

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

இந்த மாறி, _allowances, முன்பு விளக்கப்பட்ட அலவன்ஸ்களைச் சேமிக்கிறது. முதல் குறியீட்டு (index) டோக்கன்களின் உரிமையாளர், இரண்டாவது அலவன்ஸ் கொண்ட ஒப்பந்தம். முகவரி B-இன் கணக்கிலிருந்து முகவரி A செலவிடக்கூடிய தொகையை அணுக, _allowances[B][A]-ஐப் பயன்படுத்தவும்.

 

1 uint256 private _totalSupply;

பெயர் குறிப்பிடுவது போல, இந்த மாறி டோக்கன்களின் மொத்த விநியோகத்தைக் கண்காணிக்கிறது.

 

1 string private _name;
2 string private _symbol;
3 uint8 private _decimals;

இந்த மூன்று மாறிகள் வாசிப்புத்திறனை மேம்படுத்தப் பயன்படுத்தப்படுகின்றன. முதல் இரண்டு சுய விளக்கமளிக்கும், ஆனால் _decimals அப்படி இல்லை.

ஒருபுறம், Ethereum-இல் மிதக்கும் புள்ளி (floating point) அல்லது பின்ன மாறிகள் (fractional variables) இல்லை. மறுபுறம், மனிதர்கள் டோக்கன்களைப் பிரிக்க விரும்புவார்கள். நாணயத்திற்காக மக்கள் தங்கத்தைத் தேர்ந்தெடுத்ததற்கு ஒரு காரணம், யாராவது ஒரு வாத்தின் மதிப்புள்ள பசுவை வாங்க விரும்பியபோது சில்லறை கொடுப்பது கடினமாக இருந்தது.

இதற்கான தீர்வு முழு எண்களைக் கண்காணிப்பதாகும், ஆனால் உண்மையான டோக்கனுக்குப் பதிலாக கிட்டத்தட்ட மதிப்பற்ற ஒரு பின்ன டோக்கனைக் கணக்கிடுவதாகும். ஈதரைப் பொறுத்தவரை, பின்ன டோக்கன் wei என்று அழைக்கப்படுகிறது, மேலும் 10^18 wei என்பது ஒரு ETH-க்கு சமம். எழுதும் நேரத்தில், 10,000,000,000,000 wei என்பது தோராயமாக ஒரு US அல்லது Euro சென்ட் ஆகும்.

டோக்கன் இருப்பை எப்படிக் காண்பிப்பது என்பதைப் பயன்பாடுகள் தெரிந்து கொள்ள வேண்டும். ஒரு பயனரிடம் 3,141,000,000,000,000,000 wei இருந்தால், அது 3.14 ETH ஆ? 31.41 ETH ஆ? 3,141 ETH ஆ? ஈதரைப் பொறுத்தவரை இது ETH-க்கு 10^18 wei என வரையறுக்கப்பட்டுள்ளது, ஆனால் உங்கள் டோக்கனுக்கு நீங்கள் வேறு மதிப்பைத் தேர்ந்தெடுக்கலாம். டோக்கனைப் பிரிப்பதில் அர்த்தமில்லை என்றால், நீங்கள் ஒரு _decimals மதிப்பை பூஜ்ஜியமாகப் பயன்படுத்தலாம். நீங்கள் ETH-இன் அதே தரநிலையைப் பயன்படுத்த விரும்பினால், 18 என்ற மதிப்பைப் பயன்படுத்தவும்.

கன்ஸ்ட்ரக்டர் (The Constructor)

1 /* *
2 * @dev {name} மற்றும் {symbol} க்கான மதிப்புகளை அமைக்கிறது, {decimals} ஐ 18 என்ற இயல்புநிலை மதிப்புடன் துவக்குகிறது.
3 *
4 * {decimals} க்கு வேறு மதிப்பைத் தேர்ந்தெடுக்க, {_setupDecimals} ஐப் பயன்படுத்தவும்.
5 *
6 * இந்த மூன்று மதிப்புகளும் மாற்ற முடியாதவை: கட்டமைப்பின் போது (construction) ஒரு முறை மட்டுமே இவற்றை அமைக்க முடியும். */
7 constructor (string memory name_, string memory symbol_) public {
8 // Solidity ≥0.7.0 இல், 'public' என்பது மறைமுகமானது மற்றும் தவிர்க்கப்படலாம்.
9
10 _name = name_;
11 _symbol = symbol_;
12 _decimals = 18;
13 }

ஒப்பந்தம் முதன்முதலில் உருவாக்கப்படும் போது கன்ஸ்ட்ரக்டர் அழைக்கப்படுகிறது. வழக்கமாக, செயல்பாட்டு அளவுருக்கள் (function parameters) <something>_ எனப் பெயரிடப்படுகின்றன.

பயனர் இடைமுகச் செயல்பாடுகள்

1 /* *
2 * @dev டோக்கனின் பெயரை வழங்குகிறது. */
3 function name() public view returns (string memory) {
4 return _name;
5 }
6
7 /* *
8 * @dev டோக்கனின் குறியீட்டை வழங்குகிறது, இது பொதுவாக பெயரின் சுருக்கமான வடிவமாகும். */
9 function symbol() public view returns (string memory) {
10 return _symbol;
11 }
12
13 /* *
14 * @dev பயனர் பிரதிநிதித்துவத்தைப் பெறப் பயன்படுத்தப்படும் தசமங்களின் எண்ணிக்கையை வழங்குகிறது.
15 * எடுத்துக்காட்டாக, `decimals` `2` க்கு சமமாக இருந்தால், `505` டோக்கன்களின் இருப்பு
16 * பயனருக்கு `5,05` (`505 / 10 ** 2`) ஆகக் காட்டப்பட வேண்டும்.
17 *
18 * ஈதர் மற்றும் வெய் (wei) இடையேயான உறவைப் பின்பற்றி, டோக்கன்கள் பொதுவாக 18 என்ற மதிப்பைத் தேர்ந்தெடுக்கின்றன. {_setupDecimals} அழைக்கப்படாவிட்டால், {ERC20} பயன்படுத்தும் மதிப்பு இதுதான்.
19 *
20 * குறிப்பு: இந்தத் தகவல் _காட்சி_ (display) நோக்கங்களுக்காக மட்டுமே பயன்படுத்தப்படுகிறது: இது
21 * {IERC20-balanceOf} மற்றும் {IERC20-transfer} உட்பட ஒப்பந்தத்தின் எந்தவொரு எண்கணிதத்தையும் எந்த வகையிலும் பாதிக்காது. */
22 function decimals() public view returns (uint8) {
23 return _decimals;
24 }

இந்தச் செயல்பாடுகள், name, symbol மற்றும் decimals ஆகியவை பயனர் இடைமுகங்கள் உங்கள் ஒப்பந்தத்தைப் பற்றித் தெரிந்துகொள்ள உதவுகின்றன, இதனால் அவை அதைச் சரியாகக் காண்பிக்க முடியும்.

திரும்பப் பெறும் வகை string memory ஆகும், அதாவது நினைவகத்தில் சேமிக்கப்பட்டுள்ள ஒரு சரத்தை (string) வழங்குகிறது. மாறிகள், அதாவது சரங்கள் போன்றவற்றை மூன்று இடங்களில் சேமிக்கலாம்:

வாழ்நாள் (Lifetime)ஒப்பந்த அணுகல் (Contract Access)கேஸ் செலவு (Gas Cost)
Memoryசெயல்பாட்டு அழைப்பு (Function call)படிக்க/எழுத (Read/Write)பத்துகள் அல்லது நூறுகள் (அதிக இடங்களுக்கு அதிகம்)
Calldataசெயல்பாட்டு அழைப்பு (Function call)படிக்க மட்டும் (Read Only)திரும்பப் பெறும் வகையாகப் பயன்படுத்த முடியாது, செயல்பாட்டு அளவுரு வகையாக மட்டுமே
Storageமாற்றப்படும் வரை (Until changed)படிக்க/எழுத (Read/Write)அதிகம் (படிக்க 800, எழுத 20k)

இந்த விஷயத்தில், memory சிறந்த தேர்வாகும்.

டோக்கன் தகவலைப் படிக்கவும்

இவை டோக்கனைப் பற்றிய தகவல்களை வழங்கும் செயல்பாடுகள், மொத்த விநியோகம் அல்லது ஒரு கணக்கின் இருப்பு.

1 /* *
2 * @dev {IERC20-totalSupply} ஐப் பார்க்கவும். */
3 function totalSupply() public view override returns (uint256) {
4 return _totalSupply;
5 }

totalSupply செயல்பாடு டோக்கன்களின் மொத்த விநியோகத்தை வழங்குகிறது.

 

1 /* *
2 * @dev {IERC20-balanceOf} ஐப் பார்க்கவும். */
3 function balanceOf(address account) public view override returns (uint256) {
4 return _balances[account];
5 }

ஒரு கணக்கின் இருப்பைப் படிக்கவும். வேறு யாருடைய கணக்கு இருப்பையும் பெற எவரும் அனுமதிக்கப்படுகிறார்கள் என்பதை நினைவில் கொள்ளவும். இந்தத் தகவலை மறைக்க முயற்சிப்பதில் எந்த அர்த்தமும் இல்லை, ஏனெனில் இது எப்படியும் ஒவ்வொரு நோடிலும் கிடைக்கிறது. பிளாக்செயினில் எந்த ரகசியங்களும் இல்லை.

டோக்கன்களை மாற்றவும்

1 /* *
2 * @dev {IERC20-transfer} ஐப் பார்க்கவும்.
3 *
4 * தேவைகள்:
5 *
6 * - `recipient` பூஜ்ஜிய முகவரியாக இருக்க முடியாது.
7 * - அழைப்பாளரிடம் குறைந்தபட்சம் `amount` இருப்பு இருக்க வேண்டும். */
8 function transfer(address recipient, uint256 amount) public virtual override returns (bool) {

அனுப்புநரின் கணக்கிலிருந்து வேறு கணக்கிற்கு டோக்கன்களை மாற்ற transfer செயல்பாடு அழைக்கப்படுகிறது. இது ஒரு பூலியன் (boolean) மதிப்பை வழங்கினாலும், அந்த மதிப்பு எப்போதும் true ஆக இருக்கும் என்பதை நினைவில் கொள்ளவும். பரிமாற்றம் தோல்வியுற்றால், ஒப்பந்தம் அழைப்பைத் திரும்பப் பெறும் (reverts).

 

1 _transfer(_msgSender(), recipient, amount);
2 return true;
3 }

_transfer செயல்பாடு உண்மையான வேலையைச் செய்கிறது. இது ஒரு தனிப்பட்ட (private) செயல்பாடாகும், இது பிற ஒப்பந்தச் செயல்பாடுகளால் மட்டுமே அழைக்கப்பட முடியும். வழக்கமாக தனிப்பட்ட செயல்பாடுகள் நிலை மாறிகளைப் போலவே _<something> எனப் பெயரிடப்படுகின்றன.

பொதுவாக Solidity-இல் செய்தி அனுப்புநருக்கு msg.sender-ஐப் பயன்படுத்துகிறோம். இருப்பினும், அது OpenGSN (opens in a new tab)-ஐ உடைக்கிறது. எங்கள் டோக்கனுடன் ஈதர் இல்லாத பரிவர்த்தனைகளை அனுமதிக்க விரும்பினால், நாங்கள் _msgSender()-ஐப் பயன்படுத்த வேண்டும். இது சாதாரண பரிவர்த்தனைகளுக்கு msg.sender-ஐ வழங்குகிறது, ஆனால் ஈதர் இல்லாத பரிவர்த்தனைகளுக்கு செய்தியை ரிலே செய்த ஒப்பந்தத்தை அல்லாமல் அசல் கையொப்பமிட்டவரை (original signer) வழங்குகிறது.

அலவன்ஸ் செயல்பாடுகள்

இவை அலவன்ஸ் செயல்பாட்டைச் செயல்படுத்தும் செயல்பாடுகள்: allowance, approve, transferFrom மற்றும் _approve. கூடுதலாக, OpenZeppelin செயலாக்கம் பாதுகாப்பை மேம்படுத்தும் சில அம்சங்களைச் சேர்க்க அடிப்படைத் தரநிலைக்கு அப்பால் செல்கிறது: increaseAllowance மற்றும் decreaseAllowance.

allowance செயல்பாடு

1 /* *
2 * @dev {IERC20-allowance} ஐப் பார்க்கவும். */
3 function allowance(address owner, address spender) public view virtual override returns (uint256) {
4 return _allowances[owner][spender];
5 }

allowance செயல்பாடு எந்தவொரு அலவன்ஸையும் சரிபார்க்க அனைவரையும் அனுமதிக்கிறது.

approve செயல்பாடு

1 /* *
2 * @dev {IERC20-approve} ஐப் பார்க்கவும்.
3 *
4 * தேவைகள்:
5 *
6 * - `spender` பூஜ்ஜிய முகவரியாக இருக்க முடியாது. */
7 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} நிகழ்வை வெளியிடுகிறது. இது EIP ஆல் கோரப்படவில்லை. {ERC20} இன் தொடக்கத்தில் உள்ள குறிப்பைப் பார்க்கவும்.
5 *
6 * தேவைகள்:
7 *
8 * - `sender` மற்றும் `recipient` பூஜ்ஜிய முகவரியாக இருக்க முடியாது.
9 * - `sender` குறைந்தபட்சம் `amount` இருப்பைக் கொண்டிருக்க வேண்டும்.
10 * - அழைப்பாளருக்கு ``sender`` இன் டோக்கன்களில் குறைந்தபட்சம் `amount` க்கான அனுமதி இருக்க வேண்டும். */
11 function transferFrom(address sender, address recipient, uint256 amount) public virtual
12 override returns (bool) {
13 _transfer(sender, recipient, amount);

 

a.sub(b, "message") செயல்பாட்டு அழைப்பு இரண்டு விஷயங்களைச் செய்கிறது. முதலில், இது a-b-ஐக் கணக்கிடுகிறது, இது புதிய அலவன்ஸ் ஆகும். இரண்டாவதாக, இந்த முடிவு எதிர்மறையாக (negative) இல்லை என்பதைச் சரிபார்க்கிறது. அது எதிர்மறையாக இருந்தால், வழங்கப்பட்ட செய்தியுடன் அழைப்பு திரும்பப் பெறப்படும். ஒரு அழைப்பு திரும்பப் பெறப்படும் போது, அந்த அழைப்பின் போது முன்பு செய்யப்பட்ட எந்தவொரு செயலாக்கமும் புறக்கணிக்கப்படும் என்பதை நினைவில் கொள்ளவும், எனவே நாம் _transfer-ஐ செயல்தவிர்க்க (undo) வேண்டியதில்லை.

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

OpenZeppelin பாதுகாப்புச் சேர்த்தல்கள்

பூஜ்ஜியமற்ற அலவன்ஸை மற்றொரு பூஜ்ஜியமற்ற மதிப்பிற்கு அமைப்பது ஆபத்தானது, ஏனெனில் நீங்கள் உங்கள் சொந்த பரிவர்த்தனைகளின் வரிசையை மட்டுமே கட்டுப்படுத்துகிறீர்கள், வேறு யாருடையதையும் அல்ல. உங்களிடம் இரண்டு பயனர்கள் இருப்பதாக கற்பனை செய்து பாருங்கள், அப்பாவியான ஆலிஸ் மற்றும் நேர்மையற்ற பில். ஆலிஸ் பில்லிடமிருந்து சில சேவைகளை விரும்புகிறார், அதற்கு ஐந்து டோக்கன்கள் செலவாகும் என்று அவர் நினைக்கிறார் - எனவே அவர் பில்லுக்கு ஐந்து டோக்கன்கள் அலவன்ஸ் கொடுக்கிறார்.

பின்னர் ஏதோ மாறுகிறது மற்றும் பில்லின் விலை பத்து டோக்கன்களாக உயர்கிறது. இன்னும் சேவையை விரும்பும் ஆலிஸ், பில்லின் அலவன்ஸை பத்தாக அமைக்கும் பரிவர்த்தனையை அனுப்புகிறார். பரிவர்த்தனை பூலில் (transaction pool) இந்தப் புதிய பரிவர்த்தனையை பில் பார்த்தவுடன், அவர் ஆலிஸின் ஐந்து டோக்கன்களைச் செலவழிக்கும் ஒரு பரிவர்த்தனையை அனுப்புகிறார், மேலும் அதிக கேஸ் விலையைக் கொண்டுள்ளார், எனவே அது வேகமாக மைன் செய்யப்படும். அந்த வகையில் பில் முதலில் ஐந்து டோக்கன்களைச் செலவழிக்கலாம், பின்னர், ஆலிஸின் புதிய அலவன்ஸ் மைன் செய்யப்பட்டவுடன், ஆலிஸ் அங்கீகரிக்க நினைத்ததை விட அதிகமாக, மொத்தம் பதினைந்து டோக்கன்களுக்கு மேலும் பத்து டோக்கன்களைச் செலவழிக்கலாம். இந்த நுட்பம் ஃபிரண்ட்-ரன்னிங் (front-running) (opens in a new tab) என்று அழைக்கப்படுகிறது.

ஆலிஸ் பரிவர்த்தனை (Alice Transaction)ஆலிஸ் நான்ஸ் (Alice Nonce)பில் பரிவர்த்தனை (Bill Transaction)பில் நான்ஸ் (Bill Nonce)பில்லின் அலவன்ஸ் (Bill's Allowance)ஆலிஸிடமிருந்து பில்லின் மொத்த வருமானம் (Bill Total Income from Alice)
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
approve(Bill, 10)11105
transferFrom(Alice, Bill, 10)10,124015

இந்தச் சிக்கலைத் தவிர்க்க, இந்த இரண்டு செயல்பாடுகளும் (increaseAllowance மற்றும் decreaseAllowance) ஒரு குறிப்பிட்ட தொகையால் அலவன்ஸை மாற்ற உங்களை அனுமதிக்கின்றன. எனவே பில் ஏற்கனவே ஐந்து டோக்கன்களைச் செலவழித்திருந்தால், அவரால் மேலும் ஐந்து டோக்கன்களை மட்டுமே செலவழிக்க முடியும். நேரத்தைப் பொறுத்து, இது செயல்பட இரண்டு வழிகள் உள்ளன, இவை இரண்டும் பில்லுக்கு பத்து டோக்கன்கள் மட்டுமே கிடைப்பதில் முடிவடைகின்றன:

A:

ஆலிஸ் பரிவர்த்தனை (Alice Transaction)ஆலிஸ் நான்ஸ் (Alice Nonce)பில் பரிவர்த்தனை (Bill Transaction)பில் நான்ஸ் (Bill Nonce)பில்லின் அலவன்ஸ் (Bill's Allowance)ஆலிஸிடமிருந்து பில்லின் மொத்த வருமானம் (Bill Total Income from Alice)
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
increaseAllowance(Bill, 5)110+5 = 55
transferFrom(Alice, Bill, 5)10,124010

B:

ஆலிஸ் பரிவர்த்தனை (Alice Transaction)ஆலிஸ் நான்ஸ் (Alice Nonce)பில் பரிவர்த்தனை (Bill Transaction)பில் நான்ஸ் (Bill Nonce)பில்லின் அலவன்ஸ் (Bill's Allowance)ஆலிஸிடமிருந்து பில்லின் மொத்த வருமானம் (Bill Total Income from Alice)
approve(Bill, 5)1050
increaseAllowance(Bill, 5)115+5 = 100
transferFrom(Alice, Bill, 10)10,124010
1 /* *
2 * @dev அழைப்பாளரால் `spender` க்கு வழங்கப்பட்ட அனுமதியை அணுரீதியாக (Atomically) அதிகரிக்கிறது.
3 *
4 * இது {approve} க்கு மாற்றாகும், இது {IERC20-approve} இல் விவரிக்கப்பட்டுள்ள சிக்கல்களைத் தணிக்கப் பயன்படுத்தப்படலாம்.
5 *
6 * புதுப்பிக்கப்பட்ட அனுமதியைக் குறிக்கும் {Approval} நிகழ்வை வெளியிடுகிறது.
7 *
8 * தேவைகள்:
9 *
10 * - `spender` பூஜ்ஜிய முகவரியாக இருக்க முடியாது. */
11 function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
12 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
13 return true;
14 }

a.add(b) செயல்பாடு ஒரு பாதுகாப்பான கூட்டல் ஆகும். a+b>=2^256 என்ற சாத்தியமில்லாத நிலையில், இது சாதாரண கூட்டல் செய்யும் விதத்தில் சுற்றிக் கொள்ளாது (wrap around).

1
2 /* *
3 * @dev அழைப்பாளரால் `spender` க்கு வழங்கப்பட்ட அனுமதியை அணுரீதியாக (Atomically) குறைக்கிறது.
4 *
5 * இது {approve} க்கு மாற்றாகும், இது {IERC20-approve} இல் விவரிக்கப்பட்டுள்ள சிக்கல்களைத் தணிக்கப் பயன்படுத்தப்படலாம்.
6 *
7 * புதுப்பிக்கப்பட்ட அனுமதியைக் குறிக்கும் {Approval} நிகழ்வை வெளியிடுகிறது.
8 *
9 * தேவைகள்:
10 *
11 * - `spender` பூஜ்ஜிய முகவரியாக இருக்க முடியாது.
12 * - `spender` அழைப்பாளருக்கு குறைந்தபட்சம் `subtractedValue` க்கான அனுமதியைக் கொண்டிருக்க வேண்டும். */
13 function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
14 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue,
15 "ERC20: decreased allowance below zero"));
16 return true;
17 }

டோக்கன் தகவலை மாற்றும் செயல்பாடுகள்

உண்மையான வேலையைச் செய்யும் நான்கு செயல்பாடுகள் இவை: _transfer, _mint, _burn மற்றும் _approve.

_transfer செயல்பாடு

1 /* *
2 * @dev `sender` இடமிருந்து `recipient` க்கு `amount` டோக்கன்களை மாற்றுகிறது.
3 *
4 * இந்த உள்ளக (internal) செயல்பாடு {transfer} க்கு சமமானது, மேலும் இது தானியங்கி டோக்கன் கட்டணங்கள், ஸ்லாஷிங் (slashing) பொறிமுறைகள் போன்றவற்றைச் செயல்படுத்தப் பயன்படுத்தப்படலாம்.
5 *
6 * {Transfer} நிகழ்வை வெளியிடுகிறது.
7 *
8 * தேவைகள்:
9 *
10 * - `sender` பூஜ்ஜிய முகவரியாக இருக்க முடியாது.
11 * - `recipient` பூஜ்ஜிய முகவரியாக இருக்க முடியாது.
12 * - `sender` குறைந்தபட்சம் `amount` இருப்பைக் கொண்டிருக்க வேண்டும். */
13 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-இல் பூஜ்ஜிய முகவரியை (address zero) உண்மையில் யாரும் கொண்டிருக்கவில்லை (அதாவது, பொருந்தக்கூடிய பொது விசை பூஜ்ஜிய முகவரிக்கு மாற்றப்படும் தனிப்பட்ட விசை யாருக்கும் தெரியாது). மக்கள் அந்த முகவரியைப் பயன்படுத்தும் போது, அது பொதுவாக ஒரு மென்பொருள் பிழையாகும் - எனவே பூஜ்ஜிய முகவரி அனுப்புநராகவோ அல்லது பெறுநராகவோ பயன்படுத்தப்பட்டால் நாங்கள் தோல்வியடைகிறோம்.

 

1 _beforeTokenTransfer(sender, recipient, amount);
2

இந்த ஒப்பந்தத்தைப் பயன்படுத்த இரண்டு வழிகள் உள்ளன:

  1. உங்கள் சொந்தக் குறியீட்டிற்கான டெம்ப்ளேட்டாக இதைப் பயன்படுத்தவும்
  2. இதிலிருந்து மரபுரிமையாகப் பெற்று (Inherit) (opens in a new tab), நீங்கள் மாற்ற வேண்டிய செயல்பாடுகளை மட்டும் மேலெழுதவும் (override)

இரண்டாவது முறை மிகவும் சிறந்தது, ஏனெனில் OpenZeppelin ERC-20 குறியீடு ஏற்கனவே தணிக்கை செய்யப்பட்டு பாதுகாப்பானது எனக் காட்டப்பட்டுள்ளது. நீங்கள் மரபுரிமையைப் பயன்படுத்தும் போது நீங்கள் மாற்றியமைக்கும் செயல்பாடுகள் என்ன என்பது தெளிவாகத் தெரியும், மேலும் உங்கள் ஒப்பந்தத்தை நம்புவதற்கு மக்கள் அந்த குறிப்பிட்ட செயல்பாடுகளை மட்டுமே தணிக்கை செய்ய வேண்டும்.

ஒவ்வொரு முறையும் டோக்கன்கள் கைமாறும் போது ஒரு செயல்பாட்டைச் செய்வது பெரும்பாலும் பயனுள்ளதாக இருக்கும். இருப்பினும், _transfer என்பது மிக முக்கியமான செயல்பாடாகும், மேலும் அதைப் பாதுகாப்பற்ற முறையில் எழுத முடியும் (கீழே காண்க), எனவே அதை மேலெழுதாமல் இருப்பது நல்லது. இதற்கான தீர்வு _beforeTokenTransfer, இது ஒரு ஹூக் செயல்பாடு (hook function) (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) டோக்கன்களின் மொத்த விநியோகத்தை மாற்றியமைக்கின்றன. அவை அகச் செயல்பாடுகள் (internal) மற்றும் இந்த ஒப்பந்தத்தில் அவற்றை அழைக்கும் எந்தச் செயல்பாடும் இல்லை, எனவே நீங்கள் ஒப்பந்தத்திலிருந்து மரபுரிமையாகப் பெற்று, எந்த நிபந்தனைகளின் கீழ் புதிய டோக்கன்களை மின்ட் (mint) செய்வது அல்லது ஏற்கனவே உள்ளவற்றை பர்ன் (burn) செய்வது என்பதைத் தீர்மானிக்க உங்கள் சொந்த தர்க்கத்தைச் சேர்த்தால் மட்டுமே அவை பயனுள்ளதாக இருக்கும்.

குறிப்பு: ஒவ்வொரு ERC-20 டோக்கனும் டோக்கன் நிர்வாகத்தைக் கட்டளையிடும் அதன் சொந்த வணிக தர்க்கத்தைக் கொண்டுள்ளது. உதாரணமாக, ஒரு நிலையான விநியோக ஒப்பந்தம் கன்ஸ்ட்ரக்டரில் _mint-ஐ மட்டுமே அழைக்கலாம் மற்றும் _burn-ஐ ஒருபோதும் அழைக்காது. டோக்கன்களை விற்கும் ஒரு ஒப்பந்தம் பணம் செலுத்தப்படும் போது _mint-ஐ அழைக்கும், மேலும் பணவீக்கத்தைத் தவிர்க்க ஒரு கட்டத்தில் _burn-ஐ அழைக்கும்.

1 /* * @dev `amount` டோக்கன்களை உருவாக்கி அவற்றை `account` க்கு ஒதுக்குகிறது, மொத்த விநியோகத்தை அதிகரிக்கிறது.
2 *
3 * `from` பூஜ்ஜிய முகவரிக்கு அமைக்கப்பட்டு {Transfer} நிகழ்வை வெளியிடுகிறது.
4 *
5 * தேவைகள்:
6 *
7 * - `to` பூஜ்ஜிய முகவரியாக இருக்க முடியாது. */
8 function _mint(address account, uint256 amount) internal virtual {
9 require(account != address(0), "ERC20: mint to the zero address");
10 _beforeTokenTransfer(address(0), account, amount);
11 _totalSupply = _totalSupply.add(amount);
12 _balances[account] = _balances[account].add(amount);
13 emit Transfer(address(0), account, amount);
14 }

டோக்கன்களின் மொத்த எண்ணிக்கை மாறும் போது _totalSupply-ஐப் புதுப்பிக்க மறக்காதீர்கள்.

 

1 /* *
2 * @dev `account` இலிருந்து `amount` டோக்கன்களை அழிக்கிறது, மொத்த விநியோகத்தைக் குறைக்கிறது.
3 *
4 * `to` பூஜ்ஜிய முகவரிக்கு அமைக்கப்பட்டு {Transfer} நிகழ்வை வெளியிடுகிறது.
5 *
6 * தேவைகள்:
7 *
8 * - `account` பூஜ்ஜிய முகவரியாக இருக்க முடியாது.
9 * - `account` குறைந்தபட்சம் `amount` டோக்கன்களைக் கொண்டிருக்க வேண்டும். */
10 function _burn(address account, uint256 amount) internal virtual {
11 require(account != address(0), "ERC20: burn from the zero address");
12
13 _beforeTokenTransfer(account, address(0), amount);
14
15 _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
16 _totalSupply = _totalSupply.sub(amount);
17 emit Transfer(account, address(0), amount);
18 }

_burn செயல்பாடு _mint-ஐப் போலவே இருக்கும், ஆனால் இது எதிர் திசையில் செல்கிறது.

_approve செயல்பாடு

இது உண்மையில் அலவன்ஸ்களைக் குறிப்பிடும் செயல்பாடாகும். உரிமையாளரின் தற்போதைய இருப்பை விட அதிக அலவன்ஸைக் குறிப்பிட இது உரிமையாளரை அனுமதிக்கிறது என்பதை நினைவில் கொள்ளவும். இது பரவாயில்லை, ஏனென்றால் பரிமாற்றத்தின் போது இருப்பு சரிபார்க்கப்படுகிறது, அப்போது அது அலவன்ஸ் உருவாக்கப்படும் போது இருந்த இருப்பிலிருந்து வேறுபட்டிருக்கலாம்.

1 /* *
2 * @dev `owner` இன் டோக்கன்களில் `spender` க்கான அனுமதியாக `amount` ஐ அமைக்கிறது.
3 *
4 * இந்த உள்ளக (internal) செயல்பாடு `approve` க்கு சமமானது, மேலும் இது குறிப்பிட்ட துணை அமைப்புகளுக்கு தானியங்கி அனுமதிகளை அமைக்கப் பயன்படுத்தப்படலாம்.
5 *
6 * {Approval} நிகழ்வை வெளியிடுகிறது.
7 *
8 * தேவைகள்:
9 *
10 * - `owner` பூஜ்ஜிய முகவரியாக இருக்க முடியாது.
11 * - `spender` பூஜ்ஜிய முகவரியாக இருக்க முடியாது. */
12 function _approve(address owner, address spender, uint256 amount) internal virtual {
13 require(owner != address(0), "ERC20: approve from the zero address");
14 require(spender != address(0), "ERC20: approve to the zero address");
15
16 _allowances[owner][spender] = amount;

 

ஒரு Approval நிகழ்வை வெளியிடவும். பயன்பாடு எவ்வாறு எழுதப்பட்டுள்ளது என்பதைப் பொறுத்து, ஸ்பெண்டர் ஒப்பந்தத்திற்கு ஒப்புதல் பற்றி உரிமையாளர் மூலமாகவோ அல்லது இந்த நிகழ்வுகளைக் கேட்கும் சேவையகம் மூலமாகவோ தெரிவிக்கப்படலாம்.

1 emit Approval(owner, spender, amount);
2 }
3

Decimals மாறியை மாற்றவும்

1
2
3 /* *
4 * @dev {decimals} ஐ 18 என்ற இயல்புநிலை மதிப்பைத் தவிர வேறு மதிப்பிற்கு அமைக்கிறது.
5 *
6 * எச்சரிக்கை: இந்தச் செயல்பாடு கட்டமைப்பாளரிடமிருந்து (constructor) மட்டுமே அழைக்கப்பட வேண்டும். டோக்கன் ஒப்பந்தங்களுடன் தொடர்பு கொள்ளும் பெரும்பாலான பயன்பாடுகள் {decimals} எப்போதாவது மாறும் என்று எதிர்பார்க்காது, அவ்வாறு மாறினால் தவறாகச் செயல்படலாம். */
7 function _setupDecimals(uint8 decimals_) internal {
8 _decimals = decimals_;
9 }

இந்தச் செயல்பாடு _decimals மாறியை மாற்றியமைக்கிறது, இது தொகையை எவ்வாறு விளக்குவது என்பதைப் பயனர் இடைமுகங்களுக்குச் சொல்லப் பயன்படுகிறது. நீங்கள் அதை கன்ஸ்ட்ரக்டரிலிருந்து அழைக்க வேண்டும். அதைத் தொடர்ந்து எந்தப் புள்ளியிலும் அழைப்பது நேர்மையற்றதாக இருக்கும், மேலும் பயன்பாடுகள் அதைக் கையாளும் வகையில் வடிவமைக்கப்படவில்லை.

ஹூக்குகள் (Hooks)

1
2 /* *
3 * @dev டோக்கன்களின் எந்தவொரு மாற்றத்திற்கும் முன் அழைக்கப்படும் ஹூக் (Hook). இதில் உருவாக்குதல் (minting) மற்றும் அழித்தல் (burning) ஆகியவை அடங்கும்.
4 *
5 * அழைப்பதற்கான நிபந்தனைகள்:
6 *
7 * - `from` மற்றும் `to` இரண்டும் பூஜ்ஜியமாக இல்லாதபோது, ``from`` இன் `amount` டோக்கன்கள் `to` க்கு மாற்றப்படும்.
8 * - `from` பூஜ்ஜியமாக இருக்கும்போது, `to` க்காக `amount` டோக்கன்கள் உருவாக்கப்படும்.
9 * - `to` பூஜ்ஜியமாக இருக்கும்போது, ``from`` இன் `amount` டோக்கன்கள் அழிக்கப்படும்.
10 * - `from` மற்றும் `to` இரண்டும் ஒருபோதும் பூஜ்ஜியமாக இருக்காது.
11 *
12 * ஹூக்குகள் பற்றி மேலும் அறிய, xref:ROOT:extending-contracts.adoc#using-hooks[ஹூக்குகளைப் பயன்படுத்துதல்] ஐப் பார்க்கவும். */
13 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
14}

பரிமாற்றங்களின் போது அழைக்கப்பட வேண்டிய ஹூக் செயல்பாடு இதுவாகும். இது இங்கே காலியாக உள்ளது, ஆனால் அது ஏதாவது செய்ய வேண்டும் என நீங்கள் விரும்பினால், அதை மேலெழுதவும்.

முடிவுரை

மதிப்பாய்வுக்காக, இந்த ஒப்பந்தத்தில் உள்ள சில மிக முக்கியமான யோசனைகள் இங்கே உள்ளன (என் கருத்துப்படி, உங்களுடையது மாறுபடலாம்):

  • பிளாக்செயினில் எந்த ரகசியங்களும் இல்லை. ஒரு ஸ்மார்ட் ஒப்பந்தம் அணுகக்கூடிய எந்தத் தகவலும் உலகம் முழுவதற்கும் கிடைக்கும்.
  • உங்கள் சொந்த பரிவர்த்தனைகளின் வரிசையை நீங்கள் கட்டுப்படுத்தலாம், ஆனால் மற்றவர்களின் பரிவர்த்தனை எப்போது நடக்கும் என்பதை அல்ல. அலவன்ஸை மாற்றுவது ஆபத்தானதாக இருப்பதற்கு இதுவே காரணம், ஏனெனில் இது ஸ்பெண்டரை இரண்டு அலவன்ஸ்களின் தொகையையும் செலவிட அனுமதிக்கிறது.
  • 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-இல் பார்ப்பது போல). ஏனென்றால், நிலை மாற்றத்தின் போது நீங்கள் ஒரு முரண்பாடான நிலையைக் கொண்டிருக்கிறீர்கள். உதாரணமாக, அனுப்புநரின் இருப்பிலிருந்து நீங்கள் கழிக்கும் நேரத்திற்கும் பெறுநரின் இருப்பில் நீங்கள் சேர்க்கும் நேரத்திற்கும் இடையில் இருக்க வேண்டியதை விட குறைவான டோக்கன்களே உள்ளன. அவற்றுக்கிடையே செயல்பாடுகள் இருந்தால், குறிப்பாக வேறு ஒப்பந்தத்திற்கான அழைப்புகள் இருந்தால், இது தவறாகப் பயன்படுத்தப்படலாம்.

OpenZeppelin ERC-20 ஒப்பந்தம் எவ்வாறு எழுதப்பட்டுள்ளது என்பதையும், குறிப்பாக அது எவ்வாறு மிகவும் பாதுகாப்பானதாக மாற்றப்பட்டுள்ளது என்பதையும் இப்போது நீங்கள் பார்த்துள்ளீர்கள், சென்று உங்கள் சொந்த பாதுகாப்பான ஒப்பந்தங்கள் மற்றும் பயன்பாடுகளை எழுதுங்கள்.

எனது மேலும் பல பணிகளுக்கு இங்கே பார்க்கவும் (opens in a new tab).

பக்கம் கடைசியாகப் புதுப்பிக்கப்பட்டது: 3 மார்ச், 2026

இந்த வழிகாட்டி பயனுள்ளதாக இருந்ததா?