WEB3DEV

Cover image for Crie Sua Própria DAO Full Stack na Blockchain Celo
Fatima Lima
Fatima Lima

Posted on

Crie Sua Própria DAO Full Stack na Blockchain Celo

Crie Sua Própria DAO Full Stack na Blockchain Celo

Image description

Introdução

Uma DAO é uma organização autônoma descentralizada que se tornou possível graças à blockchain. Elas são construídas e sustentadas por uma comunidade de indivíduos que investem pessoalmente nelas e as fortalecem por meio de um mecanismo de votação por consenso. Neste tutorial, mostrarei a você como criar um contrato DAO no Solidity. Vamos analisar a implementação de uma DAO simples que permite que os membros proponham e votem em propostas e executem as propostas depois de aprovadas. Abordaremos os aspectos essenciais de uma DAO, como a estrutura do contrato inteligente, as funções para adicionar e remover membros, criar e votar em propostas e executar as propostas aprovadas. Ao final deste tutorial, você terá uma sólida compreensão de como funciona uma DAO.

Aqui está um link de demonstração do que iremos criar.

E um screenshot.

Image description

Pré-requisitos

Para acompanhar integralmente esses tutoriais, você deve ter um conhecimento básico das seguintes tecnologias:

  • Solidity, conceitos de contrato inteligente de blockchain.
  • React.
  • Desenvolvimento básico da Web.

Requisitos

  • Solidity.
  • React.
  • Bootstrap.
  • NodeJS na versão 12.0.1 ou superior instalado.
  • Carteira de Extensão Celo.
  • IDE do Remix

Contrato inteligente

Vamos começar a escrever nosso contrato inteligente no IDE do Remix

O código concluído deve ter a seguinte aparência:


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract CELODAO {

    address owner;

    struct MemberInfo {

        address memberAddress;

        uint256 votingPower;

    }

    mapping (address => MemberInfo) public members;

    uint256 public memberCount;



    event NewMember(address indexed _address, uint256 _votingPower);

    event MemberRemoved(address indexed _address);

    event ProposalCreated(uint256 indexed proposalId, address indexed proposer, string description);

    event ProposalVoted(uint256 indexed proposalId, address indexed voter, bool vote);

    struct Proposal {

        uint256 proposalId;

        address proposer;

        string description;

        uint256 yesVotes;

        uint256 noVotes;

        mapping (address => bool) votes;

        bool executed;

    }

    mapping (uint256 => Proposal) public proposals;

    uint256 public proposalCount;

    constructor()  {

        owner = msg.sender;

    }

    function addMember(address _address, uint256 _votingPower) public {

        require(msg.sender == owner, "Only contract owner can add a new member.");

        require(members[_address].memberAddress == address(0), "The address is already a member.");

        require(_votingPower > 0, "The voting power must be positive.");

        memberCount ++;

        members[_address] = MemberInfo(_address, _votingPower);

        emit NewMember(_address, _votingPower);

    }

    function removeMember(address _address) public {

        require(msg.sender == owner, "Only contract owner can remove a member.");

        require(members[_address].memberAddress != address(0), "The address is not a member.");

        require(proposals[proposalCount].proposer != _address, "Member cannot be removed while they have an active proposal.");

        members[_address].memberAddress = address(0);

        memberCount --;

        emit MemberRemoved(_address);

    }

    function createProposal(string memory _description) public {

        Proposal storage proposal = proposals[proposalCount];

        proposal.proposalId = proposalCount;

        proposal.proposer = msg.sender;

        proposal.description = _description;

        proposal.yesVotes = 0;

        proposal.noVotes = 0;

        proposal.executed = false;

        proposalCount ++;

        emit ProposalCreated(proposalCount, msg.sender, _description);

    }

     function getProposal(uint _index) public view returns(

        uint,

        address,

        string memory,

        uint,

        uint,

        bool

    ){

         Proposal storage proposal = proposals[_index];

         return(

             proposal.proposalId,

             proposal.proposer,

             proposal.description,

             proposal.yesVotes,

             proposal.noVotes,

             proposal.executed

         );

    }

    function vote(uint256 _proposalId, bool _vote) public {

        require(proposals[_proposalId].votes[msg.sender] == false, "The member has already voted on this proposal.");

        require(proposals[_proposalId].executed == false, "The proposal has already been executed.");

        proposals[_proposalId].votes[msg.sender] = _vote;

        if (_vote) {

            proposals[_proposalId].yesVotes += members[msg.sender].votingPower;

        } else {

            proposals[_proposalId].noVotes += members[msg.sender].votingPower;

        }

        proposals[_proposalId].votes[msg.sender] == true;

        emit ProposalVoted(_proposalId, msg.sender, _vote);

    }

    function executeProposal(uint256 _proposalId) public {

        require(proposals[_proposalId].proposer == msg.sender, "Only the proposer can execute the proposal.");

        require(proposals[_proposalId].executed == false, "The proposal has already been executed.");

        require(proposals[_proposalId].yesVotes > proposals[_proposalId].noVotes, "The proposal must have more yes votes than no votes.");

        proposals[_proposalId].executed = true;

        // Execute as ações descritas na proposta aqui

        // ...

    }

     function getProposalsLength() public view returns(uint){

        return(proposalCount);

    }  

}

