WEB3DEV

Cover image for Uma Versão Melhor do Kickstarter usando Web3.0 — Solidity
Paulo Gio
Paulo Gio

Posted on

Uma Versão Melhor do Kickstarter usando Web3.0 — Solidity

Para evitar fraudes no Kickstarter, os contribuidores podem votar em decisões monetárias

https://miro.medium.com/max/1100/1*KxaNo5iT4f7QYt44gUQdCw.png

Landing Page

Neste blog, explicarei como fazer um protótipo para o Kickstarter que leve em consideração os votos dos contribuidores que têm o apoio do projeto para tomar decisões importantes sobre o destino dos fundos. Nesse caso, o gerente da campanha pode criar uma requisição na qual ele pode mencionar o endereço e os serviços do destinatário e as pessoas podem votar se aprovam ou não o serviço.

https://miro.medium.com/max/1100/1*gv8HdbZnQUVIEK_UGq-kQw.png

Exemplo de uma requisição visualizada por um contribuidor.

Isso, portanto, é um contrato inteligente. Um contrato inteligente pode ser definido como uma entidade que pode enviar e receber moedas (criptográficas) além de apenas usuários da web. É um pedaço de código que gerencia seu dinheiro e como você o direciona para executar certas funções - ou seja, programas na blockchain!

Esses programas, uma vez implantados, não podem ser modificados e são definitivos. Não vamos nos aprofundar nos muitos aspectos benéficos dos contratos inteligentes, pois existem muitos e o tempo é relativo.

Explicarei 2 contratos inteligentes aqui.

  1. Um contrato que cria campanhas.
  2. Um contrato que trata de como o gerente de uma campanha pode criar uma requisição e como outras pessoas podem interagir com ela.

Vamos começar com nossa versão do compilador e função de contrato.

pragma solidity ^0.8.15;
contract CampaignCreator{
}
Enter fullscreen mode Exit fullscreen mode

https://miro.medium.com/max/1100/1*VY_cqCOh9t2lB4C8aufF3A.png

Crie uma página da campanha.

Começaremos criando uma struct, que inclui os detalhes acima.

struct CreateNewCampaign{
   address addressOfNewCampaign;
   string name;
   uint minimumContribution;
}
Enter fullscreen mode Exit fullscreen mode
  • addressOfNewCampaign → endereço da campanha a ser criada.
  • name → Título da campanha a ser criada.
  • minimumContribution → valor mínimo que se deve contribuir para votar nas decisões da campanha.

Agora, uma matriz para armazenar uma lista das structs acima.

CreateNewCampaign[] public deployedCampaigns;
Enter fullscreen mode Exit fullscreen mode

Aqui, a sintaxe para criar uma matriz é a seguinte:

data_type [] visibility nome_da_matriz;
Enter fullscreen mode Exit fullscreen mode

Agora, uma função para criar campanhas, que leva em 2 parâmetros → contribuição mínima e título da campanha.

function createCampaign(uint minimum, string memory _name) public {

Campaign newCampaign = new Campaign(minimum, msg.sender);

deployedCampaigns.push( CreateNewCampaign( address(newCampaign), _name, minimum) );
}
Enter fullscreen mode Exit fullscreen mode

Linha: 1 — Campaign newCampaign = new Campaign(minimum, msg.sender); Aqui, Campaign é o contrato de campanha que codificaremos mais tarde e newCampaign é sua instância. Passamos dois parâmetros, minimum e msg.sender, que chama o construtor do contrato da campanha, que também veremos mais adiante neste blog.

Linha: 2 — deploymentCampaigns.push( \
CreateNewCampaign( address(newCampaign), _name, minimum) \
);
Aqui estamos apenas inserindo os valores do contrato recém-criado em nossa lista de campanhas que criamos.

Por último neste contrato, uma função para recuperar todas as campanhas.

function getDeployedCampaigns() public view returns ( 
CreateNewCampaign[] memory) {

return deployedCampaigns;

}
Enter fullscreen mode Exit fullscreen mode

Vamos agora iniciar o Contrato da Campanha

contract Campaign{

}
Enter fullscreen mode Exit fullscreen mode

Agora, a primeira coisa que é invocada quando um contrato é chamado é o construtor.

Criaremos 2 variáveis, manager e minimumContribution, que obtêm o valor do gerente da campanha e a contribuição mínima da campanha.

address public manager;
uint public minimumContribution;

constructor(uint minimum, address creator) {

   manager = creator;
   minimumContribution = minimum;

}
Enter fullscreen mode Exit fullscreen mode

https://miro.medium.com/max/1100/1*aZE5Oxtb1whX8ZN4hKoqng.png

Acessando o construtor do contrato Campaign.

Agora, crie uma função que receba as contribuições. Quem contribui, consegue aprovar as decisões/requisições de campanha. Mas antes disso, precisamos armazenar nossos aprovadores em algum lugar.

mapping(address => bool) public contributer;
Enter fullscreen mode Exit fullscreen mode

Este mapeamento nos ajuda a verificar se um endereço específico (usuário) contribuiu ou não. Só precisamos alterar a chave/endereço para true. Por padrão, todos os valores neste mapeamento serão falsos.

Além disso, vamos adicionar uma variável que obtém a contagem de contribuidores.

uint public countOfContributers;

function contribute() public payable{

 require( msg.value > minimumContribution );
 if(contributers[msg.sender] == false)
 {
   contributers[msg.sender] = true;
   countOfContributers++;
 }

}
Enter fullscreen mode Exit fullscreen mode

Em primeiro lugar, a função está marcada como payable (pagável), o que significa que aceita pagamentos.

Linha 1 — require( msg.value > MinimumContribution ); esta linha verifica se o valor enviado é maior que a contribuição mínima. msg.value é a quantidade de ETH que está sendo enviada.

https://miro.medium.com/max/786/1*XxNDzF2q4wh3QJrmSTlM8w.png

Como a função acima é chamada.

A próxima instrução if verifica se o endereço (usuário) que está chamando a função de contribuição já é um contribuidor ou não, caso contrário, ele grava no mapeamento de contributers e aumenta o uint countOfContributers. Isso é feito para contar corretamente o número de contribuidores, caso alguém contribua mais de uma vez.

Agora a parte difícil, fazer requisições nas quais os contribuidores podem votar.

Em primeiro lugar, crie uma struct que defina a aparência de uma requisição.

struct Request{ 

  string title;
  uint fundsRequired;
  address receiver;
  uint approvalCount;
  bool complete;

}
Enter fullscreen mode Exit fullscreen mode

title → Título da requisição

fundsRequired → Fundos necessários

receiver → Endereço para quem o dinheiro ( ETH ) irá ao finalizar a requisição.

approvalCount → Número de pessoas que aprovaram a requisição.

complete → Status da requisição, se concluído então verdadeiro.

https://miro.medium.com/max/1100/1*JseibqEXj1n_i0LnOGdYlA.png

Criar uma página de requisição

Agora uma matriz para armazenar uma lista das requisições.

Request[] public requestsArray;
Enter fullscreen mode Exit fullscreen mode

Agora, uma função para criar uma requisição.

function createRequest ( string memory _title , uint _funds , address _receiver ) public{

requestsArray.push( Request( _title, _funds,_receiver,0, false ) );

}
Enter fullscreen mode Exit fullscreen mode

Observação:

Usamos memória (memory) enquanto acessamos uma string como parâmetro em uma função. Basicamente o que a memória faz é limitar o escopo da variável acessada à própria função.

Agora vamos aprovar essas requisições :)

function approveRequest( uint idOfRequestToApprove ) public {

Request storage requestToApprove = 
requestsArray[idOfRequestToApprove];

require( contributers[msg.sender] == true );

require( approvalsOfAllRequests[msg.sender][idOfRequestToApprove] == false );

approvalsOfAllRequests[msg.sender][idOfRequestToApprove] = true;

requestToApprove.approvalCount++;

}
Enter fullscreen mode Exit fullscreen mode

