paint-brush
Làm thế nào để viết một hợp đồng thông minh vững chắc và triển khai nó trên Ropstentừ tác giả@johnjvester
679 lượt đọc
679 lượt đọc

Làm thế nào để viết một hợp đồng thông minh vững chắc và triển khai nó trên Ropsten

từ tác giả John Vester35m2022/08/05
Read on Terminal Reader
Read this story w/o Javascript

dài quá đọc không nổi

Hợp đồng thông minh cho phép hai bên ký kết thỏa thuận. Tìm hiểu sâu về hợp đồng thông minh để chỉ ra cách sử dụng lại nhiều lần một hợp đồng duy nhất.

People Mentioned

Mention Thumbnail
Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Làm thế nào để viết một hợp đồng thông minh vững chắc và triển khai nó trên Ropsten
John Vester HackerNoon profile picture


Các " Chuyển từ Full-Stack Developer sang Web3 Pioneer ”Xuất bản đã cung cấp một cái nhìn tổng quan cấp cao để cung cấp cho các nhà phát triển full-stack cái nhìn sơ lược về thế giới phát triển Web3. Nếu bạn chưa có cơ hội xem lại bài báo đó, hãy xem xét, vì nó cũng cung cấp một phần giới thiệu tốt về Web3 ở đó.


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.

Giới thiệu về Hợp đồng thông minh

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


Máy Gumball

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.

Các trường hợp sử dụng hợp đồng thông minh

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:

  • Thử nghiệm lâm sàng - kết quả của các thử nghiệm độc lập
  • Bầu cử - người tham gia bỏ phiếu
  • Danh tính - cho phép các cá nhân xác định người mà họ chia sẻ danh tính của mình
  • Chính sách Bảo hiểm - các chính sách và điều khoản riêng lẻ
  • Theo dõi sản phẩm và cung ứng - theo dõi trạng thái để theo dõi sản xuất và cung cấp
  • Bất động sản và Đất đai - các chứng từ liên quan đến bất động sản và đất đai, có thể được sử dụng để xác định chủ sở hữu hiện tại vào bất kỳ thời điểm nào
  • Thông tin ghi chép - hồ sơ chính thức và bảng điểm (như Địa chỉ Gettysburg )


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.

Hợp đồng thông minh không phải là gì

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.

Trường hợp sử dụng: Tạo một lá phiếu HOA thực tế

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:

  • Chọn Chủ tịch HOA


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.

Xác định Hợp đồng Thông minh Mới của Chúng tôi

Sử dụng Solidity, tôi đã làm việc với Paul McAviney , người đã tạo ra hợp đồng thông minh cho lá phiếu HOA như hình dưới đây:


 // 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:


  • Theo mặc định, không có ứng cử viên nào trên lá phiếu.
  • Có thể thêm ứng cử viên (chỉ bởi chủ sở hữu hợp đồng thông minh) bằng cách sử dụng hàm addCandidate ().
  • Tương tự, có thể loại bỏ các ứng cử viên (chỉ bởi chủ sở hữu hợp đồng thông minh) bằng cách sử dụng hàm removeCandidate ().
  • Việc bỏ phiếu sẽ tận dụng hàm getCandidates (), có thể được sử dụng trong Dapp tương ứng để gọi hàm addVoteByName ().
  • Phương thức getCandidates () tương tự có thể được gọi để xác định số phiếu bầu hiện tại.
  • Hợp đồng có thể sở hữu của OpenZeppelin cho phép quyền sở hữu hợp đồng, cũng như khả năng chuyển quyền sở hữu đến một địa chỉ khác.


Bây giờ, hãy chuẩn bị hợp đồng thông minh để sử dụng.

Chuẩn bị sử dụng Hợp đồng thông minh

Để 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 đã được cài đặt , chạy lệnh:


 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 Bảng điều khiển Truffle , điều này sẽ giúp việc triển khai hợp đồng thông minh của chúng tôi trở nên dễ dàng hơn và an toàn hơn đáng kể, vì chúng tôi sẽ không phải chia sẻ các khóa ví cá nhân hoặc các cụm từ dễ nhớ. Tuy nhiên, chúng ta sẽ làm điều đó sau một chút.


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 metamask.io . Làm theo các bước để tạo tài khoản và sau đó quay lại Trang tổng quan Truffle để kết nố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ể yêu cầu một số ở vòi này .


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:

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


Bây giờ chúng ta có thể chuyển sang và bắt đầu xây dựng Dapp.

Tạo Dapp Lá phiếu HOA bằng React

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:


  • Thêm một ứng cử viên tổng thống HOA
  • Xóa một ứng cử viên tổng thống HOA
  • Nhận danh sách các ứng cử viên tổng thống HOA
  • Bỏ phiếu cho ứng cử viên tổng thống HOA
  • Xác định chủ tịch HOA


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


Để 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:


  • Tôi đã xác thực rằng tôi là chủ sở hữu hợp đồng, vì giá trị "Địa chỉ được kết nối của bạn" khớp chính xác với giá trị "Lá phiếu này thuộc sở hữu của" và phần Hành động của chủ sở hữu phiếu bầu được hiển thị.
  • Với tư cách là chủ sở hữu hợp đồng, tôi có thể xem và sử dụng nút THÊM THÍ SINH MỚI để thiết lập các ứng cử viên cho cuộc bầu cử. Tôi đã sử dụng tên Dave Brown và Steve Smith cho ví dụ này.
  • Với tư cách là chủ sở hữu hợp đồng, tôi cũng có thể sử dụng nút XÓA THÍ SINH.
  • Sau khi tạo cả hai ứng cử viên, tôi bình chọn cho một trong hai ứng viên bằng nút VOTE trên cùng hàng với ứng viên mong muốn. Tôi đã bình chọn cho Dave Brown.
  • Người chiến thắng hiện tại của cuộc bầu cử được hiển thị bên dưới bảng các ứng cử viên. Trong trường hợp này, đó là Dave Brown.


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


Sự kết luận

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!