L'Arme Secrète du Développeur Web3 : Maîtrisez le Staking et Fidélisez Votre Communauté

Table of contents
- Le dernier niveau de notre aventure Web3
- Le staking: au-delà du "parking à tokens"
- Préparons notre atelier de développement
- Notre token ERC-20 stakable
- Le contrat de staking - le boss final
- Déploiement et interaction: mettre notre création en vie
- Cas d'usage réels: où la théorie rencontre la pratique
- 🔒 Sécurité: protéger notre fort contre les barbares
- Au-delà du staking simple: des idées pour aller plus loin
- La fin d'un cycle, le début d'un autre

Le dernier niveau de notre aventure Web3
Bonjour les aventuriers du code! Nous y voilà - le dernier épisode de notre série "De Web2 à Web3"! Si vous me rejoignez pour la première fois, je vous invite vivement à explorer nos articles précédents pour ne rien manquer. Et si vous êtes un fidèle lecteur depuis le début, félicitations d'être arrivé jusqu'ici! 🎉
Aujourd'hui, nous abordons ce qui pourrait bien être la compétence la plus recherchée dans l'écosystème DeFi: la création d'un système de staking. Vous savez, ce mécanisme magique qui permet aux hodlers de gagner des récompenses pendant qu'ils dorment? Et oui, c'est un peu le rêve de tout développeur: faire travailler son code pendant qu'on boit tranquillement son café! ☕
🐦 Challenge Twitter: Avant de commencer, partagez votre vision du staking sur Twitter avec le hashtag #Web2àWeb3Staking. La définition la plus originale recevra une review personnalisée de son code par notre équipe! @ETHCongo
Le staking: au-delà du "parking à tokens"
Quand j'ai expliqué le staking à ma grand-mère, je lui ai dit: "Imagine que tu mettes ton argent dans une tirelire qui grossit toute seule". Elle m'a répondu: "Comme un livret A, mais qui rapporte vraiment quelque chose?" Exactement, mamie! 😂
Mais sérieusement, le staking est bien plus qu'un simple "parking à tokens". C'est un mécanisme fondamental qui:
Sécurise les réseaux Proof-of-Stake
Favorise la gouvernance participative
Crée des incitatifs économiques pour la fidélité
Permet la distribution contrôlée de nouveaux tokens
Si vous avez lu notre article "Pourquoi Ethereum et pas seulement Bitcoin ?", vous savez déjà pourquoi le passage au Proof-of-Stake était si crucial. Notre staking contract va nous permettre de toucher du doigt cette révolution.
Préparons notre atelier de développement
Avant de plonger dans le code, assurez-vous d'avoir:
Un environnement de développement Solidity (Remix ou Hardhat - voir "Installer Hardhat et créer son premier projet")
Une compréhension basique des tokens ERC-20
Une tasse de café bien chaude (élément non négociable)
Vous vous souvenez de notre article "Ethers.js pour les nuls"? Il nous sera utile pour tester notre contrat une fois déployé!
Notre token ERC-20 stakable
Commençons par créer notre jeton, celui que les utilisateurs pourront staker:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract JetonFinal is ERC20, Ownable {
// Petit clin d'œil à notre parcours ensemble!
string public constant SERIE_NAME = "De Web2 a Web3";
constructor(uint256 initialSupply) ERC20("Token Final", "FINAL") Ownable(msg.sender) {
_mint(msg.sender, initialSupply * 10 ** decimals());
}
// Pour créer des récompenses - pas d'inquiétude on évite les erreurs d'inflation!
function mint(address to, uint256 amount) public onlyOwner {
// Vous vous souvenez des "5 erreurs fréquentes de gas"?
// Pas de gaspillage ici!
_mint(to, amount);
}
}
Remarquez comment j'ai glissé une petite référence à notre série directement dans le contrat? C'est non seulement un clin d'œil aux fidèles lecteurs, mais aussi une bonne pratique pour la documentation on-chain!
Le contrat de staking - le boss final
Voici maintenant notre pièce de résistance - un contrat de staking complet qui récompense les utilisateurs fidèles:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract StakingFinal is ReentrancyGuard, Ownable {
using SafeERC20 for IERC20;
// Notre protagoniste: le token à staker
IERC20 public stakingToken;
// Notre récompense: potentiellement le même token
IERC20 public rewardToken;
// À quelle vitesse distribuer les récompenses?
uint256 public rewardRate;
// Horodatage blockchain pour les calculs
uint256 public lastUpdateBlock;
// Notre compteur de récompenses global
uint256 public rewardPerTokenStored;
// Suivi individuel des récompenses
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;
// Notre trésor collectif
uint256 public totalStaked;
// Les contributions individuelles
mapping(address => uint256) public balances;
// Période de "HODLing" obligatoire (en blocs)
uint256 public lockPeriod;
// Quand avez-vous commencé à staker?
mapping(address => uint256) public userLastStakeBlock;
// Notre journal d'aventures (events)
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 reward);
event LevelUp(address indexed user, uint256 newLevel);
// Système de niveau pour gamifier l'expérience
mapping(address => uint256) public userLevel;
constructor(
address _stakingToken,
address _rewardToken,
uint256 _rewardRate,
uint256 _lockPeriod
) Ownable(msg.sender) {
stakingToken = IERC20(_stakingToken);
rewardToken = IERC20(_rewardToken);
rewardRate = _rewardRate;
lockPeriod = _lockPeriod;
lastUpdateBlock = block.number;
}
// Notre formule magique de récompenses
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateBlock = block.number;
if (account != address(0)) {
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
}
_;
}
// Calcul des récompenses - la partie mathématique
function rewardPerToken() public view returns (uint256) {
if (totalStaked == 0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored + (
((block.number - lastUpdateBlock) * rewardRate * 1e18) / totalStaked
);
}
// Combien avez-vous gagné jusqu'à présent?
function earned(address account) public view returns (uint256) {
return (
(balances[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18
) + rewards[account];
}
// Déposer vos tokens et rejoindre l'aventure
function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {
// Pas de microtransactions! (Vous vous rappelez notre article sur les optimisations?)
require(amount > 0, "On ne stake pas des miettes!");
totalStaked += amount;
balances[msg.sender] += amount;
userLastStakeBlock[msg.sender] = block.number;
// Système de niveau: 1000 tokens = 1 niveau
uint256 newLevel = balances[msg.sender] / 1000 ether;
if (newLevel > userLevel[msg.sender]) {
userLevel[msg.sender] = newLevel;
emit LevelUp(msg.sender, newLevel);
}
// Transfert sécurisé comme vu dans "Comprendre les failles fréquentes"
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}
// Récupérer vos tokens - si la période de lock est terminée
function withdraw(uint256 amount) external nonReentrant updateReward(msg.sender) {
require(amount > 0, "On ne retire pas des miettes!");
require(balances[msg.sender] >= amount, "Pas assez de tokens stakés");
require(
block.number >= userLastStakeBlock[msg.sender] + lockPeriod,
"Patience! La période de blocage n'est pas terminée"
);
totalStaked -= amount;
balances[msg.sender] -= amount;
stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
// Réclamer vos récompenses durement gagnées
function getReward() external nonReentrant updateReward(msg.sender) {
uint256 reward = rewards[msg.sender];
if (reward > 0) {
rewards[msg.sender] = 0;
rewardToken.safeTransfer(msg.sender, reward);
// Petit easter egg pour nos lecteurs attentifs ;)
if (reward > 100 ether && userLevel[msg.sender] >= 3) {
// On émet un événement spécial pour les meilleurs stakers
emit RewardPaid(msg.sender, reward + 1); // +1 comme un petit clin d'œil
} else {
emit RewardPaid(msg.sender, reward);
}
}
}
// Fonction d'urgence - comme vu dans "Les meilleures pratiques"
function emergencyWithdraw(address token, uint256 amount) external onlyOwner {
IERC20(token).safeTransfer(owner(), amount);
}
// Ajuster le taux de récompense
function setRewardRate(uint256 _rewardRate) external onlyOwner updateReward(address(0)) {
rewardRate = _rewardRate;
}
// Modifier la période de blocage
function setLockPeriod(uint256 _lockPeriod) external onlyOwner {
lockPeriod = _lockPeriod;
}
// Une petite fonction pour vérifier quand vous pourrez débloquer vos tokens
function timeUntilUnlock(address user) external view returns (uint256) {
uint256 endBlock = userLastStakeBlock[user] + lockPeriod;
if (block.number >= endBlock) {
return 0;
}
return endBlock - block.number;
}
}
Déploiement et interaction: mettre notre création en vie
Oubliez ces démos ennuyeuses! Imaginons plutôt un scénario réel:
Alice, développeuse web3 française, vient de lancer son projet "CroissantDAO" - une organisation décentralisée qui permet à ses membres de voter sur les meilleurs croissants de Paris. Pour fidéliser sa communauté et distribuer le token $CROISSANT, elle déploie notre contrat de staking.
Voici comment Alice procèderait:
Elle déploie d'abord le token
JetonFinal
avec une offre initiale de 1,000,000 $CROISSANTElle déploie ensuite
StakingFinal
avec:_stakingToken
: l'adresse du token $CROISSANT_rewardToken
: la même adresse (récompenses en $CROISSANT)_rewardRate
: 10 tokens par bloc (environ 2,5 $CROISSANT par minute)_lockPeriod
: 40320 blocs (environ 1 semaine)
Elle transfère 200,000 $CROISSANT au contrat de staking pour les récompenses
// Côté frontend avec ethers.js
// Rappelez-vous notre article "Ethers.js pour les nuls"?
async function stake(amount) {
// Approve first
await tokenContract.approve(stakingContract.address, amount);
// Then stake
await stakingContract.stake(amount);
console.log(`Vous avez staké ${ethers.utils.formatEther(amount)} $CROISSANT!`);
console.log(`Niveau actuel: ${await stakingContract.userLevel(signer.address)}`);
}
🐦 Mini-jeu Twitter: Quel niveau pensez-vous pouvoir atteindre avec notre système de staking? Postez votre objectif avec #Web2àWeb3Niveau! @ETHCongo
Cas d'usage réels: où la théorie rencontre la pratique
CroissantDAO (notre exemple fictif)
Alice utilise notre contrat pour:
Récompenser les membres actifs qui participent aux votes
Créer un sentiment d'appartenance avec le système de niveaux
Stabiliser le prix du token en encourageant le verrouillage
"C'est comme si j'avais créé un mini-Masterchef de SushiSwap, mais en version pâtisserie française!" - s'exclame Alice lors d'un meetup parisien.
Une expérience réelle: La DeFi française
J'ai récemment travaillé avec une startup française qui développait une solution DeFi pour les PME. Leur mécanisme de staking, similaire au nôtre, permettait aux entreprises de "prouver leur peau dans le jeu" avant d'accéder à certains services financiers.
Le plus intéressant? Les périodes de lock variables selon le niveau de l'utilisateur. Plus vous êtes fidèle, plus votre période de verrouillage diminue - exactement l'inverse de ce qu'on voit habituellement!
🔒 Sécurité: protéger notre fort contre les barbares
Vous vous souvenez de notre article "Comprendre les failles fréquentes dans les smart contracts"? Appliquons ces leçons:
Protection contre la réentrance: notre fidèle
nonReentrant
garde l'entréefunction stake(uint256 amount) external nonReentrant updateReward(msg.sender) { // Contenu sécurisé }
SafeERC20: parce que tous les tokens ne sont pas nés égaux
using SafeERC20 for IERC20; // ... stakingToken.safeTransferFrom(msg.sender, address(this), amount);
Gestion des précisions: notre multiplication puis division par 1e18 évite les erreurs d'arrondi
Bouton d'urgence: comme tout bon contrat devrait avoir
function emergencyWithdraw(address token, uint256 amount) external onlyOwner { // En cas d'incendie, briser la vitre }
🛡️ Pro-tip: Avant tout déploiement en production, faites auditer votre contrat! Même les meilleurs développeurs peuvent manquer une faille. Comme on dit dans la DeFi: "Vérifiez deux fois, déployez une fois!"
Au-delà du staking simple: des idées pour aller plus loin
Notre contrat est déjà impressionnant, mais voici comment vous pourriez l'améliorer (peut-être un sujet pour votre propre article?):
Staking avec boost NFT: Intégrez notre contrat avec l'article "Défi : Écris ton premier NFT ERC-721" pour créer des boosts de staking basés sur des NFT!
Staking multi-tokens: Permettez aux utilisateurs de staker plusieurs types de tokens avec différents poids
Gamification avancée: Implémentez un système complet de quêtes et récompenses comme dans un RPG
Gouvernance pondérée: Utilisez les niveaux de staking pour la gouvernance, comme expliqué dans "Comprendre les comptes externes vs contrats intelligents"
🐦 Question Twitter: Quelle extension de ce contrat de staking aimeriez-vous voir dans un prochain article? Votez avec #Web2àWeb3ExtensionETHCongo
La fin d'un cycle, le début d'un autre
Nous voilà arrivés au terme de notre série "De Web2 à Web3". J'espère que cette aventure vous a donné les outils et la confiance nécessaires pour vous lancer dans l'écosystème blockchain en tant que développeur. Du simple token ERC-20 au système de staking complet, vous avez désormais les bases pour créer vos propres applications décentralisées.
Ce que j'adore dans le développement blockchain, c'est qu'on apprend toujours. Comme je le dis souvent à mes étudiants: "En Web2, vous construisez sur des fondations établies. En Web3, vous êtes les architectes des fondations."
Si vous avez suivi tous nos articles, de "Pourquoi Ethereum et pas seulement Bitcoin ?" jusqu'à aujourd'hui, vous avez accompli un parcours impressionnant. Bravo!
🐦 Dernière action: Partagez votre certification virtuelle! Postez une capture d'écran de votre contrat déployé avec #Web2àWeb3Diplômé et taguez @ETHCongo. Les 5 meilleurs projets seront présentés dans notre prochaine newsletter!
À bientôt pour de nouvelles aventures dans le métavers du code! 🚀
Subscribe to my newsletter
Read articles from Daniel Kambale directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Daniel Kambale
Daniel Kambale
Hello! I’m Daniel, a Web3 developer specializing in Solidity and smart contract development. My journey in the blockchain space is driven by a vision of a fully decentralized world, where technology empowers individuals and transforms industries. As a Web3 ambassador , I’m committed to fostering growth and innovation in this space, helping to shape a future that values transparency and security. Fluent in both English and French, I enjoy connecting with diverse communities and sharing my insights across languages. This is why you’ll find some of my articles in French, while others are in Swahili, as I believe knowledge should be accessible to all. I use my Hashnode blog to document my learning process, explore decentralized solutions, and share practical tutorials on Web3 development. Whether it's diving deep into Solidity, discussing the latest in blockchain, or exploring new tools, I’m passionate about contributing to a decentralized future and connecting with others who share this vision. Let’s build the future together!