Roadmap
Avançado

𝕏 (Twitter) Descentralizado

Crie um clone do twitter na blockchain

ethers
solidity
vue
tailwind
vercel
github

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:

XPost.sol
// 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;
    }
}
Clique aqui para expandir

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 do block.

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:

run.js
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();
Clique aqui para expandir

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:

deploy.js
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();
Clique aqui para expandir

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".

dados.txt
deployedAddress: //Cole o endereço depois dos dois pontos
Clique aqui para expandir

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.

Opa, calma aí!

Parece que você não está logado, caso tenha interesse em salvar seu progresso de estudo faça login agora.