从零开始,构建你的第一个以太坊项目,一份完整教学指南

以太坊,作为全球第二大加密货币和领先的智能合约平台,不仅仅是一种数字资产,更是一个去中心化的、可编程的世界计算机,它为开发者提供了构建去中心化应用(DApps)的强大能力,从金融(DeFi)到游戏,从艺术品(NFT)到身份验证,其应用潜力无穷。

本指南将带领你,从一个完全的初学者开始,一步步完成一个完整的以太坊项目,我们将使用最流行和成熟的工具栈,涵盖从环境搭建、智能合约编写、前端交互到项目部署的全过程,无论你是否有编程经验,只要跟随本教程,你就能亲手打造出属于自己的第一个DApp。


第一部分:准备工作——搭建你的开发环境

在开始编码之前,我们需要安装几个核心工具,它们就像是你的“开发工具箱”。

  1. Node.js 和 npm (Node Package Manager)

    • 作用:JavaScript 运行环境和包管理器,我们将用它来运行代码、管理项目依赖。
    • 安装:访问 Node.js 官网 下载并安装 LTS(长期支持)版本,安装完成后,打开终端(或命令提示符),输入 node -vnpm -v 确认安装成功。
  2. 代码编辑器

    • 推荐Visual Studio Code (VS Code),它免费、强大,并拥有丰富的插件生态。
    • 推荐插件
      • Solidity by Juan Blanco:提供智能合约的语法高亮、代码提示和格式化。
      • Hardhat for VS Code:为 Hardhat 框架提供更好的集成体验。
      • Prettier - Code formatter:保持代码风格统一。
  3. MetaMask 钱包

    • 作用:这是你与以太坊区块链交互的桥梁,它是一个浏览器插件钱包,用于管理你的账户、私钥,并与你的 DApp 进行连接。
    • 安装:访问 MetaMask 官网,下载对应浏览器的插件,并按照提示创建一个新钱包。请务必妥善保存你的助记词,这是你资产的唯一凭证,绝不泄露给任何人!
  4. 测试网 ETH

    • 作用:以太坊主网上的真实资产非常宝贵,不适合用于开发和测试,我们使用“测试网”,它是一个模拟的真实网络,其中的“测试ETH”没有实际价值,但可以免费获取,用于测试交易和部署。
    • 获取:你可以去 faucets.chain.link 等水龙头网站,使用你的 MetaMask 钱包地址免费获取 Sepolia 测试网的 ETH。

第二部分:项目构建——从智能合约到前端交互

我们将构建一个简单的“留言板”DApp,用户可以付费在链上留下一条留言,并查看所有留言。

步骤 1:初始化项目与安装框架

我们选择 Hardhat 作为我们的开发框架,它是一个功能全面的以太坊开发环境,能让编译、测试、部署和调试变得异常简单。

  1. 创建一个项目文件夹并进入:

    mkdir eth-message-board
    cd eth-message-board
  2. 初始化 npm 项目:

    npm init -y
  3. 安装 Hardhat:

    npm install --save-dev hardhat
  4. 初始化 Hardhat 项目:

    npx hardhat

    在交互式提示中,选择 "Create a JavaScript project",然后一路回车接受默认配置,这会创建一个包含 contracts/, scripts/, test/ 等标准目录结构。

步骤 2:编写智能合约

智能合约是以太坊应用的“后端逻辑”,它运行在区块链上。

  1. 打开 contracts/ 目录,删除 Lock.sol随机配图
de>,然后创建一个新文件 MessageBoard.sol
  • 编写以下代码:
  • // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    // 导入 OpenZeppelin 的合约,以获得更安全的实现
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    /**MessageBoard
     * @dev 一个简单的留言板合约,允许用户支付费用来存储消息。
     */
    contract MessageBoard {
        // 定义一个结构体来存储留言信息
        struct Message {
            address author;  // 留言作者地址
            string content; // 留言内容
            uint256 timestamp; // 留言时间戳
        }
        // 定义一个事件,用于在前端监听新留言
        event NewMessage(address indexed author, string content, uint256 timestamp);
        // 存储所有留言的数组
        Message[] public messages;
        // 定义留言费用(单位:wei,1 ETH = 10^18 wei)
        uint256 public constant MESSAGE_FEE = 1 ether;
        /**
         * @dev 提交一条新留言
         * @param _content 要提交的留言内容
         */
        function postMessage(string memory _content) public payable {
            // 确保用户支付了正确的费用
            require(msg.value == MESSAGE_FEE, "Incorrect message fee!");
            // 创建一个新的留言对象,并添加到数组中
            messages.push(Message({
                author: msg.sender,
                content: _content,
                timestamp: block.timestamp
            }));
            // 触发事件,通知前端
            emit NewMessage(msg.sender, _content, block.timestamp);
        }
        /**
         * @dev 获取所有留言的数量
         */
        function getMessagesCount() public view returns (uint256) {
            return messages.length;
        }
        /**
         * @dev 获取指定索引的留言
         */
        function getMessage(uint256 index) public view returns (address, string memory, uint256) {
            require(index < messages.length, "Message index out of bounds");
            Message storage message = messages[index];
            return (message.author, message.content, message.timestamp);
        }
    }

    代码解释

    • 我们定义了一个 Message 结构体来存储每条留言的作者、内容和时间。
    • postMessage 函数是核心功能,它要求用户支付 MESSAGE_FEE(1个测试ETH),然后将新留言存入数组,并触发 NewMessage 事件。
    • getMessagegetMessagesCount 是“查看”函数,它们不修改链上状态,因此成本很低,可以随时被调用。

    步骤 3:编译与测试

    1. 安装依赖:我们需要 OpenZeppelin 的合约库来保证代码安全。

      npm install @openzeppelin/contracts
    2. 编译合约:在终端运行:

      npx hardhat compile

      成功后,你会在 artifacts/ 目录下看到编译好的合约字节码。

    3. 编写测试:在 test/ 目录下创建 messageBoard.test.js,编写测试用例来确保我们的合约按预期工作。

    const { expect } = require("chai");
    const { ethers } = require("hardhat");
    describe("MessageBoard", function () {
        let MessageBoard;
        let messageBoard;
        let owner;
        let addr1;
        beforeEach(async function () {
            // 在每个测试前部署新的合约实例
            [owner, addr1] = await ethers.getSigners();
            MessageBoard = await ethers.getContractFactory("MessageBoard");
            messageBoard = await MessageBoard.deploy();
            await messageBoard.waitForDeployment();
        });
        it("Should post a message and emit an event", async function () {
            const messageContent = "Hello, Ethereum!";
            const fee = ethers.parseEther("1"); // 1 ETH in wei
            // 监听事件
            await expect(messageBoard.connect(addr1).postMessage(messageContent, { value: fee }))
                .to.emit(messageBoard, "NewMessage")
                .withArgs(addr1.address, messageContent, await ethers.provider.getBlock());
            // 验证留言是否被正确存储
            const messagesCount = await messageBoard.getMessagesCount();
            expect(messagesCount).to.equal(1);
            const message = await messageBoard.getMessage(0);
            expect(message.author).to.equal(addr1.address);
            expect(message.content).to.equal(messageContent);
        });
        it("Should not allow posting with incorrect fee", async function () {
            const messageContent = "This should fail";
            const wrongFee = ethers.parseEther("0.5"); // 支付的费用不足
            await expect(
                messageBoard.connect(addr1).postMessage(messageContent, { value: wrongFee })
            ).to.be.revertedWith("Incorrect message fee!");
        });
    });
    1. 运行测试
      npx hardhat test

    本文由用户投稿上传,若侵权请提供版权资料并联系删除!