Nhảy đến nội dung chính

Hợp đồng thông minh Hello World dành cho người mới bắt đầu - Fullstack

solidity
hardhat
từ Alchemy
hợp đồng thông minh
triển khai
trình duyệt khối
frontend
các giao dịch
Người mới bắt đầu
nstrike2
25 tháng 10, 2021
60 số phút đọc

Hướng dẫn này dành cho bạn nếu bạn là người mới tìm hiểu về phát triển chuỗi khối và không biết bắt đầu từ đâu hoặc cách triển khai và tương tác với các hợp đồng thông minh. Chúng tôi sẽ hướng dẫn cách tạo và triển khai một hợp đồng thông minh đơn giản trên mạng thử nghiệm Goerli bằng cách sử dụng MetaMask (opens in a new tab), Solidity (opens in a new tab), Hardhat (opens in a new tab)Alchemy (opens in a new tab).

Bạn sẽ cần một tài khoản Alchemy để hoàn thành hướng dẫn này. Đăng ký tài khoản miễn phí (opens in a new tab).

Nếu bạn có bất kỳ câu hỏi nào, vui lòng liên hệ trong Alchemy Discord (opens in a new tab)!

Phần 1 - Tạo và triển khai hợp đồng thông minh của bạn bằng Hardhat

Kết nối với mạng Ethereum

Có nhiều cách để thực hiện yêu cầu đến chuỗi Ethereum. Để đơn giản, chúng tôi sẽ sử dụng tài khoản miễn phí trên Alchemy, một nền tảng và Giao diện Lập trình Ứng dụng dành cho nhà phát triển chuỗi khối, cho phép chúng tôi giao tiếp với chuỗi Ethereum mà không cần tự chạy một nút. Alchemy cũng có các công cụ dành cho nhà phát triển để theo dõi và phân tích; chúng tôi sẽ tận dụng những công cụ này trong hướng dẫn này để hiểu những gì đang diễn ra trong quá trình triển khai hợp đồng thông minh của chúng tôi.

Tạo ứng dụng và khóa Giao diện Lập trình Ứng dụng của bạn

Sau khi tạo tài khoản Alchemy, bạn có thể tạo một khóa Giao diện Lập trình Ứng dụng bằng cách tạo một ứng dụng. Điều này sẽ cho phép bạn thực hiện các yêu cầu đến mạng thử nghiệm Goerli. Nếu bạn không quen thuộc với các mạng thử nghiệm, bạn có thể đọc hướng dẫn của Alchemy về cách chọn mạng (opens in a new tab).

Trên bảng điều khiển Alchemy, tìm menu thả xuống Apps trong thanh điều hướng và nhấp vào Create App.

Tạo ứng dụng Hello world

Đặt tên cho ứng dụng của bạn là 'Hello World' và viết một mô tả ngắn. Chọn Staging làm môi trường và Goerli làm mạng của bạn.

tạo chế độ xem ứng dụng hello world

Lưu ý: hãy chắc chắn chọn Goerli, nếu không hướng dẫn này sẽ không hoạt động.

Nhấp vào Create app. Ứng dụng của bạn sẽ xuất hiện trong bảng dưới đây.

Tạo tài khoản Ethereum

Bạn cần có tài khoản Ethereum để gửi và nhận các giao dịch. Chúng tôi sẽ sử dụng MetaMask, một ví ảo trong trình duyệt cho phép người dùng quản lý địa chỉ tài khoản Ethereum của họ.

Bạn có thể tải xuống và tạo tài khoản MetaMask miễn phí tại đây (opens in a new tab). Khi bạn tạo tài khoản, hoặc nếu bạn đã có tài khoản, hãy đảm bảo chuyển sang “Mạng thử nghiệm Goerli” ở phía trên bên phải (để chúng ta không phải giao dịch bằng tiền thật).

Bước 4: Thêm ether từ Faucet

Để triển khai hợp đồng thông minh của bạn trên mạng thử nghiệm, bạn sẽ cần một ít ETH giả. Để nhận ETH trên mạng Goerli, hãy truy cập vào một vòi Goerli và nhập địa chỉ tài khoản Goerli của bạn. Lưu ý rằng các vòi Goerli gần đây có thể hơi không đáng tin cậy - xem trang các mạng thử nghiệm để biết danh sách các tùy chọn để thử:

Lưu ý: do tắc nghẽn mạng, việc này có thể mất một lúc. ``

Bước 5: Kiểm tra số dư của bạn

Để kiểm tra lại xem ETH đã có trong ví của bạn chưa, chúng ta hãy thực hiện một yêu cầu eth_getBalance (opens in a new tab) bằng cách sử dụng công cụ soạn thảo của Alchemy (opens in a new tab). Thao tác này sẽ trả về lượng ETH có trong ví của chúng ta. Để tìm hiểu thêm, hãy xem hướng dẫn ngắn của Alchemy về cách sử dụng công cụ soạn thảo (opens in a new tab).

Nhập địa chỉ tài khoản MetaMask của bạn và nhấp vào Send Request. Bạn sẽ thấy một phản hồi trông giống như đoạn mã dưới đây.

