ప్రధాన కంటెంట్‌కు దాటవేయి

సేఫ్టీ రైల్స్‌తో ERC-20

erc-20
ప్రారంభ స్థాయి
ఓరి పోమెరాంట్జ్
15 ఆగస్టు, 2022
7 నిమిషాల పఠనం

పరిచయం

ఎథీరియం గురించిన గొప్ప విషయాలలో ఒకటి ఏమిటంటే, మీ లావాదేవీలను సవరించగల లేదా రద్దు చేయగల కేంద్ర అధికారం ఏదీ లేదు. ఎథీరియంతో ఉన్న పెద్ద సమస్యలలో ఒకటి ఏమిటంటే, వినియోగదారుల తప్పులను లేదా చట్టవిరుద్ధమైన లావాదేవీలను రద్దు చేసే అధికారం ఉన్న కేంద్ర అధికారం ఏదీ లేదు. ఈ ఆర్టికల్‌లో మీరు ERC-20 టోకెన్‌లతో వినియోగదారులు చేసే కొన్ని సాధారణ తప్పుల గురించి, అలాగే ఆ తప్పులను నివారించడంలో వినియోగదారులకు సహాయపడే లేదా కేంద్ర అధికారానికి కొంత అధికారాన్ని (ఉదాహరణకు ఖాతాలను స్తంభింపజేయడానికి) ఇచ్చే ERC-20 కాంట్రాక్ట్‌లను ఎలా సృష్టించాలో నేర్చుకుంటారు.

మేము ఓపెన్‌జెప్పెలిన్ ERC-20 టోకెన్ కాంట్రాక్ట్ (opens in a new tab)ని ఉపయోగిస్తున్నప్పటికీ, ఈ ఆర్టికల్ దానిని చాలా వివరంగా వివరించదని గమనించండి. మీరు ఈ సమాచారాన్ని ఇక్కడ కనుగొనవచ్చు.

మీరు పూర్తి సోర్స్ కోడ్‌ను చూడాలనుకుంటే:

  1. Remix IDE (opens in a new tab)ని తెరవండి.
  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 కాంట్రాక్ట్ అవసరం. ఈ ఆర్టికల్‌లో మేము ఓపెన్‌జెప్పెలిన్ కాంట్రాక్ట్స్ విజార్డ్ (opens in a new tab)ని ఉపయోగిస్తాము. దానిని మరొక బ్రౌజర్‌లో తెరిచి, ఈ సూచనలను అనుసరించండి:

  1. ERC20ని ఎంచుకోండి.

  2. ఈ సెట్టింగ్‌లను నమోదు చేయండి:

    పరామితివిలువ
    పేరుSafetyRailsToken
    చిహ్నంSAFE
    ప్రీమింట్1000
    ఫీచర్లుఏదీ లేదు
    యాక్సెస్ కంట్రోల్Ownable
    అప్‌గ్రేడబిలిటీఏదీ లేదు
  3. పైకి స్క్రోల్ చేసి, Open in Remix (Remix కోసం) లేదా వేరే వాతావరణాన్ని ఉపయోగించడానికి Download క్లిక్ చేయండి. మీరు Remix ఉపయోగిస్తున్నారని నేను అనుకుంటున్నాను, మీరు వేరే ఏదైనా ఉపయోగిస్తే తగిన మార్పులు చేయండి.

  4. ఇప్పుడు మనకు పూర్తిగా పనిచేసే ERC-20 కాంట్రాక్ట్ ఉంది. దిగుమతి చేసిన కోడ్‌ను చూడటానికి మీరు .deps > npmని విస్తరించవచ్చు.

  5. ఇది ERC-20 కాంట్రాక్ట్‌గా పనిచేస్తుందో లేదో చూడటానికి కాంట్రాక్ట్‌ను కంపైల్ చేయండి, డిప్లాయ్ చేయండి మరియు ప్లే చేయండి. మీరు Remix ఎలా ఉపయోగించాలో నేర్చుకోవాలనుకుంటే, ఈ ట్యుటోరియల్‌ని ఉపయోగించండి (opens in a new tab).

సాధారణ తప్పులు

తప్పులు

