Cum se creează un token ERC20 într-un mod simplu
Publicat: 2022-03-11Scopul acestui articol este de a demonstra cum să creați un token ERC20 în cât mai puțin timp posibil.
Să începem cu elementele de bază: Ce este un token ERC20?
În ultimii ani, specificația jetoanelor ERC20 a devenit standardul de facto pentru jetoanele Ethereum. Cu alte cuvinte, majoritatea contractelor Ethereum de astăzi sunt conforme cu ERC20. Acest articol va detalia cum vă puteți crea propriul token Ethereum, dar înainte de a începe, să aruncăm o privire mai atentă la standardul ERC20.
Ce face ca jetoanele ERC20 să fie atât de atractive și de succes? Există mai mulți factori în joc:
- Tokenurile ERC20 sunt simple și ușor de implementat, așa cum veți vedea în acest tutorial.
- Standardul ERC20 rezolvă o problemă semnificativă, deoarece piețele bazate pe blockchain și cripto-portofele au nevoie de un singur set standardizat de comenzi pentru a comunica cu gama de token-uri pe care le gestionează. Aceasta include reguli de interacțiune între diferite jetoane, precum și reguli de achiziție de jetoane.
- A fost prima specificație populară care a oferit standardizarea jetoanelor Ethereum. Nu a fost în niciun caz primul , dar datorită popularității sale, a devenit rapid standardul industriei.
La fel ca și alte jetoane Ethereum, jetoanele ERC20 sunt implementate ca contracte inteligente și executate pe mașina virtuală Ethereum (EVM) într-o manieră descentralizată.
Solidity: limbajul de programare Smart Contract
Contractele inteligente Ethereum sunt scrise în Solidity. Deși există limbi alternative, aproape nimeni nu le folosește în acest scop. Solidity este similar cu JavaScript, așa că dacă aveți cunoștințe despre JavaScript, sau chiar despre Java și alte limbaje asemănătoare C, nu ar trebui să aveți probleme în a vă da seama că o bucată de cod din Solidity are, chiar înainte de a stăpâni suficient de mult Solidity pentru a o utiliza. aceasta.
Aici începe distracția, deoarece ar trebui să puteți începe să creați un contract ERC20 simplu în cel mai scurt timp. Aceasta este o sarcină simplă, suficient de simplă încât acest articol vă va demonstra cum puteți scrie și implementa un token ERC20 în mai puțin de o oră.
Indicatorul pe care îl vom crea în această demonstrație va fi o implementare simplă ERC20, fără prea multe clopote și fluiere. Cu toate acestea, am văzut multe jetoane la fel de simple în lumea reală și tind să se descurce destul de bine.
Prezentare generală a standardului ERC20 Token
Ce este ERC20?
Mai simplu, standardul ERC20 definește un set de funcții care urmează să fie implementate de toate jetoanele ERC20, astfel încât să permită integrarea cu alte contracte, portofele sau piețe. Acest set de funcții este destul de scurt și de bază.
function totalSupply() public view returns (uint256); function balanceOf(address tokenOwner) public view returns (uint); function allowance(address tokenOwner, address spender) public view returns (uint); function transfer(address to, uint tokens) public returns (bool); function approve(address spender, uint tokens) public returns (bool); function transferFrom(address from, address to, uint tokens) public returns (bool);
Funcțiile ERC20 permit unui utilizator extern, să zicem o aplicație cripto-portofel, să afle soldul unui utilizator și să transfere fonduri de la un utilizator la altul cu autorizarea corespunzătoare.
Contractul inteligent definește două evenimente definite în mod specific:
event Approval(address indexed tokenOwner, address indexed spender, uint tokens); event Transfer(address indexed from, address indexed to, uint tokens);
Aceste evenimente vor fi invocate sau emise atunci când unui utilizator i se acordă drepturi de a retrage jetoane dintr-un cont și după ce jetoanele sunt efectiv transferate.
Pe lângă funcțiile standard ERC20, multe jetoane ERC20 prezintă, de asemenea, câmpuri suplimentare, iar unele au devenit de facto parte a standardului ERC20, dacă nu în scris, atunci în practică. Iată câteva exemple de astfel de domenii.
string public constant name; string public constant symbol; uint8 public constant decimals;
Iată câteva puncte referitoare la nomenclatura ERC20 și Solidity:
- O funcție
public
poate fi accesată în afara contractului în sine -
view
înseamnă practic constantă, adică starea internă a contractului nu va fi modificată de funcție - Un
event
este modalitatea Solidity de a permite clienților, de exemplu, aplicația dvs. de aplicație, să fie notificați cu privire la evenimente specifice din cadrul contractului
Majoritatea constructelor limbajului Solidity ar trebui să fie clare dacă dețineți deja abilități esențiale Java/JavaScript.
Scrierea unui token ERC20 în soliditate
Acum că am subliniat elementele de bază și am explicat ce este nevoie pentru a crea un token ERC20, este timpul să începem să scriem ceva logică.
În primul rând, trebuie să definim două obiecte de mapare. Aceasta este noțiunea de soliditate pentru o matrice asociativă sau cheie/valoare:
mapping(address => uint256) balances; mapping(address => mapping (address => uint256)) allowed;
Expresia mapping(address => uint256)
definește o matrice asociativă ale cărei chei sunt de tip address
— un număr folosit pentru a desemna adresele de cont și ale cărui valori sunt de tipul uint256
— un întreg de 256 de biți folosit de obicei pentru a stoca soldurile de simboluri.
Primul obiect de mapare, balances
, va deține soldul simbol al fiecărui cont de proprietar.
Al doilea obiect de mapare, allowed
, va include toate conturile aprobate pentru retragere dintr-un anumit cont împreună cu suma de retragere permisă pentru fiecare.
După cum puteți vedea, câmpul de valoare al mapării permise este prin el însuși o adresă de mapare a contului de trasare la suma sa de retragere aprobată.
Aceste mapări împreună cu toate celelalte câmpuri contractuale vor fi stocate în blockchain și vor fi extrase , ceea ce va duce la propagarea modificărilor către toate nodurile utilizatorilor rețelei.
Stocarea blockchain este costisitoare, iar utilizatorii contractului dvs. vor trebui să plătească, într-un fel sau altul. Prin urmare, ar trebui să încercați întotdeauna să minimizați dimensiunea de stocare și scrierile în blockchain.
Acum că avem structurile de date necesare, putem începe să scriem efectiv logica ERC20 în funcțiile corespunzătoare.
Setarea numărului de jetoane ICO
Cum setăm numărul de jetoane ICO? Ei bine, există o serie de moduri de a seta numărul maxim de jetoane ICO și această problemă ar putea merita o discuție lungă de la sine.
Pentru nevoile tutorialului nostru ECR20, vom folosi cea mai simplă abordare: setați cantitatea totală de jetoane la momentul creării contractului și atribuiți-le inițial pe toate „proprietarului contractului”, adică contului care a implementat contractul inteligent:
uint256 totalSupply_; constructor(uint256 total) public { totalSupply_ = total; balances[msg.sender] = _totalSupply; }
Un constructor este o funcție specială numită automat de Ethereum imediat după implementarea contractului. Este folosit de obicei pentru a inițializa starea jetonului folosind parametrii trecuți de contul de implementare al contractului.
msg
este o variabilă globală declarată și populată de Ethereum însuși. Contine date importante pentru executarea contractului. Câmpul pe care îl folosim aici: msg.sender
conține contul Ethereum care execută funcția contractului curent.
Numai contul de implementare poate intra în constructorul unui contract. Când contractul este pornit, această funcție alocă token-urile disponibile contului „proprietarul contractului”.
Obțineți aprovizionare totală de jetoane
function totalSupply() public view returns (uint256) { return totalSupply_; }
Această funcție va returna numărul tuturor jetoanelor alocate prin acest contract, indiferent de proprietar.
Obțineți soldul de token al proprietarului
function balanceOf(address tokenOwner) public view returns (uint) { return balances[tokenOwner]; }
balanceOf
va returna soldul de simbol curent al unui cont, identificat prin adresa proprietarului acestuia.
Transferați jetoane într-un alt cont
function transfer(address receiver, uint numTokens) public returns (bool) { require(numTokens <= balances[msg.sender]); balances[msg.sender] = balances[msg.sender] — numTokens; balances[receiver] = balances[receiver] + numTokens; emit Transfer(msg.sender, receiver, numTokens); return true; }
După cum sugerează și numele, funcția de transfer
este utilizată pentru a muta cantitatea de jetoane numTokens
din soldul proprietarului în cel al altui utilizator sau receiver
. Proprietarul care efectuează transferul este msg.sender
adică cel care execută funcția, ceea ce presupune că doar proprietarul token-urilor le poate transfera altora.

