paint-brush
Cómo escribir un contrato inteligente de Solidity e implementarlo en Ropstenpor@johnjvester
679 lecturas
679 lecturas

Cómo escribir un contrato inteligente de Solidity e implementarlo en Ropsten

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

Demasiado Largo; Para Leer

Los contratos inteligentes permiten que dos partes celebren un acuerdo. Sumérjase en los contratos inteligentes para mostrar cómo reutilizar un solo contrato una y otra vez.

People Mentioned

Mention Thumbnail
Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Cómo escribir un contrato inteligente de Solidity e implementarlo en Ropsten
John Vester HackerNoon profile picture


Los " Pasar de desarrollador de pila completa a Web3 Pioneer La publicación brindó una descripción general de alto nivel para brindarles a los desarrolladores completos un vistazo al mundo del desarrollo Web3. Si no ha tenido la oportunidad de revisar ese artículo, considere echarle un vistazo, ya que también proporciona una buena introducción a Web3.


El resultado final de mi artículo original demostró cómo una asociación de propietarios (HOA) podría usar la tecnología Web3 para alojar su boleta electoral. El problema con el diseño original es que el contrato inteligente subyacente solo permitía una sola respuesta de sí o no. Esto fue por diseño para mantener el contrato inteligente simple mientras se introducen otros conceptos necesarios para crear una boleta HOA usando tecnologías Web3.


El propósito de esta publicación es profundizar en los contratos inteligentes para crear una aplicación que no solo capture necesidades y funciones realistas para una boleta de HOA, sino que diseñe una que pueda reutilizarse de una elección a la siguiente.

Acerca de los contratos inteligentes

Antes de comenzar, definamos un contrato inteligente:

“Un contrato inteligente es un programa que se ejecuta en una dirección en Ethereum. Están compuestos por datos y funciones que pueden ejecutarse al recibir una transacción. Aquí hay una descripción general de lo que constituye un contrato inteligente”.


fuente ethereum.org


La máquina de chicles

Lo crea o no, se puede encontrar un ejemplo fácil de un contrato inteligente en una simple máquina de chicles:


La gente entiende fácilmente el costo relacionado con la compra de la máquina de chicles. Normalmente, se trata de una cuarta parte (EE. UU.). Es importante señalar aquí que el cliente es anónimo, ya que la máquina de chicles no requiere saber quién es una persona antes de darle un sabroso chicle.


El consumidor anónimo coloca dinero en la máquina de chicles y gira el dial para aceptar los términos del contrato. Este paso es importante porque la transacción es transparente y de igual a igual: entre usted y la máquina. La transacción también está asegurada ya que debe proporcionar la moneda esperada para usar la máquina de chicles.


Una vez que el dinero cae dentro de la máquina de chicles, se aceptan los términos del contrato y un chicle rueda hacia la parte inferior de la máquina, lo que permite que el cliente reciba su compra. En este punto, el contrato está completamente ejecutado.


El cliente debe aceptar lo que se le proporciona, lo que significa que no puede devolver el chicle o invertir el dial para recuperar su moneda. De la misma manera, los contratos inteligentes suelen ser irreversibles e inmodificables.

Casos de uso de contratos inteligentes

Aparte de los ejemplos impulsados financieramente, a continuación se indican algunos escenarios en los que se podrían implementar interacciones anónimas, sin confianza, descentralizadas y transparentes que son irreversibles e inmodificables:

  • Ensayos clínicos: resultados de pruebas independientes
  • Elecciones - votos emitidos participantes
  • Identidad: permite que las personas determinen con quién comparten su identidad
  • Pólizas de seguro - pólizas individuales y términos
  • Seguimiento de productos y suministros: seguimiento del estado de la producción y seguimiento del suministro
  • Bienes inmuebles y terrenos: escrituras relacionadas con bienes inmuebles y terrenos, que se pueden utilizar para obtener el propietario actual en cualquier momento.
  • Información de registro: registros oficiales y transcripciones (como el La direccion de Gettysburg )


En todos los casos, los contenidos del contrato inteligente se pueden recuperar y revisar con la mayor frecuencia posible, sin la posibilidad de cambiar o modificar los resultados. Cada caso de uso anterior proporciona el contrato inteligente como el sistema de registro de la información subyacente.

Qué no es un contrato inteligente

En este momento, los contratos inteligentes no son acuerdos legalmente vinculantes, excepto por algunos casos atípicos. Esto significa que si no está satisfecho con el resultado de su contrato inteligente, no es posible llevar su problema ante un juez en algún sistema judicial.

Hay algunas excepciones, como en el estado de Arizona, donde los contratos inteligentes se consideran legalmente vinculantes. Además, si se encuentra en el estado de California y su licencia de matrimonio está incluida en un contrato inteligente, ese acuerdo también es legalmente vinculante. La expectativa es que más gobiernos reconozcan los contratos inteligentes como acuerdos legalmente vinculantes en el futuro.

Caso de uso: creación de una boleta de HOA realista

Sobre la base del contrato inteligente binario simple (sí/no) de la publicación "Moving From Full-Stack Developer To Web3 Pioneer", demos un paso adelante y supongamos que existe el siguiente requisito para una boleta HOA para un vecindario que tiene un solo puesto a cubrir:

  • Seleccione el presidente de la HOA


Idealmente, el objetivo sería utilizar un solo contrato inteligente cada vez que haya una elección de HOA. Se espera que los que se postulan para el cargo de presidente cambien de una elección a la siguiente.

Ahora, comencemos a hacer un contrato inteligente para manejar nuestras necesidades.

Definición de nuestro nuevo contrato inteligente

Usando Solidity, trabajé con Pablo McAviney , quien elaboró nuestro contrato inteligente para la boleta de HOA como se muestra a continuación:


 // 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; } }


Aquí hay algunos elementos clave relacionados con el diseño del contrato inteligente:


  • Por defecto, no hay candidatos en la papeleta.
  • Los candidatos se pueden agregar (solo por el propietario del contrato inteligente) mediante la función addCandidate().
  • Del mismo modo, los candidatos pueden ser eliminados (solo por el propietario del contrato inteligente) mediante la función removeCandidate().
  • Emitir un voto aprovechará la función getCandidates(), que se puede usar en el Dapp correspondiente para llamar a la función addVoteByName().
  • Se puede llamar al mismo método getCandidates() para determinar el recuento de votos actual.
  • El contrato Ownable de OpenZeppelin permite la propiedad del contrato, así como la posibilidad de transferir la propiedad a otra dirección.


Ahora, preparemos el contrato inteligente para usar.

Preparándose para usar el contrato inteligente

Para poder utilizar nuestro contrato inteligente, construiremos un proyecto Truffle simple e implementaremos el contrato en la red de prueba de Ropsten. Para hacer esto, primero necesitaremos la versión más reciente de Truffle. Con NPM instalado , ejecute el comando:


 npm install -g truffle


Instalar la última versión nos dará acceso a la Tablero de trufas , lo que hará que la implementación de nuestro contrato inteligente sea mucho más fácil y considerablemente más segura, ya que no tendremos que compartir nuestras claves de billetera privada o frases mnemotécnicas. Sin embargo, llegaremos a eso un poco más tarde.


A continuación, cree un nuevo directorio e inicialice un nuevo proyecto Truffle.


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


Esto creará un proyecto de contrato inteligente barebones que podemos completar como mejor nos parezca. Así que abra el proyecto en su editor de código favorito y ¡manos a la obra!


Para aprovechar OpenZeppelin, también se debe ejecutar el siguiente comando en la carpeta del proyecto:


 npm install @openzeppelin/contracts


Abra el archivo truffle-config.js y agregaremos el Tablero de Truffle dentro del objeto de networks . Aparte de todo el texto repetitivo comentado, nuestro objeto ahora debería verse así:


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


Para el siguiente paso, crearemos un nuevo archivo de contrato inteligente. Dentro de la carpeta de contratos , cree un nuevo archivo y asígnele el nombre HOABallot.sol . A partir de aquí, simplemente pegaremos el contrato inteligente de arriba.


Lo último que debemos hacer antes de poder implementar este contrato es configurar el script de implementación. Usando los contenidos a continuación, necesitamos crear un nuevo archivo en la carpeta de migraciones llamado 2_hoaballot_migration.js .


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


Ahora estamos listos para implementar nuestro contrato en la red de prueba de Ropsten. En una nueva ventana de terminal, escriba el siguiente comando para iniciar el tablero:


 truffle dashboard


Una vez que se está ejecutando, nuestro navegador debería aparecer con una interfaz que nos pide que conectemos nuestra billetera. Si esto no aparece para usted, vaya a localhost:24012 .


Al hacer un solo clic en el botón METAMASK , se iniciará MetaMask a través del complemento del navegador. Si no tiene instalada una extensión de navegador de billetera, puede obtener una en metamask.io . Siga los pasos para crear una cuenta y luego regrese al Tablero de Truffle para conectarse:


Después de ingresar una contraseña válida y usar el botón Desbloquear , Truffle Dashboard confirma la red que se utilizará:


Después de hacer clic en el botón CONFIRMAR , Truffle Dashboard ahora está escuchando solicitudes:


Necesitaremos Ropsten Eth para llevar a cabo el despliegue. Si no tienes ninguno, puedes pide un poco en este faucet .


Todo lo que tenemos que hacer ahora es implementar el contrato. En la ventana de su terminal original, asegúrese de estar en la carpeta del proyecto y escriba el comando:


 truffle migrate --network dashboard


Truffle compilará automáticamente nuestro contrato inteligente y luego enrutará la solicitud a través del tablero. Cada solicitud seguirá el mismo flujo que se detalla a continuación.


Primero, el Tablero de Truffle solicita confirmación para procesar la solicitud:


Al presionar el botón PROCESAR, el complemento MetaMask también solicitará confirmación:


El botón Confirmación permitirá que se eliminen fondos de esta billetera asociada para procesar cada solicitud.


Cuando se complete el proceso, aparecerá la siguiente información en la ventana del terminal utilizada para emitir el comando de migración de truffle:


 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


Ahora, usando el valor de la dirección del contrato , podemos validar el contrato inteligente usando la siguiente URL:

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


Ahora podemos cambiar y comenzar a construir el Dapp.

Creación de la Dapp de la boleta de la HOA con React

Crearé una aplicación React llamada hoa-ballot-client usando React CLI:


 npx create-react-app hoa-ballot-client


A continuación, cambié los directorios a la carpeta recién creada y ejecuté lo siguiente para instalar las dependencias web3 y OpenZepplin en la aplicación React:

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


Basado en el contenido del archivo de contrato inteligente HOABallot.sol , navegué a la carpeta build/contracts y abrí el archivo HOBallot.json , luego usé los valores para la propiedad "abi" para la constante hoaBallot del archivo abi.js como mostrado a continuación:


 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 archivo se colocó en una carpeta abi recién creada dentro de la carpeta src de la aplicación React.


Ahora, necesitamos actualizar el archivo React Apps.js. Comencemos primero con la parte superior del archivo, que debe configurarse como se muestra a continuación:


 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);


La dirección del contrato se puede encontrar de varias maneras. En este caso, utilicé los resultados en el comando trufa — migrar CLI. Otra opción es usar el sitio de Etherscan.


Ahora, todo lo que queda es crear un código React estándar para lograr lo siguiente:


  • Agregar un candidato presidencial HOA
  • Eliminar un candidato presidencial HOA
  • Obtenga una lista de los candidatos presidenciales de la HOA
  • Vote por un candidato presidencial de la HOA
  • Determinar el presidente de la HOA


En mi publicación "Moving From Full-Stack Developer to Web3 Pioneer", también agregué el componente Nav, de modo que la dirección del votante se muestre para una fácil referencia.


La aplicación React actualizada ahora aparece de la siguiente manera:


 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 la Dapp basada en React, se puede usar la CLI de Yarn:


 yarn start


Una vez compilada y validada, la aplicación aparecerá en pantalla, como se muestra a continuación:



Durante el vídeo:


  • Validé que era el propietario del contrato, ya que el valor "Su dirección conectada" coincide exactamente con el valor "Esta boleta es propiedad de" y se muestra la sección Acciones del propietario de la boleta.
  • Como propietario del contrato, pude ver y usar el botón AGREGAR NUEVO CANDIDATO para establecer candidatos para la elección. Usé los nombres de Dave Brown y Steve Smith para este ejemplo.
  • Como propietario del contrato, también podría haber usado el botón ELIMINAR CANDIDATO.
  • Después de crear ambos candidatos, voté por uno de los dos candidatos usando el botón VOTAR en la misma fila que el candidato deseado. Voté por Dave Brown.
  • El ganador actual de la elección se muestra debajo de la tabla de candidatos. En este caso, es Dave Brown.


Desde la implementación del contrato inteligente, cualquiera puede ver el historial completo en la siguiente URL:


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


Conclusión

Desde 2021, he estado tratando de vivir de acuerdo con la siguiente declaración de misión, que creo que se puede aplicar a cualquier profesional de la tecnología:


“Concentre su tiempo en ofrecer características/funcionalidades que amplíen el valor de su propiedad intelectual. Aproveche los marcos, productos y servicios para todo lo demás”.
J. Vester


Los contratos inteligentes brindan la capacidad de permitir que dos partes celebren un acuerdo en el que el resultado del contrato se convierte en un registro oficial grabado en piedra de la transacción. La adopción de un contrato inteligente se adhiere a mi declaración de misión personal en el sentido de que el marco subyacente evita reinventar la rueda cuando surge la necesidad de dicho contrato.


Al mismo tiempo, el diseño del contrato inteligente en sí mismo va un paso más allá y cumple con mi declaración de misión desde un factor de reutilización. En este ejemplo, se puede usar el mismo contrato inteligente de HOA, a pesar de que diferentes candidatos se presenten en las elecciones actuales. Aquí aprovechamos el poder del contrato inteligente para evitar crear un nuevo contrato inteligente cada vez que hay una elección.


Al usar Etherscan para buscar el valor de conversión de una de las transacciones que usan el convertidor de ETH a USD de Google, el costo por transacción fue de 0,24 (USD) por 0,0001348975 ETH. Irónicamente, ese era el costo de un chicle modesto de una máquina de chicles cuando era niño.


Si desea obtener más información sobre los contratos inteligentes, el equipo de ConsenSys ha proporcionado excelentes recursos para ayudarlo a crear prototipos de sus ideas para ver si la adopción de contratos inteligentes es un caso de uso válido.


Si está interesado en el código fuente de este artículo, puede encontrarlo en las siguientes URL:


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

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

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


¡Que tengas un gran día!