1{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" }

Lưu ý: Kết quả này tính bằng wei, không phải ETH. Wei được sử dụng làm mệnh giá nhỏ nhất của ether.

Phù! Tiền giả của chúng ta đã có đủ.

Bước 6: Khởi tạo dự án của chúng ta

Đầu tiên, chúng ta sẽ cần tạo một thư mục cho dự án của mình. Điều hướng đến dòng lệnh của bạn và nhập nội dung sau.

1mkdir hello-world
2cd hello-world

Bây giờ chúng ta đang ở trong thư mục dự án của mình, chúng ta sẽ sử dụng npm init để khởi tạo dự án.

Nếu bạn chưa cài đặt npm, hãy làm theo các hướng dẫn sau để cài đặt Node.js và npm (opens in a new tab).

Đối với mục đích của hướng dẫn này, việc bạn trả lời các câu hỏi khởi tạo như thế nào không quan trọng. Đây là cách chúng tôi đã làm để bạn tham khảo:

1tên gói: (hello-world)
2phiên bản: (1.0.0)
3mô tả: hợp đồng thông minh hello world
4điểm vào: (index.js)
5lệnh kiểm tra:
6kho lưu trữ git:
7từ khóa:
8tác giả:
9giấy phép: (ISC)
10
11Sắp ghi vào /Users/.../.../.../hello-world/package.json:
12
13{
14 "name": "hello-world",
15 "version": "1.0.0",
16 "description": "hợp đồng thông minh hello world",
17 "main": "index.js",
18 "scripts": {
19 "test": "echo \"Lỗi: không có bài kiểm tra nào được chỉ định\" && exit 1"
20 },
21 "author": "",
22 "license": "ISC"
23}
Hiện tất cả

Phê duyệt package.json và chúng ta đã sẵn sàng!

Bước 7: Tải xuống Hardhat

Hardhat là một môi trường phát triển để biên dịch, triển khai, kiểm thử và gỡ lỗi phần mềm Ethereum của bạn. Nó giúp các nhà phát triển khi xây dựng hợp đồng thông minh và các ứng dụng phi tập trung cục bộ trước khi triển khai lên chuỗi chính.

Bên trong dự án hello-world của chúng ta, chạy:

1npm install --save-dev hardhat

Hãy xem trang này để biết thêm chi tiết về hướng dẫn cài đặt (opens in a new tab).

Bước 8: Tạo dự án Hardhat

Bên trong thư mục dự án hello-world của chúng ta, chạy:

1npx hardhat

Sau đó, bạn sẽ thấy một thông báo chào mừng và tùy chọn để chọn những gì bạn muốn làm. Chọn “create an empty hardhat.config.js”:

1888 888 888 888 888
2888 888 888 888 888
3888 888 888 888 888
48888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
5888 888 "88b 888P" d88" 888 888 "88b "88b 888
6888 888 .d888888 888 888 888 888 888 .d888888 888
7888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
9
10👷 Chào mừng đến với Hardhat v2.0.11 👷‍
11
12Bạn muốn làm gì? …
13Tạo một dự án mẫu
14❯ Tạo một tệp hardhat.config.js trống
15Thoát
Hiện tất cả

Thao tác này sẽ tạo một tệp hardhat.config.js trong dự án. Chúng ta sẽ sử dụng tệp này sau trong hướng dẫn để chỉ định thiết lập cho dự án của mình.

Bước 9: Thêm thư mục dự án

Để giữ cho dự án được tổ chức, chúng ta hãy tạo hai thư mục mới. Trong dòng lệnh, điều hướng đến thư mục gốc của dự án hello-world của bạn và nhập:

1mkdir contracts
2mkdir scripts
  • contracts/ là nơi chúng ta sẽ lưu tệp mã hợp đồng thông minh hello world
  • scripts/ là nơi chúng ta sẽ lưu giữ các tập lệnh để triển khai và tương tác với hợp đồng của mình

Bước 10: Viết hợp đồng của chúng ta

Bạn có thể đang tự hỏi, khi nào chúng ta sẽ viết mã? Đã đến lúc!

Mở dự án hello-world trong trình chỉnh sửa yêu thích của bạn. Các hợp đồng thông minh thường được viết bằng Solidity, ngôn ngữ mà chúng ta sẽ sử dụng để viết hợp đồng thông minh của mình.‌

  1. Điều hướng đến thư mục contracts và tạo một tệp mới có tên HelloWorld.sol
  2. Dưới đây là một hợp đồng thông minh Hello World mẫu mà chúng ta sẽ sử dụng cho hướng dẫn này. Sao chép nội dung bên dưới vào tệp HelloWorld.sol.

Lưu ý: Hãy chắc chắn đọc các bình luận để hiểu hợp đồng này làm gì.

1// Chỉ định phiên bản của Solidity, sử dụng lập phiên bản ngữ nghĩa.
2// Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity >=0.7.3;
4
5// Định nghĩa một hợp đồng có tên là `HelloWorld`.
6// Một hợp đồng là một tập hợp các hàm và dữ liệu (trạng thái của nó). Sau khi được triển khai, một hợp đồng nằm ở một địa chỉ cụ thể trên chuỗi khối Ethereum. Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
7contract HelloWorld {
8
9 //Phát ra khi hàm cập nhật được gọi
10 //Các sự kiện hợp đồng thông minh là một cách để hợp đồng của bạn giao tiếp rằng điều gì đó đã xảy ra trên chuỗi khối với giao diện người dùng ứng dụng của bạn, giao diện này có thể 'lắng nghe' các sự kiện nhất định và thực hiện hành động khi chúng xảy ra.
11 event UpdatedMessages(string oldStr, string newStr);
12
13 // Khai báo một biến trạng thái `message` thuộc loại `string`.
14 // Các biến trạng thái là các biến có giá trị được lưu trữ vĩnh viễn trong bộ lưu trữ hợp đồng. Từ khóa `public` giúp các biến có thể truy cập được từ bên ngoài hợp đồng và tạo ra một hàm mà các hợp đồng hoặc ứng dụng khách khác có thể gọi để truy cập giá trị.
15 string public message;
16
17 // Tương tự như nhiều ngôn ngữ lập trình hướng đối tượng dựa trên lớp, một hàm tạo là một hàm đặc biệt chỉ được thực thi khi tạo hợp đồng.
18 // Hàm tạo được sử dụng để khởi tạo dữ liệu của hợp đồng. Tìm hiểu thêm:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
19 constructor(string memory initMessage) {
20
21 // Chấp nhận một đối số chuỗi `initMessage` và đặt giá trị vào biến lưu trữ `message` của hợp đồng).
22 message = initMessage;
23 }
24
25 // Một hàm công khai chấp nhận một đối số chuỗi và cập nhật biến lưu trữ `message`.
26 function update(string memory newMessage) public {
27 string memory oldMsg = message;
28 message = newMessage;
29 emit UpdatedMessages(oldMsg, newMessage);
30 }
31}
Hiện tất cả

Đây là một hợp đồng thông minh cơ bản lưu trữ một thông điệp khi được tạo. Nó có thể được cập nhật bằng cách gọi hàm update.

Bước 11: Kết nối MetaMask & Alchemy với dự án của bạn

Chúng ta đã tạo một ví MetaMask, tài khoản Alchemy và viết hợp đồng thông minh của mình, giờ là lúc kết nối cả ba.

Mỗi giao dịch được gửi từ ví của bạn đều yêu cầu một chữ ký sử dụng khóa riêng tư duy nhất của bạn. Để cung cấp cho chương trình của chúng tôi quyền này, chúng tôi có thể lưu trữ khóa riêng tư của mình một cách an toàn trong một tệp môi trường. Chúng tôi cũng sẽ lưu trữ một khóa Giao diện Lập trình Ứng dụng cho Alchemy tại đây.

Để tìm hiểu thêm về việc gửi giao dịch, hãy xem hướng dẫn này (opens in a new tab) về việc gửi giao dịch bằng web3.

Đầu tiên, cài đặt gói dotenv trong thư mục dự án của bạn:

1npm install dotenv --save

Sau đó, tạo một tệp .env trong thư mục gốc của dự án. Thêm khóa riêng tư MetaMask và URL Giao diện Lập trình Ứng dụng HTTP của Alchemy vào đó.

Tệp môi trường của bạn phải được đặt tên là .env nếu không nó sẽ không được nhận dạng là tệp môi trường.

Không đặt tên là process.env hoặc .env-custom hoặc bất kỳ tên nào khác.

Tệp .env của bạn sẽ trông như thế này:

1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
2PRIVATE_KEY = "your-metamask-private-key"

Để thực sự kết nối những thứ này với mã của chúng ta, chúng ta sẽ tham chiếu các biến này trong tệp hardhat.config.js của mình ở bước 13.

Bước 12: Cài đặt Ethers.js

Ethers.js là một thư viện giúp tương tác và thực hiện các yêu cầu đến Ethereum dễ dàng hơn bằng cách gói các phương thức JSON-RPC tiêu chuẩn (opens in a new tab) bằng các phương thức thân thiện với người dùng hơn.

Hardhat cho phép chúng ta tích hợp các plugin (opens in a new tab) để có thêm bộ công cụ và chức năng mở rộng. Chúng ta sẽ tận dụng plugin Ethers (opens in a new tab) để triển khai hợp đồng.

Trong thư mục dự án của bạn, hãy gõ:

npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"

Bước 13: Cập nhật hardhat.config.js

Cho đến nay, chúng ta đã thêm một số phần phụ thuộc và plugin, bây giờ chúng ta cần cập nhật hardhat.config.js để dự án của chúng ta biết về tất cả chúng.

Cập nhật tệp hardhat.config.js của bạn để trông như thế này:

1/**
2 * @type import('hardhat/config').HardhatUserConfig
3 */
4
5require("dotenv").config()
6require("@nomiclabs/hardhat-ethers")
7
8const { API_URL, PRIVATE_KEY } = process.env
9
10module.exports = {
11 solidity: "0.7.3",
12 defaultNetwork: "goerli",
13 networks: {
14 hardhat: {},
15 goerli: {
16 url: API_URL,
17 accounts: [`0x${PRIVATE_KEY}`],
18 },
19 },
20}
Hiện tất cả

Bước 14: Biên dịch hợp đồng của chúng ta

Để đảm bảo mọi thứ đều hoạt động cho đến nay, hãy biên dịch hợp đồng của chúng ta. Tác vụ compile là một trong những tác vụ có sẵn của hardhat.

Từ dòng lệnh, hãy chạy:

npx hardhat compile

Bạn có thể nhận được cảnh báo về SPDX license identifier not provided in source file, nhưng không cần phải lo lắng về điều đó — hy vọng mọi thứ khác đều ổn! Nếu không, bạn luôn có thể nhắn tin trong Alchemy discord (opens in a new tab).

Bước 15: Viết tập lệnh triển khai của chúng ta

Bây giờ hợp đồng của chúng ta đã được viết và tệp cấu hình đã sẵn sàng, đã đến lúc viết tập lệnh triển khai hợp đồng của chúng ta.

Điều hướng đến thư mục scripts/ và tạo một tệp mới có tên deploy.js, thêm nội dung sau vào đó:

1async function main() {
2 const HelloWorld = await ethers.getContractFactory("HelloWorld")
3
4 // Bắt đầu triển khai, trả về một promise phân giải thành một đối tượng hợp đồng
5 const hello_world = await HelloWorld.deploy("Hello World!")
6 console.log("Hợp đồng đã được triển khai đến địa chỉ:", hello_world.address)
7}
8
9main()
10 .then(() => process.exit(0))
11 .catch((error) => {
12 console.error(error)
13 process.exit(1)
14 })
Hiện tất cả

Hardhat đã làm rất tốt việc giải thích mỗi dòng mã này làm gì trong Bài hướng dẫn về Hợp đồng (opens in a new tab) của họ, chúng tôi đã áp dụng các giải thích của họ ở đây.

1const HelloWorld = await ethers.getContractFactory("HelloWorld")

ContractFactory trong ethers.js là một lớp trừu tượng được sử dụng để triển khai các hợp đồng thông minh mới, vì vậy HelloWorld ở đây là một nhà máy (opens in a new tab) cho các phiên bản của hợp đồng hello world của chúng ta. Khi sử dụng plugin hardhat-ethers, các phiên bản ContractFactoryContract được kết nối với người ký đầu tiên (chủ sở hữu) theo mặc định.

1const hello_world = await HelloWorld.deploy()

Việc gọi deploy() trên ContractFactory sẽ bắt đầu việc triển khai và trả về một Promise phân giải thành một đối tượng Contract. Đây là đối tượng có một phương thức cho mỗi chức năng hợp đồng thông minh của chúng ta.

Bước 16: Triển khai hợp đồng của chúng ta

Cuối cùng, chúng ta đã sẵn sàng để triển khai hợp đồng thông minh của mình! Điều hướng đến dòng lệnh và chạy:

npx hardhat run scripts/deploy.js --network goerli

Sau đó, bạn sẽ thấy một cái gì đó như thế này:

Hợp đồng được triển khai đến địa chỉ: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570

Vui lòng lưu địa chỉ này. Chúng tôi sẽ sử dụng nó sau trong hướng dẫn.

Nếu chúng ta truy cập Goerli etherscan (opens in a new tab) và tìm kiếm địa chỉ hợp đồng của mình, chúng ta sẽ có thể thấy rằng nó đã được triển khai thành công. Giao dịch sẽ trông giống như thế này:

Địa chỉ From phải khớp với địa chỉ tài khoản MetaMask của bạn và địa chỉ To sẽ ghi là Contract Creation. Nếu chúng ta nhấp vào giao dịch, chúng ta sẽ thấy địa chỉ hợp đồng của mình trong trường To.

Xin chúc mừng! Bạn vừa triển khai một hợp đồng thông minh đến một mạng thử nghiệm Ethereum.

Để hiểu những gì đang diễn ra bên trong, hãy điều hướng đến tab Explorer trong bảng điều khiển Alchemy (opens in a new tab) của chúng ta. Nếu bạn có nhiều ứng dụng Alchemy, hãy đảm bảo lọc theo ứng dụng và chọn Hello World.

Ở đây, bạn sẽ thấy một số phương thức JSON-RPC mà Hardhat/Ethers đã thực hiện cho chúng ta khi chúng ta gọi hàm .deploy(). Hai phương thức quan trọng ở đây là eth_sendRawTransaction (opens in a new tab), là yêu cầu ghi hợp đồng của chúng ta lên chuỗi Goerli, và eth_getTransactionByHash (opens in a new tab), là yêu cầu đọc thông tin về giao dịch của chúng ta với hàm băm đã cho. Để tìm hiểu thêm về việc gửi giao dịch, hãy xem hướng dẫn của chúng tôi về việc gửi giao dịch bằng Web3.

Phần 2: Tương tác với hợp đồng thông minh của bạn

Bây giờ chúng ta đã triển khai thành công một hợp đồng thông minh trên mạng Goerli, hãy cùng tìm hiểu cách tương tác với nó.

Tạo một tệp interact.js

Đây là tệp mà chúng ta sẽ viết tập lệnh tương tác của mình. Chúng tôi sẽ sử dụng thư viện Ethers.js mà bạn đã cài đặt trước đó trong Phần 1.

Bên trong thư mục scripts/, tạo một tệp mới có tên interact.js và thêm mã sau:

1// interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS

Cập nhật tệp .env của bạn

Chúng tôi sẽ sử dụng các biến môi trường mới, vì vậy chúng tôi cần định nghĩa chúng trong tệp .envchúng tôi đã tạo trước đó.

Chúng ta sẽ cần thêm một định nghĩa cho API_KEY của Alchemy và CONTRACT_ADDRESS nơi hợp đồng thông minh của bạn được triển khai.

Tệp .env của bạn sẽ trông giống như sau:

# .env
API_URL = "https://eth-goerli.alchemyapi.io/v2/<your-api-key>"
API_KEY = "<your-api-key>"
PRIVATE_KEY = "<your-metamask-private-key>"
CONTRACT_ADDRESS = "0x<your contract address>"

Lấy Giao diện nhị phân ứng dụng hợp đồng của bạn

của hợp đồng của chúng tôi là giao diện để tương tác với hợp đồng thông minh của chúng tôi. Hardhat tự động tạo Giao diện nhị phân ứng dụng (ABI) và lưu nó trong HelloWorld.json. Để sử dụng Giao diện nhị phân ứng dụng, chúng ta sẽ cần phân tích cú pháp nội dung bằng cách thêm các dòng mã sau vào tệp interact.js của mình:

1// interact.js
2const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")

Nếu bạn muốn xem ABI, bạn có thể in nó ra bảng điều khiển của mình:

1console.log(JSON.stringify(contract.abi))

Để xem Giao diện nhị phân ứng dụng (ABI) của bạn được in ra bảng điều khiển, hãy điều hướng đến thiết bị đầu cuối của bạn và chạy:

npx hardhat run scripts/interact.js

Tạo một phiên bản của hợp đồng của bạn

Để tương tác với hợp đồng của mình, chúng ta cần tạo một phiên bản hợp đồng trong mã của mình. Để làm như vậy với Ethers.js, chúng ta sẽ cần làm việc với ba khái niệm:

  1. Nhà cung cấp - một nhà cung cấp nút cung cấp cho bạn quyền truy cập đọc và ghi vào chuỗi khối
  2. Người ký - đại diện cho một tài khoản Ethereum có thể ký các giao dịch
  3. Hợp đồng - một đối tượng Ethers.js đại diện cho một hợp đồng cụ thể được triển khai trên chuỗi

Chúng ta sẽ sử dụng Giao diện nhị phân ứng dụng (ABI) của hợp đồng từ bước trước để tạo phiên bản hợp đồng của mình:

1// interact.js
2
3// Nhà cung cấp
4const alchemyProvider = new ethers.providers.AlchemyProvider(
5 (network = "goerli"),
6 API_KEY
7)
8
9// Người ký
10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
11
12// Hợp đồng
13const helloWorldContract = new ethers.Contract(
14 CONTRACT_ADDRESS,
15 contract.abi,
16 signer
17)
Hiện tất cả

Tìm hiểu thêm về Nhà cung cấp, Người ký và Hợp đồng trong tài liệu tham khảo của ethers.js (opens in a new tab).

Đọc thông điệp khởi tạo

Bạn có nhớ khi chúng ta triển khai hợp đồng của mình với initMessage = "Hello world!" không? Bây giờ chúng ta sẽ đọc thông điệp đó được lưu trữ trong hợp đồng thông minh của mình và in nó ra bảng điều khiển.

Trong JavaScript, các hàm bất đồng bộ được sử dụng khi tương tác với các mạng. Để tìm hiểu thêm về các hàm không đồng bộ, hãy đọc bài viết này trên medium (opens in a new tab).

Sử dụng mã bên dưới để gọi hàm message trong hợp đồng thông minh của chúng ta và đọc thông điệp khởi tạo:

1// interact.js
2
3// ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("Thông điệp là: " + message)
8}
9main()
Hiện tất cả

