Nesse artigo iremos aprender a como criar um sistema de Staking de Recompensas para seu projeto, token ou jogo NFT.
Recompensas através de Staking
Este é um exemplo básico de um contrato que recompensa os usuários por estacarem seu token por um período de tempo, muito semelhante a um sistema de recompensas por juros.
O código é uma versão simplificada do Synthetix StakingRewards.sol
// SPDX-License-Identifier: MITpragma solidity ^0.8;contract StakingRewards {IERC20 public immutable stakingToken;IERC20 public immutable rewardsToken;address public owner;// Duração de staking para sacar as recompensas (em segundos)uint public duration;// Timestamp de quando as recompensas terminamuint public finishAt;// Mínimo da última hora atualizada e hora de término da recompensauint public updatedAt;// Reward to be paid out per seconduint public rewardRate;// Soma de (taxa de recompensa * dt * 1e18 / oferta total)uint public rewardPerTokenStored;// Enedereço do usuário => rewardPerTokenStoredmapping(address => uint) public userRewardPerTokenPaid;// Enedereço do usuário => rewards to be claimedmapping(address => uint) public rewards;// Total estocado (staked)uint public totalSupply;// Enedereço do usuário => staked amountmapping(address => uint) public balanceOf;constructor(address _stakingToken, address _rewardToken) {owner = msg.sender;stakingToken = IERC20(_stakingToken);rewardsToken = IERC20(_rewardToken);}modifier onlyOwner() {require(msg.sender == owner, "não autorizado");_;}modifier updateReward(address _account) {rewardPerTokenStored = rewardPerToken();updatedAt = lastTimeRewardApplicable();if (_account != address(0)) {rewards[_account] = earned(_account);userRewardPerTokenPaid[_account] = rewardPerTokenStored;}_;}function lastTimeRewardApplicable() public view returns (uint) {return _min(finishAt, block.timestamp);}function rewardPerToken() public view returns (uint) {if (totalSupply == 0) {return rewardPerTokenStored;}returnrewardPerTokenStored +(rewardRate * (lastTimeRewardApplicable() - updatedAt) * 1e18) /totalSupply;}function stake(uint _amount) external updateReward(msg.sender) {require(_amount > 0, "quantia = 0");stakingToken.transferFrom(msg.sender, address(this), _amount);balanceOf[msg.sender] += _amount;totalSupply += _amount;}function withdraw(uint _amount) external updateReward(msg.sender) {require(_amount > 0, "quantia = 0");balanceOf[msg.sender] -= _amount;totalSupply -= _amount;stakingToken.transfer(msg.sender, _amount);}function earned(address _account) public view returns (uint) {return((balanceOf[_account] *(rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e18) +rewards[_account];}function getReward() external updateReward(msg.sender) {uint reward = rewards[msg.sender];if (reward > 0) {rewards[msg.sender] = 0;rewardsToken.transfer(msg.sender, reward);}}function setRewardsDuration(uint _duration) external onlyOwner {require(finishAt < block.timestamp, "duração da recompensa não concluída");duration = _duration;}function notifyRewardAmount(uint _amount)externalonlyOwnerupdateReward(address(0)){if (block.timestamp >= finishAt) {rewardRate = _amount / duration;} else {uint remainingRewards = (finishAt - block.timestamp) * rewardRate;rewardRate = (_amount + remainingRewards) / duration;}require(rewardRate > 0, "taxa de recompensa = 0");require(rewardRate * duration <= rewardsToken.balanceOf(address(this)),"valor da recompensa > saldo");finishAt = block.timestamp + duration;updatedAt = block.timestamp;}function _min(uint x, uint y) private pure returns (uint) {return x <= y ? x : y;}}interface IERC20 {function totalSupply() external view returns (uint);function balanceOf(address account) external view returns (uint);function transfer(address recipient, uint amount) external returns (bool);function allowance(address owner, address spender) external view returns (uint);function approve(address spender, uint amount) external returns (bool);function transferFrom(address sender,address recipient,uint amount) external returns (bool);event Transfer(address indexed from, address indexed to, uint value);event Approval(address indexed owner, address indexed spender, uint value);}
Onde o Staking pode ser utilizado?
Em qualquer projeto onde você deseja recompensar o usuário com seu próprio token.
Como por exemplo, um jogo NFT, onde se o jogador fizer Staking de 100 moedas, ele ganhe uma recompensa de 1% ao mês por exemplo.
Outro exemplo, um sistema de VIP, onde se ele fizer Staking de 1000 moedas, ele ganha alguns benefícios durante o período de Staking.