Přejít na hlavní obsah

Průvodce kontraktem ERC-20

Solidity
erc-20
Začátečník
Ori Pomerantz
9. března 2021
24 minut čtení

Úvod

Jedním z nejčastějších způsobů využití Etherea je vytvoření obchodovatelného tokenu skupinou lidí, v jistém smyslu jejich vlastní měny. Tyto tokeny obvykle dodržují standard ERC-20. Tento standard umožňuje psát nástroje, jako jsou fondy likvidity a peněženky, které fungují se všemi tokeny ERC-20. V tomto článku budeme analyzovat implementaci ERC20 v Solidity od OpenZeppelin (opens in a new tab) a také definici rozhraní (opens in a new tab).

Toto je anotovaný zdrojový kód. Pokud chcete implementovat ERC-20, přečtěte si tento tutoriál (opens in a new tab).

Rozhraní

Účelem standardu, jako je ERC-20, je umožnit mnoho implementací tokenů, které jsou interoperabilní napříč aplikacemi, jako jsou peněženky a decentralizované burzy. Abychom toho dosáhli, vytvoříme rozhraní (opens in a new tab). Jakýkoli kód, který potřebuje použít kontrakt tokenu, může použít stejné definice v rozhraní a být kompatibilní se všemi kontrakty tokenů, které jej používají, ať už se jedná o peněženku, jako je MetaMask, decentralizovanou aplikaci (dapp), jako je etherscan.io, nebo jiný kontrakt, jako je fond likvidity.

Illustration of the ERC-20 interface

Pokud jste zkušený programátor, pravděpodobně si pamatujete, že jste podobné konstrukce viděli v Javě (opens in a new tab) nebo dokonce v hlavičkových souborech C (opens in a new tab).

Toto je definice rozhraní ERC-20 (opens in a new tab) od OpenZeppelin. Jedná se o překlad lidsky čitelného standardu (opens in a new tab) do kódu Solidity. Samotné rozhraní samozřejmě nedefinuje, jak se má něco udělat. To je vysvětleno ve zdrojovém kódu kontraktu níže.

 

// SPDX-License-Identifier: MIT

Soubory Solidity by měly obsahovat identifikátor licence. Seznam licencí si můžete prohlédnout zde (opens in a new tab). Pokud potřebujete jinou licenci, jednoduše to vysvětlete v komentářích.

 

pragma solidity >=0.6.0 <0.8.0;

Jazyk Solidity se stále rychle vyvíjí a nové verze nemusí být kompatibilní se starým kódem (viz zde (opens in a new tab)). Proto je dobré specifikovat nejen minimální verzi jazyka, ale také maximální verzi, tedy tu nejnovější, se kterou jste kód testovali.

 

/**
 * @dev Rozhraní standardu ERC-20, jak je definováno v EIP.
 */

@dev v komentáři je součástí formátu NatSpec (opens in a new tab), který se používá k vytváření dokumentace ze zdrojového kódu.

 

interface IERC20 {

Podle konvence začínají názvy rozhraní na I.

 

    /**
     * @dev Vrací množství existujících tokenů.
     */
    function totalSupply() external view returns (uint256);

Tato funkce je external, což znamená, že může být volána pouze zvenčí kontraktu (opens in a new tab). Vrací celkovou zásobu tokenů v kontraktu. Tato hodnota je vrácena pomocí nejběžnějšího typu v Ethereu, 256bitového celého čísla bez znaménka (256 bitů je nativní velikost slova EVM). Tato funkce je také view, což znamená, že nemění stav, takže může být spuštěna na jediném uzlu, místo aby ji musel spouštět každý uzel v blockchainu. Tento druh funkce negeneruje transakci a nestojí žádný gas.

Poznámka: Teoreticky by se mohlo zdát, že tvůrce kontraktu by mohl podvádět tím, že vrátí menší celkovou zásobu, než je skutečná hodnota, čímž by se každý token zdál cennější, než ve skutečnosti je. Tato obava však ignoruje skutečnou povahu blockchainu. Vše, co se děje na blockchainu, může ověřit každý uzel. Aby toho bylo dosaženo, je strojový kód a úložiště každého kontraktu k dispozici na každém uzlu. Ačkoli nejste povinni zveřejnit kód Solidity pro váš kontrakt, nikdo by vás nebral vážně, pokud nezveřejníte zdrojový kód a verzi Solidity, se kterou byl zkompilován, aby mohl být ověřen proti vámi poskytnutému strojovému kódu. Podívejte se například na tento kontrakt (opens in a new tab).

 