వినియోగదారులు కొన్నిసార్లు తప్పు చిరునామాకు టోకెన్‌లను పంపుతారు. వారు ఏమి చేయాలనుకుంటున్నారో తెలుసుకోవడానికి మేము వారి మనస్సులను చదవలేనప్పటికీ, ఎక్కువగా జరిగే మరియు సులభంగా గుర్తించగల రెండు రకాల లోపాలు ఉన్నాయి:

  1. కాంట్రాక్ట్ యొక్క స్వంత చిరునామాకు టోకెన్‌లను పంపడం. ఉదాహరణకు, Optimism యొక్క OP టోకెన్ (opens in a new tab) రెండు నెలల కంటే తక్కువ వ్యవధిలో 120,000 కంటే ఎక్కువ (opens in a new tab) OP టోకెన్‌లను కూడబెట్టుకోగలిగింది. ఇది ప్రజలు కోల్పోయిన గణనీయమైన సంపదను సూచిస్తుంది.

  2. ఖాళీ చిరునామాకు టోకెన్‌లను పంపడం, అంటే బాహ్యంగా స్వంతమైన ఖాతా (externally owned account - EOA) లేదా స్మార్ట్ కాంట్రాక్ట్కి సంబంధించని చిరునామా. ఇది ఎంత తరచుగా జరుగుతుందనే దానిపై నా వద్ద గణాంకాలు లేనప్పటికీ, ఒక సంఘటన వల్ల 20,000,000 టోకెన్‌లు నష్టపోయి ఉండవచ్చు (opens in a new tab).

బదిలీలను నిరోధించడం

ఓపెన్‌జెప్పెలిన్ ERC-20 కాంట్రాక్ట్‌లో టోకెన్ బదిలీ చేయబడటానికి ముందు పిలువబడే ఒక హుక్, _beforeTokenTransfer (opens in a new tab) ఉంటుంది. అప్రమేయంగా ఈ హుక్ ఏమీ చేయదు, కానీ ఏదైనా సమస్య ఉంటే రివర్ట్ చేసే తనిఖీల వంటి మన స్వంత కార్యాచరణను మనం దానికి జోడించవచ్చు.

హుక్‌ని ఉపయోగించడానికి, కన్స్ట్రక్టర్ తర్వాత ఈ ఫంక్షన్‌ను జోడించండి:

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

మీకు Solidity గురించి పెద్దగా పరిచయం లేకపోతే ఈ ఫంక్షన్‌లోని కొన్ని భాగాలు కొత్తగా ఉండవచ్చు:

        internal virtual

virtual కీవర్డ్ అంటే, మనం ERC20 నుండి కార్యాచరణను వారసత్వంగా పొంది, ఈ ఫంక్షన్‌ను ఓవర్‌రైడ్ చేసినట్లే, ఇతర కాంట్రాక్ట్‌లు మన నుండి వారసత్వంగా పొంది, ఈ ఫంక్షన్‌ను ఓవర్‌రైడ్ చేయగలవు.

        override(ERC20)

మేము _beforeTokenTransfer యొక్క ERC20 టోకెన్ నిర్వచనాన్ని ఓవర్‌రైడ్ (opens in a new tab) చేస్తున్నామని స్పష్టంగా పేర్కొనాలి. సాధారణంగా, భద్రతా దృక్కోణం నుండి, అవ్యక్తమైన వాటి కంటే స్పష్టమైన నిర్వచనాలు చాలా మెరుగ్గా ఉంటాయి - మీరు ఏదైనా చేశారని అది మీ కళ్ళ ముందే ఉంటే మీరు మర్చిపోలేరు. ఏ సూపర్‌క్లాస్ యొక్క _beforeTokenTransferని మనం ఓవర్‌రైడ్ చేస్తున్నామో పేర్కొనడానికి కూడా అదే కారణం.

        super._beforeTokenTransfer(from, to, amount);

ఈ లైన్ మనం వారసత్వంగా పొందిన కాంట్రాక్ట్ లేదా కాంట్రాక్ట్‌ల యొక్క _beforeTokenTransfer ఫంక్షన్‌ను పిలుస్తుంది. ఈ సందర్భంలో, అది కేవలం ERC20 మాత్రమే, Ownableకి ఈ హుక్ లేదు. ప్రస్తుతం ERC20._beforeTokenTransfer ఏమీ చేయనప్పటికీ, భవిష్యత్తులో కార్యాచరణ జోడించబడితే (మరియు డిప్లాయ్‌మెంట్ తర్వాత కాంట్రాక్ట్‌లు మారవు కాబట్టి, కాంట్రాక్ట్‌ను మళ్లీ డిప్లాయ్ చేయాలని మేము నిర్ణయించుకుంటే) మేము దానిని పిలుస్తాము.

అవసరాలను కోడింగ్ చేయడం

మేము ఫంక్షన్‌కు ఈ అవసరాలను జోడించాలనుకుంటున్నాము:

  • to చిరునామా address(this)కి సమానంగా ఉండకూడదు, ఇది ERC-20 కాంట్రాక్ట్ యొక్క స్వంత చిరునామా.
  • to చిరునామా ఖాళీగా ఉండకూడదు, ఇది ఈ క్రింది వాటిలో ఒకటి అయి ఉండాలి:
    • బాహ్యంగా స్వంతమైన ఖాతా (EOA). ఒక చిరునామా EOA అవునా కాదా అని మనం నేరుగా తనిఖీ చేయలేము, కానీ మనం చిరునామా యొక్క ETH బ్యాలెన్స్‌ని తనిఖీ చేయవచ్చు. EOAలు ఇకపై ఉపయోగించబడకపోయినా, దాదాపు ఎల్లప్పుడూ బ్యాలెన్స్‌ను కలిగి ఉంటాయి - వాటిని చివరి Wei వరకు క్లియర్ చేయడం కష్టం.
    • స్మార్ట్ కాంట్రాక్ట్. ఒక చిరునామా స్మార్ట్ కాంట్రాక్ట్ అవునా కాదా అని పరీక్షించడం కొంచెం కష్టం. బాహ్య కోడ్ పొడవును తనిఖీ చేసే EXTCODESIZE (opens in a new tab) అనే ఆప్‌కోడ్ ఉంది, కానీ ఇది నేరుగా Solidityలో అందుబాటులో లేదు. దీని కోసం మనం EVM అసెంబ్లీ అయిన Yul (opens in a new tab)ని ఉపయోగించాలి. మనం Solidity నుండి ఉపయోగించగల ఇతర విలువలు ఉన్నాయి (<address>.code మరియు <address>.codehash (opens in a new tab)), కానీ వాటికి ఎక్కువ ఖర్చవుతుంది.

కొత్త కోడ్‌ను లైన్ బై లైన్ పరిశీలిద్దాం:

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

ఇది మొదటి అవసరం, to మరియు this(address) ఒకేలా లేవని తనిఖీ చేయండి.

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

ఒక చిరునామా కాంట్రాక్ట్ అవునా కాదా అని మనం ఈ విధంగా తనిఖీ చేస్తాము. మనం Yul నుండి నేరుగా అవుట్‌పుట్‌ను స్వీకరించలేము, కాబట్టి దానికి బదులుగా ఫలితాన్ని ఉంచడానికి ఒక వేరియబుల్‌ను నిర్వచిస్తాము (ఈ సందర్భంలో isToContract). Yul పనిచేసే విధానం ఏమిటంటే, ప్రతి ఆప్‌కోడ్ ఒక ఫంక్షన్‌గా పరిగణించబడుతుంది. కాబట్టి ముందుగా మనం కాంట్రాక్ట్ పరిమాణాన్ని పొందడానికి EXTCODESIZE (opens in a new tab)ని పిలుస్తాము, ఆపై అది సున్నా కాదని తనిఖీ చేయడానికి GT (opens in a new tab)ని ఉపయోగిస్తాము (మనం అన్‌సైన్డ్ ఇంటిజర్‌లతో వ్యవహరిస్తున్నాము, కాబట్టి ఇది ఖచ్చితంగా నెగటివ్ కాలేదు). ఆ తర్వాత మనం ఫలితాన్ని isToContractకి వ్రాస్తాము.

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

మరియు చివరగా, ఖాళీ చిరునామాల కోసం అసలు తనిఖీని కలిగి ఉన్నాము.

అడ్మినిస్ట్రేటివ్ యాక్సెస్

