Alpha Web3 & AI
Trang chủ › Phát Triển Web3

Hướng dẫn lập trình Smart Contract cơ bản trên Ethereum

Ban biên tập Alpha Web3 & AI · · Cập nhật
Hướng dẫn lập trình Smart Contract cơ bản trên Ethereum

Cách nhanh nhất để học smart contract là tự viết một hợp đồng nhỏ, kiểm thử nó và triển khai lên mạng thử nghiệm. Bài này hướng dẫn bạn đi trọn quy trình đó với một dự án mẫu: hợp đồng "quỹ đóng góp" cho phép mọi người gửi ETH và chỉ chủ quỹ được rút. Bạn sẽ làm quen với Remix để thử nhanh, Hardhat để phát triển chuyên nghiệp, viết test và triển khai lên Sepolia mà không tốn tiền thật.

Nếu chưa nắm các khái niệm như EVM, gas, storage hay phạm vi hiển thị của hàm, hãy đọc trước kiến thức nền về smart contract trên Ethereum.

Chuẩn bị công cụ

Công cụDùng đểGhi chú
Remix IDEViết, biên dịch, chạy thử ngay trên trình duyệtKhông cần cài đặt, lý tưởng cho bước đầu
Node.jsChạy Hardhat và thư viện JavaScriptDùng bản LTS
Hardhat hoặc FoundryKhung phát triển: biên dịch, test, triển khaiHardhat viết test bằng JavaScript/TypeScript; Foundry viết test bằng Solidity
Ví MetaMaskKý giao dịch trên mạng thử nghiệmTạo ví riêng chỉ để phát triển
RPC providerKết nối tới mạng EthereumCác dịch vụ như Alchemy, Infura có gói miễn phí

Lưu ý an toàn quan trọng: dùng một ví hoàn toàn tách biệt cho phát triển, không bao giờ chứa tài sản thật. Khóa riêng của ví này sẽ nằm trong file cấu hình cục bộ, nên tuyệt đối không commit file đó lên Git.

Bước 1: Thiết kế hợp đồng quỹ đóng góp

Trước khi viết mã, hãy liệt kê yêu cầu bằng lời. Đây là thói quen giúp bạn kiểm thử và rà soát bảo mật sau này:

  • Ai cũng có thể đóng góp ETH, với mức tối thiểu để tránh giao dịch rác.
  • Hợp đồng ghi nhận tổng số mỗi địa chỉ đã đóng góp.
  • Chỉ chủ quỹ, tức người triển khai, được rút toàn bộ số dư.
  • Mỗi lần đóng góp và rút tiền phát ra sự kiện để giao diện theo dõi.

Bước 2: Viết hợp đồng trong Remix

Mở Remix, tạo file mới tên FundMe.sol. Hợp đồng gồm các phần sau:

  1. Khai báo: dòng giấy phép SPDX, pragma phiên bản 0.8 trở lên, khai báo contract FundMe.
  2. Biến trạng thái: một biến address public immutable owner lưu chủ quỹ; một hằng số MIN_CONTRIBUTION tính bằng wei; một mapping(address => uint256) public contributions lưu số tiền mỗi người góp.
  3. Sự kiện: Contributed(address indexed from, uint256 amount) và Withdrawn(uint256 amount).
  4. Custom error: NotOwner() và TooSmall(), tiết kiệm gas hơn chuỗi thông báo lỗi.
  5. Constructor: gán owner = msg.sender.
  6. Hàm contribute: đánh dấu external payable; nếu msg.value nhỏ hơn mức tối thiểu thì revert với TooSmall; cộng msg.value vào contributions của msg.sender; phát sự kiện Contributed.
  7. Hàm withdraw: kiểm tra msg.sender là owner, nếu không thì revert NotOwner; đọc số dư hợp đồng; phát sự kiện; gửi ETH cho owner bằng lời gọi call và kiểm tra kết quả trả về, revert nếu thất bại.

Để ý thứ tự trong hàm withdraw: kiểm tra trước, cập nhật trạng thái và phát sự kiện, rồi mới chuyển tiền ra ngoài. Đây là mẫu Checks-Effects-Interactions giúp phòng lỗi reentrancy, được giải thích trong bài những lỗ hổng smart contract cần tránh.

Bước 3: Chạy thử trong Remix

  1. Vào tab Solidity Compiler, chọn đúng phiên bản và biên dịch. Sửa mọi lỗi và cảnh báo.
  2. Vào tab Deploy & Run, chọn môi trường Remix VM, một blockchain giả lập trong trình duyệt với các tài khoản có sẵn ETH ảo.
  3. Triển khai hợp đồng từ tài khoản thứ nhất.
  4. Chuyển sang tài khoản thứ hai, nhập giá trị ETH vào ô Value và gọi contribute. Kiểm tra contributions của tài khoản đó.
  5. Thử gọi withdraw từ tài khoản thứ hai: giao dịch phải bị revert với lỗi NotOwner.
  6. Gọi withdraw từ tài khoản thứ nhất và quan sát số dư thay đổi.

Remix đủ cho thử nghiệm nhanh, nhưng dự án thật cần test tự động và quy trình lặp lại được.

