Para evitar fraudes no Kickstarter, os contribuidores podem votar em decisões monetárias
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.
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.
- Um contrato que cria campanhas.
- 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{
}
Crie uma página da campanha.
Começaremos criando uma struct, que inclui os detalhes acima.
struct CreateNewCampaign{
address addressOfNewCampaign;
string name;
uint minimumContribution;
}
-
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;
Aqui, a sintaxe para criar uma matriz é a seguinte:
data_type [] visibility nome_da_matriz;
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) );
}
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( \
Aqui estamos apenas inserindo os valores do contrato recém-criado em nossa lista de campanhas que criamos.
CreateNewCampaign( address(newCampaign), _name, minimum) \
);
Por último neste contrato, uma função para recuperar todas as campanhas.
function getDeployedCampaigns() public view returns (
CreateNewCampaign[] memory) {
return deployedCampaigns;
}
Vamos agora iniciar o Contrato da Campanha
contract Campaign{
}
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;
}
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;
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++;
}
}
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.
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;
}
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.
Criar uma página de requisição
Agora uma matriz para armazenar uma lista das requisições.
Request[] public requestsArray;
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 ) );
}
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++;
}
Chamamos a função acima com um parâmetro uint
— idOfRequestToApprove
, 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;
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;
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);
}
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
Nossa página de campanha
Links
Artigo original escrito por Moid Khan. Tradução por Paulinho Giovannini.
Oldest comments (0)