కొన్నిసార్లు తప్పులను రద్దు చేయగల అడ్మినిస్ట్రేటర్ ఉండటం ఉపయోగకరంగా ఉంటుంది. దుర్వినియోగం అయ్యే అవకాశాన్ని తగ్గించడానికి, ఈ అడ్మినిస్ట్రేటర్ ఒక మల్టీసిగ్ (opens in a new tab) కావచ్చు, తద్వారా ఒక చర్యపై బహుళ వ్యక్తులు అంగీకరించాల్సి ఉంటుంది. ఈ ఆర్టికల్‌లో మనం రెండు అడ్మినిస్ట్రేటివ్ ఫీచర్‌లను కలిగి ఉంటాము:

  1. ఖాతాలను స్తంభింపజేయడం మరియు అన్‌ఫ్రీజ్ చేయడం. ఉదాహరణకు, ఖాతా రాజీపడినప్పుడు ఇది ఉపయోగకరంగా ఉంటుంది.

  2. ఆస్తుల క్లీనప్.

    కొన్నిసార్లు మోసగాళ్లు చట్టబద్ధతను పొందడానికి నిజమైన టోకెన్ కాంట్రాక్ట్‌కు మోసపూరిత టోకెన్‌లను పంపుతారు. ఉదాహరణకు, ఇక్కడ చూడండి (opens in a new tab). చట్టబద్ధమైన ERC-20 కాంట్రాక్ట్ 0x4200....0042 (opens in a new tab). దానిలా నటించే స్కామ్ 0x234....bbe (opens in a new tab).

    ప్రజలు పొరపాటున మన కాంట్రాక్ట్‌కు చట్టబద్ధమైన ERC-20 టోకెన్‌లను పంపే అవకాశం కూడా ఉంది, వాటిని బయటకు తీయడానికి ఒక మార్గం ఉండాలని కోరుకోవడానికి ఇది మరొక కారణం.

అడ్మినిస్ట్రేటివ్ యాక్సెస్‌ను ప్రారంభించడానికి ఓపెన్‌జెప్పెలిన్ రెండు యంత్రాంగాలను అందిస్తుంది:

సరళత కోసం, ఈ ఆర్టికల్‌లో మనం Ownableని ఉపయోగిస్తాము.

కాంట్రాక్ట్‌లను స్తంభింపజేయడం మరియు అన్‌ఫ్రీజ్ చేయడం

కాంట్రాక్ట్‌లను స్తంభింపజేయడం మరియు అన్‌ఫ్రీజ్ చేయడానికి అనేక మార్పులు అవసరం:

  • ఏ చిరునామాలు స్తంభింపజేయబడ్డాయో ట్రాక్ చేయడానికి చిరునామాల నుండి బూలియన్‌ల (opens in a new tab)కు ఒక మ్యాపింగ్ (opens in a new tab). అన్ని విలువలు ప్రారంభంలో సున్నాగా ఉంటాయి, బూలియన్ విలువల కోసం ఇది ఫాల్స్‌గా అన్వయించబడుతుంది. అప్రమేయంగా ఖాతాలు స్తంభింపజేయబడవు కాబట్టి మనకు కావాల్సింది ఇదే.

        mapping(address => bool) public frozenAccounts;
    
  • ఖాతా స్తంభింపజేయబడినప్పుడు లేదా అన్‌ఫ్రీజ్ చేయబడినప్పుడు ఆసక్తి ఉన్న ఎవరికైనా తెలియజేయడానికి ఈవెంట్‌లు (opens in a new tab). సాంకేతికంగా చెప్పాలంటే ఈ చర్యలకు ఈవెంట్‌లు అవసరం లేదు, కానీ ఆఫ్‌చైన్ కోడ్ ఈ ఈవెంట్‌లను వినడానికి మరియు ఏమి జరుగుతుందో తెలుసుకోవడానికి ఇది సహాయపడుతుంది. వేరొకరికి సంబంధించిన ఏదైనా జరిగినప్పుడు స్మార్ట్ కాంట్రాక్ట్ వాటిని విడుదల చేయడం మంచి పద్ధతిగా పరిగణించబడుతుంది.

    ఈవెంట్‌లు ఇండెక్స్ చేయబడతాయి కాబట్టి ఖాతా స్తంభింపజేయబడిన లేదా అన్‌ఫ్రీజ్ చేయబడిన అన్ని సమయాల కోసం వెతకడం సాధ్యమవుతుంది.

      // ఖాతాలు ఫ్రీజ్ లేదా అన్‌ఫ్రీజ్ చేయబడినప్పుడు
      event AccountFrozen(address indexed _addr);
      event AccountThawed(address indexed _addr);
    
  • ఖాతాలను స్తంభింపజేయడానికి మరియు అన్‌ఫ్రీజ్ చేయడానికి ఫంక్షన్‌లు. ఈ రెండు ఫంక్షన్‌లు దాదాపు ఒకేలా ఉంటాయి, కాబట్టి మనం ఫ్రీజ్ ఫంక్షన్‌ను మాత్రమే పరిశీలిస్తాము.

        function freezeAccount(address addr)
          public
          onlyOwner
    

    public (opens in a new tab) అని గుర్తు పెట్టబడిన ఫంక్షన్‌లను ఇతర స్మార్ట్ కాంట్రాక్ట్‌ల నుండి లేదా నేరుగా లావాదేవీ ద్వారా పిలవవచ్చు.

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

    ఖాతా ఇప్పటికే స్తంభింపజేయబడి ఉంటే, రివర్ట్ చేయండి. లేకపోతే, దానిని స్తంభింపజేసి, ఒక ఈవెంట్‌ను emit చేయండి.

  • స్తంభింపజేసిన ఖాతా నుండి డబ్బు తరలించబడకుండా నిరోధించడానికి _beforeTokenTransferని మార్చండి. స్తంభింపజేసిన ఖాతాలోకి డబ్బును ఇంకా బదిలీ చేయవచ్చని గమనించండి.

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

