Tutoriel·10 min de lecture·Par Solingo

Tutoriel ERC-20 — Créez Votre Premier Token en Solidity

Guide étape par étape pour créer, tester et déployer votre premier token ERC-20. Du code au mainnet.

# Tutoriel ERC-20 — Créez Votre Premier Token en Solidity

Créer son propre token ERC-20 est une compétence fondamentale pour tout développeur Solidity. Que vous construisiez un token de gouvernance, un système de récompenses, ou que vous appreniez simplement les bases, comprendre ERC-20 est essentiel.

Dans ce tutoriel, nous allons construire un token ERC-20 from scratch, le tester rigoureusement, et le déployer sur un testnet. À la fin, vous aurez un contrat de token production-ready.

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Connaissances de base en Solidity (variables, fonctions, mappings)
  • Un wallet (MetaMask recommandé)
  • Du testnet ETH (Sepolia recommandé)
  • Foundry installé (curl -L https://foundry.paradigm.xyz | bash)

Qu'est-ce qu'ERC-20 ?

ERC-20 est le standard de token le plus largement adopté sur Ethereum. Il définit une interface commune que tous les tokens doivent implémenter, assurant la compatibilité avec les wallets, exchanges et dApps.

Le standard spécifie 6 fonctions principales et 2 événements :

Fonctions :

  • totalSupply() : Supply totale du token
  • balanceOf(address account) : Obtenir le solde d'une adresse
  • transfer(address to, uint256 amount) : Transférer des tokens
  • approve(address spender, uint256 amount) : Approuver une dépense
  • allowance(address owner, address spender) : Vérifier l'allowance
  • transferFrom(address from, address to, uint256 amount) : Transférer au nom de quelqu'un

Événements :

  • Transfer(address indexed from, address indexed to, uint256 value)
  • Approval(address indexed owner, address indexed spender, uint256 value)

Construire ERC-20 From Scratch

Créons un token ERC-20 complet. Voici l'implémentation :

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

contract MyToken {

string public name;

string public symbol;

uint8 public decimals = 18;

uint256 public totalSupply;

mapping(address => uint256) public balanceOf;

mapping(address => mapping(address => uint256)) public allowance;

event Transfer(address indexed from, address indexed to, uint256 value);

event Approval(address indexed owner, address indexed spender, uint256 value);

constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {

name = _name;

symbol = _symbol;

totalSupply = _initialSupply * 10**decimals;

balanceOf[msg.sender] = totalSupply;

emit Transfer(address(0), msg.sender, totalSupply);

}

function transfer(address to, uint256 amount) public returns (bool) {

require(to != address(0), "Transfer to zero address");

require(balanceOf[msg.sender] >= amount, "Insufficient balance");

balanceOf[msg.sender] -= amount;

balanceOf[to] += amount;

emit Transfer(msg.sender, to, amount);

return true;

}

function approve(address spender, uint256 amount) public returns (bool) {

require(spender != address(0), "Approve to zero address");

allowance[msg.sender][spender] = amount;

emit Approval(msg.sender, spender, amount);

return true;

}

function transferFrom(address from, address to, uint256 amount) public returns (bool) {

require(from != address(0), "Transfer from zero address");

require(to != address(0), "Transfer to zero address");

require(balanceOf[from] >= amount, "Insufficient balance");

require(allowance[from][msg.sender] >= amount, "Allowance exceeded");

balanceOf[from] -= amount;

balanceOf[to] += amount;

allowance[from][msg.sender] -= amount;

emit Transfer(from, to, amount);

return true;

}

}

Détails d'Implémentation

Decimals

Les tokens utilisent 18 décimales par convention (comme ETH). Cela signifie :

  • 1 token = 1 × 10¹⁸ unités de base
  • 0.5 token = 500000000000000000 unités

Transfer vs TransferFrom

  • transfer : vous transférez vos propres tokens
  • transferFrom : vous transférez les tokens de quelqu'un d'autre (après approbation)

Le pattern approve/transferFrom est crucial pour les DEX et protocoles DeFi.

Utiliser OpenZeppelin

En production, utilisez les implémentations battle-tested d'OpenZeppelin :

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {

constructor(uint256 initialSupply) ERC20("MyToken", "MTK") Ownable(msg.sender) {

_mint(msg.sender, initialSupply * 10**decimals());

}

function mint(address to, uint256 amount) public onlyOwner {

_mint(to, amount);

}

}

Tests avec Foundry

// test/MyToken.t.sol

pragma solidity ^0.8.20;

import "forge-std/Test.sol";

import "../src/MyToken.sol";

contract MyTokenTest is Test {

MyToken token;

address alice = address(0x1);

address bob = address(0x2);

function setUp() public {

token = new MyToken("MyToken", "MTK", 1000000);

}

function testInitialSupply() public {

assertEq(token.totalSupply(), 1000000 * 10**18);

assertEq(token.balanceOf(address(this)), 1000000 * 10**18);

}

function testTransfer() public {

token.transfer(alice, 100 * 10**18);

assertEq(token.balanceOf(alice), 100 * 10**18);

}

function testApproveAndTransferFrom() public {

token.transfer(alice, 1000 * 10**18);

vm.prank(alice);

token.approve(bob, 500 * 10**18);

vm.prank(bob);

token.transferFrom(alice, bob, 300 * 10**18);

assertEq(token.balanceOf(bob), 300 * 10**18);

assertEq(token.allowance(alice, bob), 200 * 10**18);

}

function testFuzz_transfer(address to, uint256 amount) public {

vm.assume(to != address(0));

vm.assume(amount <= token.balanceOf(address(this)));

token.transfer(to, amount);

assertEq(token.balanceOf(to), amount);

}

}

Lancez les tests :

forge test -vv

Déploiement

1. Script de Déploiement

// script/Deploy.s.sol

pragma solidity ^0.8.20;

import "forge-std/Script.sol";

import "../src/MyToken.sol";

contract DeployScript is Script {

function run() external {

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

vm.startBroadcast(deployerPrivateKey);

MyToken token = new MyToken("MyToken", "MTK", 1000000);

console.log("Token deployed at:", address(token));

vm.stopBroadcast();

}

}

2. Déployer sur Sepolia

forge script script/Deploy.s.sol \

--rpc-url https://sepolia.infura.io/v3/YOUR_KEY \

--broadcast \

--verify

3. Vérifier sur Etherscan

forge verify-contract \

--chain-id 11155111 \

--compiler-version v0.8.20 \

CONTRACT_ADDRESS \

src/MyToken.sol:MyToken

Fonctionnalités Avancées

Minting/Burning

function mint(address to, uint256 amount) public onlyOwner {

totalSupply += amount;

balanceOf[to] += amount;

emit Transfer(address(0), to, amount);

}

function burn(uint256 amount) public {

require(balanceOf[msg.sender] >= amount);

totalSupply -= amount;

balanceOf[msg.sender] -= amount;

emit Transfer(msg.sender, address(0), amount);

}

Snapshots (pour governance)

import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";

contract MyToken is ERC20, ERC20Snapshot, Ownable {

function snapshot() public onlyOwner returns (uint256) {

return _snapshot();

}

}

Pausable (circuit breaker)

import "@openzeppelin/contracts/security/Pausable.sol";

contract MyToken is ERC20, Pausable, Ownable {

function pause() public onlyOwner {

_pause();

}

function _beforeTokenTransfer(address from, address to, uint256 amount)

internal whenNotPaused override

{

super._beforeTokenTransfer(from, to, amount);

}

}

Sécurité : Checklist

Avant de déployer en production :

  • [ ] Tests unitaires (coverage > 95%)
  • [ ] Fuzz testing sur les fonctions critiques
  • [ ] Analyse avec Slither (slither .)
  • [ ] Audit professionnel (si high value)
  • [ ] Test sur testnet pendant 1+ semaine
  • [ ] Vérification du contrat sur Etherscan
  • [ ] Renonciation au ownership si token immutable
  • [ ] Documentation claire

Cas d'Usage Réels

  • Governance Token : Voting dans une DAO
  • Reward Token : Récompenses pour staking
  • Utility Token : Accès à des services
  • Stablecoin : Token indexé sur une devise
  • Wrapped Token : Représentation d'un asset
  • Prochaines Étapes

    Maintenant que vous maîtrisez ERC-20 :

  • Ajoutez du staking (stake pour gagner des rewards)
  • Implémentez des vesting schedules (unlock progressif)
  • Créez un DEX simple (swap de votre token)
  • Ajoutez de la governance (vote avec le token)
  • Conclusion

    Vous savez maintenant créer un token ERC-20 professionnel. Les tokens sont la base de nombreux protocoles DeFi, NFT marketplaces, DAOs et applications blockchain.

    Pratiquez sur Solingo avec nos 100+ exercices ERC-20, du token basique au système de gouvernance avancé.

    ---

    Ressources complémentaires :

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement