paint-brush
Como escrever um contrato inteligente de solidity e implantá-lo no Ropstenpor@johnjvester
679 leituras
679 leituras

Como escrever um contrato inteligente de solidity e implantá-lo no Ropsten

por John Vester35m2022/08/05
Read on Terminal Reader
Read this story w/o Javascript

Muito longo; Para ler

Os contratos inteligentes permitem que duas partes entrem em um acordo. Mergulhe fundo nos contratos inteligentes para mostrar como reutilizar um único contrato repetidamente.

People Mentioned

Mention Thumbnail
Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Como escrever um contrato inteligente de solidity e implantá-lo no Ropsten
John Vester HackerNoon profile picture


O " Mudando de desenvolvedor Full-Stack para Web3 Pioneer ” a publicação forneceu uma visão geral de alto nível para dar aos desenvolvedores full-stack um vislumbre do mundo do desenvolvimento Web3. Se você ainda não teve a chance de revisar esse artigo, considere dar uma olhada, pois ele também fornece uma boa introdução ao Web3.


O resultado final do meu artigo original demonstrou como uma associação de proprietários de imóveis (HOA) poderia usar a tecnologia Web3 para hospedar sua cédula eleitoral. O problema com o design original é que o contrato inteligente subjacente permitia apenas uma única resposta sim ou não. Isso foi planejado para manter o contrato inteligente simples e, ao mesmo tempo, introduzir outros conceitos necessários para criar uma cédula HOA usando tecnologias Web3.


O objetivo desta publicação é aprofundar os contratos inteligentes para criar um aplicativo que não apenas capture necessidades e funções realistas para uma cédula de HOA, mas também projete um que possa ser reutilizado de uma eleição para outra.

Sobre contratos inteligentes

Antes de começarmos, vamos definir um contrato inteligente:

“Um contrato inteligente é um programa executado em um endereço no Ethereum. Eles são compostos de dados e funções que podem ser executados ao receber uma transação. Aqui está uma visão geral do que compõe um contrato inteligente.”


fonte ethereum.org


A máquina de chicletes

Acredite ou não, um exemplo fácil de contrato inteligente pode ser encontrado em uma simples máquina de chicletes:


As pessoas entendem facilmente o custo relacionado à compra com a máquina de chicletes. Normalmente, este é um trimestre (EUA). É importante ressaltar aqui que o cliente é anônimo, pois a máquina de chicletes não exige saber quem é a pessoa antes de lhe dar um chiclete salgado.


O consumidor anônimo coloca dinheiro na máquina de chicletes e gira o botão para aceitar os termos do contrato. Essa etapa é importante porque a transação é transparente e ponto a ponto: entre você e a máquina. A transação também é segura, pois você deve fornecer a moeda esperada para usar a máquina de chicletes.


Assim que a moeda cai dentro da máquina de chicletes, os termos do contrato são aceitos e um chiclete rola até o fundo da máquina, permitindo que o cliente receba sua compra. Neste ponto, o contrato é totalmente executado.


O cliente deve aceitar o que é fornecido, o que significa que não pode devolver o chiclete ou inverter o mostrador para receber a moeda de volta. Da mesma forma, os contratos inteligentes geralmente são irreversíveis e não modificáveis.

Casos de uso de contrato inteligente

Além de exemplos com base financeira, alguns cenários em que interações anônimas, sem confiança, descentralizadas e transparentes que são irreversíveis e não modificáveis podem ser implementadas são observados abaixo:

  • Ensaios Clínicos - resultados de testes independentes
  • Eleições - votos dos participantes
  • Identidade - permite que os indivíduos determinem com quem compartilham sua identidade
  • Apólices de Seguro - apólices e termos individuais
  • Rastreamento de produtos e suprimentos - rastreamento de status para rastreamento de produção e suprimentos
  • Imóveis e Terrenos - escrituras relacionadas a imóveis e terrenos, que podem ser usadas para determinar o proprietário atual a qualquer momento
  • Informações de gravação - registros oficiais e transcrições (como o Endereço de Gettysburg )


Em todos os casos, o conteúdo do contrato inteligente pode ser recuperado e revisado sempre que possível, sem a capacidade de alterar ou modificar os resultados. Cada caso de uso acima fornece o contrato inteligente como o sistema de registro para as informações subjacentes.

O que um contrato inteligente não é

Neste momento, os contratos inteligentes não são acordos juridicamente vinculativos, exceto por alguns valores discrepantes. Isso significa que, se você não estiver satisfeito com o resultado do seu contrato inteligente, não será possível levar sua questão a um juiz em algum sistema judicial.

Existem algumas exceções, como no estado do Arizona, onde os contratos inteligentes são considerados juridicamente vinculativos. Além disso, se você estiver no estado da Califórnia e sua certidão de casamento estiver contida em um contrato inteligente, esse contrato também será juridicamente vinculativo. A expectativa é que mais governos reconheçam os contratos inteligentes como acordos juridicamente vinculativos no futuro.

Caso de uso: criando uma cédula HOA realista

Com base no contrato inteligente binário simples (sim/não) da publicação “Moving From Full-Stack Developer To Web3 Pioneer”, vamos dar um passo à frente e assumir que existe o seguinte requisito para uma cédula HOA para um bairro que tem um único cargo a preencher:

  • Selecione o presidente HOA


Idealmente, o objetivo seria que um único contrato inteligente fosse usado toda vez que houvesse uma eleição de HOA. Espera-se que os candidatos ao cargo de presidente mudem de uma eleição para outra.

Agora, vamos começar a fazer um contrato inteligente para atender às nossas necessidades.

Definindo nosso novo contrato inteligente

Usando o Solidity, trabalhei com Paul McAviney , que criou nosso contrato inteligente para a cédula HOA, conforme mostrado abaixo:


 // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; /********************************************************/ /* For learning purposes ONLY. Do not use in production */ /********************************************************/ // Download into project folder with `npm install @openzeppelin/contracts` import "@openzeppelin/contracts/access/Ownable.sol"; // Inherits the Ownable contract so we can use its functions and modifiers contract HOABallot is Ownable { // Custom type to describe a Presidential Candidate and hold votes struct Candidate { string name; uint256 votes; } // Array of Presidential Candidates Candidate[] public candidates; // Add a President Candidate - onlyOwner function addCandidate(string memory _name) public onlyOwner { require(bytes(_name).length > 0, "addCandidate Error: Please enter a name"); candidates.push(Candidate({name: _name, votes: 0})); } // Remove a Candidate - onlyOwner function removeCandidate(string memory _name) public onlyOwner { require(bytes(_name).length > 0, "removeCandidate Error: Please enter a name"); bool foundCandidate = false; uint256 index; bytes32 nameEncoded = keccak256(abi.encodePacked(_name)); // Set index number for specific candidate for (uint256 i = 0; i < candidates.length; i++) { if (keccak256(abi.encodePacked(candidates[i].name)) == nameEncoded) { index = i; foundCandidate = true; } } // Make sure a candidate was found require(foundCandidate, "removeCandidate Error: Candidate not found"); // shift candidate to be removed to the end of the array and the rest forward for (uint256 i = index; i < candidates.length - 1; i++) { candidates[i] = candidates[i + 1]; } // remove last item from array candidates.pop(); } // Reset the President Vote Counts - onlyOwner function resetVoteCount() public onlyOwner { for (uint256 p = 0; p < candidates.length; p++) { candidates[p].votes = 0; } } // Add a vote to a candidate by name function addVoteByName(string memory _name) public { require(bytes(_name).length > 0, "addVoteByName Error: Please enter a name"); // Encode name so only need to do once bytes32 nameEncoded = keccak256(abi.encodePacked(_name)); for (uint256 i = 0; i < candidates.length; i++) { // solidity can't compare strings directly, need to compare hash if (keccak256(abi.encodePacked(candidates[i].name)) == nameEncoded) { candidates[i].votes += 1; } } } // Returns all the Presidential Candidates and their vote counts function getCandidates() public view returns (Candidate[] memory) { return candidates; } function getWinner() public view returns (Candidate memory winner) { uint256 winningVoteCount = 0; for (uint256 i = 0; i < candidates.length; i++) { if (candidates[i].votes > winningVoteCount) { winningVoteCount = candidates[i].votes; winner = candidates[i]; } } return winner; } }


Aqui estão alguns itens importantes relacionados ao design do contrato inteligente:


  • Por padrão, não há candidatos na cédula.
  • Os candidatos podem ser adicionados (somente pelo proprietário do contrato inteligente) usando a função addCandidate().
  • Da mesma forma, os candidatos podem ser removidos (somente pelo proprietário do contrato inteligente) usando a função removeCandidate().
  • Lançar um voto alavancará a função getCandidates(), que pode ser usada no Dapp correspondente para chamar a função addVoteByName().
  • O mesmo método getCandidates() pode ser chamado para determinar a contagem de votos atual.
  • O contrato Ownable do OpenZeppelin permite a propriedade do contrato, bem como a capacidade de transferir a propriedade para outro endereço.


Agora, vamos preparar o contrato inteligente para uso.

Preparando-se para usar o contrato inteligente

Para poder usar nosso contrato inteligente, criaremos um projeto Truffle simples e implantaremos o contrato na rede de teste Ropsten. Para fazer isso, primeiro precisamos da versão mais recente do Truffle. Com NPM instalado , execute o comando:


 npm install -g truffle


A instalação da versão mais recente nos dará acesso ao Painel de trufas , o que tornará a implantação de nosso contrato inteligente muito mais fácil e consideravelmente mais segura, pois não teremos que compartilhar nossas chaves de carteira privadas ou frases mnemônicas. Nós vamos chegar a isso um pouco mais tarde.


Em seguida, crie um novo diretório e inicialize um novo projeto Truffle.


 mkdir hoa-ballot-contract && cd hoa-ballot-contract truffle init


Isso criará um projeto de contrato inteligente básico que podemos preencher como acharmos adequado. Então abra o projeto em seu editor de código favorito e vamos ao que interessa!


Para aproveitar o OpenZeppelin, o seguinte comando também precisa ser executado na pasta do projeto:


 npm install @openzeppelin/contracts


Abra o arquivo truffle-config.js e adicionaremos o Truffle Dashboard dentro do objeto de networks . Além de todo o clichê comentado, nosso objeto agora deve ficar assim:


 networks: { dashboard: { port: 24012, } }


Para a próxima etapa, criaremos um novo arquivo de contrato inteligente. Dentro da pasta de contratos , crie um novo arquivo e nomeie-o HOABallot.sol . A partir daqui, vamos apenas colar o contrato inteligente acima.


A última coisa que precisamos fazer antes de implantar este contrato é configurar o script de implantação. Usando o conteúdo abaixo, precisamos criar um novo arquivo na pasta migrations chamado 2_hoaballot_migration.js .


 const HOABallot = artifacts.require("HOABallot"); Module.exports = function (deployer) { deployer.deploy(HOABallot); }


Agora estamos prontos para implantar nosso contrato na rede de teste Ropsten. Em uma nova janela de terminal, digite o seguinte comando para iniciar o painel:


 truffle dashboard


Uma vez em execução, nosso navegador deve aparecer com uma interface solicitando que conectemos nossa carteira. Se isso não aparecer para você, navegue até localhost:24012 .


Clicar uma vez no botão METAMASK iniciará o MetaMask por meio do plug-in do navegador. Se você não tiver uma extensão de navegador de carteira instalada, poderá obter uma em metamask.io . Siga as etapas para criar uma conta e, em seguida, retorne ao Painel de trufas para se conectar:


Após inserir uma senha válida e utilizar o botão Desbloquear , o Painel de Trufas confirma a rede a ser utilizada:


Depois de clicar no botão CONFIRMAR , o Painel de trufas agora está ouvindo as solicitações:


Vamos precisar de Ropsten Eth para realizar a implantação. Se você não tiver nenhum, você pode solicite alguns nesta torneira .


Tudo o que temos a fazer agora é implantar o contrato. Na janela original do terminal, verifique se você está na pasta do projeto e digite o comando:


 truffle migrate --network dashboard


O Truffle compilará automaticamente nosso contrato inteligente e encaminhará a solicitação por meio do painel. Cada solicitação seguirá o mesmo fluxo listado abaixo.


Primeiramente, o Painel de Trufas pede confirmação para processar o pedido:


Ao pressionar o botão PROCESS , o plug-in MetaMask também pedirá confirmação:


O botão de confirmação permitirá que os fundos sejam removidos desta carteira associada para processar cada solicitação.