ఆస్తుల క్లీనప్

ఈ కాంట్రాక్ట్ కలిగి ఉన్న ERC-20 టోకెన్‌లను విడుదల చేయడానికి, అవి చెందిన టోకెన్ కాంట్రాక్ట్‌పై మనం ఒక ఫంక్షన్‌ను పిలవాలి, అది transfer (opens in a new tab) లేదా approve (opens in a new tab) కావచ్చు. ఈ సందర్భంలో అలవెన్సులపై గ్యాస్‌ను వృధా చేయడంలో అర్థం లేదు, మనం నేరుగా బదిలీ చేయవచ్చు.

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

మనం చిరునామాను స్వీకరించినప్పుడు కాంట్రాక్ట్ కోసం ఆబ్జెక్ట్‌ను సృష్టించడానికి ఇది సింటాక్స్. సోర్స్ కోడ్‌లో భాగంగా ERC20 టోకెన్‌ల కోసం మనకు నిర్వచనం ఉన్నందున మనం దీన్ని చేయవచ్చు (లైన్ 4 చూడండి), మరియు ఆ ఫైల్‌లో ఓపెన్‌జెప్పెలిన్ ERC-20 కాంట్రాక్ట్ కోసం ఇంటర్‌ఫేస్ అయిన IERC20 కోసం నిర్వచనం (opens in a new tab) ఉంటుంది.

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

ఇది క్లీనప్ ఫంక్షన్, కాబట్టి మనం ఎలాంటి టోకెన్‌లను వదిలివేయకూడదనుకుంటున్నాము. వినియోగదారు నుండి మాన్యువల్‌గా బ్యాలెన్స్‌ను పొందే బదులు, మనం ఈ ప్రక్రియను ఆటోమేట్ చేయవచ్చు.

ముగింపు

ఇది సరైన పరిష్కారం కాదు - "వినియోగదారు తప్పు చేశారు" అనే సమస్యకు సరైన పరిష్కారం లేదు. అయితే, ఈ రకమైన తనిఖీలను ఉపయోగించడం కనీసం కొన్ని తప్పులను నివారించగలదు. ఖాతాలను స్తంభింపజేసే సామర్థ్యం ప్రమాదకరమైనప్పటికీ, దొంగిలించబడిన నిధులను హ్యాకర్‌కు నిరాకరించడం ద్వారా కొన్ని హ్యాక్‌ల నష్టాన్ని పరిమితం చేయడానికి దీనిని ఉపయోగించవచ్చు.

నా మరిన్ని పనుల కోసం ఇక్కడ చూడండి (opens in a new tab).