Các "
Kết quả cuối cùng của bài viết gốc của tôi đã chứng minh cách một hiệp hội chủ nhà (HOA) có thể sử dụng công nghệ Web3 để tổ chức lá phiếu bầu cử của họ. Vấn đề với thiết kế ban đầu là hợp đồng thông minh cơ bản chỉ cho phép một câu trả lời có hoặc không. Điều này được thiết kế để giữ cho hợp đồng thông minh đơn giản trong khi giới thiệu các khái niệm khác cần thiết để tạo một lá phiếu HOA bằng công nghệ Web3.
Mục đích của ấn phẩm này là đi sâu hơn vào các hợp đồng thông minh để xây dựng một ứng dụng không chỉ nắm bắt các nhu cầu và chức năng thực tế cho một lá phiếu HOA mà còn thiết kế một ứng dụng có thể được sử dụng lại từ cuộc bầu cử này sang cuộc bầu cử tiếp theo.
Trước khi bắt đầu, hãy xác định một hợp đồng thông minh:
“Hợp đồng thông minh là một chương trình chạy tại một địa chỉ trên Ethereum. Chúng được tạo thành từ dữ liệu và các chức năng có thể thực thi khi nhận được một giao dịch. Dưới đây là tổng quan về những gì tạo nên một hợp đồng thông minh. "
nguồn
ethereum.org
Bạn có tin hay không, một ví dụ đơn giản về hợp đồng thông minh có thể được tìm thấy trong một máy đánh bóng kẹo cao su đơn giản:
Mọi người dễ dàng hiểu được chi phí liên quan đến việc mua máy đánh bóng kẹo cao su. Thông thường, đây là một quý (Hoa Kỳ). Điều quan trọng cần chỉ ra ở đây là khách hàng là người ẩn danh, vì máy đánh kẹo cao su không yêu cầu biết ai là người trước khi đưa một miếng kẹo cao su mặn cho họ.
Người tiêu dùng ẩn danh đặt tiền vào máy chơi kẹo cao su và xoay mặt số để chấp nhận các điều khoản của hợp đồng. Bước này rất quan trọng vì giao dịch là minh bạch và ngang hàng: giữa bạn và máy. Giao dịch cũng được bảo mật vì bạn phải cung cấp đơn vị tiền tệ dự kiến để sử dụng máy chơi kẹo cao su.
Khi tiền tệ giảm xuống bên trong máy chơi kẹo cao su, các điều khoản hợp đồng được chấp nhận và một viên bi lăn về phía dưới cùng của máy, cho phép khách hàng nhận được giao dịch mua của họ. Tại thời điểm này, hợp đồng đã được thực hiện đầy đủ.
Khách hàng phải chấp nhận những gì được cung cấp, có nghĩa là họ không thể trả lại viên kẹo cao su hoặc đảo ngược quay số để lấy lại tiền của họ. Theo cách tương tự, các hợp đồng thông minh thường không thể thay đổi và không thể sửa đổi.
Bên cạnh các ví dụ định hướng về tài chính, một số trường hợp trong đó các tương tác ẩn danh, không tin cậy, phi tập trung và minh bạch, không thể đảo ngược và không thể thay đổi có thể được thực hiện dưới đây:
Trong mọi trường hợp, nội dung của hợp đồng thông minh có thể được gọi lại và xem xét thường xuyên nhất có thể, mà không có khả năng thay đổi hoặc sửa đổi kết quả. Mỗi trường hợp sử dụng ở trên cung cấp hợp đồng thông minh như một hệ thống ghi lại thông tin cơ bản.
Tại thời điểm này, hợp đồng thông minh không phải là thỏa thuận ràng buộc về mặt pháp lý, ngoại trừ một số ngoại lệ. Điều này có nghĩa là nếu bạn không hài lòng với kết quả của hợp đồng thông minh của mình, thì việc đưa vấn đề của bạn ra trước thẩm phán trong một số hệ thống tòa án là không thể.
Có một vài trường hợp ngoại lệ như ở bang Arizona, nơi các hợp đồng thông minh được coi là ràng buộc về mặt pháp lý. Ngoài ra, nếu bạn ở bang California và giấy đăng ký kết hôn của bạn nằm trong một hợp đồng thông minh, thì thỏa thuận đó cũng có giá trị pháp lý ràng buộc. Kỳ vọng là sẽ có nhiều chính phủ công nhận hợp đồng thông minh là thỏa thuận ràng buộc về mặt pháp lý trong tương lai.
Xây dựng dựa trên hợp đồng thông minh nhị phân (có / không) đơn giản từ ấn phẩm “Chuyển từ nhà phát triển toàn ngăn xếp sang người tiên phong trong Web3”, chúng ta hãy tiến hành một bước và giả sử yêu cầu sau tồn tại đối với một lá phiếu HOA cho một vùng lân cận có một vị trí cần điền:
Lý tưởng nhất, mục tiêu sẽ là sử dụng một hợp đồng thông minh duy nhất mỗi khi có cuộc bầu cử HOA. Những người tranh cử vị trí tổng thống dự kiến sẽ thay đổi từ cuộc bầu cử này sang cuộc bầu cử tiếp theo.
Bây giờ, chúng ta hãy bắt đầu tạo một hợp đồng thông minh để xử lý các nhu cầu của chúng ta.
Sử dụng Solidity, tôi đã làm việc với
// 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; } }
Dưới đây là một số mục chính liên quan đến thiết kế hợp đồng thông minh:
Bây giờ, hãy chuẩn bị hợp đồng thông minh để sử dụng.
Để có thể sử dụng hợp đồng thông minh của chúng tôi, chúng tôi sẽ xây dựng một dự án Truffle đơn giản và triển khai hợp đồng tới mạng thử nghiệm Ropsten. Để làm điều này, trước tiên chúng ta cần phiên bản Truffle mới nhất. Với
npm install -g truffle
Cài đặt phiên bản mới nhất sẽ cung cấp cho chúng tôi quyền truy cập vào
Tiếp theo, tạo một thư mục mới và khởi tạo một dự án Truffle mới.
mkdir hoa-ballot-contract && cd hoa-ballot-contract truffle init
Điều này sẽ tạo ra một dự án hợp đồng thông minh rõ ràng mà chúng tôi có thể điền vào khi chúng tôi thấy phù hợp. Vì vậy, hãy mở dự án trong trình soạn thảo mã yêu thích của bạn và bắt đầu với nó!
Để tận dụng OpenZeppelin, lệnh sau cũng cần được thực thi trong thư mục dự án:
npm install @openzeppelin/contracts
Mở tệp truffle-config.js và chúng tôi sẽ thêm Trang tổng quan Truffle vào bên trong đối tượng networks
. Bên cạnh tất cả bảng soạn sẵn được chú thích, đối tượng của chúng ta bây giờ sẽ trông như thế này:
networks: { dashboard: { port: 24012, } }
Đối với bước tiếp theo, chúng tôi sẽ tạo một tệp hợp đồng thông minh mới. Bên trong thư mục hợp đồng , tạo một tệp mới và đặt tên là HOABallot.sol . Từ đây, chúng tôi sẽ chỉ dán vào hợp đồng thông minh ở trên.
Điều cuối cùng chúng ta cần làm trước khi có thể triển khai hợp đồng này là thiết lập kịch bản triển khai. Sử dụng nội dung bên dưới, chúng ta cần tạo một tệp mới trong thư mục di chuyển có tên là 2_hoaballot_migration.js .
const HOABallot = artifacts.require("HOABallot"); Module.exports = function (deployer) { deployer.deploy(HOABallot); }
Bây giờ chúng tôi đã sẵn sàng triển khai hợp đồng của mình với Ropsten testnet. Trong một cửa sổ dòng lệnh mới, hãy nhập lệnh sau để bắt đầu bảng điều khiển:
truffle dashboard
Khi nó đang chạy, trình duyệt của chúng tôi sẽ bật lên với một giao diện yêu cầu chúng tôi kết nối ví của mình. Nếu điều này không bật lên cho bạn, hãy điều hướng đến localhost:24012
.
Nhấp một lần vào nút METAMASK sẽ khởi chạy MetaMask thông qua trình cắm của trình duyệt. Nếu bạn chưa cài đặt tiện ích mở rộng trình duyệt ví, bạn có thể tải xuống tại
Sau khi nhập mật khẩu hợp lệ và sử dụng nút Mở khóa , Trang tổng quan Truffle xác nhận mạng sẽ được sử dụng:
Sau khi nhấp vào nút XÁC NHẬN , Trang tổng quan Truffle hiện đang lắng nghe các yêu cầu:
Chúng tôi sẽ cần Ropsten Eth để triển khai. Nếu bạn không có bất kỳ, bạn có thể
Tất cả những gì chúng tôi phải làm bây giờ là triển khai hợp đồng. Trong cửa sổ đầu cuối ban đầu của bạn, hãy đảm bảo rằng bạn đang ở trong thư mục dự án và nhập lệnh:
truffle migrate --network dashboard
Truffle sẽ tự động biên dịch hợp đồng thông minh của chúng tôi và sau đó định tuyến yêu cầu thông qua trang tổng quan. Mỗi yêu cầu sẽ tuân theo cùng một quy trình được liệt kê bên dưới.
Đầu tiên, Trang tổng quan Truffle yêu cầu xác nhận để xử lý yêu cầu:
Khi nhấn nút QUÁ TRÌNH , trình cắm MetaMask cũng sẽ yêu cầu xác nhận:
Nút Xác nhận sẽ cho phép rút tiền khỏi ví được liên kết này để xử lý từng yêu cầu.
Khi quá trình hoàn tất, thông tin sau sẽ xuất hiện trong cửa sổ đầu cuối được sử dụng để đưa ra lệnh di chuyển 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
Bây giờ, bằng cách sử dụng giá trị địa chỉ hợp đồng , chúng tôi có thể xác thực hợp đồng thông minh bằng cách sử dụng URL sau:
Bây giờ chúng ta có thể chuyển sang và bắt đầu xây dựng Dapp.
Tôi sẽ tạo một ứng dụng React có tên là hoa-ballot-client
bằng cách sử dụng React CLI:
npx create-react-app hoa-ballot-client
Tiếp theo, tôi đã thay đổi các thư mục thành thư mục mới được tạo và thực hiện như sau để cài đặt các phụ thuộc web3 và OpenZepplin vào ứng dụng React:
cd hoa-ballot-client npm install web3 npm install @openzeppelin/contracts —save
Dựa trên nội dung của tệp hợp đồng thông minh HOABallot.sol
, tôi đã điều hướng đến thư mục xây dựng / hợp đồng và mở tệp HOBallot.json
, sau đó sử dụng các giá trị cho thuộc tính “abi” cho hằng số hoaBallot
của tệp abi.js
như hiển thị bên dưới:
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 } ];
Tệp này được đặt vào một thư mục abi mới được tạo bên trong thư mục src của ứng dụng React.
Bây giờ, chúng ta cần cập nhật tệp React Apps.js. Trước tiên, hãy bắt đầu với phần trên cùng của tệp, cần được định cấu hình như hình dưới đây:
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);
Địa chỉ hợp đồng có thể được tìm thấy theo một số cách. Trong trường hợp này, tôi đã sử dụng kết quả trong lệnh truffle - migrate CLI. Một lựa chọn khác là sử dụng trang Etherscan.
Bây giờ, tất cả những gì còn lại là tạo mã React chuẩn để thực hiện những việc sau:
Trong ấn phẩm “Chuyển từ nhà phát triển toàn ngăn xếp sang người tiên phong trong Web3”, tôi cũng đã thêm thành phần Nav để địa chỉ của cử tri được hiển thị để dễ dàng tham khảo.
Ứng dụng React được cập nhật hiện xuất hiện như sau:
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> {(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;
Để bắt đầu Dapp dựa trên React, Yarn CLI có thể được sử dụng:
yarn start
Sau khi được biên dịch và xác thực, ứng dụng sẽ xuất hiện trên màn hình, như hình dưới đây:
Trong video:
Kể từ khi triển khai hợp đồng thông minh, bất kỳ ai cũng có thể xem toàn bộ lịch sử tại URL sau:
https://ropsten.etherscan.io/address/0x2981d347e288E2A4040a3C17c7e5985422e3cAf2
Kể từ năm 2021, tôi đã cố gắng sống theo tuyên bố sứ mệnh sau đây mà tôi cảm thấy có thể áp dụng cho bất kỳ chuyên gia công nghệ nào:
“Tập trung thời gian của bạn vào việc cung cấp các tính năng / chức năng giúp mở rộng giá trị tài sản trí tuệ của bạn. Tận dụng các khuôn khổ, sản phẩm và dịch vụ cho mọi thứ khác ”.
J. Vester
Hợp đồng thông minh cung cấp khả năng cho phép hai bên tham gia vào một thỏa thuận trong đó kết quả của hợp đồng trở thành một bản ghi chính thức cơ bản của giao dịch. Việc thông qua hợp đồng thông minh tuân theo tuyên bố sứ mệnh cá nhân của tôi trong đó khuôn khổ cơ bản tránh phát minh lại bánh xe khi phát sinh nhu cầu cho một hợp đồng như vậy.
Đồng thời, bản thân thiết kế hợp đồng thông minh còn tiến thêm một bước nữa và đáp ứng tuyên bố sứ mệnh của tôi từ yếu tố khả năng tái sử dụng. Trong ví dụ này, có thể sử dụng cùng một hợp đồng thông minh HOA, bất chấp các ứng cử viên khác nhau đang tranh cử trong cuộc bầu cử hiện tại. Ở đây, chúng tôi tận dụng sức mạnh của hợp đồng thông minh để tránh tạo hợp đồng thông minh mới mỗi khi có bầu cử.
Khi sử dụng Etherscan để tra cứu giá trị chuyển đổi của một trong các giao dịch bằng công cụ chuyển đổi ETH sang USD của Google, chi phí mỗi giao dịch là 0,24 (USD) cho 0,0001348975 ETH. Trớ trêu thay, đó là chi phí cho một viên kẹo cao su khiêm tốn từ một chiếc máy chơi kẹo cao su khi tôi còn nhỏ.
Nếu bạn muốn tìm hiểu thêm về hợp đồng thông minh, nhóm tại ConsenSys đã cung cấp các tài nguyên tuyệt vời để giúp bạn tạo nguyên mẫu ý tưởng của mình để xem liệu việc áp dụng hợp đồng thông minh có phải là trường hợp sử dụng hợp lệ hay không.
Nếu bạn quan tâm đến mã nguồn của bài viết này, bạn có thể tìm thấy nó tại các URL sau:
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
Có một ngày thực sự tuyệt vời!