Sau khi chạy tệp bằng npx hardhat run scripts/interact.js trong thiết bị đầu cuối, chúng ta sẽ thấy phản hồi này:

1Thông điệp là: Hello world!

Xin chúc mừng! Bạn vừa đọc thành công dữ liệu hợp đồng thông minh từ chuỗi khối Ethereum, làm tốt lắm!

Cập nhật thông điệp

Thay vì chỉ đọc thông điệp, chúng ta cũng có thể cập nhật thông điệp được lưu trong hợp đồng thông minh của mình bằng hàm update! Tuyệt vời, phải không?

Để cập nhật thông điệp, chúng ta có thể gọi trực tiếp hàm update trên đối tượng Hợp đồng được khởi tạo của mình:

1// interact.js
2
3// ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("Thông điệp là: " + message)
8
9 console.log("Đang cập nhật thông điệp...")
10 const tx = await helloWorldContract.update("Đây là thông điệp mới.")
11 await tx.wait()
12}
13main()
Hiện tất cả

Lưu ý rằng ở dòng 11, chúng ta thực hiện một lệnh gọi đến .wait() trên đối tượng giao dịch được trả về. Điều này đảm bảo rằng tập lệnh của chúng ta đợi giao dịch được khai thác trên chuỗi khối trước khi thoát khỏi hàm. Nếu lệnh gọi .wait() không được bao gồm, tập lệnh có thể không thấy giá trị message được cập nhật trong hợp đồng.

Đọc thông điệp mới

Bạn có thể lặp lại bước trước đó để đọc giá trị message đã được cập nhật. Hãy dành một chút thời gian và xem liệu bạn có thể thực hiện những thay đổi cần thiết để in ra giá trị mới đó không!

Nếu bạn cần gợi ý, đây là tệp interact.js của bạn sẽ trông như thế nào vào thời điểm này:

1// interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
6
7const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
8
9// provider - Alchemy
10const alchemyProvider = new ethers.providers.AlchemyProvider(
11 (network = "goerli"),
12 API_KEY
13)
14
15// signer - you
16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
17
18// contract instance
19const helloWorldContract = new ethers.Contract(
20 CONTRACT_ADDRESS,
21 contract.abi,
22 signer
23)
24
25async function main() {
26 const message = await helloWorldContract.message()
27 console.log("Thông điệp là: " + message)
28
29 console.log("Đang cập nhật thông điệp...")
30 const tx = await helloWorldContract.update("đây là thông điệp mới")
31 await tx.wait()
32
33 const newMessage = await helloWorldContract.message()
34 console.log("Thông điệp mới là: " + newMessage)
35}
36
37main()
Hiện tất cả

Bây giờ chỉ cần chạy tập lệnh và bạn sẽ có thể thấy thông điệp cũ, trạng thái cập nhật và thông điệp mới được in ra thiết bị đầu cuối của bạn!

npx hardhat run scripts/interact.js --network goerli

1Thông điệp là: Hello World!
2Đang cập nhật thông điệp...
3Thông điệp mới là: Đây là thông điệp mới.

Trong khi chạy tập lệnh đó, bạn có thể nhận thấy rằng bước Updating the message... mất một lúc để tải trước khi thông điệp mới tải. Đó là do quá trình khai thác; nếu bạn tò mò về việc theo dõi các giao dịch trong khi chúng đang được khai thác, hãy truy cập bể ghi nhớ của Alchemy (opens in a new tab) để xem trạng thái của một giao dịch. Nếu giao dịch bị hủy, bạn cũng nên kiểm tra Goerli Etherscan (opens in a new tab) và tìm kiếm hàm băm giao dịch của mình.

Phần 3: Xuất bản hợp đồng thông minh của bạn lên Etherscan

Bạn đã làm tất cả công việc khó khăn để đưa hợp đồng thông minh của mình vào cuộc sống; bây giờ là lúc chia sẻ nó với thế giới!

Bằng cách xác minh hợp đồng thông minh của bạn trên Etherscan, bất kỳ ai cũng có thể xem mã nguồn của bạn và tương tác với hợp đồng thông minh của bạn. Bắt đầu ngay!

Bước 1: Tạo khóa Giao diện Lập trình Ứng dụng trên tài khoản Etherscan của bạn

Cần có khóa Giao diện Lập trình Ứng dụng Etherscan để xác minh rằng bạn sở hữu hợp đồng thông minh mà bạn đang cố gắng xuất bản.

Nếu bạn chưa có tài khoản Etherscan, hãy đăng ký một tài khoản (opens in a new tab).

Sau khi đăng nhập, tìm tên người dùng của bạn trong thanh điều hướng, di chuột qua nó và chọn nút Hồ sơ của tôi.

Trên trang hồ sơ của bạn, bạn sẽ thấy một thanh điều hướng bên cạnh. Từ thanh điều hướng bên cạnh, chọn Khóa Giao diện Lập trình Ứng dụng. Tiếp theo, nhấn nút "Thêm" để tạo một khóa Giao diện Lập trình Ứng dụng mới, đặt tên cho ứng dụng của bạn là hello-world và nhấn nút Tạo Khóa Giao diện Lập trình Ứng dụng Mới.

Khóa Giao diện Lập trình Ứng dụng mới của bạn sẽ xuất hiện trong bảng khóa Giao diện Lập trình Ứng dụng. Sao chép khóa Giao diện Lập trình Ứng dụng vào khay nhớ tạm của bạn.

Tiếp theo, chúng ta cần thêm khóa Giao diện Lập trình Ứng dụng Etherscan vào tệp .env của mình.

Sau khi thêm nó, tệp .env của bạn sẽ trông như thế này:

1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
2PUBLIC_KEY = "your-public-account-address"
3PRIVATE_KEY = "your-private-account-address"
4CONTRACT_ADDRESS = "your-contract-address"
5ETHERSCAN_API_KEY = "your-etherscan-key"

Các hợp đồng thông minh được triển khai bởi Hardhat

Cài đặt hardhat-etherscan

Việc xuất bản hợp đồng của bạn lên Etherscan bằng Hardhat rất đơn giản. Trước tiên, bạn sẽ cần cài đặt plugin hardhat-etherscan để bắt đầu. hardhat-etherscan sẽ tự động xác minh mã nguồn và Giao diện nhị phân ứng dụng (ABI) của hợp đồng thông minh trên Etherscan. Để thêm điều này, trong thư mục hello-world, hãy chạy:

1npm install --save-dev @nomiclabs/hardhat-etherscan

Sau khi cài đặt, bao gồm câu lệnh sau ở đầu hardhat.config.js của bạn và thêm các tùy chọn cấu hình Etherscan:

1// hardhat.config.js
2
3require("dotenv").config()
4require("@nomiclabs/hardhat-ethers")
5require("@nomiclabs/hardhat-etherscan")
6
7const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env
8
9module.exports = {
10 solidity: "0.7.3",
11 defaultNetwork: "goerli",
12 networks: {
13 hardhat: {},
14 goerli: {
15 url: API_URL,
16 accounts: [`0x${PRIVATE_KEY}`],
17 },
18 },
19 etherscan: {
20 // Khóa Giao diện Lập trình Ứng dụng của bạn cho Etherscan
21 // Lấy một khóa tại https://etherscan.io/
22 apiKey: ETHERSCAN_API_KEY,
23 },
24}
Hiện tất cả

Xác minh hợp đồng thông minh của bạn trên Etherscan

Đảm bảo tất cả các tệp được lưu và tất cả các biến .env được cấu hình chính xác.

Chạy tác vụ verify, truyền địa chỉ hợp đồng và mạng nơi nó được triển khai:

1npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'

Đảm bảo rằng DEPLOYED_CONTRACT_ADDRESS là địa chỉ của hợp đồng thông minh đã được triển khai của bạn trên mạng thử nghiệm Goerli. Ngoài ra, đối số cuối cùng ('Hello World!') phải có cùng giá trị chuỗi được sử dụng trong bước triển khai ở phần 1.

Nếu mọi việc suôn sẻ, bạn sẽ thấy thông điệp sau trong thiết bị đầu cuối của mình:

1Đã gửi thành công mã nguồn cho hợp đồng
2contracts/HelloWorld.sol:HelloWorld tại 0xdeployed-contract-address
3để xác minh trên Etherscan. Đang chờ kết quả xác minh...
4
5
6Đã xác minh thành công hợp đồng HelloWorld trên Etherscan.
7https://goerli.etherscan.io/address/<contract-address>#contracts

Xin chúc mừng! Mã hợp đồng thông minh của bạn đã có trên Etherscan!

Kiểm tra hợp đồng thông minh của bạn trên Etherscan!

Khi bạn điều hướng đến liên kết được cung cấp trong thiết bị đầu cuối của mình, bạn sẽ có thể thấy mã hợp đồng thông minh và Giao diện nhị phân ứng dụng (ABI) của mình được xuất bản trên Etherscan!

Wahooo - bạn đã làm được rồi, nhà vô địch! Bây giờ bất kỳ ai cũng có thể gọi hoặc ghi vào hợp đồng thông minh của bạn! Chúng tôi rất mong được xem bạn sẽ xây dựng gì tiếp theo!

Phần 4 - Tích hợp hợp đồng thông minh của bạn với giao diện người dùng

Vào cuối hướng dẫn này, bạn sẽ biết cách:

  • Kết nối ví MetaMask với dự án ứng dụng phi tập trung của bạn
  • Đọc dữ liệu từ hợp đồng thông minh của bạn bằng cách sử dụng Giao diện Lập trình Ứng dụng Alchemy Web3 (opens in a new tab)
  • Ký các giao dịch Ethereum bằng MetaMask

Đối với ứng dụng phi tập trung này, chúng tôi sẽ sử dụng React (opens in a new tab) làm khuôn khổ giao diện người dùng của mình; tuy nhiên, điều quan trọng cần lưu ý là chúng tôi sẽ không dành nhiều thời gian để phân tích các nguyên tắc cơ bản của nó, vì chúng tôi sẽ chủ yếu tập trung vào việc đưa chức năng Web3 vào dự án của mình.

Như một điều kiện tiên quyết, bạn nên có một sự hiểu biết ở mức độ sơ cấp về React. Nếu không, chúng tôi khuyên bạn nên hoàn thành hướng dẫn Giới thiệu về React (opens in a new tab) chính thức.

Sao chép các tệp khởi đầu

Đầu tiên, hãy truy cập kho lưu trữ GitHub hello-world-part-four (opens in a new tab) để lấy các tệp khởi đầu cho dự án này và sao chép kho lưu trữ này vào máy cục bộ của bạn.

Mở kho lưu trữ đã sao chép cục bộ. Lưu ý rằng nó chứa hai thư mục: starter-filescompleted.

  • starter-files- chúng ta sẽ làm việc trong thư mục này, chúng ta sẽ kết nối giao diện người dùng với ví Ethereum của bạn và hợp đồng thông minh mà chúng ta đã xuất bản lên Etherscan trong Phần 3.
  • completed chứa toàn bộ hướng dẫn đã hoàn thành và chỉ nên được sử dụng làm tài liệu tham khảo nếu bạn gặp khó khăn.

Tiếp theo, mở bản sao starter-files của bạn vào trình soạn thảo mã yêu thích của bạn, sau đó điều hướng vào thư mục src.

Tất cả mã chúng ta sẽ viết sẽ nằm trong thư mục src. Chúng ta sẽ chỉnh sửa thành phần HelloWorld.js và các tệp JavaScript util/interact.js để cung cấp cho dự án của chúng ta chức năng Web3.

Kiểm tra các tệp khởi đầu

Trước khi bắt đầu viết mã, hãy cùng khám phá những gì được cung cấp cho chúng ta trong các tệp khởi đầu.

Chạy dự án react của bạn

Hãy bắt đầu bằng cách chạy dự án React trong trình duyệt của chúng ta. Vẻ đẹp của React là một khi chúng ta có dự án đang chạy trong trình duyệt, bất kỳ thay đổi nào chúng ta lưu sẽ được cập nhật trực tiếp trong trình duyệt của chúng ta.

Để chạy dự án, hãy điều hướng đến thư mục gốc của thư mục starter-files và chạy npm install trong thiết bị đầu cuối của bạn để cài đặt các phụ thuộc của dự án:

cd starter-files
npm install

Sau khi chúng đã cài đặt xong, hãy chạy npm start trong terminal của bạn:

npm start

Làm như vậy sẽ mở http://localhost:3000/ (opens in a new tab) trong trình duyệt của bạn, nơi bạn sẽ thấy giao diện người dùng cho dự án của chúng ta. Nó sẽ bao gồm một trường (một nơi để cập nhật thông điệp được lưu trữ trong hợp đồng thông minh của bạn), một nút "Kết nối Ví" và một nút "Cập nhật".

Nếu bạn thử nhấp vào một trong hai nút, bạn sẽ nhận thấy rằng chúng không hoạt động—đó là vì chúng ta vẫn cần lập trình chức năng của chúng.

Thành phần HelloWorld.js

Hãy quay lại thư mục src trong trình chỉnh sửa của chúng ta và mở tệp HelloWorld.js. Việc hiểu mọi thứ trong tệp này là cực kỳ quan trọng, vì đây là thành phần React chính mà chúng ta sẽ làm việc.

Ở đầu tệp này, bạn sẽ nhận thấy chúng ta có một số câu lệnh nhập cần thiết để chạy dự án của mình, bao gồm thư viện React, các hook useEffect và useState, một số mục từ ./util/interact.js (chúng ta sẽ mô tả chúng chi tiết hơn ngay sau đây!), và logo Alchemy.

1// HelloWorld.js
2
3import React from "react"
4import { useEffect, useState } from "react"
5import {
6 helloWorldContract,
7 connectWallet,
8 updateMessage,
9 loadCurrentMessage,
10 getCurrentWalletConnected,
11} from "./util/interact.js"
12
13import alchemylogo from "./alchemylogo.svg"
Hiện tất cả

Tiếp theo, chúng ta có các biến trạng thái mà chúng ta sẽ cập nhật sau các sự kiện cụ thể.

1// HelloWorld.js
2
3//Các biến trạng thái
4const [walletAddress, setWallet] = useState("")
5const [status, setStatus] = useState("")
6const [message, setMessage] = useState("Không có kết nối với mạng.")
7const [newMessage, setNewMessage] = useState("")

Đây là những gì mỗi biến đại diện:

  • walletAddress - một chuỗi lưu trữ địa chỉ ví của người dùng
  • status- một chuỗi lưu trữ một thông điệp hữu ích hướng dẫn người dùng cách tương tác với ứng dụng phi tập trung
  • message - một chuỗi lưu trữ thông điệp hiện tại trong hợp đồng thông minh
  • newMessage - một chuỗi lưu trữ thông điệp mới sẽ được ghi vào hợp đồng thông minh

Sau các biến trạng thái, bạn sẽ thấy năm hàm chưa được triển khai: useEffect ,addSmartContractListener, addWalletListener , connectWalletPressed, và onUpdatePressed. Chúng tôi sẽ giải thích những gì chúng làm dưới đây:

1// HelloWorld.js
2
3//chỉ được gọi một lần
4useEffect(async () => {
5 //TODO: thực hiện
6}, [])
7
8function addSmartContractListener() {
9 //TODO: thực hiện
10}
11
12function addWalletListener() {
13 //TODO: thực hiện
14}
15
16const connectWalletPressed = async () => {
17 //TODO: thực hiện
18}
19
20const onUpdatePressed = async () => {
21 //TODO: thực hiện
22}
Hiện tất cả
  • useEffect (opens in a new tab)- đây là một hook của React được gọi sau khi thành phần của bạn được kết xuất. Bởi vì nó có một mảng trống [] được truyền vào (xem dòng 4), nó sẽ chỉ được gọi trong lần kết xuất đầu tiên của thành phần. Ở đây chúng ta sẽ tải thông điệp hiện tại được lưu trữ trong hợp đồng thông minh của mình, gọi các trình lắng nghe hợp đồng thông minh và ví của chúng ta, và cập nhật giao diện người dùng của chúng ta để phản ánh liệu một ví đã được kết nối hay chưa.
  • addSmartContractListener- hàm này thiết lập một trình lắng nghe sẽ theo dõi sự kiện UpdatedMessages của hợp đồng HelloWorld của chúng ta và cập nhật giao diện người dùng của chúng ta khi thông điệp được thay đổi trong hợp đồng thông minh của chúng ta.
  • addWalletListener- hàm này thiết lập một trình lắng nghe phát hiện các thay đổi trong trạng thái ví MetaMask của người dùng, chẳng hạn như khi người dùng ngắt kết nối ví của họ hoặc chuyển đổi địa chỉ.
  • connectWalletPressed- hàm này sẽ được gọi để kết nối ví MetaMask của người dùng với ứng dụng phi tập trung của chúng tôi.
  • onUpdatePressed - hàm này sẽ được gọi khi người dùng muốn cập nhật thông điệp được lưu trữ trong hợp đồng thông minh.

Gần cuối tệp này, chúng ta có UI của thành phần của mình.

1// HelloWorld.js
2
3//giao diện người dùng của thành phần của chúng ta
4return (
5 <div id="container">
6 <img id="logo" src={alchemylogo}></img>
7 <button id="walletButton" onClick={connectWalletPressed}>
8 {walletAddress.length > 0 ? (
9 "Đã kết nối: " +
10 String(walletAddress).substring(0, 6) +
11 "..." +
12 String(walletAddress).substring(38)
13 ) : (
14 <span>Kết nối Ví</span>
15 )}
16 </button>
17
18 <h2 style={{ paddingTop: "50px" }}>Thông điệp Hiện tại:</h2>
19 <p>{message}</p>
20
21 <h2 style={{ paddingTop: "18px" }}>Thông điệp Mới:</h2>
22
23 <div>
24 <input
25 type="text"
26 placeholder="Cập nhật thông điệp trong hợp đồng thông minh của bạn."
27 onChange={(e) => setNewMessage(e.target.value)}
28 value={newMessage}
29 />
30 <p id="status">{status}</p>
31
32 <button id="publishButton" onClick={onUpdatePressed}>
33 Cập nhật
34 </button>
35</div>
36
37</div>
38)
Hiện tất cả

Nếu bạn quét mã này một cách cẩn thận, bạn sẽ nhận thấy nơi chúng ta sử dụng các biến trạng thái khác nhau trong giao diện người dùng của mình:

  • Ở các dòng 6-12, nếu ví của người dùng được kết nối (tức là walletAddress.length > 0), chúng ta sẽ hiển thị một phiên bản rút gọn của walletAddress của người dùng trong nút có ID "walletButton;" nếu không, nó chỉ đơn giản là "Kết nối Ví."
  • Trên dòng 17, chúng ta hiển thị thông điệp hiện tại được lưu trữ trong hợp đồng thông minh, được ghi lại trong chuỗi message.
  • Trên các dòng 23-26, chúng tôi sử dụng một thành phần được kiểm soát (opens in a new tab) để cập nhật biến trạng thái newMessage của mình khi đầu vào trong trường văn bản thay đổi.

Ngoài các biến trạng thái của chúng ta, bạn cũng sẽ thấy rằng các hàm connectWalletPressedonUpdatePressed được gọi khi các nút có ID publishButtonwalletButton được nhấp tương ứng.

Cuối cùng, hãy giải quyết vấn đề thành phần HelloWorld.js này được thêm vào đâu.

Nếu bạn vào tệp App.js, là thành phần chính trong React hoạt động như một vùng chứa cho tất cả các thành phần khác, bạn sẽ thấy rằng thành phần HelloWorld.js của chúng ta được chèn vào dòng 7.

Cuối cùng nhưng không kém phần quan trọng, hãy kiểm tra thêm một tệp nữa được cung cấp cho bạn, tệp interact.js.

Tệp interact.js

Bởi vì chúng tôi muốn tuân theo mô hình M-V-C (opens in a new tab), chúng tôi sẽ muốn có một tệp riêng chứa tất cả các hàm của mình để quản lý logic, dữ liệu và quy tắc của ứng dụng phi tập trung của mình, và sau đó có thể xuất các hàm đó sang giao diện người dùng của chúng tôi (thành phần HelloWorld.js của chúng tôi).

👆🏽Đây chính là mục đích của tệp interact.js của chúng ta!

Điều hướng đến thư mục util trong thư mục src của bạn, và bạn sẽ nhận thấy chúng tôi đã bao gồm một tệp có tên interact.js sẽ chứa tất cả các hàm và biến tương tác hợp đồng thông minh và ví của chúng tôi.

1// interact.js
2
3//export const helloWorldContract;
4
5export const loadCurrentMessage = async () => {}
6
7export const connectWallet = async () => {}
8
9const getCurrentWalletConnected = async () => {}
10
11export const updateMessage = async (message) => {}
Hiện tất cả

Bạn sẽ nhận thấy ở đầu tệp rằng chúng tôi đã chú thích đối tượng helloWorldContract. Sau này trong hướng dẫn này, chúng tôi sẽ bỏ chú thích đối tượng này và khởi tạo hợp đồng thông minh của mình trong biến này, sau đó chúng tôi sẽ xuất nó vào thành phần HelloWorld.js của mình.

Bốn hàm chưa được triển khai sau đối tượng helloWorldContract của chúng ta thực hiện những việc sau:

  • loadCurrentMessage - hàm này xử lý logic tải thông điệp hiện tại được lưu trữ trong hợp đồng thông minh. Nó sẽ thực hiện một lệnh gọi đọc đến hợp đồng thông minh Hello World bằng cách sử dụng Giao diện Lập trình Ứng dụng Alchemy Web3 (opens in a new tab).
  • connectWallet - hàm này sẽ kết nối MetaMask của người dùng với ứng dụng phi tập trung của chúng tôi.
  • getCurrentWalletConnected - hàm này sẽ kiểm tra xem một tài khoản Ethereum đã được kết nối với ứng dụng phi tập trung của chúng ta khi tải trang hay chưa và cập nhật giao diện người dùng của chúng ta cho phù hợp.
  • updateMessage - hàm này sẽ cập nhật thông điệp được lưu trữ trong hợp đồng thông minh. Nó sẽ thực hiện một lệnh gọi ghi đến hợp đồng thông minh Hello World, vì vậy ví MetaMask của người dùng sẽ phải ký một giao dịch Ethereum để cập nhật thông điệp.

Bây giờ chúng ta đã hiểu những gì chúng ta đang làm việc, hãy cùng tìm hiểu cách đọc từ hợp đồng thông minh của chúng ta!

Bước 3: Đọc từ hợp đồng thông minh của bạn

Để đọc từ hợp đồng thông minh của bạn, bạn sẽ cần thiết lập thành công:

  • Kết nối Giao diện Lập trình Ứng dụng với chuỗi Ethereum
  • Một phiên bản đã tải của hợp đồng thông minh của bạn
  • Một hàm để gọi đến hàm hợp đồng thông minh của bạn
  • Một trình lắng nghe để theo dõi các cập nhật khi dữ liệu bạn đang đọc từ hợp đồng thông minh thay đổi

Điều này nghe có vẻ như rất nhiều bước, nhưng đừng lo lắng! Chúng tôi sẽ hướng dẫn bạn cách thực hiện từng bước một! :)

Thiết lập kết nối Giao diện Lập trình Ứng dụng với chuỗi Ethereum

Vậy bạn có nhớ trong Phần 2 của hướng dẫn này, chúng ta đã sử dụng khóa Alchemy Web3 để đọc từ hợp đồng thông minh của mình không (https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library (opens in a new tab))? Bạn cũng sẽ cần một khóa Alchemy Web3 trong ứng dụng phi tập trung của mình để đọc từ chuỗi.

Nếu bạn chưa có, trước tiên hãy cài đặt Alchemy Web3 (opens in a new tab) bằng cách điều hướng đến thư mục gốc của starter-files và chạy lệnh sau trong thiết bị đầu cuối của bạn:

1npm install @alch/alchemy-web3

Alchemy Web3 (opens in a new tab) là một trình bao bọc xung quanh Web3.js (opens in a new tab), cung cấp các phương thức Giao diện Lập trình Ứng dụng nâng cao và các lợi ích quan trọng khác để giúp cuộc sống của bạn với tư cách là một nhà phát triển web3 dễ dàng hơn. Nó được thiết kế để yêu cầu cấu hình tối thiểu để bạn có thể bắt đầu sử dụng nó trong ứng dụng của mình ngay lập tức!

Sau đó, cài đặt gói dotenv (opens in a new tab) trong thư mục dự án của bạn, để chúng ta có một nơi bảo mật để lưu trữ khóa Giao diện Lập trình Ứng dụng của mình sau khi chúng ta lấy nó.

1npm install dotenv --save

Đối với ứng dụng phi tập trung của chúng ta, chúng ta sẽ sử dụng khóa Giao diện Lập trình Ứng dụng Websockets thay vì khóa Giao diện Lập trình Ứng dụng HTTP của mình, vì nó sẽ cho phép chúng ta thiết lập một trình lắng nghe phát hiện khi thông điệp được lưu trữ trong hợp đồng thông minh thay đổi.

Sau khi có khóa Giao diện Lập trình Ứng dụng, hãy tạo tệp .env trong thư mục gốc của bạn và thêm url Alchemy Websockets của bạn vào đó. Sau đó, tệp .env của bạn sẽ trông như sau:

1REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<key>

Bây giờ, chúng ta đã sẵn sàng để thiết lập điểm cuối Alchemy Web3 trong ứng dụng phi tập trung của mình! Hãy quay lại interact.js của chúng ta, nằm trong thư mục util của chúng ta và thêm mã sau vào đầu tệp:

1// interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8//export const helloWorldContract;

Ở trên, chúng tôi đã nhập khóa Alchemy từ tệp .env của mình và sau đó truyền alchemyKey của mình cho createAlchemyWeb3 để thiết lập điểm cuối Alchemy Web3 của mình.

Với điểm cuối này đã sẵn sàng, đã đến lúc tải hợp đồng thông minh của chúng ta!

Tải hợp đồng thông minh Hello World của bạn

Để tải hợp đồng thông minh Hello World của bạn, bạn sẽ cần địa chỉ hợp đồng và Giao diện nhị phân ứng dụng (ABI) của nó, cả hai đều có thể được tìm thấy trên Etherscan nếu bạn đã hoàn thành Phần 3 của hướng dẫn này.

Cách lấy Giao diện nhị phân ứng dụng (ABI) của hợp đồng của bạn từ Etherscan

Nếu bạn bỏ qua Phần 3 của hướng dẫn này, bạn có thể sử dụng hợp đồng HelloWorld với địa chỉ 0x6f3f635A9762B47954229Ea479b4541eAF402A6A (opens in a new tab). Giao diện nhị phân ứng dụng (ABI) của nó có thể được tìm thấy tại đây (opens in a new tab).

