O que é Staking
Staking é o bloqueio de suas criptomoedas por um período de tempo específico e, durante esse período, você pode ganhar juros.
Recentemente, a Ethereum passou do mecanismo de prova de trabalho (PoW) para o mecanismo de consenso de prova de participação (PoS). Aqui, o Staker (quem faz staking) bloqueará sua criptomoeda e poderá ganhar juros sobre ela, pois a blockchain colocará os valores que estão em stake para trabalhar para validar as transações.
NOTA: Presumo que você tenha alguma experiência com Solidity, React e Truffle. Se você está apenas começando, verifique nosso blog anterior.
Fluxo do Aplicativo
- O usuário irá fazer stake e desfazer o stake do montante.
- O staker precisa fazer stake antes de um horário específico.
- O staker precisa desfazer o stake (retirada) antes de um período específico.
- O staker ganhará juros durante o tempo de staking.
- Após o período de bloqueio, o staker pode desfazer o stake (retirar) o montante em stake + os juros auferidos.
Excelente, agora vamos botar a mão na massa com o React e o Solidity.
Este tutorial é inspirado no dapp demo alchemy.
- Criar um novo aplicativo React
yarn create react-app stacking
- Mover para a pasta
cd stacking
- Inicializar o Truffle
truffle init
- Substituir
truffle-config.js
pelo código abaixo
module.exports = {
networks: {
development: {
host: "127.0.0.1", // Localhost (padrão: none)
port: 8545, // Porta padrão Ethereum (padrão:none)
network_id: "*", // Qualquer rede (padrão: none)
},
},
contracts_directory: "./src/contracts",
contracts_build_directory: "./src/contracts/build",
// Configure seus compiladores
compilers: {
solc: {
version: "0.8.16",
settings: {
optimizer: {
enabled: false,
runs: 200
},
}
}
}
};
- Mover a pasta do contrato src/contracts sob o diretório src
- Criar um novo contrato Staking.sol
- É necessário rastrear o montante em stake e a hora em que o usuário fez stake do montante, então precisamos de 2 mapeamentos
mapping(address=>uint) balances;
mapping(address=>uint) stackingTime;
- É necessário especificar o tempo limite do staking.
uint stakingDeadline = block.timestamp + 1 minutes;
- É necessário especificar o horário do prazo de retirada
uint withdrawalDeadline = block.timestamp + 2 minutes;
- Ponto de recompensa por segundo
uint constant rewardRatePerSecond = 0.1 ether
- Eventos de staking e retirada
event Stake(address indexed sender, uint256 amount);
event Withdrawal(address, uint);
- Função Stack
function stack() public payable {
uint stakingTimeLeft = stakingDeadline - block.timestamp;
require(stakingTimeLeft>0, "Staking deadline has been reached");
require(msg.value>0, "Amount should be > 0");
uint stackedAmount = balances[msg.sender];
require(stackedAmount==0, "Can not stack 2nd time");
balances[msg.sender] = msg.value;
stackingTime[msg.sender] = block.timestamp;
emit Stake(msg.sender, msg.value);
}
- Função Withdrawal
function withdraw() public payable {
//não pode Retirar até o tempo de stacking acabar
uint stakingTimeLeft = block.timestamp - stakingDeadline;
require(stakingTimeLeft>0, "Staking deadline has been reached");
//não pode Retirar depois de withdrawalDealline
uint withdrawalTimeLeft = withdrawalDeadline - block.timestamp;
require(withdrawalTimeLeft>0, "Withdrawal deadline has been reached");
//obtenha o montante em stake e verifique que deve ser > 0
uint stackedAmount = balances[msg.sender];
require(stackedAmount>0, "you have no balance to withdraw");
// calcule os juros ganhos durante o tempo de staking.
uint amountWithInterest = stackedAmount + ((block.timestamp - stackingTime[msg.sender]) * rewardRatePerSecond);
balances[msg.sender] = 0;
(bool sent,) = msg.sender.call{value: amountWithInterest}("");
require(sent, "RIP; withdrawal failed :( ");
emit Withdrawal(msg.sender, amountWithInterest);
}
- Arquivo final Staking.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Staking {
mapping(address=>uint) balances;
mapping(address=>uint) stackingTime;
uint stakingDeadline = block.timestamp + 1 minutes;
uint withdrawalDeadline = block.timestamp + 2 minutes;
uint constant rewardRatePerSecond = 0.0000001 ether;
// Eventos
event Stake(address indexed sender, uint256 amount);
event Withdrawal(address, uint);
function stack() public payable {
uint stakingTimeLeft = stakingDeadline - block.timestamp;
require(stakingTimeLeft>0, "Staking deadline has been reached");
require(msg.value>0, "Amount should be > 0");
uint stackedAmount = balances[msg.sender];
require(stackedAmount==0, "Can not stack 2nd time");
balances[msg.sender] = msg.value;
//precisamos obter tempo para calcular os juros
stackingTime[msg.sender] = block.timestamp;
emit Stake(msg.sender, msg.value);
}
function withdraw() public payable {
//não pode Retirar até que o tempo de staking termine
uint stakingTimeLeft = block.timestamp - stakingDeadline;
require(stakingTimeLeft>0, "Staking is still in progress");
//não pode Retirar após withdrawalDealline
uint withdrawalTimeLeft = withdrawalDeadline - block.timestamp;
require(withdrawalTimeLeft>0, "Withdrawal deadline has been reached");
//obtenha o montante em stake e verifique que deve ser > 0
uint stackedAmount = balances[msg.sender];
require(stackedAmount>0, "you have no balance to withdraw");
// calcular os juros ganhos durante o tempo de staking.
uint amountWithInterest = stackedAmount + ((block.timestamp - stackingTime[msg.sender]) * rewardRatePerSecond);
balances[msg.sender] = 0;
(bool sent,) = msg.sender.call{value: amountWithInterest}("");
require(sent, "RIP; withdrawal failed :( ");
emit Withdrawal(msg.sender, amountWithInterest);
}
function getStakedBalance() public view returns (uint) {
return address(this).balance;
}
function myStakedAmount() public view returns (uint) {
return balances[msg.sender];
}
}
- Crie um 2_deploy.js na pasta migrações
const Staking = artifacts.require("Staking");
module.exports = function (deployer) {
deployer.deploy(Staking);
};
- Compilar Smartcontract
truffle compile
- Implante o contrato, certifique-se de que a Ganache esteja sendo executado localmente
truffle deploy
EXCELENTE! Seu contrato está implantado na Ganache agora
- Instale ether.js
npm i ethers
- Substitua App.js pelo código abaixo
import './App.css';
import {ethers} from 'ethers';
import { Component } from 'react';
import Staking from './contracts/build/Staking.json'
class App extends Component {
render() {
return (
<div className="App">
Hello
</div>
);
};
}
export default App;
Vamos escrever o código de conexão à metamask que você pode consultar aqui
Agora precisamos escrever funções para interagir com o contrato inteligente
Obtenha o saldo total de stake para retornar o valor total em stake no endereço do contrato inteligente
getStakedBalance = async () => {
const contract = this.state.contract;
const stakedBalance = await contract.getStakedBalance();
const balanceInEth = ethers.utils.formatEther(stakedBalance);
this.setState({stakedBalance:balanceInEth});
}
- Montante em stake
stake = async() => {
const contract = this.state.contract;
await contract.stack({from:this.state.account,value:ethers.utils.parseEther(this.state.stakeAmount)});
}
- Montante de retirada
withdraw = async() => {
const contract = this.state.contract;
await contract.withdraw({from:this.state.account});
}
- myStakedAmount que exibe o valor total em stake por login de usuário
myStakedAmount = async() => {
const contract = this.state.contract;
const amount = await contract.myStakedAmount({from:this.state.account});
const balanceInEth = ethers.utils.formatEther(amount);
this.setState({myStakedAmount:balanceInEth});
}
- Arquivo App.js completo
import './App.css';
import {ethers} from 'ethers';
import { Component } from 'react';
import Staking from './contracts/build/Staking.json'
class App extends Component {
constructor(props) {
super(props);
this.state = {
provider: null,
account: null,
contract: null,
myBalance:0,
stakedBalance:0,
stakeAmount:0,
myStakedAmount:0,
}
}
async componentDidMount() {
await this.loadWallet();
await this.setContract();
};
loadWallet = async () => {
// Um Web3Provider envolve um provedor Web3 padrão, que é
// o que MetaMask injeta como window.ethereum em cada página
const provedor = novo
const provider = new ethers.providers.Web3Provider(window.ethereum);
// // MetaMask requer permissão para conectar contas de usuários await provider.send("eth_requestAccounts", []);
const accounts = await provider.listAccounts();
const account = accounts[0];
const balance = await provider.getBalance(account);
const balanceInEth = ethers.utils.formatEther(balance);
this.setState({provider: provider, account: account, myBalance: balanceInEth});
}
setContract = async() => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const { chainId } = await provider.getNetwork();
const networkData = Staking.networks[5777];
if(networkData) {
const abi = Staking.abi;
const contractAddress = networkData.address;
const signer = provider.getSigner();
const contract = await new ethers.Contract(contractAddress, abi, signer);
this.setState({contract:contract});
}
}
getStakedBalance = async () => {
const contract = this.state.contract;
const stakedBalance = await contract.getStakedBalance();
const balanceInEth = ethers.utils.formatEther(stakedBalance);
this.setState({stakedBalance:balanceInEth});
}
stake = async() => {
const contract = this.state.contract;
await contract.stack({from:this.state.account,value:ethers.utils.parseEther(this.state.stakeAmount)});
}
withdraw = async() => {
const contract = this.state.contract;
await contract.withdraw({from:this.state.account});
}
myStakedAmount = async() => {
const contract = this.state.contract;
const amount = await contract.myStakedAmount({from:this.state.account});
const balanceInEth = ethers.utils.formatEther(amount);
this.setState({myStakedAmount:balanceInEth});
}
handleChange = event => {
this.setState({stakeAmount:event.target.value});
};
render() {
return (
<div className="App">
Hello {this.state.account}
<div>
<b>Balance:</b> {this.state.myBalance}
</div>
<div>
<b>My Staked Amout:</b> {this.state.myStakedAmount} <button onClick={this.myStakedAmount}>Refresh</button>
</div>
<div>
<b>Total Staked Amount: </b> {this.state.stakedBalance} <button onClick={this.getStakedBalance}>Refresh</button>
</div>
<div>
<div>
<input type="text" id="amount" name="amount" onChange={this.handleChange} value={this.state.stakeAmount} autoComplete="off"></input>
<button onClick={this.stake}>Stake</button>
</div>
<button onClick={this.withdraw}>Withdraw</button>
</div>
</div>
);
};
}
export default App;
- Execute o aplicativo React usando
yarn start
- Você pode adicionar várias contas na metamask e testar a funcionalidade do aplicativo. Faça o teste de stake e retirada no seu fim.
CÓDIGO GitHub: https://github.com/HiteshR90/Staking
Outro código para explorar: https://github.com/HiteshR90/defi-staking-app
Autor: Hitesh Umaletiya
Siga-nos em https://www.linkedin.com/company/brilworks
Entre em contato https://www.brilworks.com/contact-us/
Artigo escrito por Brilworks e traduzido por Marcelo Panegali.
Latest comments (0)