Enter fullscreen mode Exit fullscreen mode

Desmembramento


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

Enter fullscreen mode Exit fullscreen mode

Primeiro, declaramos nossa licença e a versão do Solidity.


contract CELODAO {

    address owner;

    struct MemberInfo {

        address memberAddress;

        uint256 votingPower;

    }

    mapping (address => MemberInfo) public members;

    uint256 public memberCount;

    }

Enter fullscreen mode Exit fullscreen mode

Nesta seção, definimos nosso contrato inteligente CELODAO. Em seguida, declaramos uma variável de estado chamada owner que armazenará o endereço do proprietário do contrato inteligente.

Também declaramos uma nova struct MemberInfo que contém dois campos: memberAddress e votingPower. Essa struct será usada para armazenar informações sobre cada membro da DAO.

Por fim, declaramos um mapeamento público chamado members que mapeia um endereço para uma struct MemberInfo. Ele será usado para armazenar informações sobre cada membro da DAO e, em seguida, declaramos uma variável de estado chamada memberCount, que manterá o controle do número total de membros em nossa DAO.


    event NewMember(address indexed _address, uint256 _votingPower);

    event MemberRemoved(address indexed _address);

    event ProposalCreated(uint256 indexed proposalId, address indexed proposer, string description);

    event ProposalVoted(uint256 indexed proposalId, address indexed voter, bool vote);

Enter fullscreen mode Exit fullscreen mode

Aqui declaramos vários eventos que serão emitidos quando determinadas ações forem executadas na DAO. Esses eventos podem ser ouvidos por aplicativos externos para rastrear o estado da nossa DAO.


 struct Proposal {

        uint256 proposalId;

        address proposer;

        string description;

        uint256 yesVotes;

        uint256 noVotes;

        mapping (address => bool) votes;

        bool executed;

    }

    mapping (uint256 => Proposal) public proposals;

    uint256 public proposalCount;

    constructor()  {

        owner = msg.sender;

    }

Enter fullscreen mode Exit fullscreen mode

Nesta seção, declaramos uma nova struct chamada Proposal que será usada para armazenar informações sobre cada proposta. Ela contém vários campos, incluindo o ID da proposta, o endereço do proponente, uma descrição da proposta, o número de votos "sim", o número de votos "não", um mapeamento do voto de cada membro e um sinalizador para indicar se a proposta foi executada.

Também declaramos um mapeamento público chamado proposals que mapeia um ID de proposta para uma struct Proposal. O proposalCount manterá o controle do número total de propostas em nossa DAO.

Por fim, adicionamos uma função de construtor para o contrato CELODAO. Ela define a variável de estado do proprietário como o endereço do criador do contrato.


  function addMember(address _address, uint256 _votingPower) public {

        require(msg.sender == owner, "Only contract owner can add a new member.");

        require(members[_address].memberAddress == address(0), "The address is already a member.");

        require(_votingPower > 0, "The voting power must be positive.");

        memberCount ++;

        members[_address] = MemberInfo(_address, _votingPower);

        emit NewMember(_address, _votingPower);

    }

Enter fullscreen mode Exit fullscreen mode

Em seguida, adicionamos uma nova função chamada addMember. Essa função adiciona um novo membro ao nosso contrato DAO. Ela recebe dois argumentos _address, que é o endereço do novo membro, e _votingPower, que é o poder de voto do novo membro. A função primeiro verifica se o chamador da função é o proprietário do contrato e se o _address fornecido ainda não é um membro. Em seguida, aumenta a contagem de membros, cria uma nova estrutura MemberInfo para o novo membro e a adiciona ao mapeamento de membros usando o _address como chave. Por fim, ele emite um evento NewMember com o endereço e o poder de voto do novo membro.


 function removeMember(address _address) public {

        require(msg.sender == owner, "Only contract owner can remove a member.");

        require(members[_address].memberAddress != address(0), "The address is not a member.");

        require(proposals[proposalCount].proposer != _address, "Member cannot be removed while they have an active proposal.");

        members[_address].memberAddress = address(0);

        memberCount --;

        emit MemberRemoved(_address);

    }

Enter fullscreen mode Exit fullscreen mode

Em seguida, adicionamos uma função removeMember. Essa função remove um membro da nossa DAO. Ela recebe um argumento _address, que é o endereço do membro a ser removido. A função primeiro verifica se o chamador da função é o proprietário do contrato, se o _address fornecido é realmente um membro e se o membro não tem uma proposta ativa. Em seguida, ela define o memberAddress do membro como address(0), diminui o memberCount e emite um evento MemberRemoved com o endereço do membro removido.


function createProposal(string memory _description) public {

        Proposal storage proposal = proposals[proposalCount];

        proposal.proposalId = proposalCount;

        proposal.proposer = msg.sender;

        proposal.description = _description;

        proposal.yesVotes = 0;

        proposal.noVotes = 0;

        proposal.executed = false;

        proposalCount ++;

        emit ProposalCreated(proposalCount, msg.sender, _description);

    }

Enter fullscreen mode Exit fullscreen mode

Agora vamos dar uma olhada na função createProposal. Essa função cria uma nova proposta em nossa DAO. Ela recebe um argumento _description, que é uma string que contém uma descrição da proposta.

A função primeiro cria uma referência para a struct Proposal no índice proposalCount na matriz de propostas usando a palavra-chave storage. Em seguida, define o proposalId como o valor de proposalCount, o proposer como o endereço do chamador, a description como a descrição fornecida e define os yesVotes e noVotes iniciais como 0.

Por fim, ele define o sinalizador executed como false (falso), indicando que a proposta ainda não foi executada.

No final da função, o proposalCount é incrementado e a nova proposta é adicionada à matriz de propostas.


 function getProposal(uint _index) public view returns(

        uint,

        address,

        string memory,

        uint,

        uint,

        bool

    ){

         Proposal storage proposal = proposals[_index];

         return(

             proposal.proposalId,

             proposal.proposer,

             proposal.description,

             proposal.yesVotes,

             proposal.noVotes,

             proposal.executed

         );

    }

Enter fullscreen mode Exit fullscreen mode

A próxima função é a getProposal(). Essa é uma função de exibição que recebe um parâmetro _index e retorna uma tupla contendo as várias propriedades de uma proposta: proposalId, proposer, description, yesVotes, noVotes e executed.

Ela cria um objeto Proposal com o _index correspondente e retorna as propriedades da proposta como uma tupla.


   function vote(uint256 _proposalId, bool _vote) public {

        require(proposals[_proposalId].votes[msg.sender] == false, "The member has already voted on this proposal.");

        require(proposals[_proposalId].executed == false, "The proposal has already been executed.");

        proposals[_proposalId].votes[msg.sender] = _vote;

        if (_vote) {

            proposals[_proposalId].yesVotes += members[msg.sender].votingPower;

        } else {

            proposals[_proposalId].noVotes += members[msg.sender].votingPower;

        }

        proposals[_proposalId].votes[msg.sender] == true;

        emit ProposalVoted(_proposalId, msg.sender, _vote);

    } 

Enter fullscreen mode Exit fullscreen mode

Em seguida, criamos uma função vote(). A função vote permite que um membro vote em uma proposta. A função recebe dois argumentos _proposalId é o ID da proposta que está sendo votada e _vote é um booleano que indica se o membro está votando a favor ou contra a proposta.

A primeira declaração require verifica se o membro ainda não votou na proposta. Se o membro já tiver votado, a função falhará com uma mensagem de erro.

A segunda declaração require verifica se a proposta ainda não foi executada. Se a proposta já tiver sido executada, a função falhará com uma mensagem de erro.