Giao diện nhị phân ứng dụng (ABI) của hợp đồng là cần thiết để chỉ định hàm mà hợp đồng sẽ gọi cũng như đảm bảo rằng hàm sẽ trả về dữ liệu ở định dạng bạn mong đợi. Sau khi chúng ta đã sao chép Giao diện nhị phân ứng dụng (ABI) của hợp đồng, hãy lưu nó dưới dạng tệp JSON có tên contract-abi.json trong thư mục src của bạn.

contract-abi.json của bạn nên được lưu trữ trong thư mục src của bạn.

Với địa chỉ hợp đồng, Giao diện nhị phân ứng dụng (ABI) và điểm cuối Alchemy Web3, chúng ta có thể sử dụng phương thức hợp đồng (opens in a new tab) để tải một phiên bản của hợp đồng thông minh của mình. Nhập Giao diện nhị phân ứng dụng (ABI) của hợp đồng vào tệp interact.js và thêm địa chỉ hợp đồng của bạn.

1// interact.js
2
3const contractABI = require("../contract-abi.json")
4const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"

Bây giờ chúng ta có thể cuối cùng bỏ chú thích biến helloWorldContract của mình và tải hợp đồng thông minh bằng điểm cuối AlchemyWeb3 của chúng ta:

1// interact.js
2export const helloWorldContract = new web3.eth.Contract(
3 contractABI,
4 contractAddress
5)

Để tóm tắt, 12 dòng đầu tiên của interact.js của bạn bây giờ sẽ trông như thế này:

1// interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8const contractABI = require("../contract-abi.json")
9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
10
11export const helloWorldContract = new web3.eth.Contract(
12 contractABI,
13 contractAddress
14)
Hiện tất cả

Bây giờ chúng ta đã tải hợp đồng của mình, chúng ta có thể triển khai hàm loadCurrentMessage của mình!

Triển khai loadCurrentMessage trong tệp interact.js của bạn

Hàm này cực kỳ đơn giản. Chúng ta sẽ thực hiện một lệnh gọi web3 không đồng bộ đơn giản để đọc từ hợp đồng của mình. Hàm của chúng ta sẽ trả về thông điệp được lưu trữ trong hợp đồng thông minh:

Cập nhật loadCurrentMessage trong tệp interact.js của bạn thành như sau:

1// interact.js
2
3export const loadCurrentMessage = async () => {
4 const message = await helloWorldContract.methods.message().call()
5 return message
6}

Vì chúng tôi muốn hiển thị hợp đồng thông minh này trong giao diện người dùng của mình, hãy cập nhật hàm useEffect trong thành phần HelloWorld.js của chúng tôi thành như sau:

1// HelloWorld.js
2
3//chỉ được gọi một lần
4useEffect(async () => {
5 const message = await loadCurrentMessage()
6 setMessage(message)
7}, [])

Lưu ý, chúng ta chỉ muốn loadCurrentMessage được gọi một lần trong lần kết xuất đầu tiên của thành phần. Chúng tôi sẽ sớm triển khai addSmartContractListener để tự động cập nhật giao diện người dùng sau khi thông điệp trong hợp đồng thông minh thay đổi.

Trước khi đi sâu vào trình lắng nghe của chúng ta, hãy kiểm tra những gì chúng ta đã có cho đến nay! Lưu các tệp HelloWorld.jsinteract.js của bạn, sau đó truy cập http://localhost:3000/ (opens in a new tab)

Bạn sẽ nhận thấy rằng thông điệp hiện tại không còn ghi "Không có kết nối với mạng." Thay vào đó, nó phản ánh thông điệp được lưu trữ trong hợp đồng thông minh. Tuyệt!

Giao diện người dùng của bạn bây giờ sẽ phản ánh thông điệp được lưu trữ trong hợp đồng thông minh

Bây giờ nói về trình lắng nghe đó...

Triển khai addSmartContractListener

Nếu bạn nghĩ lại về tệp HelloWorld.sol mà chúng ta đã viết trong Phần 1 của loạt hướng dẫn này (opens in a new tab), bạn sẽ nhớ lại rằng có một sự kiện hợp đồng thông minh được gọi là UpdatedMessages được phát ra sau khi hàm update của hợp đồng thông minh của chúng ta được gọi (xem các dòng 9 và 27):

1// HelloWorld.sol
2
3// Chỉ định phiên bản của Solidity, sử dụng lập phiên bản ngữ nghĩa.
4// Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
5pragma solidity ^0.7.3;
6
7// Định nghĩa một hợp đồng có tên là `HelloWorld`.
8// Một hợp đồng là một tập hợp các hàm và dữ liệu (trạng thái của nó). Sau khi được triển khai, một hợp đồng nằm ở một địa chỉ cụ thể trên chuỗi khối Ethereum. Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 //Phát ra khi hàm cập nhật được gọi
12 //Các sự kiện hợp đồng thông minh là một cách để hợp đồng của bạn giao tiếp rằng điều gì đó đã xảy ra trên chuỗi khối với giao diện người dùng ứng dụng của bạn, giao diện này có thể 'lắng nghe' các sự kiện nhất định và thực hiện hành động khi chúng xảy ra.
13 event UpdatedMessages(string oldStr, string newStr);
14
15 // Khai báo một biến trạng thái `message` thuộc loại `string`.
16 // Các biến trạng thái là các biến có giá trị được lưu trữ vĩnh viễn trong bộ lưu trữ hợp đồng. Từ khóa `public` giúp các biến có thể truy cập được từ bên ngoài hợp đồng và tạo ra một hàm mà các hợp đồng hoặc ứng dụng khách khác có thể gọi để truy cập giá trị.
17 string public message;
18
19 // Tương tự như nhiều ngôn ngữ lập trình hướng đối tượng dựa trên lớp, một hàm tạo là một hàm đặc biệt chỉ được thực thi khi tạo hợp đồng.
20 // Hàm tạo được sử dụng để khởi tạo dữ liệu của hợp đồng. Tìm hiểu thêm:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) {
22
23 // Chấp nhận một đối số chuỗi `initMessage` và đặt giá trị vào biến lưu trữ `message` của hợp đồng).
24 message = initMessage;
25 }
26
27 // Một hàm công khai chấp nhận một đối số chuỗi và cập nhật biến lưu trữ `message`.
28 function update(string memory newMessage) public {
29 string memory oldMsg = message;
30 message = newMessage;
31 emit UpdatedMessages(oldMsg, newMessage);
32 }
33}
Hiện tất cả

Các sự kiện hợp đồng thông minh là một cách để hợp đồng của bạn giao tiếp rằng điều gì đó đã xảy ra (tức là có một sự kiện) trên chuỗi khối với ứng dụng giao diện người dùng của bạn, ứng dụng này có thể 'lắng nghe' các sự kiện cụ thể và thực hiện hành động khi chúng xảy ra.

Hàm addSmartContractListener sẽ lắng nghe cụ thể sự kiện UpdatedMessages của hợp đồng thông minh Hello World của chúng ta và cập nhật giao diện người dùng của chúng ta để hiển thị thông điệp mới.

Sửa đổi addSmartContractListener thành như sau:

1// HelloWorld.js
2
3function addSmartContractListener() {
4 helloWorldContract.events.UpdatedMessages({}, (error, data) => {
5 if (error) {
6 setStatus("😥 " + error.message)
7 } else {
8 setMessage(data.returnValues[1])
9 setNewMessage("")
10 setStatus("🎉 Thông điệp của bạn đã được cập nhật!")
11 }
12 })
13}
Hiện tất cả

Hãy cùng phân tích những gì xảy ra khi trình lắng nghe phát hiện một sự kiện:

  • Nếu có lỗi xảy ra khi sự kiện được phát ra, nó sẽ được phản ánh trong giao diện người dùng thông qua biến trạng thái status của chúng ta.
  • Nếu không, chúng ta sẽ sử dụng đối tượng data được trả về. data.returnValues là một mảng được lập chỉ mục tại 0, trong đó phần tử đầu tiên trong mảng lưu trữ thông điệp trước đó và phần tử thứ hai lưu trữ thông điệp đã cập nhật. Nói chung, trong một sự kiện thành công, chúng ta sẽ đặt chuỗi message của mình thành thông điệp đã cập nhật, xóa chuỗi newMessage, và cập nhật biến trạng thái status của mình để phản ánh rằng một thông điệp mới đã được xuất bản trên hợp đồng thông minh của chúng ta.

Cuối cùng, hãy gọi trình lắng nghe của chúng ta trong hàm useEffect để nó được khởi tạo trong lần kết xuất đầu tiên của thành phần HelloWorld.js. Nói chung, hàm useEffect của bạn sẽ trông như thế này:

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7}, [])

Bây giờ chúng ta đã có thể đọc từ hợp đồng thông minh của mình, sẽ rất tuyệt nếu tìm ra cách viết vào nó nữa! Tuy nhiên, để ghi vào ứng dụng phi tập trung của mình, trước tiên chúng ta phải có một ví Ethereum được kết nối với nó.

Vì vậy, tiếp theo chúng ta sẽ giải quyết việc thiết lập ví Ethereum của mình (MetaMask) và sau đó kết nối nó với ứng dụng phi tập trung của mình!

Bước 4: Thiết lập ví Ethereum của bạn

Để viết bất cứ điều gì lên chuỗi Ethereum, người dùng phải ký các giao dịch bằng khóa riêng tư của ví ảo của họ. Đối với hướng dẫn này, chúng tôi sẽ sử dụng MetaMask (opens in a new tab), một ví ảo trong trình duyệt được sử dụng để quản lý địa chỉ tài khoản Ethereum của bạn, vì nó giúp việc ký giao dịch này trở nên siêu dễ dàng cho người dùng cuối.

Nếu bạn muốn tìm hiểu thêm về cách thức hoạt động của các giao dịch trên Ethereum, hãy xem trang này từ Ethereum Foundation.

Tải xuống MetaMask

Bạn có thể tải xuống và tạo tài khoản MetaMask miễn phí tại đây (opens in a new tab). Khi bạn tạo tài khoản, hoặc nếu bạn đã có tài khoản, hãy đảm bảo chuyển sang “Mạng thử nghiệm Goerli” ở phía trên bên phải (để chúng ta không phải giao dịch bằng tiền thật).

