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

பாதுகாப்பு வளையங்களுடன் ERC-20

erc-20
தொடக்கநிலை
ஓரி பொமரன்ட்ஸ்
15 ஆகஸ்ட், 2022
7 நிமிட வாசிப்பு

அறிமுகம்

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

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

முழுமையான மூலக் குறியீட்டை நீங்கள் பார்க்க விரும்பினால்:

  1. Remix IDE (புதிய தாவலில் திறக்கப்படும்)-ஐத் திறக்கவும்.
  2. குளோன் GitHub ஐகானைக் கிளிக் செய்யவும் (clone github icon).
  3. https://github.com/qbzzt/20220815-erc20-safety-rails என்ற GitHub களஞ்சியத்தைக் குளோன் செய்யவும்.
  4. contracts > erc20-safety-rails.sol என்பதைத் திறக்கவும்.

ஒரு ERC-20 ஒப்பந்தத்தை உருவாக்குதல்

பாதுகாப்பு வளையச் செயல்பாட்டைச் சேர்ப்பதற்கு முன், நமக்கு ஒரு ERC-20 ஒப்பந்தம் தேவை. இந்தக் கட்டுரையில் நாம் ஓப்பன்செப்பெலின் ஒப்பந்த வழிகாட்டியைப் (OpenZeppelin Contracts Wizard) (புதிய தாவலில் திறக்கப்படும்) பயன்படுத்துவோம். அதை வேறொரு உலாவியில் திறந்து, இந்த வழிமுறைகளைப் பின்பற்றவும்:

  1. ERC20 என்பதைத் தேர்ந்தெடுக்கவும்.

  2. இந்த அமைப்புகளை உள்ளிடவும்:

    அளவுருமதிப்பு
    பெயர்SafetyRailsToken
    குறியீடுSAFE
    முன்-அச்சிடல் (Premint)1000
    அம்சங்கள்ஏதுமில்லை
    அணுகல் கட்டுப்பாடுOwnable
    மேம்படுத்தும் திறன்ஏதுமில்லை
  3. மேலே ஸ்க்ரோல் செய்து, Open in Remix (Remix-க்கு) என்பதைக் கிளிக் செய்யவும் அல்லது வேறு சூழலைப் பயன்படுத்த Download என்பதைக் கிளிக் செய்யவும். நீங்கள் Remix-ஐப் பயன்படுத்துகிறீர்கள் என்று நான் கருதுகிறேன், நீங்கள் வேறு எதையாவது பயன்படுத்தினால் அதற்கேற்ப மாற்றங்களைச் செய்யுங்கள்.

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

  5. இது ஒரு ERC-20 ஒப்பந்தமாகச் செயல்படுகிறதா என்பதைப் பார்க்க, ஒப்பந்தத்தைத் தொகுத்து, நிலைநிறுத்தி, அதனுடன் விளையாடிப் பாருங்கள். Remix-ஐ எவ்வாறு பயன்படுத்துவது என்பதை நீங்கள் அறிய விரும்பினால், இந்த வழிகாட்டியைப் பயன்படுத்தவும் (புதிய தாவலில் திறக்கப்படும்).

பொதுவான தவறுகள்

தவறுகள்

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

  1. ஒப்பந்தத்தின் சொந்த முகவரிக்கே வில்லைகளை அனுப்புவது. உதாரணமாக, ஆப்டிமிசத்தின் OP வில்லை (புதிய தாவலில் திறக்கப்படும்) இரண்டு மாதங்களுக்குள் 120,000-க்கும் மேற்பட்ட (புதிய தாவலில் திறக்கப்படும்) OP வில்லைகளைக் குவித்துள்ளது. இது மக்கள் இழந்திருக்கக்கூடிய கணிசமான அளவு செல்வத்தைக் குறிக்கிறது.

  2. வெளிப்புறமாகச் சொந்தமான கணக்கு (externally owned account) அல்லது திறன் ஒப்பந்தத்திற்கு தொடர்பில்லாத வெற்று முகவரிக்கு வில்லைகளை அனுப்புவது. இது எவ்வளவு அடிக்கடி நிகழ்கிறது என்பதற்கான புள்ளிவிவரங்கள் என்னிடம் இல்லை என்றாலும், ஒரு சம்பவத்தில் 20,000,000 வில்லைகள் இழக்கப்பட்டிருக்கலாம் (புதிய தாவலில் திறக்கப்படும்).

