WEB3DEV

Cover image for Como Escrever um Contrato Inteligente para Staking de Tokens
Panegali
Panegali

Posted on

Como Escrever um Contrato Inteligente para Staking de Tokens

Entenda como funciona o staking e como criar um contrato inteligente para staking de tokens.

Hoje estamos ouvindo muitas palavras novas no mundo Blockchain e staking é uma delas. As pessoas usam o conceito de staking geralmente para engajamento. Hoje vamos entender os pontos abaixo sobre staking.

  1. O que é Staking?
  2. Como escrever um contrato inteligente para staking de tokens?
  3. Como funciona o Processo de Staking?
  4. Implantação na rede de teste Rinkeby.

O que é Staking?

  • Staking é uma forma de ganhar uma renda passiva extra por juros em sua criptomoeda, depositando-a por um período fixo ou dinâmico.
  • Atualmente, algumas empresas utilizam o termo staking para engajamento.
  • Nele, você coloca seu token ou NFT em stake e em troca recebe uma recompensa (depende do tempo que você deixou em stake).

Como Escrever um Contrato Inteligente para Staking de Tokens?

Observe as etapas abaixo:

  • Criamos o Token ERC-20 Apple (APL) de exemplo.
  • Ofereceremos fazer o stake do token por 3 meses (você pode mantê-lo por quanto tempo quiser).
  • Após 3 meses, você receberá a recompensa de 32% do token que estava em stake. Exemplo: Se você colocar 100 tokens em stake, após 3 meses você receberá 132 tokens no total.

Processo do Contrato de Staking

  • Primeiro, transferimos alguns tokens APL para o endereço do contrato de staking.
  • Chamamos o método approve no contrato ERC20 com dois parâmetros. O primeiro endereço do contrato de staking e o segundo é o montante (aqui nós damos 50% do fornecimento total).
  • Chamamos o método stakeToken no contrato de Staking com o parâmetro de montante (Montante que você deseja fazer stake).
  • Após o término do período de staking, você pode chamar o método claimReward no contrato de staking e receberá seu montante com juros.