    /**
     * @dev Vrací množství tokenů vlastněných účtem `account`.
     */
    function balanceOf(address account) external view returns (uint256);

Jak název napovídá, balanceOf vrací zůstatek účtu. Účty na Ethereu jsou v Solidity identifikovány pomocí typu address, který má 160 bitů. Je to také external a view.

 

    /**
     * @dev Přesune `amount` tokenů z účtu volajícího na `recipient`.
     *
     * Vrací booleovskou hodnotu indikující, zda operace byla úspěšná.
     *
     * Vyvolá událost {Transfer}.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

Funkce transfer provádí převod tokenů od volajícího na jinou adresu. To zahrnuje změnu stavu, takže to není view. Když uživatel zavolá tuto funkci, vytvoří se transakce a stojí to gas. Také emituje událost Transfer, aby informovala všechny na blockchainu o této události.

Funkce má dva typy výstupu pro dva různé typy volajících:

  • Uživatelé, kteří volají funkci přímo z uživatelského rozhraní. Uživatel obvykle odešle transakci a nečeká na odpověď, což by mohlo trvat neurčitou dobu. Uživatel může zjistit, co se stalo, vyhledáním stvrzenky transakce (která je identifikována pomocí hashe transakce) nebo vyhledáním události Transfer.
  • Ostatní kontrakty, které volají funkci jako součást celkové transakce. Tyto kontrakty získají výsledek okamžitě, protože běží ve stejné transakci, takže mohou použít návratovou hodnotu funkce.

Stejný typ výstupu vytvářejí i další funkce, které mění stav kontraktu.

 

Povolené limity umožňují účtu utratit některé tokeny, které patří jinému vlastníkovi. To je užitečné například pro kontrakty, které fungují jako prodejci. Kontrakty nemohou sledovat události, takže pokud by kupující převedl tokeny přímo na kontrakt prodejce, tento kontrakt by nevěděl, že mu bylo zaplaceno. Místo toho kupující povolí kontraktu prodejce utratit určitou částku a prodejce tuto částku převede. To se provádí prostřednictvím funkce, kterou volá kontrakt prodejce, takže kontrakt prodejce může vědět, zda byl úspěšný.

    /**
     * @dev Vrací zbývající počet tokenů, které bude mít `spender`
     * povoleno utratit jménem účtu `owner` prostřednictvím {transferFrom}. Ve výchozím nastavení je to
     * nula.
     *
     * Tato hodnota se mění, když jsou zavolány funkce {approve} nebo {transferFrom}.
     */
    function allowance(address owner, address spender) external view returns (uint256);

Funkce allowance umožňuje komukoli dotázat se, jaký je povolený limit, který jedna adresa (owner) umožňuje utratit jiné adrese (spender).

 

Funkce approve vytváří povolený limit. Nezapomeňte si přečíst zprávu o tom, jak to může být zneužito. V Ethereu řídíte pořadí svých vlastních transakcí, ale nemůžete řídit pořadí, ve kterém budou provedeny transakce jiných lidí, pokud neodešlete svou vlastní transakci až poté, co uvidíte, že proběhla transakce druhé strany.

 

Nakonec se transferFrom používá k tomu, aby utrácející skutečně utratil povolený limit.

 

Tyto události jsou emitovány, když se změní stav kontraktu ERC-20.

Samotný kontrakt

Toto je samotný kontrakt, který implementuje standard ERC-20, převzatý odtud (opens in a new tab). Není určen k použití tak, jak je, ale můžete z něj dědit (opens in a new tab) a rozšířit jej na něco použitelného.

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

 

Příkazy importu

Kromě výše uvedených definic rozhraní importuje definice kontraktu dva další soubory:


import "../../GSN/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
  • GSN/Context.sol jsou definice potřebné k použití OpenGSN (opens in a new tab), systému, který umožňuje uživatelům bez etheru používat blockchain. Všimněte si, že se jedná o starou verzi, pokud se chcete integrovat s OpenGSN, použijte tento tutoriál (opens in a new tab).
  • Knihovna SafeMath (opens in a new tab), která zabraňuje aritmetickému přetečení/podtečení pro verze Solidity <0.8.0. V Solidity ≥0.8.0 se aritmetické operace automaticky zvrátí při přetečení/podtečení, takže SafeMath je zbytečná. Tento kontrakt používá SafeMath pro zpětnou kompatibilitu se staršími verzemi kompilátoru.

 

Tento komentář vysvětluje účel kontraktu.

Definice kontraktu

contract ERC20 is Context, IERC20 {

Tento řádek specifikuje dědičnost, v tomto případě z IERC20 shora a Context pro OpenGSN.

 


    using SafeMath for uint256;

Tento řádek připojuje knihovnu SafeMath k typu uint256. Tuto knihovnu najdete zde (opens in a new tab).

Definice proměnných

Tyto definice specifikují stavové proměnné kontraktu. Tyto proměnné jsou deklarovány jako private, ale to znamená pouze to, že je nemohou číst jiné kontrakty na blockchainu. Na blockchainu nejsou žádná tajemství, software na každém uzlu má stav každého kontraktu v každém bloku. Podle konvence jsou stavové proměnné pojmenovány _<something>.

První dvě proměnné jsou mapování (mappings) (opens in a new tab), což znamená, že se chovají zhruba stejně jako asociativní pole (opens in a new tab), s tím rozdílem, že klíče jsou číselné hodnoty. Úložiště je alokováno pouze pro položky, které mají hodnoty odlišné od výchozí (nula).

    mapping (address => uint256) private _balances;

První mapování, _balances, představuje adresy a jejich příslušné zůstatky tohoto tokenu. Pro přístup k zůstatku použijte tuto syntaxi: _balances[<address>].

 

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

Tato proměnná, _allowances, ukládá povolené limity vysvětlené dříve. První index je vlastník tokenů a druhý je kontrakt s povoleným limitem. Pro přístup k částce, kterou může adresa A utratit z účtu adresy B, použijte _allowances[B][A].

 

    uint256 private _totalSupply;

Jak název napovídá, tato proměnná sleduje celkovou zásobu tokenů.

 

    string private _name;
    string private _symbol;
    uint8 private _decimals;

Tyto tři proměnné se používají ke zlepšení čitelnosti. První dvě jsou samovysvětlující, ale _decimals není.

Na jedné straně Ethereum nemá proměnné s plovoucí desetinnou čárkou nebo zlomky. Na druhé straně lidé rádi dělí tokeny. Jedním z důvodů, proč se lidé rozhodli pro zlato jako měnu, bylo to, že bylo těžké vracet drobné, když si někdo chtěl koupit krávu v hodnotě kachny.

Řešením je sledovat celá čísla, ale místo skutečného tokenu počítat zlomkový token, který je téměř bezcenný. V případě etheru se zlomkový token nazývá Wei a 10^18 Wei se rovná jednomu ETH. V době psaní tohoto článku je 10 000 000 000 000 Wei přibližně jeden americký nebo eurový cent.

Aplikace potřebují vědět, jak zobrazit zůstatek tokenů. Pokud má uživatel 3 141 000 000 000 000 000 Wei, je to 3,14 ETH? 31,41 ETH? 3 141 ETH? V případě etheru je definováno 10^18 Wei na ETH, ale pro váš token si můžete vybrat jinou hodnotu. Pokud dělení tokenu nedává smysl, můžete použít hodnotu _decimals nula. Pokud chcete použít stejný standard jako ETH, použijte hodnotu 18.

Konstruktor

Konstruktor se volá při prvním vytvoření kontraktu. Podle konvence jsou parametry funkce pojmenovány <something>_.

Funkce uživatelského rozhraní

Tyto funkce, name, symbol a decimals, pomáhají uživatelským rozhraním dozvědět se o vašem kontraktu, aby jej mohla správně zobrazit.

Návratový typ je string memory, což znamená vrácení řetězce, který je uložen v paměti. Proměnné, jako jsou řetězce, mohou být uloženy na třech místech:

ŽivotnostPřístup z kontraktuCena plynu
PaměťVolání funkceČtení/ZápisDesítky nebo stovky (vyšší pro vyšší lokace)
CalldataVolání funkcePouze pro čteníNelze použít jako návratový typ, pouze jako typ parametru funkce
ÚložištěDokud není změněnoČtení/ZápisVysoká (800 za čtení, 20k za zápis)

V tomto případě je memory nejlepší volbou.

Čtení informací o tokenu

Toto jsou funkce, které poskytují informace o tokenu, ať už jde o celkovou zásobu nebo zůstatek účtu.

    /**
     * @dev Viz {IERC20-totalSupply}.
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

Funkce totalSupply vrací celkovou zásobu tokenů.

 

    /**
     * @dev Viz {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

Přečtení zůstatku účtu. Všimněte si, že kdokoli má povoleno získat zůstatek účtu kohokoli jiného. Nemá smysl se snažit tyto informace skrývat, protože jsou stejně dostupné na každém uzlu. Na blockchainu nejsou žádná tajemství.

Převod tokenů

Funkce transfer se volá k převodu tokenů z účtu odesílatele na jiný. Všimněte si, že i když vrací booleovskou hodnotu, tato hodnota je vždy true. Pokud převod selže, kontrakt volání zvrátí.

 

        _transfer(_msgSender(), recipient, amount);
        return true;
    }

Funkce _transfer provádí skutečnou práci. Je to privátní funkce, kterou mohou volat pouze jiné funkce kontraktu. Podle konvence jsou privátní funkce pojmenovány _<something>, stejně jako stavové proměnné.

Normálně v Solidity používáme msg.sender pro odesílatele zprávy. To však rozbíjí OpenGSN (opens in a new tab). Pokud chceme s naším tokenem povolit transakce bez etheru, musíme použít _msgSender(). Vrací msg.sender pro normální transakce, ale pro ty bez etheru vrací původního podepisujícího, a ne kontrakt, který zprávu předal.

Funkce povolených limitů

Toto jsou funkce, které implementují funkcionalitu povolených limitů: allowance, approve, transferFrom a _approve. Implementace OpenZeppelin navíc přesahuje základní standard a zahrnuje některé funkce, které zlepšují bezpečnost: increaseAllowance a decreaseAllowance.

Funkce allowance

    /**
     * @dev Viz {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

Funkce allowance umožňuje komukoli zkontrolovat jakýkoli povolený limit.

Funkce approve

    /**
     * @dev Viz {IERC20-approve}.
     *
     * Požadavky:
     *
     * - `spender` nesmí být nulová adresa.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {

Tato funkce se volá k vytvoření povoleného limitu. Je podobná funkci transfer výše:

  • Funkce pouze volá interní funkci (v tomto případě _approve), která provádí skutečnou práci.
  • Funkce buď vrátí true (pokud je úspěšná), nebo se zvrátí (pokud ne).

 

        _approve(_msgSender(), spender, amount);
        return true;
    }

Používáme interní funkce, abychom minimalizovali počet míst, kde dochází ke změnám stavu. Jakákoli funkce, která mění stav, je potenciálním bezpečnostním rizikem, které je třeba auditovat z hlediska bezpečnosti. Tímto způsobem máme menší šanci udělat chybu.

Funkce transferFrom

Toto je funkce, kterou utrácející volá, aby utratil povolený limit. To vyžaduje dvě operace: převést utrácenou částku a snížit povolený limit o tuto částku.

 

Volání funkce a.sub(b, "message") dělá dvě věci. Zaprvé vypočítá a-b, což je nový povolený limit. Zadruhé zkontroluje, zda tento výsledek není záporný. Pokud je záporný, volání se zvrátí s poskytnutou zprávou. Všimněte si, že když se volání zvrátí, jakékoli zpracování provedené dříve během tohoto volání je ignorováno, takže nemusíme vracet zpět _transfer.

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

Bezpečnostní doplňky OpenZeppelin

Je nebezpečné nastavit nenulový povolený limit na jinou nenulovou hodnotu, protože řídíte pouze pořadí svých vlastních transakcí, ne nikoho jiného. Představte si, že máte dva uživatele, Alici, která je naivní, a Billa, který je nečestný. Alice chce od Billa nějakou službu, o které si myslí, že stojí pět tokenů – takže dá Billovi povolený limit pět tokenů.

Pak se něco změní a Billova cena stoupne na deset tokenů. Alice, která stále chce službu, odešle transakci, která nastaví Billův povolený limit na deset. V okamžiku, kdy Bill uvidí tuto novou transakci v transakčním poolu, odešle transakci, která utratí Aliciných pět tokenů a má mnohem vyšší cenu plynu, takže bude vytěžena rychleji. Tímto způsobem může Bill utratit prvních pět tokenů a poté, jakmile je vytěžen Alicin nový povolený limit, utratit dalších deset za celkovou cenu patnácti tokenů, což je více, než Alice zamýšlela autorizovat. Tato technika se nazývá předbíhání (opens in a new tab)

Transakce AliceNonce AliceTransakce BillaNonce BillaBillův povolený limitBillův celkový příjem od Alice
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
approve(Bill, 10)11105
transferFrom(Alice, Bill, 10)10,124015

Aby se tomuto problému předešlo, tyto dvě funkce (increaseAllowance a decreaseAllowance) vám umožňují upravit povolený limit o konkrétní částku. Takže pokud Bill již utratil pět tokenů, bude moci utratit jen dalších pět. V závislosti na načasování existují dva způsoby, jak to může fungovat, přičemž oba končí tím, že Bill získá pouze deset tokenů:

A:

Transakce AliceNonce AliceTransakce BillaNonce BillaBillův povolený limitBillův celkový příjem od Alice
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
increaseAllowance(Bill, 5)110+5 = 55
transferFrom(Alice, Bill, 5)10,124010

B:

Transakce AliceNonce AliceTransakce BillaNonce BillaBillův povolený limitBillův celkový příjem od Alice
approve(Bill, 5)1050
increaseAllowance(Bill, 5)115+5 = 100
transferFrom(Alice, Bill, 10)10,124010

Funkce a.add(b) je bezpečné sčítání. V nepravděpodobném případě, že a+b>=2^256, nedojde k přetečení (wrap around) tak, jak to dělá normální sčítání.

Funkce, které mění informace o tokenu

Toto jsou čtyři funkce, které provádějí skutečnou práci: _transfer, _mint, _burn a _approve.

Funkce _transfer

Tato funkce, _transfer, převádí tokeny z jednoho účtu na druhý. Je volána jak transfer (pro převody z vlastního účtu odesílatele), tak transferFrom (pro použití povolených limitů k převodu z účtu někoho jiného).

 

        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

Nikdo ve skutečnosti nevlastní nulovou adresu v Ethereu (to znamená, že nikdo nezná soukromý klíč, jehož odpovídající veřejný klíč je transformován na nulovou adresu). Když lidé tuto adresu použijí, je to obvykle softwarová chyba – takže selžeme, pokud je nulová adresa použita jako odesílatel nebo příjemce.

 

        _beforeTokenTransfer(sender, recipient, amount);

Existují dva způsoby, jak tento kontrakt použít:

  1. Použít jej jako šablonu pro váš vlastní kód
  2. Dědit z něj (opens in a new tab) a přepsat pouze ty funkce, které potřebujete upravit

Druhá metoda je mnohem lepší, protože kód ERC-20 od OpenZeppelin již byl auditován a ukázalo se, že je bezpečný. Když použijete dědičnost, je jasné, jaké funkce upravujete, a aby lidé vašemu kontraktu důvěřovali, stačí jim auditovat pouze tyto konkrétní funkce.

Často je užitečné provést funkci pokaždé, když tokeny změní majitele. Nicméně _transfer je velmi důležitá funkce a je možné ji napsat nebezpečně (viz níže), takže je nejlepší ji nepřepisovat. Řešením je _beforeTokenTransfer, funkce hook (opens in a new tab). Tuto funkci můžete přepsat a bude volána při každém převodu.

 

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);

Toto jsou řádky, které skutečně provádějí převod. Všimněte si, že mezi nimi nic není a že převedenou částku odečteme od odesílatele předtím, než ji přičteme příjemci. To je důležité, protože pokud by uprostřed bylo volání jiného kontraktu, mohlo by to být použito k podvedení tohoto kontraktu. Tímto způsobem je převod atomický, uprostřed se nemůže nic stát.

 

        emit Transfer(sender, recipient, amount);
    }

Nakonec emitujte událost Transfer. Události nejsou přístupné chytrým kontraktům, ale kód běžící mimo blockchain může událostem naslouchat a reagovat na ně. Například peněženka může sledovat, kdy vlastník získá více tokenů.

Funkce _mint a _burn

Tyto dvě funkce (_mint a _burn) mění celkovou zásobu tokenů. Jsou interní a v tomto kontraktu není žádná funkce, která by je volala, takže jsou užitečné pouze tehdy, pokud z kontraktu dědíte a přidáte vlastní logiku, která rozhodne, za jakých podmínek razit nové tokeny nebo spálit ty stávající.

POZNÁMKA: Každý token ERC-20 má svou vlastní obchodní logiku, která diktuje správu tokenů. Například kontrakt s pevnou zásobou může volat _mint pouze v konstruktoru a nikdy nevolat _burn. Kontrakt, který prodává tokeny, zavolá _mint, když je mu zaplaceno, a pravděpodobně v určitém okamžiku zavolá _burn, aby se vyhnul nekontrolovatelné inflaci.

Nezapomeňte aktualizovat _totalSupply, když se změní celkový počet tokenů.

 

Funkce _burn je téměř identická s _mint, s tím rozdílem, že jde opačným směrem.

Funkce _approve

Toto je funkce, která skutečně specifikuje povolené limity. Všimněte si, že umožňuje vlastníkovi specifikovat povolený limit, který je vyšší než aktuální zůstatek vlastníka. To je v pořádku, protože zůstatek se kontroluje v době převodu, kdy se může lišit od zůstatku v době vytvoření povoleného limitu.

 

Emitujte událost Approval. V závislosti na tom, jak je aplikace napsána, může být kontraktu utrácejícího sděleno schválení buď vlastníkem, nebo serverem, který těmto událostem naslouchá.

        emit Approval(owner, spender, amount);
    }

Úprava proměnné Decimals

Tato funkce upravuje proměnnou _decimals, která se používá k tomu, aby uživatelským rozhraním řekla, jak interpretovat částku. Měli byste ji volat z konstruktoru. Bylo by nečestné ji volat v jakémkoli pozdějším okamžiku a aplikace nejsou navrženy tak, aby to zvládly.

Hooks

Toto je funkce hook, která se má volat během převodů. Zde je prázdná, ale pokud potřebujete, aby něco dělala, jednoduše ji přepíšete.

Závěr

Pro zopakování, zde jsou některé z nejdůležitějších myšlenek v tomto kontraktu (podle mého názoru, váš se pravděpodobně bude lišit):

  • Na blockchainu nejsou žádná tajemství. Jakékoli informace, ke kterým má chytrý kontrakt přístup, jsou dostupné celému světu.
  • Můžete řídit pořadí svých vlastních transakcí, ale ne to, kdy proběhnou transakce jiných lidí. To je důvod, proč může být změna povoleného limitu nebezpečná, protože umožňuje utrácejícímu utratit součet obou povolených limitů.
  • Hodnoty typu uint256 přetékají (wrap around). Jinými slovy, 0-1=2^256-1. Pokud to není požadované chování, musíte to zkontrolovat (nebo použít knihovnu SafeMath, která to udělá za vás). Všimněte si, že to se změnilo v Solidity 0.8.0 (opens in a new tab).
  • Provádějte všechny změny stavu konkrétního typu na konkrétním místě, protože to usnadňuje auditování. To je důvod, proč máme například _approve, kterou volají approve, transferFrom, increaseAllowance a decreaseAllowance
  • Změny stavu by měly být atomické, bez jakékoli další akce uprostřed (jak můžete vidět v _transfer). Je to proto, že během změny stavu máte nekonzistentní stav. Například mezi okamžikem, kdy odečtete ze zůstatku odesílatele, a okamžikem, kdy přičtete k zůstatku příjemce, existuje méně tokenů, než by mělo. To by mohlo být potenciálně zneužito, pokud by mezi nimi byly operace, zejména volání jiného kontraktu.

Nyní, když jste viděli, jak je napsán kontrakt ERC-20 od OpenZeppelin, a zejména jak je zabezpečen, jděte a napište své vlastní bezpečné kontrakty a aplikace.

Zde najdete více z mé práce (opens in a new tab).