Thêm ether từ một Faucet

Để ký một giao dịch trên chuỗi khối Ethereum, chúng ta sẽ cần một ít Eth giả. Để nhận Eth, bạn có thể vào FaucETH (opens in a new tab) và nhập địa chỉ tài khoản Goerli của bạn, nhấp vào “Request funds”, sau đó chọn “Ethereum Testnet Goerli” trong menu thả xuống và cuối cùng nhấp lại vào nút “Request funds”. Bạn sẽ sớm thấy Eth trong tài khoản MetaMask của mình!

Kiểm tra số dư của bạn

Để kiểm tra lại số dư của chúng ta, hãy thực hiện một yêu cầu eth_getBalance (opens in a new tab) bằng cách sử dụng công cụ soạn thảo của Alchemy (opens in a new tab). Thao tác này sẽ trả về số lượng Eth trong ví của chúng ta. Sau khi bạn nhập địa chỉ tài khoản MetaMask của mình và nhấp vào “Send Request”, bạn sẽ thấy một phản hồi như sau:

1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}

LƯU Ý: Kết quả này tính bằng wei chứ không phải eth. Wei được sử dụng làm mệnh giá nhỏ nhất của ether. Việc chuyển đổi từ wei sang eth là: 1 eth = 10¹⁸ wei. Vì vậy, nếu chúng ta chuyển đổi 0xde0b6b3a7640000 sang hệ thập phân, chúng ta sẽ nhận được 1*10¹⁸, tương đương với 1 eth.

Phù! Tiền giả của chúng ta đã có đủ! 🤑

Bước 5: Kết nối MetaMask với giao diện người dùng của bạn

Bây giờ ví MetaMask của chúng ta đã được thiết lập, hãy kết nối ứng dụng phi tập trung của chúng ta với nó!

Hàm connectWallet

Trong tệp interact.js của chúng ta, hãy triển khai hàm connectWallet, sau đó chúng ta có thể gọi nó trong thành phần HelloWorld.js của mình.

Hãy sửa đổi connectWallet thành như sau:

1// interact.js
2
3export const connectWallet = async () => {
4 if (window.ethereum) {
5 try {
6 const addressArray = await window.ethereum.request({
7 method: "eth_requestAccounts",
8 })
9 const obj = {
10 status: "👆🏽 Viết một thông điệp vào trường văn bản ở trên.",
11 address: addressArray[0],
12 }
13 return obj
14 } catch (err) {
15 return {
16 address: "",
17 status: "😥 " + err.message,
18 }
19 }
20 } else {
21 return {
22 address: "",
23 status: (
24 <span>
25 <p>
26 {" "}
27 🦊 <a target="_blank" href={`https://metamask.io/download`}>
28 Bạn phải cài đặt MetaMask, một ví Ethereum ảo, trong trình duyệt của bạn.
29 </a>
30 </p>
31 </span>
32 ),
33 }
34 }
35}
Hiện tất cả

Vậy khối mã khổng lồ này chính xác làm gì?

Chà, đầu tiên, nó kiểm tra xem window.ethereum có được bật trong trình duyệt của bạn hay không.

window.ethereum là một API toàn cầu được chèn bởi MetaMask và các nhà cung cấp ví khác cho phép các trang web yêu cầu tài khoản Ethereum của người dùng. Nếu được chấp thuận, nó có thể đọc dữ liệu từ các chuỗi khối mà người dùng được kết nối và đề nghị người dùng ký các thông điệp và giao dịch. Hãy xem tài liệu MetaMask (opens in a new tab) để biết thêm thông tin!

Nếu window.ethereum không có mặt, điều đó có nghĩa là MetaMask chưa được cài đặt. Điều này dẫn đến một đối tượng JSON được trả về, trong đó address được trả về là một chuỗi rỗng và đối tượng status JSX chuyển tiếp rằng người dùng phải cài đặt MetaMask.

Bây giờ nếu window.ethereum mặt, thì đó là lúc mọi thứ trở nên thú vị.

Sử dụng vòng lặp try/catch, chúng ta sẽ cố gắng kết nối với MetaMask bằng cách gọi window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Việc gọi hàm này sẽ mở MetaMask trong trình duyệt, qua đó người dùng sẽ được nhắc kết nối ví của họ với ứng dụng phi tập trung của bạn.

  • Nếu người dùng chọn kết nối, method: "eth_requestAccounts" sẽ trả về một mảng chứa tất cả các địa chỉ tài khoản của người dùng đã kết nối với ứng dụng phi tập trung. Nói chung, hàm connectWallet của chúng ta sẽ trả về một đối tượng JSON chứa address đầu tiên trong mảng này (xem dòng 9) và một thông báo status nhắc người dùng viết một tin nhắn cho hợp đồng thông minh.
  • Nếu người dùng từ chối kết nối, thì đối tượng JSON sẽ chứa một chuỗi rỗng cho address được trả về và một thông báo status phản ánh rằng người dùng đã từ chối kết nối.

Bây giờ chúng ta đã viết hàm connectWallet này, bước tiếp theo là gọi nó đến thành phần HelloWorld.js của chúng ta.

Thêm hàm connectWallet vào Thành phần giao diện người dùng HelloWorld.js của bạn

Điều hướng đến hàm connectWalletPressed trong HelloWorld.js và cập nhật nó thành như sau:

1// HelloWorld.js
2
3const connectWalletPressed = async () => {
4 const walletResponse = await connectWallet()
5 setStatus(walletResponse.status)
6 setWallet(walletResponse.address)
7}

Lưu ý cách hầu hết các chức năng của chúng ta được trừu tượng hóa khỏi thành phần HelloWorld.js từ tệp interact.js? Điều này là để chúng ta tuân thủ mô hình M-V-C!

Trong connectWalletPressed, chúng ta chỉ cần thực hiện một lệnh gọi await đến hàm connectWallet đã nhập của mình, và sử dụng phản hồi của nó, chúng ta cập nhật các biến statuswalletAddress của mình thông qua các hook trạng thái của chúng.

Bây giờ, hãy lưu cả hai tệp (HelloWorld.jsinteract.js) và kiểm tra giao diện người dùng của chúng ta cho đến nay.

Mở trình duyệt của bạn trên trang http://localhost:3000/ (opens in a new tab) và nhấn nút "Kết nối Ví" ở trên cùng bên phải của trang.

Nếu bạn đã cài đặt MetaMask, bạn sẽ được nhắc kết nối ví của mình với ứng dụng phi tập trung của bạn. Chấp nhận lời mời kết nối.

Bạn sẽ thấy rằng nút ví bây giờ phản ánh rằng địa chỉ của bạn đã được kết nối! Tuyệt vời 🔥

Tiếp theo, hãy thử làm mới trang... Điều này thật lạ. Nút ví của chúng ta đang nhắc chúng ta kết nối MetaMask, mặc dù nó đã được kết nối...

Tuy nhiên, đừng sợ! Chúng ta có thể dễ dàng giải quyết vấn đề đó (bạn hiểu chứ?) bằng cách triển khai getCurrentWalletConnected, sẽ kiểm tra xem một địa chỉ đã được kết nối với ứng dụng phi tập trung của chúng ta hay chưa và cập nhật giao diện người dùng của chúng ta cho phù hợp!

Hàm getCurrentWalletConnected

Cập nhật hàm getCurrentWalletConnected của bạn trong tệp interact.js thành như sau:

1// interact.js
2
3export const getCurrentWalletConnected = async () => {
4 if (window.ethereum) {
5 try {
6 const addressArray = await window.ethereum.request({
7 method: "eth_accounts",
8 })
9 if (addressArray.length > 0) {
10 return {
11 address: addressArray[0],
12 status: "👆🏽 Viết một thông điệp vào trường văn bản ở trên.",
13 }
14 } else {
15 return {
16 address: "",
17 status: "🦊 Kết nối với MetaMask bằng nút trên cùng bên phải.",
18 }
19 }
20 } catch (err) {
21 return {
22 address: "",
23 status: "😥 " + err.message,
24 }
25 }
26 } else {
27 return {
28 address: "",
29 status: (
30 <span>
31 <p>
32 {" "}
33 🦊 <a target="_blank" href={`https://metamask.io/download`}>
34 Bạn phải cài đặt MetaMask, một ví Ethereum ảo, trong trình duyệt của bạn.
35 </a>
36 </p>
37 </span>
38 ),
39 }
40 }
41}
Hiện tất cả

Mã này rất tương tự như hàm connectWallet mà chúng ta vừa viết ở bước trước.

Sự khác biệt chính là thay vì gọi phương thức eth_requestAccounts, phương thức này sẽ mở MetaMask để người dùng kết nối ví của họ, ở đây chúng ta gọi phương thức eth_accounts, phương thức này chỉ đơn giản trả về một mảng chứa các địa chỉ MetaMask hiện đang được kết nối với ứng dụng phi tập trung của chúng ta.

Để xem hàm này hoạt động, hãy gọi nó trong hàm useEffect của thành phần HelloWorld.js của chúng ta:

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11}, [])
Hiện tất cả

Lưu ý, chúng ta sử dụng phản hồi của lệnh gọi đến getCurrentWalletConnected để cập nhật các biến trạng thái walletAddressstatus của mình.

Bây giờ bạn đã thêm mã này, hãy thử làm mới cửa sổ trình duyệt của chúng ta.

Tuyệt vời! Nút sẽ hiển thị rằng bạn đã kết nối và hiển thị bản xem trước địa chỉ ví đã kết nối của bạn - ngay cả sau khi bạn làm mới!

Triển khai addWalletListener

Bước cuối cùng trong quá trình thiết lập ví ứng dụng phi tập trung của chúng ta là triển khai trình nghe ví để UI của chúng ta cập nhật khi trạng thái ví thay đổi, chẳng hạn như khi người dùng ngắt kết nối hoặc chuyển đổi tài khoản.

Trong tệp HelloWorld.js của bạn, sửa đổi hàm addWalletListener của bạn như sau:

1// HelloWorld.js
2
3function addWalletListener() {
4 if (window.ethereum) {
5 window.ethereum.on("accountsChanged", (accounts) => {
6 if (accounts.length > 0) {
7 setWallet(accounts[0])
8 setStatus("👆🏽 Viết một thông điệp vào trường văn bản ở trên.")
9 } else {
10 setWallet("")
11 setStatus("🦊 Kết nối với MetaMask bằng nút trên cùng bên phải.")
12 }
13 })
14 } else {
15 setStatus(
16 <p>
17 {" "}
18 🦊 <a target="_blank" href={`https://metamask.io/download`}>
19 Bạn phải cài đặt MetaMask, một ví Ethereum ảo, trong trình duyệt của bạn.
20 </a>
21 </p>
22 )
23 }
24}
Hiện tất cả

Tôi cá là bạn thậm chí không cần sự trợ giúp của chúng tôi để hiểu những gì đang diễn ra ở đây vào thời điểm này, nhưng vì mục đích kỹ lưỡng, hãy nhanh chóng phân tích nó:

  • Đầu tiên, hàm của chúng ta kiểm tra xem window.ethereum có được bật hay không (tức là MetaMask đã được cài đặt).
    • Nếu không, chúng ta chỉ cần thiết lập biến trạng thái status của mình thành một chuỗi JSX nhắc người dùng cài đặt MetaMask.
    • Nếu nó được bật, chúng ta sẽ thiết lập trình nghe window.ethereum.on("accountsChanged") trên dòng 3 để lắng nghe các thay đổi trạng thái trong ví MetaMask, bao gồm khi người dùng kết nối thêm một tài khoản vào ứng dụng phi tập trung, chuyển đổi tài khoản hoặc ngắt kết nối một tài khoản. Nếu có ít nhất một tài khoản được kết nối, biến trạng thái walletAddress sẽ được cập nhật thành tài khoản đầu tiên trong mảng accounts được trả về bởi trình nghe. Ngược lại, walletAddress được đặt thành một chuỗi rỗng.

Cuối cùng nhưng không kém phần quan trọng, chúng ta phải gọi nó trong hàm useEffect của mình:

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11
12 addWalletListener()
13}, [])
Hiện tất cả

Vậy là xong! Chúng ta đã hoàn thành thành công việc lập trình tất cả các chức năng ví của mình! Bây giờ đến nhiệm vụ cuối cùng của chúng ta: cập nhật thông điệp được lưu trữ trong hợp đồng thông minh của chúng ta!

Bước 6: Triển khai hàm updateMessage

Được rồi các bạn, chúng ta đã đến chặng cuối cùng! Trong updateMessage của tệp interact.js của bạn, chúng ta sẽ làm những việc sau:

  1. Đảm bảo thông điệp chúng ta muốn xuất bản trong liên hệ thông minh của mình là hợp lệ
  2. Ký giao dịch của chúng ta bằng MetaMask
  3. Gọi hàm này từ thành phần giao diện người dùng HelloWorld.js của chúng ta

Điều này sẽ không mất nhiều thời gian; hãy hoàn thành ứng dụng phi tập trung này!

Xử lý lỗi đầu vào

Đương nhiên, việc có một số loại xử lý lỗi đầu vào ở đầu hàm là hợp lý.

Chúng ta sẽ muốn hàm của mình trả về sớm nếu không có tiện ích mở rộng MetaMask nào được cài đặt, không có ví nào được kết nối (tức là address được truyền vào là một chuỗi trống), hoặc message là một chuỗi trống. Hãy thêm xử lý lỗi sau vào updateMessage:

1// interact.js
2
3export const updateMessage = async (address, message) => {
4 if (!window.ethereum || address === null) {
5 return {
6 status:
7 "💡 Kết nối ví MetaMask của bạn để cập nhật thông điệp trên chuỗi khối.",
8 }
9 }
10
11 if (message.trim() === "") {
12 return {
13 status: "❌ Thông điệp của bạn không thể là một chuỗi trống.",
14 }
15 }
16}
Hiện tất cả

Bây giờ nó đã có xử lý lỗi đầu vào phù hợp, đã đến lúc ký giao dịch qua MetaMask!

Ký giao dịch của chúng ta

Nếu bạn đã quen thuộc với các giao dịch Ethereum web3 truyền thống, mã chúng ta viết tiếp theo sẽ rất quen thuộc. Bên dưới mã xử lý lỗi đầu vào của bạn, thêm nội dung sau vào updateMessage:

1// interact.js
2
3//thiết lập các tham số giao dịch
4const transactionParameters = {
5 to: contractAddress, // Bắt buộc trừ khi xuất bản hợp đồng.
6 from: address, // phải khớp với địa chỉ đang hoạt động của người dùng.
7 data: helloWorldContract.methods.update(message).encodeABI(),
8}
9
10//ký giao dịch
11try {
12 const txHash = await window.ethereum.request({
13 method: "eth_sendTransaction",
14 params: [transactionParameters],
15 })
16 return {
17 status: (
18 <span>
19{" "}
20 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
21 Xem trạng thái giao dịch của bạn trên Etherscan!
22 </a>
23 <br />
24 ℹ️ Sau khi giao dịch được mạng xác minh, thông điệp sẽ được
25 cập nhật tự động.
26 </span>
27 ),
28 }
29} catch (error) {
30 return {
31 status: "😥 " + error.message,
32 }
33}
Hiện tất cả

Hãy cùng phân tích xem điều gì đang xảy ra. Đầu tiên, chúng ta thiết lập các tham số giao dịch của mình, trong đó:

  • to chỉ định địa chỉ người nhận (hợp đồng thông minh của chúng ta)
  • from chỉ định người ký giao dịch, biến address mà chúng ta đã truyền vào hàm của mình
  • data chứa lệnh gọi đến phương thức update của hợp đồng thông minh Hello World của chúng ta, nhận biến chuỗi message của chúng ta làm đầu vào

Sau đó, chúng tôi thực hiện một lệnh gọi await, window.ethereum.request, trong đó chúng tôi yêu cầu MetaMask ký giao dịch. Lưu ý, ở dòng 11 và 12, chúng ta đang chỉ định phương thức eth của mình, eth_sendTransaction và truyền vào transactionParameters của chúng ta.

Tại thời điểm này, MetaMask sẽ mở ra trong trình duyệt và nhắc người dùng ký hoặc từ chối giao dịch.

  • Nếu giao dịch thành công, hàm sẽ trả về một đối tượng JSON trong đó chuỗi JSX status nhắc người dùng kiểm tra Etherscan để biết thêm thông tin về giao dịch của họ.
  • Nếu giao dịch thất bại, hàm sẽ trả về một đối tượng JSON trong đó chuỗi status chuyển tiếp thông điệp lỗi.

Nói chung, hàm updateMessage của chúng ta sẽ trông như thế này:

1// interact.js
2
3export const updateMessage = async (address, message) => {
4 //xử lý lỗi đầu vào
5 if (!window.ethereum || address === null) {
6 return {
7 status:
8 "💡 Kết nối ví MetaMask của bạn để cập nhật thông điệp trên chuỗi khối.",
9 }
10 }
11
12 if (message.trim() === "") {
13 return {
14 status: "❌ Thông điệp của bạn không thể là một chuỗi trống.",
15 }
16 }
17
18 //thiết lập các tham số giao dịch
19 const transactionParameters = {
20 to: contractAddress, // Bắt buộc trừ khi xuất bản hợp đồng.
21 from: address, // phải khớp với địa chỉ đang hoạt động của người dùng.
22 data: helloWorldContract.methods.update(message).encodeABI(),
23 }
24
25 //ký giao dịch
26 try {
27 const txHash = await window.ethereum.request({
28 method: "eth_sendTransaction",
29 params: [transactionParameters],
30 })
31 return {
32 status: (
33 <span>
34{" "}
35 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
36 Xem trạng thái giao dịch của bạn trên Etherscan!
37 </a>
38 <br />
39 ℹ️ Sau khi giao dịch được mạng xác minh, thông điệp sẽ được
40 cập nhật tự động.
41 </span>
42 ),
43 }
44 } catch (error) {
45 return {
46 status: "😥 " + error.message,
47 }
48 }
49}
Hiện tất cả

Cuối cùng nhưng không kém phần quan trọng, chúng ta cần kết nối hàm updateMessage của mình với thành phần HelloWorld.js.

Kết nối updateMessage với giao diện người dùng HelloWorld.js

Hàm onUpdatePressed của chúng ta sẽ thực hiện một lệnh gọi await đến hàm updateMessage được nhập và sửa đổi biến trạng thái status để phản ánh xem giao dịch của chúng ta đã thành công hay thất bại:

1// HelloWorld.js
2
3const onUpdatePressed = async () => {
4 const { status } = await updateMessage(walletAddress, newMessage)
5 setStatus(status)
6}

Nó siêu gọn gàng và đơn giản. Và đoán xem... ỨNG DỤNG PHI TẬP TRUNG CỦA BẠN ĐÃ HOÀN THÀNH!!!

Hãy tiếp tục và kiểm tra nút Cập nhật!

Tạo ứng dụng phi tập trung tùy chỉnh của riêng bạn

Wooooo, bạn đã đi đến cuối hướng dẫn! Để tóm tắt, bạn đã học cách:

  • Kết nối ví MetaMask với dự án ứng dụng phi tập trung của bạn
  • Đọc dữ liệu từ hợp đồng thông minh của bạn bằng cách sử dụng Giao diện Lập trình Ứng dụng Alchemy Web3 (opens in a new tab)
  • Ký các giao dịch Ethereum bằng MetaMask

Bây giờ bạn đã được trang bị đầy đủ để áp dụng các kỹ năng từ hướng dẫn này để xây dựng dự án ứng dụng phi tập trung tùy chỉnh của riêng bạn! Như mọi khi, nếu bạn có bất kỳ câu hỏi nào, đừng ngần ngại liên hệ với chúng tôi để được trợ giúp trong Alchemy Discord (opens in a new tab). 🧙‍♂️

Sau khi hoàn thành hướng dẫn này, hãy cho chúng tôi biết trải nghiệm của bạn như thế nào hoặc nếu bạn có bất kỳ phản hồi nào bằng cách gắn thẻ chúng tôi trên Twitter @alchemyplatform (opens in a new tab)!

Lần cập nhật trang lần cuối: 26 tháng 2, 2026

Hướng dẫn này có hữu ích không?