Outils·8 min de lecture·Par Solingo

OpenZeppelin Contracts — La Bibliothèque Standard pour Solidity

OpenZeppelin Contracts est la bibliothèque la plus fiable pour le développement sécurisé de smart contracts. Apprenez à utiliser ERC20, ERC721, contrôle d'accès et d'autres composants éprouvés.

# OpenZeppelin Contracts — La Bibliothèque Standard pour Solidity

Lors de la construction de smart contracts, vous ne devriez pas réinventer la roue. OpenZeppelin Contracts est la bibliothèque standard de l'industrie fournissant des implémentations sécurisées, auditées et optimisées en gas de patterns communs. Des tokens ERC20 aux contrôles d'accès, des contrats upgradeables aux utilitaires de sécurité, OpenZeppelin est la fondation de milliers de contrats en production sécurisant des milliards de dollars.

Qu'est-ce qu'OpenZeppelin Contracts ?

OpenZeppelin Contracts est une bibliothèque open-source de composants smart contracts réutilisables pour Ethereum et les chaînes compatibles EVM. Développée et maintenue par OpenZeppelin, la principale entreprise de sécurité blockchain, chaque contrat est :

  • Audité par plusieurs entreprises de sécurité
  • Éprouvé en production par les protocoles majeurs
  • Optimisé en gas pour l'efficacité
  • Bien documenté avec des guides complets
  • Activement maintenu avec des mises à jour de sécurité

La bibliothèque est modulaire — utilisez uniquement ce dont vous avez besoin et composez des contrats à partir de petits composants ciblés.

Installation

Avec npm

npm install @openzeppelin/contracts

Avec Foundry

forge install OpenZeppelin/openzeppelin-contracts

Ajoutez à foundry.toml :

[dependencies]

openzeppelin-contracts = "5.2.0"

Import dans vos contrats

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

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

Solidity trouve le package dans node_modules.

Standards de tokens

ERC20 — Tokens fongibles

ERC20 est le standard pour les tokens fongibles (cryptomonnaies, stablecoins, tokens de gouvernance).

Token ERC20 basique :

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.26;

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

contract MyToken is ERC20 {

constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {

_mint(msg.sender, initialSupply);

}

}

C'est tout ! Vous avez maintenant un token ERC20 pleinement fonctionnel avec :

  • totalSupply()
  • balanceOf(address)
  • transfer(address, uint256)
  • approve(address, uint256)
  • transferFrom(address, address, uint256)
  • allowance(address, address)

Ajouter le minting et burning :

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

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

contract MyToken is ERC20, Ownable {

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

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

_mint(to, amount);

}

function burn(uint256 amount) public {

_burn(msg.sender, amount);

}

}

Ajouter Pausable :

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

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

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

contract MyToken is ERC20, Pausable, Ownable {

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

function pause() public onlyOwner {

_pause();

}

function unpause() public onlyOwner {

_unpause();

}

function _update(address from, address to, uint256 value)

internal

override

whenNotPaused

{

super._update(from, to, value);

}

}

ERC721 — Tokens non-fongibles (NFTs)

ERC721 est le standard pour les tokens uniques, non-fongibles (NFTs).

NFT basique :

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

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

contract MyNFT is ERC721, Ownable {

uint256 private _tokenIdCounter;

constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {}

function mint(address to) public onlyOwner {

uint256 tokenId = _tokenIdCounter++;

_safeMint(to, tokenId);

}

}

Ajouter un URI de métadonnées :

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

contract MyNFT is ERC721URIStorage, Ownable {

uint256 private _tokenIdCounter;

constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {}

function mint(address to, string memory uri) public onlyOwner {

uint256 tokenId = _tokenIdCounter++;

_safeMint(to, tokenId);

_setTokenURI(tokenId, uri);

}

}

Ajouter Enumerable :

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

contract MyNFT is ERC721Enumerable, Ownable {

constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {}

// Maintenant vous obtenez :

// - totalSupply()

// - tokenByIndex(uint256 index)

// - tokenOfOwnerByIndex(address owner, uint256 index)

}

ERC1155 — Standard multi-tokens

ERC1155 prend en charge à la fois les tokens fongibles et non-fongibles dans un seul contrat.

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

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