A linha proposals[_proposalId].votes[msg.sender] = _vote registra o voto do membro no mapeamento de votos da proposta. O mapeamento de votos armazena um valor booleano que indica se um membro votou ou não na proposta. Se o membro estiver votando a favor da proposta, sua contagem de yesVotes será incrementada pelo seu poder de voto. Se ele estiver votando contra a proposta, sua contagem de noVotes será incrementada pelo seu poder de voto.

Finalmente, a função emite um evento ProposalVoted, passando o ID da proposta, o endereço do membro e seu voto. Esse evento pode ser usado para rastrear o progresso de uma proposta à medida que os membros votam nela.


function executeProposal(uint256 _proposalId) public {

        require(proposals[_proposalId].proposer == msg.sender, "Only the proposer can execute the proposal.");

        require(proposals[_proposalId].executed == false, "The proposal has already been executed.");

        require(proposals[_proposalId].yesVotes > proposals[_proposalId].noVotes, "The proposal must have more yes votes than no votes.");

        proposals[_proposalId].executed = true;

        // Execute as ações descritas na proposta aqui.

        // ...

    }

Enter fullscreen mode Exit fullscreen mode

O executeProposal() é uma função que permite que o proponente de uma proposta a execute. A função primeiro verifica se o proponente é quem está chamando a função, se a proposta ainda não foi executada e se o número de votos "sim" é maior que o número de votos "não". Se todas essas condições forem atendidas, a função define o sinalizador executed como verdadeiro, indicando que a proposta foi executada. Por fim, todas as ações descritas na proposta podem ser executadas.


 function getProposalsLength() public view returns(uint){

        return(proposalCount);

    }  

Enter fullscreen mode Exit fullscreen mode

Por fim, getProposalsLength() é uma função simples que retorna o número de propostas criadas no contrato. Ela simplesmente retorna o valor da variável proposalCount.

Com isso, examinamos todo o código em nosso Contrato DAO. Esse contrato permite que os membros adicionem e removam outros membros, criem e votem em propostas e executem propostas que tenham sido aprovadas pelos membros. É uma implementação básica de uma DAO e pode ser estendida ou modificada para atender às necessidades de um caso de uso específico.

Implantação

Para implantar nosso contrato inteligente com sucesso, precisamos da carteira de extensão Celo, que pode ser baixada aqui.

Em seguida, precisamos depositar fundos em nossa carteira recém-criada, o que pode ser feito usando a faucet (torneira) Alfajores do Celo aqui.

Agora você pode depositar fundos em sua carteira e implementar seu contrato usando o plugin celo no Remix.

Frontend

Clique neste repositório de seu github.

  • Clone o repositório em seu computador.
  • Abra o projeto a partir do vscode.
  • Execute o comando npm install para instalar todas as dependências necessárias para executar o aplicativo localmente.

App.js

O código completo deve ter a seguinte aparência:


import "./App.css";

import Home from "./components/home";

import { Proposals } from "./components/proposals";

import { useState, useEffect, useCallback } from "react";

import Web3 from "web3";

import { newKitFromWeb3 } from "@Celo_Academy/contractkit";

import celodao from "./contracts/celo-dao.abi.json";

const ERC20_DECIMALS = 18;

const contractAddress = "0x69dfb020bA12Ce303118E3eF81f9b9E4eB08cE17";

