Atualizando o contrato inteligente
Nas aulas do tópico "Crie seu primeiro contrato", criamos apenas algumas funções básicas para mostrar como funcionava a criação do contrato inteligente de forma simples.
Nessa aula, a ideia é adicionar as funções de criação do post no Twitter Descentralizado. Para isso, criaremos uma função responsável por criar uma publicação no Twitter descentralizado e, para cada post criado, você tem a chance de ganhar um prêmio de 0.0001 Ethereum.
Esse prêmio é só uma brincadeira para fazer as pessoas publicarem mais, sinta-se à vontade para remover.
Além disso, adicionaremos uma regra que restringe a criação de um post a cada 30 segundos, a fim de evitar spam.
Para começar, abra no projeto x-smart-contracts
, abra o arquivo contracts/XPost.sol
e cole o seguinte código:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import "hardhat/console.sol";
contract XPost {
uint256 totalPosts;
uint256 private seed;
event NewPost(address indexed from, uint256 timestamp, string message);
struct Post {
address sender;
string message;
uint256 timestamp;
}
Post[] posts;
// Esse mapping vai nos auxiliar para salvar a próxima data que o usuário poderá postar, pois cada usuário só poderá postar a cada 30 segundos (para diminuir spam)
mapping(address => uint256) public lastPostedAt;
constructor() payable {
console.log("O contrato foi criado!");
// Essa "seed" é um número aleatório entre 1 e 100 que vai determinar se o usuário vai ou não ganhar um prêmio no post que ele fez
seed = (block.timestamp + block.prevrandao) % 100;
}
function createPost(string memory _message) public {
// Lembra do mapping? Aqui a gente tá conferindo se o mesmo usuário (mesmo endereço) está tentando criar mais de um post em menos de 30 segundos
require(lastPostedAt[msg.sender] + 30 seconds < block.timestamp, "Must wait 30 seconds before waving again.");
// Aqui a gente tá salvando a data e hora do último post para ser usado nessa função de cima
lastPostedAt[msg.sender] = block.timestamp;
totalPosts += 1;
console.log("%s criou um post!", msg.sender);
// Aqui nós estamos salvando o post na blockchain, todos os posts salvos poderão ser lidos no front-end
posts.push(Post(msg.sender, _message, block.timestamp));
// Aqui nós estamos atualizando a "seed" (número aleatório) para a descobrir se a pessoa ganhou ou não o prêmio
seed = (block.prevrandao + block.timestamp + seed) % 100;
if (seed <= 50) {
console.log("%s ganhou um premio de 0.0001 Ethereum!", msg.sender);
uint256 prizeAmount = 0.0001 ether;
require(
prizeAmount <= address(this).balance,
"O contrato nao tem fundos suficience para pagar o premio."
);
(bool success, ) = (msg.sender).call{value: prizeAmount}("");
require(success, "Nao foi possivel enviar o premio.");
}
// Toda vez que alguém criar um post, esse evento NewPost vai ser disparado e poderá ser "escutado" pelo front-end para atualizar os posts em "tempo real"
emit NewPost(msg.sender, block.timestamp, _message);
}
function getAllPosts() public view returns (Post[] memory) {
return posts;
}
function getTotalPosts() public view returns (uint256) {
console.log("Eu tenho %d posts!", totalPosts);
return totalPosts;
}
}
Atualizamos bastante o código, por isso que adicionei vários comentários, mas irei explicar um pouquinho como funciona.
O evento chamado NewPost
irá disparar toda vez que chamarmos a função emit NewPost
e, dessa forma, conseguiremos "escutar" esse evento no front-end e atualizar a interface.
Estamos definindo no struct Post
quais são os parâmetros da publicação, que, dado o contexto to aplicativo, terá um address
(endereço da pessoa que ta criando a publicação), uma message
(o conteúdo da publicação) e um timestamp
(a data e hora que a publicação foi realizada).
No código uint256 prizeAmount = 0.0001 ether;
definimos qual o valor de Ethereum que a pessoa receberá (50% de chance) toda vez que criar uma publicação.
Dica: entre na documentação da Solidity para entender como funciona o
mapping
,require
e os atributos doblock
.
Atualizando o arquivo para testar os contratos inteligentes
Agora que atualizamos o contrato inteligente, precisaremos atualizar o arquivo scripts/run.js
para testar as novas funções do contrato inteligente, então atualizaremos ele com o seguinte código:
const main = async () => {
const [owner] = await hre.ethers.getSigners();
const xPost = await hre.ethers.deployContract("XPost", {
value: hre.ethers.parseEther("0.1"),
});
await xPost.waitForDeployment();
console.log("Deploy do contrato no endereço:", xPost.target);
console.log("Deploy do contrato feito por:", owner.address);
let contractBalance = await hre.ethers.provider.getBalance(
xPost.target
);
console.log(
"Saldo do contrato:",
hre.ethers.formatEther(contractBalance)
);
const postTxn = await xPost.createPost("Enviando o post #1");
await postTxn.wait();
contractBalance = await hre.ethers.provider.getBalance(xPost.target);
console.log(
"Saldo do contrato após um post ser criado:",
hre.ethers.formatEther(contractBalance)
);
await xPost.getTotalPosts();
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
O que você precisa saber é que, na função de deployContract
adicionamos uma linha com value: hre.ethers.parseEther("0.1")
para adicionar um saldo de 0.1 Ethereum naquele contrato.
Na função await xPost.createPost("Enviando o post #1");
, estamos criando a primeira publicação com 50% de chance de ganhar 0.0001 Ethereum.
Lembre-se: quando nós chamamos a função
hre.ethers.deployContract
nós estamos criando esse contrato com um endereço fake temporário, quando subirmos para produção, o contrato será criado pelo endereço da sua MetaMask (lembra da public key?), então o prêmio de 0.0001 Ethereum será retirado da sua própria carteira.
Agora, rode o comando no terminal npx hardhat run scripts/run.js
e perceba que seu terminal mostrará algo tipo assim:
O mais legal é que a linha 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 ganhou um premio de 0.0001 Ethereum!
tem 50% de chance de aparecer, que é exatamente a probabilidade de ganhar o prêmio. Portanto, rode o mesmo comando npx hardhat run scripts/run.js
várias vezes e teste se vai ganhar o prêmio ou não.
Subindo seu contrato inteligente atualizado para produção na Alchemy
Agora, atualize o arquivo scripts/deploy.js
com o seguinte código:
const main = async () => {
const [owner] = await hre.ethers.getSigners();
const accountBalance = await owner.provider.getBalance(owner.address);
console.log("Deploy do contrato feito por:", owner.address);
console.log("Saldo da conta:", accountBalance.toString());
const xPost = await hre.ethers.deployContract("XPost", {
value: hre.ethers.parseEther("0.1"),
});
await xPost.waitForDeployment();
console.log("Deploy do contrato no endereço:", xPost.target);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
Em seguida, rode o comando npx hardhat run scripts/deploy.js --network staging
, e perceba algo semelhante à isso no seu terminal:
Agora, cire um arquivo chamado dados.txt
dentro do projeto x-smart-contracts
e salve o endereço que aparece na última linha "Deploy do contrato no endereço".
deployedAddress: //Cole o endereço depois dos dois pontos
Esse endereço será importante para usarmos no app, então não esqueça de salvá-lo! Caso você perca esse endereço, faça o deploy novamente com o comando npx hardhat run scripts/deploy.js --network staging
que gerará um novo endereço.