contract MyMultiToken is ERC1155, Ownable {

uint256 public constant GOLD = 0;

uint256 public constant SILVER = 1;

uint256 public constant SWORD = 2;

constructor() ERC1155("https://game.example/api/item/{id}.json") Ownable(msg.sender) {}

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

_mint(to, id, amount, "");

}

function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) public onlyOwner {

_mintBatch(to, ids, amounts, "");

}

}

Contrôle d'accès

Ownable — Propriétaire unique

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

contract MyContract is Ownable {

constructor() Ownable(msg.sender) {}

function restrictedFunction() public onlyOwner {

// Seul le propriétaire peut appeler ceci

}

function transferOwnership(address newOwner) public override onlyOwner {

super.transferOwnership(newOwner);

}

}

AccessControl — Contrôle d'accès basé sur les rôles

Pour des systèmes multi-rôles (admin, minter, pauser, etc.) :

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

contract MyToken is AccessControl {

bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

constructor() {

_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

_grantRole(MINTER_ROLE, msg.sender);

}

function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {

_mint(to, amount);

}

function pause() public onlyRole(PAUSER_ROLE) {

_pause();

}

}

Ownable2Step — Transfert de propriété plus sûr

Empêche le transfert accidentel de propriété à la mauvaise adresse :

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

contract MyContract is Ownable2Step {

constructor() Ownable(msg.sender) {}

// Le propriétaire appelle transferOwnership(newOwner)

// Le nouveau propriétaire doit appeler acceptOwnership() pour confirmer

}

Utilitaires de sécurité

ReentrancyGuard — Prévenir les attaques de réentrance

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract Bank is ReentrancyGuard {

mapping(address => uint256) public balances;

function withdraw(uint256 amount) public nonReentrant {

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

balances[msg.sender] -= amount;

(bool success, ) = msg.sender.call{value: amount}("");

require(success);

}

}

Le modificateur nonReentrant empêche les appels réentrants pendant l'exécution.

Pausable — Arrêt d'urgence

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

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

contract MyContract is Pausable, Ownable {

constructor() Ownable(msg.sender) {}

function criticalFunction() public whenNotPaused {

// Ne peut pas être appelé en pause

}

function pause() public onlyOwner {

_pause();

}

function unpause() public onlyOwner {

_unpause();

}

}

SafeERC20 — Transferts de tokens sûrs

Certains tokens ne suivent pas correctement le standard ERC20 (USDT ne retourne pas bool). SafeERC20 gère ces cas limites :

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

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

contract Vault {

using SafeERC20 for IERC20;

function deposit(IERC20 token, uint256 amount) public {

token.safeTransferFrom(msg.sender, address(this), amount);

// Revient si le transfert échoue, même si le token ne retourne pas bool

}

}

Contrats upgradeables

OpenZeppelin fournit des versions upgradeables de tous les contrats utilisant le pattern proxy.

Installation

npm install @openzeppelin/contracts-upgradeable @openzeppelin/hardhat-upgrades

ERC20 upgradeable

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract MyTokenUpgradeable is Initializable, ERC20Upgradeable, OwnableUpgradeable {

/// @custom:oz-upgrades-unsafe-allow constructor

constructor() {

_disableInitializers();

}

function initialize(uint256 initialSupply) public initializer {

__ERC20_init("MyToken", "MTK");

__Ownable_init(msg.sender);

_mint(msg.sender, initialSupply);

}

}

Différences clés :

  • Hériter des versions *Upgradeable
  • Utiliser initialize() au lieu de constructor()
  • Appeler __ContractName_init() dans l'initialiseur
  • Désactiver les initialiseurs dans le constructeur

Déployer des contrats upgradeables

Avec Hardhat :

const { ethers, upgrades } = require("hardhat");

async function main() {

const MyToken = await ethers.getContractFactory("MyTokenUpgradeable");

const token = await upgrades.deployProxy(MyToken, [1000000], {

initializer: "initialize"

});

await token.waitForDeployment();

console.log("Token deployed to:", await token.getAddress());

}

Upgrader :

const MyTokenV2 = await ethers.getContractFactory("MyTokenUpgradeableV2");

const upgraded = await upgrades.upgradeProxy(proxyAddress, MyTokenV2);