Quando o processo estiver concluído, as seguintes informações aparecerão na janela do terminal usado para emitir o comando truffle migrar:


 2_hoaballot_migration.js ======================== Deploying 'HOABallot' --------------------- > transaction hash: 0x5370b6f9ee1f69e92cc6289f9cb0880386f15bff389b54ab09a966c5d144f59esage. > Blocks: 0 Seconds: 32 > contract address: 0x2981d347e288E2A4040a3C17c7e5985422e3cAf2 > block number: 12479257 > block timestamp: 1656386400 > account: 0x7fC3EF335D16C0Fd4905d2C44f49b29BdC233C94 > balance: 41.088173901232893417 > gas used: 1639525 (0x190465) > gas price: 2.50000001 gwei > value sent: 0 ETH > total cost: 0.00409881251639525 ETH > Saving migration to chain. > Saving artifacts ------------------------------------- > Total cost: 0.00409881251639525 ETH Summary ======= > Total deployments: 1 > Final cost: 0.00409881251639525 ETH


Agora, usando o valor do endereço do contrato , podemos validar o contrato inteligente usando a seguinte URL:

https://ropsten.etherscan.io/address/0x2981d347e288E2A4040a3C17c7e5985422e3cAf2


Agora podemos mudar e começar a construir o Dapp.

Criando o Dapp de cédula HOA usando React

Vou criar um aplicativo React chamado hoa-ballot-client usando o React CLI:


 npx create-react-app hoa-ballot-client


Em seguida, alterei os diretórios para a pasta recém-criada e executei o seguinte para instalar as dependências web3 e OpenZepplin no aplicativo React:

 cd hoa-ballot-client npm install web3 npm install @openzeppelin/contracts —save


