# 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 deconstructor()
- 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
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
- Lisez la documentation OpenZeppelin sur docs.openzeppelin.com