Utilitaires

Cryptographie

Vérification de signature ECDSA :

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

contract SignatureVerifier {

using ECDSA for bytes32;

using MessageHashUtils for bytes32;

function verify(address signer, bytes32 messageHash, bytes memory signature) public pure returns (bool) {

bytes32 ethSignedHash = messageHash.toEthSignedMessageHash();

address recoveredSigner = ethSignedHash.recover(signature);

return recoveredSigner == signer;

}

}

Preuves Merkle :

import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

contract Whitelist {

bytes32 public merkleRoot;

constructor(bytes32 _merkleRoot) {

merkleRoot = _merkleRoot;

}

function claim(bytes32[] calldata proof) public {

bytes32 leaf = keccak256(abi.encodePacked(msg.sender));

require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof");

// Accorder l'accès

}

}

Counters (Déprécié, utiliser uint256 natif)

Dans les anciennes versions, Counters était utilisé pour les IDs auto-incrémentés. Solidity moderne peut le faire nativement :

contract NFT {

uint256 private _tokenIdCounter; // Moins cher que Counters

function mint() public {

uint256 tokenId = _tokenIdCounter++;

_safeMint(msg.sender, tokenId);

}

}

Utilitaires mathématiques

import "@openzeppelin/contracts/utils/math/Math.sol";

contract Calculator {

function average(uint256 a, uint256 b) public pure returns (uint256) {

return Math.average(a, b); // Empêche l'overflow

}

function sqrt(uint256 x) public pure returns (uint256) {

return Math.sqrt(x);

}

}

Bonnes pratiques

1. Utiliser la dernière version stable

npm install @openzeppelin/contracts@latest

Vérifiez les releases sur github.com/OpenZeppelin/openzeppelin-contracts/releases

2. Comprendre ce que vous importez

N'héritez pas aveuglément des contrats. Lisez le code source pour comprendre le comportement, surtout pour :

  • Les hooks _update() dans les tokens
  • Les fonctions virtuelles que vous pouvez surcharger
  • Les fonctions internes que vous pouvez appeler

3. Surcharger avec précaution

Lors de la surcharge de fonctions, appelez toujours super :

function _update(address from, address to, uint256 value) internal override {

super._update(from, to, value); // Appeler l'implémentation parente

// Votre logique personnalisée

}

4. Composer, ne pas modifier

Au lieu de modifier le code OpenZeppelin, composez les contrats :

// Bon

contract MyToken is ERC20, Ownable, Pausable {

// Composer des contrats existants

}

// Mauvais - modifier les internals OZ

contract MyToken is ERC20 {

function transfer() public override {

// Réécrire la logique de transfer = dangereux

}

}

5. Vérifier les mises à jour

Abonnez-vous aux avis de sécurité OpenZeppelin et mettez à jour régulièrement.

Wizard — Générer des contrats visuellement

OpenZeppelin Contracts Wizard génère des contrats prêts à déployer :

Visitez wizard.openzeppelin.com

  • Sélectionnez le type de contrat (ERC20, ERC721, ERC1155, Governor, Custom)
  • Choisissez les fonctionnalités (Mintable, Burnable, Pausable, Votes, etc.)
  • Configurez les paramètres
  • Copiez le code généré
  • Le wizard est parfait pour :

    • Apprendre quels contrats combiner
    • Démarrer rapidement de nouveaux projets
    • Comprendre la composition de contrats

    Conclusion

    OpenZeppelin Contracts est la fondation du développement sécurisé de smart contracts. En utilisant des implémentations éprouvées et auditées, vous évitez les vulnérabilités courantes et vous concentrez sur votre logique métier unique. Des tokens simples aux systèmes de gouvernance complexes, OpenZeppelin fournit les briques dont vous avez besoin.

    Commencez à développer en toute confiance — installez OpenZeppelin Contracts et explorez la bibliothèque. Puis affûtez vos compétences avec les défis smart contracts de Solingo pour maîtriser les patterns de développement sécurisé.

    Prochaines étapes :

    • Déployez votre premier token ERC20 avec OpenZeppelin
    • Explorez le pattern AccessControl pour les systèmes multi-rôles
    • Apprenez les contrats upgradeables et le pattern proxy

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement