Ruka kwenda kwenye maudhui makuu

ERC-20 yenye Kinga za Usalama

erc-20
Mwanzo
Ori Pomerantz
15 Agosti 2022
8 soma ndani ya dakika

Utangulizi

Moja ya mambo makuu kuhusu Ethereum ni kwamba hakuna mamlaka kuu inayoweza kurekebisha au kutengua miamala yako. Moja ya matatizo makuu ya Ethereum ni kwamba hakuna mamlaka kuu yenye uwezo wa kutengua makosa ya mtumiaji au miamala haramu. Katika makala haya utajifunza kuhusu baadhi ya makosa ya kawaida ambayo watumiaji hufanya na tokeni za ERC-20, pamoja na jinsi ya kuunda mikataba ya ERC-20 inayowasaidia watumiaji kuepuka makosa hayo, au inayopa mamlaka kuu nguvu fulani (kwa mfano kufungia akaunti).

Kumbuka kwamba ingawa tutatumia mkataba wa tokeni wa OpenZeppelin ERC-20 (opens in a new tab), makala haya hayaielezei kwa undani sana. Unaweza kupata maelezo haya hapa.

Ikiwa unataka kuona msimbo chanzo kamili:

  1. Fungua Remix IDE (opens in a new tab).
  2. Bofya aikoni ya kuiga ya github (aikoni ya kuiga ya github).
  3. Iga hifadhi ya github https://github.com/qbzzt/20220815-erc20-safety-rails.
  4. Fungua contracts > erc20-safety-rails.sol.

Kuunda mkataba wa ERC-20

Kabla hatujaongeza utendaji wa kinga za usalama, tunahitaji mkataba wa ERC-20. Katika makala haya tutatumia Mchawi wa Mikataba ya OpenZeppelin (opens in a new tab). Ifungue katika kivinjari kingine na ufuate maagizo haya:

  1. Chagua ERC20.

  2. Weka mipangilio hii:

    KigezoThamani
    JinaSafetyRailsToken
    AlamaSAFE
    Premint1000
    VipengeleHakuna
    Udhibiti wa UfikiajiOwnable
    Uwezo wa KuboreshaHakuna
  3. Sogeza juu na ubofye Fungua kwenye Remix (kwa Remix) au Pakua ili utumie mazingira tofauti. Nitachukulia kuwa unatumia Remix, ikiwa unatumia kitu kingine, fanya tu mabadiliko yanayofaa.

  4. Sasa tuna mkataba wa ERC-20 unaofanya kazi kikamilifu. Unaweza kupanua .deps > npm ili kuona msimbo ulioingizwa.

  5. Kusanya, sambaza, na cheza na mkataba ili kuona kuwa inafanya kazi kama mkataba wa ERC-20. Ikiwa unahitaji kujifunza jinsi ya kutumia Remix, tumia mafunzo haya (opens in a new tab).

Makosa ya kawaida

Makosa

Wakati mwingine watumiaji hutuma tokeni kwenye anwani isiyo sahihi. Ingawa hatuwezi kusoma akili zao kujua walichokusudia kufanya, kuna aina mbili za makosa yanayotokea sana na ni rahisi kugundua:

  1. Kutuma tokeni kwenye anwani ya mkataba wenyewe. Kwa mfano, tokeni ya OP ya Optimism (opens in a new tab) iliweza kukusanya zaidi ya 120,000 (opens in a new tab) tokeni za OP katika chini ya miezi miwili. Hii inawakilisha kiasi kikubwa cha mali ambayo inawezekana watu wameipoteza tu.

  2. Kutuma tokeni kwenye anwani tupu, ile ambayo haiendani na akaunti inayomilikiwa nje au mkataba-erevu. Ingawa sina takwimu za mara ngapi hili hutokea, tukio moja lingeweza kugharimu tokeni 20,000,000 (opens in a new tab).

Kuzuia uhamisho

Mkataba wa OpenZeppelin ERC-20 unajumuisha kiunganishi, _beforeTokenTransfer (opens in a new tab), ambacho huitwa kabla ya tokeni kuhamishwa. Kwa chaguo-msingi kiunganishi hiki hakifanyi chochote, lakini tunaweza kuweka utendaji wetu wenyewe juu yake, kama vile ukaguzi unaorejesha nyuma ikiwa kuna tatizo.

Ili kutumia kiunganishi, ongeza chaguo hili la kukokotoa baada ya kiunda:

1 function _beforeTokenTransfer(address from, address to, uint256 amount)
2 internal virtual
3 override(ERC20)
4 {
5 super._beforeTokenTransfer(from, to, amount);
6 }

Baadhi ya sehemu za chaguo hili la kukokotoa zinaweza kuwa mpya ikiwa huna uzoefu sana na Solidity:

1 internal virtual