பரிமாற்றங்களைத் தடுத்தல்

ஓப்பன்செப்பெலின் ERC-20 ஒப்பந்தத்தில் ஒரு கொக்கி (hook), _beforeTokenTransfer (புதிய தாவலில் திறக்கப்படும்) உள்ளது, இது ஒரு வில்லை பரிமாற்றம் செய்யப்படுவதற்கு முன்பு அழைக்கப்படுகிறது. இயல்பாக இந்தக் கொக்கி எதையும் செய்யாது, ஆனால் ஏதேனும் சிக்கல் இருந்தால் மீளமைக்கும் (revert) சரிபார்ப்புகள் போன்ற நமது சொந்தச் செயல்பாடுகளை இதில் இணைக்கலாம்.

இந்தக் கொக்கியைப் பயன்படுத்த, ஆக்கிக்குப் (constructor) பிறகு இந்தச் சார்பைச் சேர்க்கவும்:

    function _beforeTokenTransfer(address from, address to, uint256 amount)
        internal virtual
        override(ERC20)
    {
        super._beforeTokenTransfer(from, to, amount);
    }

நீங்கள் Solidity-ஐப் பற்றி அதிகம் அறிந்திருக்கவில்லை என்றால், இந்தச் சார்பின் சில பகுதிகள் புதியதாக இருக்கலாம்:

        internal virtual

virtual என்ற சிறப்புச்சொல், நாம் ERC20 என்பதிலிருந்து செயல்பாட்டைப் பெற்று இந்தச் சார்பை மேலெழுதியது (override) போலவே, பிற ஒப்பந்தங்களும் நம்மிடமிருந்து பெற்று இந்தச் சார்பை மேலெழுதலாம் என்பதைக் குறிக்கிறது.

        override(ERC20)

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

        super._beforeTokenTransfer(from, to, amount);

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

தேவைகளைக் குறியீடாக்குதல்

இந்தத் தேவைகளைச் சார்பில் சேர்க்க விரும்புகிறோம்:

  • to முகவரியானது, ERC-20 ஒப்பந்தத்தின் சொந்த முகவரியான address(this)-க்குச் சமமாக இருக்க முடியாது.
  • to முகவரி காலியாக இருக்க முடியாது, அது பின்வருவனவற்றில் ஒன்றாக இருக்க வேண்டும்:
    • வெளிப்புறமாகச் சொந்தமான கணக்கு (EOA). ஒரு முகவரி EOA தானா என்பதை நம்மால் நேரடியாகச் சரிபார்க்க முடியாது, ஆனால் ஒரு முகவரியின் ETH இருப்பைச் சரிபார்க்கலாம். EOA-க்கள் இனி பயன்படுத்தப்படாவிட்டாலும், அவை எப்போதும் இருப்பைக் கொண்டிருக்கும் - அவற்றை கடைசி Wei வரை அழிப்பது கடினம்.
    • ஒரு திறன் ஒப்பந்தம். ஒரு முகவரி திறன் ஒப்பந்தமா என்பதைச் சோதிப்பது சற்று கடினம். வெளிப்புறக் குறியீட்டின் நீளத்தைச் சரிபார்க்கும் EXTCODESIZE (புதிய தாவலில் திறக்கப்படும்) எனப்படும் செயல்பாட்டுக் குறியீடு (opcode) உள்ளது, ஆனால் அது நேரடியாக Solidity-யில் கிடைக்காது. அதற்கு EVM அசெம்பிளியான Yul (புதிய தாவலில் திறக்கப்படும்)-ஐப் பயன்படுத்த வேண்டும். Solidity-யிலிருந்து நாம் பயன்படுத்தக்கூடிய பிற மதிப்புகளும் உள்ளன (<address>.code மற்றும் <address>.codehash (புதிய தாவலில் திறக்கப்படும்)), ஆனால் அவற்றுக்கு அதிகச் செலவாகும்.

புதிய குறியீட்டை வரி வரியாகப் பார்ப்போம்:

        require(to != address(this), "Can't send tokens to the contract address");

இது முதல் தேவை, to மற்றும் this(address) ஆகியவை ஒன்றல்ல என்பதைச் சரிபார்க்கவும்.

        bool isToContract;
        assembly {
           isToContract := gt(extcodesize(to), 0)
        }

ஒரு முகவரி ஒப்பந்தமா என்பதை இப்படித்தான் சரிபார்க்கிறோம். Yul-லிருந்து நேரடியாக வெளியீட்டைப் பெற முடியாது, எனவே முடிவை வைத்திருக்க ஒரு மாறியை (variable) வரையறுக்கிறோம் (இந்த நிலையில் isToContract). Yul செயல்படும் விதம் என்னவென்றால், ஒவ்வொரு செயல்பாட்டுக் குறியீடும் ஒரு சார்பாகக் கருதப்படுகிறது. எனவே முதலில் ஒப்பந்தத்தின் அளவைப் பெற EXTCODESIZE (புதிய தாவலில் திறக்கப்படும்)-ஐ அழைக்கிறோம், பின்னர் அது பூஜ்ஜியமாக இல்லை என்பதைச் சரிபார்க்க GT (புதிய தாவலில் திறக்கப்படும்)-ஐப் பயன்படுத்துகிறோம் (நாம் குறியற்ற முழு எண்களைக் (unsigned integers) கையாளுகிறோம், எனவே நிச்சயமாக அது எதிர்மறையாக இருக்க முடியாது). பின்னர் முடிவை isToContract-ல் எழுதுகிறோம்.

        require(to.balance != 0 || isToContract, "Can't send tokens to an empty address");

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

நிர்வாக அணுகல்

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

  1. கணக்குகளை முடக்குதல் மற்றும் முடக்கத்தை நீக்குதல். உதாரணமாக, ஒரு கணக்கு சமரசம் செய்யப்பட்டிருக்கலாம் (compromised) என்ற நிலையில் இது பயனுள்ளதாக இருக்கும்.

  2. சொத்துச் சுத்திகரிப்பு.

    சில நேரங்களில் மோசடி செய்பவர்கள் சட்டபூர்வமான தன்மையைப் பெற உண்மையான வில்லை ஒப்பந்தத்திற்கு மோசடியான வில்லைகளை அனுப்புகிறார்கள். உதாரணமாக, இங்கே பார்க்கவும் (புதிய தாவலில் திறக்கப்படும்). சட்டபூர்வமான ERC-20 ஒப்பந்தம் 0x4200....0042 (புதிய தாவலில் திறக்கப்படும்) ஆகும். அதைப் போல நடிக்கும் மோசடி 0x234....bbe (புதிய தாவலில் திறக்கப்படும்) ஆகும்.

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

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

எளிமைக்காக, இந்தக் கட்டுரையில் நாம் Ownable-ஐப் பயன்படுத்துகிறோம்.

ஒப்பந்தங்களை முடக்குதல் மற்றும் முடக்கத்தை நீக்குதல்

ஒப்பந்தங்களை முடக்குவதற்கும் முடக்கத்தை நீக்குவதற்கும் பல மாற்றங்கள் தேவை:

  • எந்த முகவரிகள் முடக்கப்பட்டுள்ளன என்பதைக் கண்காணிக்க, முகவரிகளிலிருந்து பூலியன்களுக்கு (booleans) (புதிய தாவலில் திறக்கப்படும்) ஒரு மேப்பிங் (mapping) (புதிய தாவலில் திறக்கப்படும்). அனைத்து மதிப்புகளும் ஆரம்பத்தில் பூஜ்ஜியமாக இருக்கும், இது பூலியன் மதிப்புகளுக்குத் தவறு (false) எனப் பொருள் கொள்ளப்படுகிறது. இயல்பாகக் கணக்குகள் முடக்கப்படாததால், இதுவே நமக்குத் தேவை.

        mapping(address => bool) public frozenAccounts;
    
  • ஒரு கணக்கு முடக்கப்படும்போது அல்லது முடக்கம் நீக்கப்படும்போது ஆர்வமுள்ள எவருக்கும் தெரிவிக்க நிகழ்வுகள் (Events) (புதிய தாவலில் திறக்கப்படும்). தொழில்நுட்ப ரீதியாகப் பார்த்தால் இந்தச் செயல்களுக்கு நிகழ்வுகள் தேவையில்லை, ஆனால் புறச்சங்கிலிக் (offchain) (புதிய தாவலில் திறக்கப்படும்) குறியீடு இந்த நிகழ்வுகளைக் கேட்டு என்ன நடக்கிறது என்பதை அறிய இது உதவுகிறது. வேறொருவருக்குத் தொடர்புடைய ஏதாவது நடக்கும்போது திறன் ஒப்பந்தம் அவற்றை வெளியிடுவது நல்ல பழக்கமாகக் கருதப்படுகிறது.

    நிகழ்வுகள் அட்டவணைப்படுத்தப்பட்டுள்ளன (indexed), எனவே ஒரு கணக்கு முடக்கப்பட்ட அல்லது முடக்கம் நீக்கப்பட்ட அனைத்து நேரங்களையும் தேட முடியும்.

      // கணக்குகள் முடக்கப்படும் அல்லது முடக்கம் நீக்கப்படும் போது
      event AccountFrozen(address indexed _addr);
      event AccountThawed(address indexed _addr);
    
  • கணக்குகளை முடக்குவதற்கும் முடக்கத்தை நீக்குவதற்குமான சார்புகள். இந்த இரண்டு சார்புகளும் ஏறக்குறைய ஒரே மாதிரியானவை, எனவே முடக்கும் சார்பை மட்டும் பார்ப்போம்.

        function freezeAccount(address addr)
          public
          onlyOwner
    

    public (புதிய தாவலில் திறக்கப்படும்) எனக் குறிக்கப்பட்ட சார்புகளை மற்ற திறன் ஒப்பந்தங்களிலிருந்தோ அல்லது நேரடியாக ஒரு பரிவர்த்தனை மூலமாகவோ அழைக்கலாம்.

      {
          require(!frozenAccounts[addr], "Account already frozen");
          frozenAccounts[addr] = true;
          emit AccountFrozen(addr);
      }  // freezeAccount
    

    கணக்கு ஏற்கனவே முடக்கப்பட்டிருந்தால், மீளமைக்கவும் (revert). இல்லையெனில், அதை முடக்கி ஒரு நிகழ்வை emit செய்யவும்.

  • முடக்கப்பட்ட கணக்கிலிருந்து பணம் நகர்த்தப்படுவதைத் தடுக்க _beforeTokenTransfer-ஐ மாற்றவும். முடக்கப்பட்ட கணக்கிற்குள் பணத்தை இன்னும் பரிமாற்றம் செய்ய முடியும் என்பதை நினைவில் கொள்ளவும்.

         require(!frozenAccounts[from], "The account is frozen");
    

சொத்துச் சுத்திகரிப்பு

இந்த ஒப்பந்தத்தில் உள்ள ERC-20 வில்லைகளை வெளியிட, அவை எந்த வில்லை ஒப்பந்தத்தைச் சேர்ந்தவையோ அந்த ஒப்பந்தத்தில் transfer (புதிய தாவலில் திறக்கப்படும்) அல்லது approve (புதிய தாவலில் திறக்கப்படும்) என்ற சார்பை நாம் அழைக்க வேண்டும். இந்த நிலையில் கொடுப்பனவுகளுக்காக (allowances) எரிவாயுவை (gas) வீணாக்குவதில் எந்த அர்த்தமும் இல்லை, நாம் நேரடியாகவே பரிமாற்றம் செய்யலாம்.

    function cleanupERC20(
        address erc20,
        address dest
    )
        public
        onlyOwner
    {
        IERC20 token = IERC20(erc20);

முகவரியைப் பெறும்போது ஒரு ஒப்பந்தத்திற்கான பொருளை (object) உருவாக்குவதற்கான தொடரியல் (syntax) இதுவாகும். மூலக் குறியீட்டின் ஒரு பகுதியாக ERC20 வில்லைகளுக்கான வரையறை நம்மிடம் இருப்பதால் இதைச் செய்ய முடியும் (வரி 4-ஐப் பார்க்கவும்), மேலும் அந்தக் கோப்பில் ஓப்பன்செப்பெலின் ERC-20 ஒப்பந்தத்திற்கான இடைமுகமான (interface) IERC20-க்கான வரையறையும் (புதிய தாவலில் திறக்கப்படும்) அடங்கும்.

        uint balance = token.balanceOf(address(this));
        token.transfer(dest, balance);
    }

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

முடிவுரை

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

எனது மேலும் பல பணிகளை இங்கே காணவும் (புதிய தாவலில் திறக்கப்படும்).