Bước 4: Chuyển sang Hardhat

  1. Tạo thư mục dự án, khởi tạo npm và cài Hardhat làm phụ thuộc phát triển.
  2. Chạy lệnh khởi tạo của Hardhat, chọn mẫu dự án TypeScript hoặc JavaScript.
  3. Đặt FundMe.sol vào thư mục contracts.
  4. Chạy lệnh biên dịch của Hardhat; artifact gồm bytecode và ABI được tạo trong thư mục artifacts.

Bước 5: Viết test

Test là phần quan trọng nhất của smart contract vì bạn không thể vá sau khi triển khai. Với dự án mẫu, bộ test tối thiểu nên kiểm tra:

  • Owner được gán đúng sau khi triển khai.
  • Đóng góp dưới mức tối thiểu bị từ chối với đúng lỗi.
  • Đóng góp hợp lệ cập nhật contributions và phát đúng sự kiện.
  • Nhiều lần đóng góp từ cùng địa chỉ được cộng dồn.
  • Người không phải owner không rút được.
  • Owner rút được toàn bộ số dư, và số dư hợp đồng về 0.

Hardhat chạy test trên mạng cục bộ tích hợp, rất nhanh. Bạn có thể đo độ phủ mã bằng plugin coverage và xem báo cáo gas để biết hàm nào tốn kém. Nếu dùng Foundry, hãy thử thêm fuzz test: Foundry tự sinh hàng loạt giá trị đầu vào ngẫu nhiên để tìm trường hợp bạn không nghĩ tới.

Bước 6: Triển khai lên mạng thử nghiệm Sepolia

  1. Lấy ETH thử nghiệm cho ví phát triển từ một faucet Sepolia.
  2. Tạo endpoint RPC cho Sepolia từ nhà cung cấp.
  3. Lưu URL RPC và khóa riêng của ví phát triển vào biến môi trường hoặc tính năng quản lý biến cấu hình của Hardhat, không ghi thẳng vào mã.
  4. Khai báo mạng Sepolia trong file cấu hình Hardhat.
  5. Viết script triển khai hoặc dùng Hardhat Ignition, chạy với tham số mạng Sepolia.
  6. Ghi lại địa chỉ hợp đồng, tra cứu trên trình khám phá khối Sepolia.
  7. Xác minh mã nguồn bằng plugin verify để người khác đọc được mã của bạn.

Bước 7: Kết nối với giao diện

Hợp đồng đã chạy, bước tiếp theo là cho người dùng tương tác qua web: kết nối ví, hiển thị tổng đóng góp, gửi giao dịch. Phần này được hướng dẫn chi tiết trong bài xây dựng DApp với React và Ethers.js, và bạn có thể dùng chính hợp đồng FundMe làm phần backend.

Mở rộng bài tập

  • Thêm mục tiêu gây quỹ và hạn chót; nếu không đạt, cho phép người đóng góp tự rút lại tiền của mình.
  • Thay owner đơn lẻ bằng thư viện Ownable của OpenZeppelin, thử chuyển quyền sở hữu.
  • Thêm hàm receive để hợp đồng nhận ETH gửi trực tiếp.
  • Chạy Slither để xem công cụ phân tích tĩnh cảnh báo gì.

Những lỗi người mới hay mắc

  • Quên đánh dấu payable nên hàm không nhận được ETH.
  • Nhầm đơn vị: nhập 1 nghĩ là 1 ETH nhưng thực ra là 1 wei.
  • Dùng chung ví chính cho phát triển, để lộ khóa riêng.
  • Bỏ qua kiểm tra giá trị trả về khi gửi ETH bằng call.
  • Triển khai lên mạng chính khi chưa có test đầy đủ và chưa được người khác rà soát.

Câu hỏi thường gặp

Nên chọn Hardhat hay Foundry?

Hardhat phù hợp nếu bạn quen JavaScript hoặc TypeScript và muốn tích hợp với frontend. Foundry chạy rất nhanh, viết test bằng Solidity và có fuzz testing mạnh. Nhiều đội dùng kết hợp cả hai; người mới có thể bắt đầu với công cụ gần ngôn ngữ mình quen.

Faucet Sepolia không cấp ETH thử thì làm sao?

Một số faucet yêu cầu ví có số dư nhỏ trên mạng chính hoặc đăng nhập tài khoản để chống lạm dụng. Bạn có thể thử faucet của các nhà cung cấp RPC khác nhau, hoặc phát triển chủ yếu trên mạng cục bộ của Hardhat và Foundry rồi chỉ dùng testnet cho bước cuối.

Mất bao lâu để viết được smart contract dùng thực tế?

Viết được hợp đồng đơn giản chạy đúng chỉ mất vài ngày đến vài tuần. Viết hợp đồng đủ an toàn để giữ tài sản thật của người khác đòi hỏi hiểu sâu về bảo mật, kiểm thử và thường cần kiểm toán độc lập.

Có thể sửa hợp đồng sau khi triển khai lên Sepolia không?

Không sửa được hợp đồng đã triển khai, nhưng trên mạng thử nghiệm bạn chỉ cần sửa mã và triển khai lại để nhận địa chỉ mới. Đây là cơ hội tốt để luyện quy trình trước khi phải đối mặt với tính bất biến trên mạng chính.

Bài liên quan