பாதுகாப்பு வளையங்களுடன் ERC-20
அறிமுகம்
Ethereum இன் சிறந்த விஷயங்களில் ஒன்று, உங்கள் பரிவர்த்தனைகளை மாற்றவோ அல்லது ரத்து செய்யவோ எந்தவொரு மைய அதிகாரமும் இல்லை என்பதாகும். Ethereum இன் பெரிய சிக்கல்களில் ஒன்று, பயனர்களின் தவறுகள் அல்லது சட்டவிரோத பரிவர்த்தனைகளை ரத்து செய்யும் அதிகாரம் கொண்ட மைய அதிகாரம் எதுவும் இல்லை என்பதாகும். இந்தக் கட்டுரையில், ERC-20 டோக்கன்களில் பயனர்கள் செய்யும் சில பொதுவான தவறுகளைப் பற்றியும், அந்தத் தவறுகளைத் தவிர்க்க பயனர்களுக்கு உதவும் அல்லது மைய அதிகாரத்திற்கு சில அதிகாரங்களை (உதாரணமாக கணக்குகளை முடக்குவதற்கு) வழங்கும் ERC-20 ஒப்பந்தங்களை எவ்வாறு உருவாக்குவது என்பது பற்றியும் நீங்கள் அறிந்துகொள்வீர்கள்.
நாங்கள் OpenZeppelin ERC-20 token contract (opens in a new tab)-ஐப் பயன்படுத்தினாலும், இந்தக் கட்டுரை அதை விரிவாக விளக்கவில்லை என்பதை நினைவில் கொள்ளவும். இந்தத் தகவலை நீங்கள் இங்கே காணலாம்.
முழுமையான மூலக் குறியீட்டை (source code) நீங்கள் பார்க்க விரும்பினால்:
- Remix IDE (opens in a new tab)-ஐத் திறக்கவும்.
- clone github ஐகானைக் கிளிக் செய்யவும் (
). https://github.com/qbzzt/20220815-erc20-safety-railsஎன்ற github ரெபோசிட்டரியை குளோன் (clone) செய்யவும்.- contracts > erc20-safety-rails.sol என்பதைத் திறக்கவும்.
ஒரு ERC-20 ஒப்பந்தத்தை உருவாக்குதல்
பாதுகாப்பு வளைய செயல்பாட்டைச் சேர்ப்பதற்கு முன், நமக்கு ஒரு ERC-20 ஒப்பந்தம் தேவை. இந்தக் கட்டுரையில் the OpenZeppelin Contracts Wizard (opens in a new tab)-ஐப் பயன்படுத்துவோம். அதை வேறொரு பிரவுசரில் திறந்து, இந்த வழிமுறைகளைப் பின்பற்றவும்:
-
ERC20 என்பதைத் தேர்ந்தெடுக்கவும்.
-
இந்த அமைப்புகளை உள்ளிடவும்:
அளவுரு (Parameter) மதிப்பு (Value) Name SafetyRailsToken Symbol SAFE Premint 1000 Features None Access Control Ownable Upgradability None -
மேலே ஸ்க்ரோல் செய்து, Open in Remix (Remix-க்கு) அல்லது வேறு சூழலைப் பயன்படுத்த Download என்பதைக் கிளிக் செய்யவும். நீங்கள் Remix-ஐப் பயன்படுத்துகிறீர்கள் என்று நான் கருதுகிறேன், நீங்கள் வேறு எதையாவது பயன்படுத்தினால் அதற்கேற்ப மாற்றங்களைச் செய்யுங்கள்.
-
இப்போது நம்மிடம் முழுமையாகச் செயல்படும் ERC-20 ஒப்பந்தம் உள்ளது. இறக்குமதி செய்யப்பட்ட குறியீட்டைக் காண
.deps>npmஎன்பதை நீங்கள் விரிவுபடுத்தலாம். -
இது ஒரு ERC-20 ஒப்பந்தமாகச் செயல்படுகிறதா என்பதைப் பார்க்க, ஒப்பந்தத்தை கம்பைல் (compile) செய்து, டிப்ளாய் (deploy) செய்து, அதனுடன் விளையாடிப் பாருங்கள். Remix-ஐ எவ்வாறு பயன்படுத்துவது என்பதை நீங்கள் அறிய விரும்பினால், இந்த டுடோரியலைப் பயன்படுத்தவும் (opens in a new tab).
பொதுவான தவறுகள்
தவறுகள்
பயனர்கள் சில நேரங்களில் தவறான முகவரிக்கு டோக்கன்களை அனுப்புகிறார்கள். அவர்கள் என்ன செய்ய நினைத்தார்கள் என்பதை அறிய அவர்களின் மனதை நம்மால் படிக்க முடியாது என்றாலும், அடிக்கடி நிகழும் மற்றும் எளிதில் கண்டறியக்கூடிய இரண்டு வகையான பிழைகள் உள்ளன:
-
ஒப்பந்தத்தின் சொந்த முகவரிக்கே டோக்கன்களை அனுப்புவது. உதாரணமாக, Optimism's OP token (opens in a new tab) இரண்டு மாதங்களுக்குள் 120,000-க்கும் மேற்பட்ட (opens in a new tab) OP டோக்கன்களைக் குவிக்க முடிந்தது. இது மக்கள் இழந்ததாகக் கருதப்படும் கணிசமான அளவு செல்வத்தைக் குறிக்கிறது.
-
externally owned account அல்லது smart contract-க்குச் சொந்தமில்லாத ஒரு வெற்று முகவரிக்கு டோக்கன்களை அனுப்புவது. இது எவ்வளவு அடிக்கடி நிகழ்கிறது என்பதற்கான புள்ளிவிவரங்கள் என்னிடம் இல்லை என்றாலும், ஒரு சம்பவத்தில் 20,000,000 டோக்கன்கள் இழக்கப்பட்டிருக்கலாம் (opens in a new tab).
பரிமாற்றங்களைத் தடுத்தல்
OpenZeppelin ERC-20 ஒப்பந்தத்தில் ஒரு ஹூக் (hook), _beforeTokenTransfer (opens in a new tab) உள்ளது, இது ஒரு டோக்கன் பரிமாற்றப்படுவதற்கு முன்பு அழைக்கப்படுகிறது. இயல்பாக இந்த ஹூக் எதையும் செய்யாது, ஆனால் ஏதேனும் சிக்கல் இருந்தால் பரிவர்த்தனையை ரத்து செய்யும் (revert) சரிபார்ப்புகள் போன்ற நமது சொந்த செயல்பாடுகளை இதில் இணைக்கலாம்.
இந்த ஹூக்கைப் பயன்படுத்த, கன்ஸ்ட்ரக்டருக்குப் (constructor) பிறகு இந்தச் செயல்பாட்டைச் சேர்க்கவும்:
1 function _beforeTokenTransfer(address from, address to, uint256 amount)2 internal virtual3 override(ERC20)4 {5 super._beforeTokenTransfer(from, to, amount);6 }நீங்கள் Solidity-ஐப் பற்றி நன்கு அறியாதவராக இருந்தால், இந்தச் செயல்பாட்டின் சில பகுதிகள் உங்களுக்குப் புதியதாக இருக்கலாம்:
1 internal virtualvirtual என்ற முக்கியச் சொல் (keyword) எதைக் குறிக்கிறது என்றால், நாம் ERC20-லிருந்து செயல்பாட்டைப் பெற்று (inherit) இந்தச் செயல்பாட்டை மேலெழுதியது (override) போலவே, பிற ஒப்பந்தங்களும் நம்மிடமிருந்து பெற்று இந்தச் செயல்பாட்டை மேலெழுதலாம் என்பதாகும்.
1 override(ERC20)_beforeTokenTransfer இன் ERC20 டோக்கன் வரையறையை நாம் மேலெழுதுகிறோம் (overriding) (opens in a new tab) என்பதை வெளிப்படையாகக் குறிப்பிட வேண்டும். பொதுவாக, பாதுகாப்பு கண்ணோட்டத்தில், மறைமுகமான வரையறைகளை விட வெளிப்படையான வரையறைகள் மிகவும் சிறந்தவை - நீங்கள் செய்த ஒன்று உங்கள் கண் முன்னே இருந்தால் அதை நீங்கள் மறக்க முடியாது. எந்த சூப்பர்கிளாஸின் (superclass) _beforeTokenTransfer-ஐ நாம் மேலெழுதுகிறோம் என்பதைக் குறிப்பிட வேண்டியதற்கும் இதுவே காரணம்.
1 super._beforeTokenTransfer(from, to, amount);இந்த வரி, நாம் பெற்ற (inherited) ஒப்பந்தம் அல்லது ஒப்பந்தங்களில் உள்ள _beforeTokenTransfer செயல்பாட்டை அழைக்கிறது. இந்த நிலையில், அது ERC20 மட்டுமே, Ownable-இல் இந்த ஹூக் இல்லை. தற்போது ERC20._beforeTokenTransfer எதையும் செய்யவில்லை என்றாலும், எதிர்காலத்தில் செயல்பாடு சேர்க்கப்பட்டால் (மற்றும் ஒப்பந்தங்களை டிப்ளாய் செய்த பிறகு மாற்ற முடியாது என்பதால், ஒப்பந்தத்தை மீண்டும் டிப்ளாய் செய்ய முடிவு செய்தால்) அதற்காக இதை அழைக்கிறோம்.
தேவைகளைக் குறியீடாக்குதல்
இந்தத் தேவைகளைச் செயல்பாட்டில் சேர்க்க விரும்புகிறோம்:
toமுகவரியானதுaddress(this)-க்குச் சமமாக இருக்க முடியாது, இது ERC-20 ஒப்பந்தத்தின் சொந்த முகவரியாகும்.toமுகவரி காலியாக இருக்க முடியாது, அது பின்வருவனவற்றில் ஒன்றாக இருக்க வேண்டும்:- ஒரு externally owned account (EOA). ஒரு முகவரி EOA தானா என்பதை நேரடியாகச் சரிபார்க்க முடியாது, ஆனால் ஒரு முகவரியின் ETH இருப்பைச் சரிபார்க்கலாம். EOA-க்கள் இனி பயன்படுத்தப்படாவிட்டாலும், பெரும்பாலும் எப்போதும் இருப்பைக் கொண்டிருக்கும் - அவற்றை கடைசி wei வரை அழிப்பது கடினம்.
- ஒரு ஸ்மார்ட் ஒப்பந்தம் (smart contract). ஒரு முகவரி ஸ்மார்ட் ஒப்பந்தமா என்பதைச் சோதிப்பது சற்று கடினம். வெளிப்புறக் குறியீட்டின் நீளத்தைச் சரிபார்க்கும்
EXTCODESIZE(opens in a new tab) எனப்படும் ஒரு ஆப்கோடு (opcode) உள்ளது, ஆனால் இது நேரடியாக Solidity-இல் கிடைக்காது. இதற்கு EVM அசெம்பிளியான Yul (opens in a new tab)-ஐப் பயன்படுத்த வேண்டும். Solidity-லிருந்து நாம் பயன்படுத்தக்கூடிய பிற மதிப்புகளும் உள்ளன (<address>.codeமற்றும்<address>.codehash(opens in a new tab)), ஆனால் அவற்றுக்கு அதிகச் செலவாகும்.
புதிய குறியீட்டை வரி வரியாகப் பார்ப்போம்:
1 require(to != address(this), "Can't send tokens to the contract address");இது முதல் தேவை, to மற்றும் this(address) இரண்டும் ஒன்றல்ல என்பதைச் சரிபார்க்கவும்.
1 bool isToContract;2 assembly {3 isToContract := gt(extcodesize(to), 0)4 }ஒரு முகவரி ஒப்பந்தமா என்பதை இப்படித்தான் சரிபார்க்கிறோம். Yul-லிருந்து நேரடியாக வெளியீட்டைப் பெற முடியாது, எனவே முடிவை வைத்திருக்க ஒரு மாறியை (variable) வரையறுக்கிறோம் (இந்த நிலையில் isToContract). Yul செயல்படும் விதம் என்னவென்றால், ஒவ்வொரு ஆப்கோடும் ஒரு செயல்பாடாகக் கருதப்படுகிறது. எனவே முதலில் ஒப்பந்தத்தின் அளவைப் பெற EXTCODESIZE (opens in a new tab)-ஐ அழைக்கிறோம், பின்னர் அது பூஜ்ஜியமாக இல்லை என்பதைச் சரிபார்க்க GT (opens in a new tab)-ஐப் பயன்படுத்துகிறோம் (நாம் குறியற்ற முழு எண்களைக் (unsigned integers) கையாளுகிறோம், எனவே நிச்சயமாக அது எதிர்மறையாக இருக்க முடியாது). பின்னர் முடிவை isToContract-இல் எழுதுகிறோம்.
1 require(to.balance != 0 || isToContract, "Can't send tokens to an empty address");இறுதியாக, வெற்று முகவரிகளுக்கான உண்மையான சரிபார்ப்பு நம்மிடம் உள்ளது.
நிர்வாக அணுகல்
சில நேரங்களில் தவறுகளைச் சரிசெய்யக்கூடிய ஒரு நிர்வாகி இருப்பது பயனுள்ளதாக இருக்கும். தவறாகப் பயன்படுத்தப்படுவதற்கான சாத்தியக்கூறுகளைக் குறைக்க, இந்த நிர்வாகி ஒரு multisig (opens in a new tab)-ஆக இருக்கலாம், இதனால் ஒரு செயலுக்குப் பல நபர்கள் ஒப்புக்கொள்ள வேண்டும். இந்தக் கட்டுரையில் இரண்டு நிர்வாக அம்சங்கள் இருக்கும்:
-
கணக்குகளை முடக்குதல் மற்றும் முடக்கத்தை நீக்குதல். உதாரணமாக, ஒரு கணக்கு சமரசம் (compromised) செய்யப்பட்டிருக்கலாம் எனும்போது இது பயனுள்ளதாக இருக்கும்.
-
சொத்துச் சுத்திகரிப்பு (Asset cleanup).
சில நேரங்களில் மோசடி செய்பவர்கள் சட்டபூர்வமான தன்மையைப் பெற உண்மையான டோக்கனின் ஒப்பந்தத்திற்கு மோசடியான டோக்கன்களை அனுப்புகிறார்கள். உதாரணமாக, இங்கே பார்க்கவும் (opens in a new tab). சட்டபூர்வமான ERC-20 ஒப்பந்தம் 0x4200....0042 (opens in a new tab) ஆகும். அதைப் போல நடிக்கும் மோசடி 0x234....bbe (opens in a new tab) ஆகும்.
மக்கள் தவறுதலாகச் சட்டபூர்வமான ERC-20 டோக்கன்களை நமது ஒப்பந்தத்திற்கு அனுப்புவதும் சாத்தியமாகும், அவற்றை வெளியே எடுப்பதற்கான ஒரு வழியை நாம் கொண்டிருக்க விரும்புவதற்கு இதுவும் ஒரு காரணமாகும்.
நிர்வாக அணுகலைச் செயல்படுத்த OpenZeppelin இரண்டு வழிமுறைகளை வழங்குகிறது:
Ownable(opens in a new tab) ஒப்பந்தங்கள் ஒரே ஒரு உரிமையாளரைக் கொண்டுள்ளன.onlyOwnerமாற்றியமைப்பைக் (modifier) (opens in a new tab) கொண்ட செயல்பாடுகளை அந்த உரிமையாளரால் மட்டுமே அழைக்க முடியும். உரிமையாளர்கள் உரிமையை வேறு ஒருவருக்கு மாற்றலாம் அல்லது அதை முழுமையாகத் துறக்கலாம். மற்ற அனைத்துக் கணக்குகளின் உரிமைகளும் பொதுவாக ஒரே மாதிரியானவை.AccessControl(opens in a new tab) ஒப்பந்தங்கள் பங்கு அடிப்படையிலான அணுகல் கட்டுப்பாட்டைக் (role based access control - RBAC) (opens in a new tab) கொண்டுள்ளன.
எளிமைக்காக, இந்தக் கட்டுரையில் நாம் Ownable-ஐப் பயன்படுத்துகிறோம்.
ஒப்பந்தங்களை முடக்குதல் மற்றும் முடக்கத்தை நீக்குதல்
ஒப்பந்தங்களை முடக்குவதற்கும் முடக்கத்தை நீக்குவதற்கும் பல மாற்றங்கள் தேவை:
-
எந்த முகவரிகள் முடக்கப்பட்டுள்ளன என்பதைக் கண்காணிக்க முகவரிகளிலிருந்து பூலியன்களுக்கு (booleans) (opens in a new tab) ஒரு மேப்பிங் (mapping) (opens in a new tab). அனைத்து மதிப்புகளும் ஆரம்பத்தில் பூஜ்ஜியமாக இருக்கும், இது பூலியன் மதிப்புகளுக்குத் தவறு (false) என்று விளக்கப்படுகிறது. இயல்பாகக் கணக்குகள் முடக்கப்படாததால் இதுவே நமக்குத் தேவை.
1 mapping(address => bool) public frozenAccounts; -
ஒரு கணக்கு முடக்கப்படும்போது அல்லது முடக்கம் நீக்கப்படும்போது ஆர்வமுள்ள எவருக்கும் தெரிவிக்க நிகழ்வுகள் (Events) (opens in a new tab). தொழில்நுட்ப ரீதியாகப் பார்த்தால் இந்தச் செயல்களுக்கு நிகழ்வுகள் தேவையில்லை, ஆனால் ஆஃப்செயின் (offchain) குறியீடு இந்த நிகழ்வுகளைக் கேட்கவும் என்ன நடக்கிறது என்பதை அறியவும் இது உதவுகிறது. வேறு ஒருவருக்குத் தொடர்புடைய ஏதாவது நடக்கும்போது ஒரு ஸ்மார்ட் ஒப்பந்தம் அவற்றை வெளியிடுவது (emit) நல்ல பழக்கமாகக் கருதப்படுகிறது.
நிகழ்வுகள் அட்டவணைப்படுத்தப்பட்டுள்ளன (indexed), எனவே ஒரு கணக்கு முடக்கப்பட்ட அல்லது முடக்கம் நீக்கப்பட்ட அனைத்து நேரங்களையும் தேட முடியும்.
1 // கணக்குகள் முடக்கப்படும் அல்லது முடக்கம் நீக்கப்படும் போது2 event AccountFrozen(address indexed _addr);3 event AccountThawed(address indexed _addr); -
கணக்குகளை முடக்குவதற்கும் முடக்கத்தை நீக்குவதற்குமான செயல்பாடுகள். இந்த இரண்டு செயல்பாடுகளும் கிட்டத்தட்ட ஒரே மாதிரியானவை, எனவே நாம் முடக்கும் (freeze) செயல்பாட்டை மட்டுமே பார்ப்போம்.
1 function freezeAccount(address addr)2 public3 onlyOwnerpublic(opens in a new tab) எனக் குறிக்கப்பட்ட செயல்பாடுகளைப் பிற ஸ்மார்ட் ஒப்பந்தங்களிலிருந்து அல்லது நேரடியாக ஒரு பரிவர்த்தனை மூலம் அழைக்கலாம்.1 {2 require(!frozenAccounts[addr], "Account already frozen");3 frozenAccounts[addr] = true;4 emit AccountFrozen(addr);5 } // கணக்கை முடக்குகணக்கு ஏற்கனவே முடக்கப்பட்டிருந்தால், ரத்து (revert) செய்யவும். இல்லையெனில், அதை முடக்கி ஒரு நிகழ்வை
emitசெய்யவும். -
முடக்கப்பட்ட கணக்கிலிருந்து பணம் நகர்த்தப்படுவதைத் தடுக்க
_beforeTokenTransfer-ஐ மாற்றவும். முடக்கப்பட்ட கணக்கிற்குள் இன்னும் பணத்தைப் பரிமாற்ற முடியும் என்பதை நினைவில் கொள்ளவும்.1 require(!frozenAccounts[from], "The account is frozen");
சொத்துச் சுத்திகரிப்பு
இந்த ஒப்பந்தத்தில் உள்ள ERC-20 டோக்கன்களை வெளியிட, அவை சொந்தமான டோக்கன் ஒப்பந்தத்தில் transfer (opens in a new tab) அல்லது approve (opens in a new tab) என்ற செயல்பாட்டை நாம் அழைக்க வேண்டும். இந்த நிலையில் கொடுப்பனவுகளில் (allowances) எரிவாயுவை (gas) வீணாக்குவதில் எந்த அர்த்தமும் இல்லை, நாம் நேரடியாகப் பரிமாற்றம் செய்யலாம்.
1 function cleanupERC20(2 address erc20,3 address dest4 )5 public6 onlyOwner7 {8 IERC20 token = IERC20(erc20);முகவரியைப் பெறும்போது ஒரு ஒப்பந்தத்திற்கான பொருளை (object) உருவாக்குவதற்கான தொடரியல் (syntax) இதுவாகும். மூலக் குறியீட்டின் ஒரு பகுதியாக ERC20 டோக்கன்களுக்கான வரையறை நம்மிடம் இருப்பதால் இதைச் செய்ய முடியும் (வரி 4-ஐப் பார்க்கவும்), மேலும் அந்த கோப்பில் OpenZeppelin ERC-20 ஒப்பந்தத்திற்கான இடைமுகமான (interface) IERC20-க்கான வரையறை (opens in a new tab) அடங்கும்.
1 uint balance = token.balanceOf(address(this));2 token.transfer(dest, balance);3 }இது ஒரு சுத்திகரிப்புச் செயல்பாடு, எனவே நாம் எந்த டோக்கன்களையும் விட்டுவிட விரும்பவில்லை என்று கருதலாம். பயனரிடமிருந்து கைமுறையாக இருப்பைப் பெறுவதற்குப் பதிலாக, நாம் இந்தச் செயல்முறையைத் தானியக்கமாக்கலாம்.
முடிவுரை
இது ஒரு சரியான தீர்வு அல்ல - "பயனர் தவறு செய்துவிட்டார்" என்ற சிக்கலுக்குச் சரியான தீர்வு எதுவும் இல்லை. இருப்பினும், இந்த வகையான சரிபார்ப்புகளைப் பயன்படுத்துவது குறைந்தபட்சம் சில தவறுகளையாவது தடுக்கலாம். கணக்குகளை முடக்கும் திறன், ஆபத்தானது என்றாலும், திருடப்பட்ட நிதியை ஹேக்கருக்கு மறுப்பதன் மூலம் சில ஹேக்குகளின் சேதத்தைக் கட்டுப்படுத்தப் பயன்படுத்தப்படலாம்.
எனது மேலும் பல பணிகளை இங்கே காணவும் (opens in a new tab).
பக்கம் கடைசியாகப் புதுப்பிக்கப்பட்டது: 3 மார்ச், 2026