Chamamos a função acima com um parâmetro uintidOfRequestToApprove, que é usado para acessar uma requisição específica, por exemplo: approveRequest(0) obterá a primeira requisição na matriz de requisições requestsArray.

Linha 1 — Request storage requestToApprove = requestsArray[idOfRequestToApprove]; Essa linha pega a requisição idOfRequestToApprove (0,1,2,3,4,5 … um número) na requestsArray e a armazena na struct requestToApprove. Assim, podemos acessar a requisição real de requestsArray. → Usamos o armazenamento para essa finalidade (armazenamento → armazenamento — atribuir por referência).

Linha 2 — require(contribuidores[msg.sender] == true);, apenas verifica se o endereço (usuário) que aprova a requisição é um contribuidor.

Linha 3 — Esta linha é para verificar se o endereço (usuário) já aprovou a requisição ou não. Isso é feito por um mapeamento duplo.

mapping ( address => mapping( uint => bool )) 
approvalsOfAllRequests;
Enter fullscreen mode Exit fullscreen mode

Aqui mapeamos o endereço com outro mapeamento que tem um uint como chave e bool como seu valor, em que uint é o ID da requisição e bool é se o endereço aprovou ou não a requisição. Digamos que queremos saber se o endereço XYZ votou na 3ª requisição ou não?

return approvalsOfAllRequests[XYZ][2] == true;
Enter fullscreen mode Exit fullscreen mode

https://miro.medium.com/max/1100/1*ClCFeEtr8lstJtijwa8_Hw.png

Linha 4 — Esta linha é para evitar que um endereço (usuário) aprove a mesma requisição novamente.

Linha 5 — Por fim, aumentamos a contagem de aprovação da struct Request da respectiva requisição que estamos acessando nesta função.

A última função é finalizar uma requisição. Realmente já avançamos muito :)

function finaliseRequest( uint idOfRequestToApprove)  public {

require(msg.sender == mananger);

Request storage requestToApprove = 
requestsKaArray[idOfRequestToApprove];

require(requestToApprove.approvalCount>(countOfContributers/2));

require( requestToApprove.complete == false );

require ( address(this).balance > requestToApprove.fundsRequired );

requestToApprove.complete == true;

payable( requestToApprove.receiver 
).transfer(requestToApprove.fundsRequired);

}
Enter fullscreen mode Exit fullscreen mode

https://miro.medium.com/max/1100/1*o5w7Lf0kXboHhGBSybUTDg.png

Finalizar requisição

Semelhante à função de aprovação, passamos um uint → ID da requisição que queremos acessar.

Linha 1 — require(msg.sender == manager); Isso é para verificar se o endereço (usuário) que está chamando a função de finalização é o gerenciador da campanha. Não queremos que outra pessoa finalize nossas requisições, não é?

Linha 2 — Semelhante à função de aprovação.

Linha 3 — require(requestToApprove.approvalCount >(countOfContributers/2)); Isso é para verificar se a maioria dos contribuidores aprovou a requisição.

Linha 4 — require( requestToApprove.complete == false ); Para verificar se a requisição ainda não foi finalizada.

Linha 5 — require ( address(this).balance > requestToApprove.fundsRequired ); Isso é para verificar se o contrato tem saldo suficiente para realizar a transação ao destinatário. Obtemos o saldo do contrato de address(this).balance.

Linha 6— payable( requestToApprove.receiver ). transfer ( requestToApprove . fundsRequired ); Por fim, pagamos os fundos ao destinatário :).

Aqui está o código completo:

https://gist.github.com/Khanisic/5d069575addc0716ce09d4e849027be1

https://miro.medium.com/max/1100/1*qZOJvET11QjGbqXEafeJKg.png

Nossa página de campanha

Links

Artigo original escrito por Moid Khan. Tradução por Paulinho Giovannini.

Latest comments (0)