Aqui estão os passos para fazer isso acontecer no mundo blockchain.

  • Criar o token ERC-20 APPLE (APL)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract AppleToken is ERC20 {

    uint8 constant _decimals = 18;
    uint256 constant _totalSupply = 100 * (10**6) * 10**_decimals;  // 100 milhões de tokens para distribuição

    constructor() ERC20("Apple", "APL") {        
        _mint(msg.sender, _totalSupply);
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Vamos criar um contrato StakeAPL com os dois métodos importantes
  1. Método stakeToken
  2. Método ClaimReward
  • Método StakeToken: O usuário passa o montante em um parâmetro (está no formato Wei). Este método verifica todas as condições válidas, então transfere o montante em stake para um contrato e armazena esta entrada na estrutura e emite o evento Staked.
require(stakeAmount >0, "O montante em stake deve estar correto");
require(block.timestamp < planExpired , "Plano Expirado");
require(addressStaked[_msgSender()] == false, "Você já participou");
require(aplToken.balanceOf(_msgSender()) >= stakeAmount, "Saldo insuficiente");
aplToken.transferFrom(_msgSender(), address(this), stakeAmount);
stakeInfos[_msgSender()] = StakeInfo({
           startTS: block.timestamp,
           endTS: block.timestamp + planDuration,
           amount: stakeAmount,
           claimed: 0 });
emit Staked(_msgSender(), stakeAmount);
Enter fullscreen mode Exit fullscreen mode
  • Método claimReward: O usuário recebe a recompensa após o término da duração do staking. Primeiro, obtemos o montante que o usuário colocou em stake e, em seguida, calculamos a porcentagem de juros e o somamos. Transfira este montante para o endereço do usuário e emita o evento Claimed.
uint256 stakeAmount = stakeInfos[_msgSender()].amount;
uint256 totalTokens = stakeAmount + (stakeAmount * interestRate / 100);
stakeInfos[_msgSender()].claimed == totalTokens;
aplToken.transfer(_msgSender(), totalTokens);
emit Claimed(_msgSender(), totalTokens);
Enter fullscreen mode Exit fullscreen mode

Como funciona o processo de Staking?

  • Já temos um contrato ERC-20 (Token APL) e esse endereço de contrato passa para o tempo de implantação do construtor (constructor) StakeAPL.
  • Transfira alguns tokens APL para o contrato StakeAPL.
  • O segundo usuário (que possui o token APL) chama o método approve do APL Token e dá a aprovação para o endereço do contrato de Staking com o valor do staking.
  • Chame o método stakeToken do contrato de Staking com o montante como parâmetro e pronto. Depois de amadurecer o tempo de staking, você pode chamar o método claimReward. Você receberá o montante com juros.

Implantação na Rede de Teste Rinkeby

// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.11;

import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Context.sol";

interface Token {
    function transfer(address recipient, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (uint256);    
}

contract StakeAPL is Pausable, Ownable, ReentrancyGuard {

    Token aplToken;

    // 30 Dias (30 * 24 * 60 * 60)
    uint256 public planDuration = 2592000;

    // 180 Dias (180 * 24 * 60 * 60)
    uint256 _planExpired = 15552000;

    uint8 public interestRate = 32;
    uint256 public planExpired;
    uint8 public totalStakers;

    struct StakeInfo {        
        uint256 startTS;
        uint256 endTS;        
        uint256 amount; 
        uint256 claimed;       
    }

    event Staked(address indexed from, uint256 amount);
    event Claimed(address indexed from, uint256 amount);

    mapping(address => StakeInfo) public stakeInfos;
    mapping(address => bool) public addressStaked;


    constructor(Token _tokenAddress) {
        require(address(_tokenAddress) != address(0),"O endereco do Token nao pode ser 0");                
        aplToken = _tokenAddress;        
        planExpired = block.timestamp + _planExpired;
        totalStakers = 0;
    }    

    function transferToken(address to,uint256 amount) external onlyOwner{
        require(aplToken.transfer(to, amount), "A transferencia do token falhou!");  
    }

    function claimReward() external returns (bool){
        require(addressStaked[_msgSender()] == true, "Voce nao esta participando");
        require(stakeInfos[_msgSender()].endTS < block.timestamp, "O tempo de stake ainda nao terminou");
        require(stakeInfos[_msgSender()].claimed == 0, "Ja reivindicado");

        uint256 stakeAmount = stakeInfos[_msgSender()].amount;
        uint256 totalTokens = stakeAmount + (stakeAmount * interestRate / 100);
        stakeInfos[_msgSender()].claimed == totalTokens;
        aplToken.transfer(_msgSender(), totalTokens);

        emit Claimed(_msgSender(), totalTokens);

        return true;
    }

    function getTokenExpiry() external view returns (uint256) {
        require(addressStaked[_msgSender()] == true, "Voce nao esta participando");
        return stakeInfos[_msgSender()].endTS;
    }

    function stakeToken(uint256 stakeAmount) external payable whenNotPaused {
        require(stakeAmount >0, "O montante em stake deve estar correto");
        require(block.timestamp < planExpired , "Plano Expirado");
        require(addressStaked[_msgSender()] == false, "Voce ja participou");
        require(aplToken.balanceOf(_msgSender()) >= stakeAmount, "Saldo Insuficiente");

           aplToken.transferFrom(_msgSender(), address(this), stakeAmount);
            totalStakers++;
            addressStaked[_msgSender()] = true;

            stakeInfos[_msgSender()] = StakeInfo({                
                startTS: block.timestamp,
                endTS: block.timestamp + planDuration,
                amount: stakeAmount,
                claimed: 0
            });

        emit Staked(_msgSender(), stakeAmount);
    }    


    function pause() external onlyOwner {
        _pause();
    }

    function unpause() external onlyOwner {
        _unpause();
    }
}
Enter fullscreen mode Exit fullscreen mode

Artigo escrito por Arnish Gupta e traduzido por Marcelo Panegali

Top comments (0)