Neno kuu virtual linamaanisha kuwa kama vile tulivyorithi utendaji kutoka ERC20 na kubatilisha chaguo hili la kukokotoa, mikataba mingine inaweza kurithi kutoka kwetu na kubatilisha chaguo hili la kukokotoa.

1 override(ERC20)

Lazima tubainishe waziwazi kwamba tunabatilisha (opens in a new tab) ufafanuzi wa tokeni ya ERC20 wa _beforeTokenTransfer. Kwa ujumla, ufafanuzi wa wazi ni bora zaidi, kutoka kwa mtazamo wa usalama, kuliko zile zilizodokezwa - huwezi kusahau kwamba umefanya kitu ikiwa kiko mbele yako. Hiyo pia ndiyo sababu tunahitaji kubainisha _beforeTokenTransfer ya daraja gani kuu tunayobatilisha.

1 super._beforeTokenTransfer(from, to, amount);

Mstari huu unaita chaguo la kukokotoa la _beforeTokenTransfer la mkataba au mikataba tuliyorithi kutoka kwayo ambayo inayo. Katika kesi hii, hiyo ni ERC20 tu, Ownable haina kiunganishi hiki. Ingawa kwa sasa ERC20._beforeTokenTransfer haifanyi chochote, tunaiita endapo utendaji utaongezwa katika siku zijazo (na kisha tunaamua kusambaza tena mkataba, kwa sababu mikataba haibadiliki baada ya usambazaji).

Kuweka msimbo wa mahitaji

Tunataka kuongeza mahitaji haya kwenye chaguo la kukokotoa:

  • Anwani ya to haiwezi kuwa sawa na address(this), anwani ya mkataba wa ERC-20 wenyewe.
  • Anwani ya to haiwezi kuwa tupu, lazima iwe ama:
    • Akaunti inayomilikiwa nje (EOA). Hatuwezi kuangalia kama anwani ni EOA moja kwa moja, lakini tunaweza kuangalia salio la ETH la anwani. EOA karibu kila mara huwa na salio, hata kama hazitumiki tena - ni vigumu kuzisafisha hadi wei ya mwisho.
    • Mkataba-erevu. Kupima kama anwani ni mkataba-erevu ni kugumu kidogo. Kuna opcode inayokagua urefu wa msimbo wa nje, inayoitwa EXTCODESIZE (opens in a new tab), lakini haipatikani moja kwa moja katika Solidity. Lazima tutumie Yul (opens in a new tab), ambayo ni mkusanyiko wa EVM, kwa ajili yake. Kuna thamani zingine ambazo tunaweza kutumia kutoka Solidity (<address>.code na <address>.codehash (opens in a new tab)), lakini zinagharimu zaidi.

Hebu tupitie msimbo mpya mstari kwa mstari:

1 require(to != address(this), "Haiwezi kutuma tokeni kwa anwani ya mkataba");

Hili ndilo sharti la kwanza, angalia kuwa to na this(address) si kitu kimoja.

1 bool isToContract;
2 assembly {
3 isToContract := gt(extcodesize(to), 0)
4 }

Hivi ndivyo tunavyoangalia kama anwani ni mkataba. Hatuwezi kupokea matokeo moja kwa moja kutoka Yul, kwa hivyo badala yake tunafafanua kigezo cha kushikilia matokeo (isToContract katika kesi hii). Jinsi Yul inavyofanya kazi ni kwamba kila opcode inachukuliwa kama chaguo la kukokotoa. Kwa hivyo kwanza tunaita EXTCODESIZE (opens in a new tab) ili kupata ukubwa wa mkataba, na kisha kutumia GT (opens in a new tab) kuangalia si sifuri (tunashughulika na nambari kamili zisizo na alama, kwa hivyo bila shaka haiwezi kuwa hasi). Kisha tunaandika matokeo kwa isToContract.

1 require(to.balance != 0 || isToContract, "Haiwezi kutuma tokeni kwa anwani tupu");

Na hatimaye, tuna ukaguzi halisi wa anwani tupu.

Ufikiaji wa kiutawala

Wakati mwingine ni muhimu kuwa na msimamizi anaye weza kutengua makosa. Ili kupunguza uwezekano wa matumizi mabaya, msimamizi huyu anaweza kuwa multisig (opens in a new tab) ili watu wengi wakubaliane juu ya hatua. Katika makala haya tutakuwa na vipengele viwili vya kiutawala:

  1. Kufungia na kufungua akaunti. Hii inaweza kuwa muhimu, kwa mfano, wakati akaunti inaweza kuathiriwa.

  2. Usafishaji wa mali.

    Wakati mwingine walaghai hutuma tokeni za ulaghai kwa mkataba halisi wa tokeni ili kupata uhalali. Kwa mfano, angalia hapa (opens in a new tab). Mkataba halali wa ERC-20 ni 0x4200....0042 (opens in a new tab). Ulaghai unaojifanya kuwa ni 0x234....bbe (opens in a new tab).

    Inawezekana pia kwamba watu hutuma tokeni halali za ERC-20 kwenye mkataba wetu kwa makosa, ambayo ni sababu nyingine ya kutaka kuwa na njia ya kuzitoa.