function App() {

  const [contract, setcontract] = useState(null);

  const [address, setAddress] = useState(null);

  const [kit, setKit] = useState(null);

  const [cUSDBalance, setcUSDBalance] = useState(0);

  const [proposals, setProposals] = useState([]);

  const connectToWallet = async () => {

    if (window.celo) {

      try {

        await window.celo.enable();

        const web3 = new Web3(window.celo);

        let kit = newKitFromWeb3(web3);

        const accounts = await kit.web3.eth.getAccounts();

        const user_address = accounts[0];

        kit.defaultAccount = user_address;

        await setAddress(user_address);

        await setKit(kit);

      } catch (error) {

        console.log(error);

      }

    } else {

      alert("Error Occurred");

    }

  };

  const getBalance = useCallback(async () => {

    try {

      const balance = await kit.getTotalBalance(address);

      const USDBalance = balance.cUSD.shiftedBy(-ERC20_DECIMALS).toFixed(2);

      const contract = new kit.web3.eth.Contract(celodao, contractAddress);

      setcontract(contract);

      setcUSDBalance(USDBalance);

    } catch (error) {

      console.log(error);

    }

  }, [address, kit]);

  const getProposals = useCallback(async () => {

    const proposalsLength = await contract.methods.getProposalsLength().call();

    const proposals = [];

    for (let index = 0; index < proposalsLength; index++) {

      let _proposals = new Promise(async (resolve, reject) => {

        let proposal = await contract.methods.getProposal(index).call();

        resolve({

          index: index,

          proposalId: proposal[0],

          proposer: proposal[1],

          description: proposal[2],

          yesVotes: proposal[3],

          noVotes: proposal[4],

          executed: proposal[6],

        });

      });

      proposals.push(_proposals);

    }

    const _proposals = await Promise.all(proposals);

    setProposals(_proposals);

  }, [contract]);

  const addProposal = async (_description) => {

    try {

      await contract.methods

        .createProposal(_description)

        .send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

  const addMember = async (_address, _votingPower) => {

    try {

      await contract.methods

        .addMember(_address, _votingPower)

        .send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

  const removeMember = async (_address) => {

    try {

      await contract.methods.removeMember(_address).send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

  const vote = async (_proposalId, _vote) => {

    try {

      await contract.methods.vote(_proposalId, _vote).send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

  const executeProposal = async (_proposalId) => {

    try {

      await contract.methods

        .executedProposal(_proposalId)

        .send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

  useEffect(() => {

    connectToWallet();

  }, []);

  useEffect(() => {

    if (kit && address) {

      getBalance();

    }

  }, [kit, address, getBalance]);

  useEffect(() => {

    if (contract) {

      getProposals();

    }

  }, [contract, getProposals]);

  return (

    <div className="App">

      <Home

        cUSDBalance={cUSDBalance}

        addMember={addMember}

        addProposal={addProposal}

        removeMember={removeMember}

      />

      <Proposals

        proposals={proposals}

        vote={vote}

        executeProposal={executeProposal}

        walletAddress={address}

      />

    </div>

  );

}

export default App;

Enter fullscreen mode Exit fullscreen mode

Desmembramento

Vamos dar uma olhada no arquivo App.js e dividi-lo em partes.


import "./App.css";

import Home from "./components/home";

import { Proposals } from "./components/proposals";

import { useState, useEffect, useCallback } from "react";

import Web3 from "web3";

import { newKitFromWeb3 } from "@Celo_Academy/contractkit";

import celodao from "./contracts/celo-dao.abi.json";

Enter fullscreen mode Exit fullscreen mode

O primeiro passo é importar os componentes e as bibliotecas necessários. Começamos importando os componentes Home e Proposals da pasta de componentes. Em seguida, importamos os ganchos (hooks) useState, useEffect e useCallback do React, bem como a biblioteca Web3 para interagir com a blockchain Ethereum. Por fim, importamos o contrato ABI (Application Binary Interface) para o contrato Celo-Dao da pasta contracts.


const ERC20_DECIMALS = 18;

const contractAddress = "0x69dfb020bA12Ce303118E3eF81f9b9E4eB08cE17";

Enter fullscreen mode Exit fullscreen mode

Em seguida, definimos os decimais do ERC20 e o endereço do contrato de nosso contrato inteligente.


  const [contract, setcontract] = useState(null);

  const [address, setAddress] = useState(null);

  const [kit, setKit] = useState(null);

  const [cUSDBalance, setcUSDBalance] = useState(0);

  const [proposals, setProposals] = useState([]);

Enter fullscreen mode Exit fullscreen mode

A seguir, criamos as variáveis de estado para o aplicativo. Usamos o gancho useState para criar as variáveis de estado contract, address, kit, cUSDBalance e proposals.


const connectToWallet = async () => {

    if (window.celo) {

      try {

        await window.celo.enable();

        const web3 = new Web3(window.celo);

        let kit = newKitFromWeb3(web3);

        const accounts = await kit.web3.eth.getAccounts();

        const user_address = accounts[0];

        kit.defaultAccount = user_address;

        await setAddress(user_address);

        await setKit(kit);

      } catch (error) {

        console.log(error);

      }

    } else {

      alert("Error Occurred");

    }

  };

Enter fullscreen mode Exit fullscreen mode

Em seguida, criamos a função connectToWallet() que permite que o usuário se conecte à sua carteira e defina o endereço e o kit.


const getBalance = useCallback(async () => {

    try {

      const balance = await kit.getTotalBalance(address);

      const USDBalance = balance.cUSD.shiftedBy(-ERC20_DECIMALS).toFixed(2);

      const contract = new kit.web3.eth.Contract(celo-dao, contractAddress);

      setcontract(contract);

      setcUSDBalance(USDBalance);

    } catch (error) {

      console.log(error);

    }

  }, [address, kit]);

Enter fullscreen mode Exit fullscreen mode

A função getBalance() nos permite obter o saldo cUSD do usuário e configurar o contrato.


 const getProposals = useCallback(async () => {

    const proposalsLength = await contract.methods.getProposalsLength().call();

    const proposals = [];

    for (let index = 0; index < proposalsLength; index++) {

      let _proposals = new Promise(async (resolve, reject) => {

        let proposal = await contract.methods.getProposal(index).call();

        resolve({

          index: index,

          proposalId: proposal[0],

          proposer: proposal[1],

          description: proposal[2],

          yesVotes: proposal[3],

          noVotes: proposal[4],

          executed: proposal[6],

        });

      });

      proposals.push(_proposals);

    }

    const _proposals = await Promise.all(proposals);

    setProposals(_proposals);

  }, [contract]);

Enter fullscreen mode Exit fullscreen mode

A função getProposals() é usada para obter a lista de propostas do contrato. Usamos o método getProposalsLength para obter o número de propostas e fazemos um loop em cada proposta para obter suas propriedades. Em seguida, armazenamos as propostas na variável de estado proposals.


 const addProposal = async (_description) => {

    try {

      await contract.methods

        .createProposal(_description)

        .send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

Enter fullscreen mode Exit fullscreen mode

A função addProposal é usada para adicionar uma proposta ao contrato. Usamos o método createProposal para adicionar a proposta e, em seguida, chamamos a função getProposals() para atualizar a variável de estado proposals.


 const addMember = async (_address, _votingPower) => {

    try {

      await contract.methods

        .addMember(_address, _votingPower)

        .send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

  const removeMember = async (_address) => {

    try {

      await contract.methods.removeMember(_address).send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

Enter fullscreen mode Exit fullscreen mode

Usamos os métodos addMember() e removeMember para adicionar e remover membros da nossa Dao e, em seguida, chamamos a função getProposals() para atualizar a variável de estado proposals.


const vote = async (_proposalId, _vote) => {

    try {

      await contract.methods.vote(_proposalId, _vote).send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

  const executeProposal = async (_proposalId) => {

    try {

      await contract.methods

        .executedProposal(_proposalId)

        .send({ from: address });

      getProposals();

    } catch (error) {

      alert(error);

    }

  };

Enter fullscreen mode Exit fullscreen mode

As funções vote() e executeProposal() são usadas para votar em propostas e executá-las. Usamos os métodos vote() e executedProposal() para votar e executar propostas e, em seguida, chamamos a função getProposals() para atualizar a variável de estado proposals.


useEffect(() => {

    connectToWallet();

  }, []);

  useEffect(() => {

    if (kit && address) {

      getBalance();

    }

  }, [kit, address, getBalance]);

  useEffect(() => {

    if (contract) {

      getProposals();

    }

  }, [contract, getProposals]);

Enter fullscreen mode Exit fullscreen mode

Usamos o gancho useEffect para chamar as funções connectToWallet(), getBalance() e getProposals(). Isso garante que o aplicativo esteja sempre atualizado com os dados mais recentes do contrato.


return (

    <div className="App">

      <Home

        cUSDBalance={cUSDBalance}

        addMember={addMember}

        addProposal={addProposal}

        removeMember={removeMember}

      />

      <Proposals

        proposals={proposals}

        vote={vote}

        executeProposal={executeProposal}

        walletAddress={address}

      />

    </div>

  );


    }

export default App;

Enter fullscreen mode Exit fullscreen mode

E, por fim, renderizamos o componente App e retornamos os componentes Home e proposals com as props (propriedades) necessárias.

Próximos Passos

Espero que você tenha aprendido muito com este tutorial. Aqui estão alguns links relevantes que podem ajudá-lo a aprender mais.

Sobre o autor

Sou Jonathan Iheme, um desenvolvedor full stack de blockchain da Nigéria.

Obrigado!

Esse artigo foi escrito por 4undRaiser e traduzido por Fátima lima. O original pode ser lido aqui.

Top comments (0)