Com base no conteúdo do arquivo de contrato inteligente HOABallot.sol , naveguei até a pasta build/contracts e abri o arquivo HOBallot.json , depois usei os valores da propriedade “abi” para a constante hoaBallot do arquivo abi.js como mostrado abaixo:


 export const hoaBallot = [ { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "candidates", "outputs": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "uint256", "name": "votes", "type": "uint256" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [], "name": "owner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "string", "name": "_name", "type": "string" } ], "name": "addCandidate", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "string", "name": "_name", "type": "string" } ], "name": "removeCandidate", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "resetVoteCount", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "string", "name": "_name", "type": "string" } ], "name": "addVoteByName", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "getCandidates", "outputs": [ { "components": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "uint256", "name": "votes", "type": "uint256" } ], "internalType": "struct HOABallot.Candidate[]", "name": "", "type": "tuple[]" } ], "stateMutability": "view", "type": "function", "constant": true }, { "inputs": [], "name": "getWinner", "outputs": [ { "components": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "uint256", "name": "votes", "type": "uint256" } ], "internalType": "struct HOABallot.Candidate", "name": "winner", "type": "tuple" } ], "stateMutability": "view", "type": "function", "constant": true } ];


Este arquivo foi colocado em uma pasta abi recém-criada dentro da pasta src do aplicativo React.


Agora, precisamos atualizar o arquivo React Apps.js. Vamos primeiro começar com o topo do arquivo, que precisa ser configurado conforme mostrado abaixo:


 import React, { useState } from "react"; import { hoaBallot } from "./abi/abi"; import Web3 from "web3"; import "./App.css"; const web3 = new Web3(Web3.givenProvider); const contractAddress = "0x2981d347e288E2A4040a3C17c7e5985422e3cAf2"; const storageContract = new web3.eth.Contract(hoaBallot, contractAddress);


O contractAddress pode ser encontrado de várias maneiras. Nesse caso, usei os resultados no comando truffle — migration da CLI. Outra opção é usar o site Etherscan.


Agora, tudo o que resta é criar o código React padrão para realizar as seguintes coisas:


  • Adicione um candidato presidencial HOA
  • Remover um candidato presidencial HOA
  • Obtenha uma lista de candidatos presidenciais HOA
  • Vote em um candidato presidencial HOA
  • Determinar o presidente HOA


Em minha publicação “Moving From Full-Stack Developer To Web3 Pioneer”, adicionei o componente Nav também, para que o endereço do eleitor seja exibido para fácil referência.


O aplicativo React atualizado agora aparece da seguinte forma:


 const web3 = new Web3(Web3.givenProvider); const contractAddress = "0x2981d347e288E2A4040a3C17c7e5985422e3cAf2"; const storageContract = new web3.eth.Contract(hoaBallot, contractAddress); const gasMultiplier = 1.5; const useStyles = makeStyles((theme) => ({ root: { "& > *": { margin: theme.spacing(1), }, }, })); const StyledTableCell = styled(TableCell)(({ theme }) => ({ [`&.${tableCellClasses.head}`]: { backgroundColor: theme.palette.common.black, color: theme.palette.common.white, fontSize: 14, fontWeight: 'bold' }, [`&.${tableCellClasses.body}`]: { fontSize: 14 }, })); function App() { const classes = useStyles(); const [newCandidateName, setNewCandidateName] = useState(""); const [account, setAccount] = useState(""); const [owner, setOwner] = useState(""); const [candidates, updateCandidates] = useState([]); const [winner, setWinner] = useState("unknown candidate"); const [waiting, setWaiting] = useState(false); const loadAccount = async(useSpinner) => { if (useSpinner) { setWaiting(true); } const web3 = new Web3(Web3.givenProvider || "http://localhost:8080"); const accounts = await web3.eth.getAccounts(); setAccount(accounts[0]); if (useSpinner) { setWaiting(false); } } const getOwner = async (useSpinner) => { if (useSpinner) { setWaiting(true); } const owner = await storageContract.methods.owner().call(); setOwner(owner); if (useSpinner) { setWaiting(false); } }; const getCandidates = async (useSpinner) => { if (useSpinner) { setWaiting(true); } const candidates = await storageContract.methods.getCandidates().call(); updateCandidates(candidates); await determineWinner(); if (useSpinner) { setWaiting(false); } }; const determineWinner = async () => { const winner = await storageContract.methods.getWinner().call(); if (winner && winner.name) { setWinner(winner.name); } else { setWinner("<unknown candidate>") } } const vote = async (candidate) => { setWaiting(true); const gas = (await storageContract.methods.addVoteByName(candidate).estimateGas({ data: candidate, from: account })) * gasMultiplier; let gasAsInt = gas.toFixed(0); await storageContract.methods.addVoteByName(candidate).send({ from: account, data: candidate, gasAsInt, }); await getCandidates(false); setWaiting(false); } const removeCandidate = async (candidate) => { setWaiting(true); const gas = (await storageContract.methods.removeCandidate(candidate).estimateGas({ data: candidate, from: account })) * gasMultiplier; let gasAsInt = gas.toFixed(0); await storageContract.methods.removeCandidate(candidate).send({ from: account, data: candidate, gasAsInt, }); await getCandidates(false); setWaiting(false); } const addCandidate = async () => { setWaiting(true); const gas = (await storageContract.methods.addCandidate(newCandidateName).estimateGas({ data: newCandidateName, from: account })) * gasMultiplier; let gasAsInt = gas.toFixed(0); await storageContract.methods.addCandidate(newCandidateName).send({ from: account, data: newCandidateName, gasAsInt, }); await getCandidates(false); setWaiting(false); } React.useEffect(() => { setWaiting(true); getOwner(false).then(r => { loadAccount(false).then(r => { getCandidates(false).then(r => { setWaiting(false); }); }); }); // eslint-disable-next-line react-hooks/exhaustive-deps },[]); return ( <div className={classes.root}> <Nav /> <div className="main"> <div className="card"> <Typography variant="h3"> HOABallot </Typography> {(owner && owner.length > 0) && ( <div className="paddingBelow"> <Typography variant="caption" > This ballot is owned by: {owner} </Typography> </div> )} {waiting && ( <div className="spinnerArea" > <CircularProgress /> <Typography gutterBottom> Processing Request ... please wait </Typography> </div> )} {(owner && owner.length > 0 && account && account.length > 0 && owner === account) && ( <div className="ownerActions generalPadding"> <Grid container spacing={3}> <Grid item xs={12}> <Typography variant="h6" gutterBottom> Ballot Owner Actions </Typography> </Grid> <Grid item xs={6} sm={6}> <TextField id="newCandidateName" value={newCandidateName} label="Candidate Name" variant="outlined" onChange={event => { const { value } = event.target; setNewCandidateName(value); }} /> </Grid> <Grid item xs={6} sm={6}> <Button id="addCandidateButton" className="button" variant="contained" color="primary" type="button" size="large" onClick={addCandidate}>Add New Candidate</Button> </Grid> </Grid> </div> )} <Typography variant="h5" gutterBottom className="generalPadding"> Candidates </Typography> {(!candidates || candidates.length === 0) && ( <div> <div className="paddingBelow"> <Typography variant="normal"> No candidates current exist. </Typography> </div> <div> <Typography variant="normal" gutterBottom> Ballot owner must use the <strong>ADD NEW CANDIDATE</strong> button to add candidates. </Typography> </div> </div> )} {(candidates && candidates.length > 0) && ( <div> <TableContainer component={Paper}> <Table sx={{ minWidth: 650 }} aria-label="customized table"> <TableHead> <TableRow> <StyledTableCell>Candidate Name</StyledTableCell> <StyledTableCell align="right">Votes</StyledTableCell> <StyledTableCell align="center">Actions</StyledTableCell> </TableRow> </TableHead> <TableBody> {candidates.map((row) => ( <TableRow key={row.name} sx={{ '&:last-child td, &:last-child th': { border: 0 } }} > <TableCell component="th" scope="row"> {row.name} </TableCell> <TableCell align="right">{row.votes}</TableCell> <TableCell align="center"> <Button color="success" variant="contained" onClick={() => { vote(row.name); }} > Vote </Button> &nbsp; {(owner && owner.length > 0 && account && account.length > 0 && owner === account) && <Button color="error" variant="contained" onClick={() => { removeCandidate(row.name); }} > Remove Candidate </Button> } </TableCell> </TableRow> ))} </TableBody> </Table> </TableContainer> <div className="generalPadding"> <Typography variant="normal" gutterBottom> {winner} is winning the election. </Typography> </div> </div> )} </div> </div> </div> ); } export default App;


Para iniciar o Dapp baseado em React, o Yarn CLI pode ser usado:


 yarn start


Depois de compilado e validado, o aplicativo aparecerá na tela, conforme imagem abaixo:



Durante o vídeo:


  • Confirmei que sou o proprietário do contrato, pois o valor "Seu endereço conectado" corresponde exatamente ao valor "Esta cédula pertence a" e a seção Ações do proprietário da cédula é exibida.
  • Como proprietário do contrato, pude ver e usar o botão ADICIONAR NOVO CANDIDATO para estabelecer candidatos para a eleição. Usei os nomes Dave Brown e Steve Smith para este exemplo.
  • Como proprietário do contrato, eu também poderia ter usado o botão REMOVER CANDIDATO.
  • Depois de criar os dois candidatos, votei em um dos dois candidatos usando o botão VOTE na mesma linha do candidato desejado. Eu votei em Dave Brown.
  • O atual vencedor da eleição é exibido abaixo da tabela de candidatos. Neste caso, é Dave Brown.


Desde a implantação do contrato inteligente, qualquer pessoa pode visualizar o histórico completo no seguinte URL:


https://ropsten.etherscan.io/address/0x2981d347e288E2A4040a3C17c7e5985422e3cAf2


Conclusão

Desde 2021, tento viver de acordo com a seguinte declaração de missão, que sinto que pode ser aplicada a qualquer profissional de tecnologia:


“Concentre seu tempo em fornecer recursos/funcionalidades que ampliem o valor de sua propriedade intelectual. Aproveite estruturas, produtos e serviços para todo o resto.”
J. Vester


Os contratos inteligentes fornecem a capacidade de permitir que duas partes entrem em um acordo em que o resultado do contrato se torna um registro oficial da transação. A adoção de um contrato inteligente segue minha declaração de missão pessoal, pois a estrutura subjacente evita reinventar a roda quando surge a necessidade de tal contrato.


Ao mesmo tempo, o próprio design do contrato inteligente vai um passo além e atende à minha declaração de missão a partir de um fator de reutilização. Neste exemplo, o mesmo contrato inteligente HOA pode ser usado, apesar de diferentes candidatos concorrerem na eleição atual. Aqui, aproveitamos o poder do contrato inteligente para evitar a criação de um novo contrato inteligente sempre que houver uma eleição.


Ao usar o Etherscan para pesquisar o valor de conversão de uma das transações usando o conversor de ETH para USD do Google, o custo por transação foi de 0,24 (USD) para 0,0001348975 ETH. Ironicamente, esse era o custo de um modesto chiclete de uma máquina de chicletes quando eu era criança.


Se você quiser saber mais sobre contratos inteligentes, a equipe da ConsenSys forneceu excelentes recursos para ajudá-lo a prototipar suas ideias para ver se a adoção de contratos inteligentes é um caso de uso válido.


Se você estiver interessado no código-fonte deste artigo, poderá encontrá-lo nos seguintes URLs:


https://github.com/paul-mcaviney/smart-contract-deep-dive/blob/main/HOABallot.sol

https://gitlab.com/johnjvester/hoa-ballot-contract

https://gitlab.com/johnjvester/hoa-ballot-client


Tenha um ótimo dia!