OpenZeppelin hutoa mifumo miwili ya kuwezesha ufikiaji wa kiutawala:

Kwa urahisi, katika makala haya tunatumia Ownable.

Kufungia na kufungua mikataba

Kufungia na kufungua mikataba kunahitaji mabadiliko kadhaa:

  • Ramani (opens in a new tab) kutoka kwa anwani hadi boolean (opens in a new tab) ili kufuatilia ni anwani gani zimefungiwa. Thamani zote awali ni sifuri, ambayo kwa thamani za boolean inatafsiriwa kama uongo. Hiki ndicho tunachotaka kwa sababu kwa chaguomsingi akaunti hazijafungiwa.

    1 mapping(address => bool) public frozenAccounts;
  • Matukio (opens in a new tab) ya kumjulisha yeyote anayevutiwa wakati akaunti imefungiwa au kufunguliwa. Kitaalamu, matukio hayahitajiki kwa vitendo hivi, lakini inasaidia msimbo wa offchain kuweza kusikiliza matukio haya na kujua kinachoendelea. Inachukuliwa kuwa ni adabu njema kwa mkataba-erevu kuzitoa wakati kitu kinachoweza kuwa muhimu kwa mtu mwingine kinapotokea.

    Matukio yameorodheshwa kwa hivyo itawezekana kutafuta kwa nyakati zote ambazo akaunti imefungiwa au kufunguliwa.

    1 // Wakati akaunti zimefungiwa au kufunguliwa
    2 event AccountFrozen(address indexed _addr);
    3 event AccountThawed(address indexed _addr);
  • Chaguo za kukokotoa za kufungia na kufungua akaunti. Chaguo hizi mbili za kukokotoa karibu zinafanana, kwa hivyo tutapitia tu chaguo la kukokotoa la kufungia.

    1 function freezeAccount(address addr)
    2 public
    3 onlyOwner

    Chaguo za kukokotoa zilizo na alama ya public (opens in a new tab) zinaweza kuitwa kutoka kwa mikataba-erevu mingine au moja kwa moja na muamala.

    1 {
    2 require(!frozenAccounts[addr], "Akaunti tayari imefungiwa");
    3 frozenAccounts[addr] = true;
    4 emit AccountFrozen(addr);
    5 } // freezeAccount

    Ikiwa akaunti tayari imefungiwa, rejesha. Vinginevyo, ifungie na toa tukio.

  • Badilisha _beforeTokenTransfer ili kuzuia pesa zisihamishwe kutoka kwa akaunti iliyofungiwa. Kumbuka kuwa pesa bado zinaweza kuhamishiwa kwenye akaunti iliyofungiwa.

    1 require(!frozenAccounts[from], "Akaunti imefungiwa");

Usafishaji wa mali

Ili kutoa tokeni za ERC-20 zinazoshikiliwa na mkataba huu tunahitaji kuita chaguo la kukokotoa kwenye mkataba wa tokeni ambao zinahusika, ama transfer (opens in a new tab) au approve (opens in a new tab). Hakuna maana ya kupoteza gesi katika kesi hii kwenye posho, tunaweza pia kuhamisha moja kwa moja.

1 function cleanupERC20(
2 address erc20,
3 address dest
4 )
5 public
6 onlyOwner
7 {
8 IERC20 token = IERC20(erc20);

Hii ndiyo sintaksia ya kuunda kitu kwa ajili ya mkataba tunapopokea anwani. Tunaweza kufanya hivi kwa sababu tuna ufafanuzi wa tokeni za ERC20 kama sehemu ya msimbo chanzo (tazama mstari wa 4), na faili hiyo inajumuisha ufafanuzi wa IERC20 (opens in a new tab), kiolesura cha mkataba wa OpenZeppelin ERC-20.

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

Hili ni chaguo la kukokotoa la usafishaji, kwa hivyo inawezekana hatutaki kuacha tokeni zozote. Badala ya kupata salio kutoka kwa mtumiaji mwenyewe, tunaweza pia kufanya mchakato uwe wa kiotomatiki.

Hitimisho

Hili si suluhisho kamili - hakuna suluhisho kamili kwa tatizo la "mtumiaji amefanya kosa". Hata hivyo, kutumia aina hizi za ukaguzi kunaweza angalau kuzuia makosa kadhaa. Uwezo wa kufungia akaunti, ingawa ni hatari, unaweza kutumika kupunguza uharibifu wa udukuzi fulani kwa kumnyima mdukuzi fedha zilizoibwa.

Tazama hapa kwa kazi zangu zaidi (opens in a new tab).

Ukurasa ulihaririwa mwisho: 3 Machi 2026

Umesaidika na mafunzo haya?