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

本指南将带领你,从一个完全的初学者开始,一步步完成一个完整的以太坊项目,我们将使用最流行和成熟的工具栈,涵盖从环境搭建、智能合约编写、前端交互到项目部署的全过程,无论你是否有编程经验,只要跟随本教程,你就能亲手打造出属于自己的第一个DApp。
在开始编码之前,我们需要安装几个核心工具,它们就像是你的“开发工具箱”。
Node.js 和 npm (Node Package Manager)
node -v 和 npm -v 确认安装成功。代码编辑器
MetaMask 钱包
测试网 ETH

我们将构建一个简单的“留言板”DApp,用户可以付费在链上留下一条留言,并查看所有留言。
我们选择 Hardhat 作为我们的开发框架,它是一个功能全面的以太坊开发环境,能让编译、测试、部署和调试变得异常简单。
创建一个项目文件夹并进入:

mkdir eth-message-board cd eth-message-board
初始化 npm 项目:
npm init -y
安装 Hardhat:
npm install --save-dev hardhat
初始化 Hardhat 项目:
npx hardhat
在交互式提示中,选择 "Create a JavaScript project",然后一路回车接受默认配置,这会创建一个包含 contracts/, scripts/, test/ 等标准目录结构。
智能合约是以太坊应用的“后端逻辑”,它运行在区块链上。
contracts/ 目录,删除 Lock.sol,然后创建一个新文件 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 事件。getMessage 和 getMessagesCount 是“查看”函数,它们不修改链上状态,因此成本很低,可以随时被调用。安装依赖:我们需要 OpenZeppelin 的合约库来保证代码安全。
npm install @openzeppelin/contracts
编译合约:在终端运行:
npx hardhat compile
成功后,你会在 artifacts/ 目录下看到编译好的合约字节码。
编写测试:在 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!");
});
});
npx hardhat test
免责声明:本文为转载,非本网原创内容,不代表本网观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
如有疑问请发送邮件至:bangqikeconnect@gmail.com