Modul solidității de a afirma un predicat este require
. În acest caz, contul de transfer are un sold suficient pentru a executa transferul. Dacă o declarație require
eșuează, tranzacția este imediat anulată, fără modificări scrise în blockchain.
Imediat înainte de a ieși, funcția declanșează Transfer
evenimentului ERC20, permițând ascultătorilor înregistrați să reacționeze la finalizarea acestuia.
Aprobați delegatul pentru retragerea jetoanelor
Această funcție este folosită cel mai adesea într-un scenariu de piață de simboluri.
function approve(address delegate, uint numTokens) public returns (bool) { allowed[msg.sender][delegate] = numTokens; emit Approval(msg.sender, delegate, numTokens); return true; }
Ceea ce face approve
este să permită unui proprietar, de exemplu, msg.sender
să aprobe un cont delegat - eventual piața în sine - să retragă jetoane din contul său și să le transfere în alte conturi.
După cum puteți vedea, această funcție este utilizată pentru scenariile în care proprietarii oferă jetoane pe o piață. Permite pieței să finalizeze tranzacția fără a aștepta aprobarea prealabilă.
La sfârșitul execuției sale, această funcție declanșează un eveniment de Approval
.
Obțineți numărul de jetoane aprobate pentru retragere
function allowance(address owner, address delegate) public view returns (uint) { return allowed[owner][delegate]; }
Această funcție returnează numărul curent aprobat de jetoane de către un proprietar unui anumit delegat, așa cum este stabilit în funcția de approve
.
Transferați jetoane de către delegat
Funcția transferFrom
este peer-ul funcției de approve
, despre care am discutat anterior. Permite unui delegat aprobat pentru retragere să transfere fonduri proprietarului într-un cont terță parte.
function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) { require(numTokens <= balances[owner]); require(numTokens <= allowed[owner][msg.sender]); balances[owner] = balances[owner] — numTokens; allowed[owner][msg.sender] = allowed[from][msg.sender] — numTokens; balances[buyer] = balances[buyer] + numTokens; Transfer(owner, buyer, numTokens); return true; }
Cele două declarații require
la începutul funcției sunt pentru a verifica dacă tranzacția este legitimă, adică dacă proprietarul are suficiente token-uri de transferat și că delegatul are aprobarea ca (cel puțin) numTokens
să se retragă.
Pe lângă transferul sumei numTokens
de la proprietar la cumpărător, această funcție scade și numTokens
din alocația delegatului. Acest lucru permite, practic, unui delegat cu o anumită alocație să o împartă în mai multe retrageri separate, ceea ce este un comportament tipic de pe piață.
Ne-am putea opri aici și să avem o implementare validă ERC20. Cu toate acestea, vrem să facem un pas mai departe, deoarece vrem un simbol de forță industrială. Acest lucru ne cere să ne facem codul puțin mai sigur, deși vom putea păstra token-ul relativ simplu, dacă nu de bază.
Biblioteca SafeMath Solidity
SafeMath este o bibliotecă Solidity menită să se ocupe de un singur mod în care se știe că hackerii încalcă contracte: atacul de supraîncărcare întreg. Într-un astfel de atac, hackerul forțează contractul să folosească valori numerice incorecte prin transmiterea unor parametri care vor duce numerele întregi relevante peste valorile lor maxime.
SafeMath protejează împotriva acestui lucru testând depășirea înainte de a efectua acțiunea aritmetică, eliminând astfel pericolul atacului de depășire. Biblioteca este atât de mică încât impactul asupra dimensiunii contractului este minim, nu implică performanță și penalități mici pentru costurile de stocare.
Să adăugăm SafeMath la codul nostru:
library SafeMath { // Only relevant functions function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a — b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } }
SafeMath utilizează instrucțiuni assert
pentru a verifica corectitudinea parametrilor trecuți. În cazul în care assert
eșuează, execuția funcției va fi oprită imediat și toate modificările blockchain vor fi anulate.
În continuare, să adăugăm următoarea declarație de introducere a bibliotecii în compilatorul Solidity:
using SafeMath for uint256;
Apoi, înlocuim aritmetica naivă pe care am folosit-o la început cu funcții SafeMath:
balances[msg.sender] = balances[msg.sender].sub(numTokens); balances[receiver] = balances[receiver].add(numTokens); balances[buyer] = balances[buyer].add(numTokens); balances[owner] = balances[owner].sub(numTokens);
Împreună totul
În Solidity, funcțiile și evenimentele unui contract inteligent sunt împachetate într-o entitate numită contract pe care o puteți traduce în tăcere într-o „clasă blockchain”. Mai jos este contractul compatibil ERC20 pe care l-am creat, inclusiv o esențială a codului nostru. Câmpurile de nume și simbol pot fi modificate după bunul plac. Majoritatea jetoanelor păstrează valoarea zecimală la 18, așa că vom face același lucru.
Implementarea contractului Ethereum
A sosit momentul să implementăm contractul nostru în blockchain. După implementare, contractul nostru va fi transferat către toate nodurile care participă la rețea. Toate modificările aduse contractului vor fi transmise tuturor nodurilor participante.
Dezvoltatorii Ethereum folosesc de obicei instrumente de implementare, cum ar fi Truffle. Chiar și Truffle este exagerat pentru nevoile limitate ale acestui articol și un instrument simplu online numit Remix va fi suficient.
Pentru a-l folosi, va trebui să instalați pluginul MetaMask în browser și un cont Rinkeby (rețea de testare Ethereum) cu cel puțin ceva Rinkeby Ether în el. Aceștia sunt pași relativ simpli, așa că nu vom intra în detalii.
În cazul în care nu aveți niciunul, mergeți la MetaMask și Rinkeby pentru link-uri de descărcare și pentru a obține instrucțiuni clare de instalare și utilizare.
Acum că avem toate elementele de bază la locul lor, ne vom îndrepta către Remix și vom lipi codul de mai sus, inclusiv linia pragma și biblioteca SafeMath, în editorul online.
Apoi, vom trece la a doua filă din dreapta numită „ Runează ” și vom face clic pe „ Deploy ”. Va apărea un pop-up MetaMask care ne cere să confirmăm tranzacția. Desigur, o vom aproba.
- Cutie verde: asigură-te că ești pe Rinkeby
- Caseta albastră: setați rezerva totală de jetoane
- Cutie roșie: Desfășoară!
Gist : https://gist.github.com/giladHaimov/8e81dbde10c9aeff69a1d683ed6870be#file-basicerc20-sol
Felicitări! Tocmai ți-ai implementat primul token ERC20, ca un adevărat profesionist Ethereum. După cum am promis, jetonul este simplu și ușor, dar complet funcțional, compatibil cu standardul ERC20 și securizat cu MathSafe. Este gata să fie achiziționat, plătit cu și transferat pe întregul Blockchain.
Asta este tot ce există în contractele inteligente?
Nu, nici măcar aproape, deoarece scurta noastră demonstrație abia zgârie suprafața și se ocupă doar de un aspect al dezvoltării unui contract inteligent.
Contractele inteligente pot fi mult mai complexe în funcție de logica dvs. de afaceri, modelarea interacțiunii utilizatorului, dacă permiteți sau nu baterea și arderea token-ului, modificările ciclului de viață pe care le introduceți în contract, nevoia de capabilități la nivel de administrator, care de obicei vine cu un set de funcții autorizat de administrator și așa mai departe. Obtii poza.
Totuși, dacă puteți replica ceea ce am făcut aici, aceasta este o bază solidă pentru a vă extinde cunoștințele și a trece la contracte mai complexe atunci când este necesar.