以太坊作为全球第二大公有链,其核心创新在于“智能合约”——一种运行在区块链上、自动执行预设规则的程序代码,它无需中介信任,即可实现资产转移、逻辑验证等复杂功能,是DeFi、NFT、DAO等应用的技术基石,本文将以“简易投票系统”为例,带你从零理解以太坊智能合约的编写、部署与交互,完成一个可落地的Demo实践。

智能合约本质上是一段部署在以太坊虚拟机(EVM)上的代码,它以“代码即法律”的方式,在满足预设条件时自动触发执行,投票合约会在投票时间结束后自动统计票数,无需人工干预;借贷合约会在抵押物价值不足时自动清算,这种去中心化、不可篡改的特性,使其成为构建可信应用的核心工具。
在编写智能合约前,需完成以下环境搭建,以保障开发流程顺畅:
智能合约开发依赖Node.js环境,需确保版本≥16.0,从nodejs官网下载安装后,通过终端运行node -v和npm -v验证安装成功。
Hardhat是当前最流行的以太坊开发框架,支持合约编译、测试、部署等全流程,在终端执行以下命令初始化项目:
mkdir ethereum-contract-demo && cd ethereum-contract-demo npm init -y npm install --save-dev hardhat npx hardhat init
按提示选择“Create a JavaScript project”,并安装依赖(如@nomicfoundation/hardhat-toolbox)。

MetaMask是浏览器端的以太坊钱包,用于管理账户、与合约交互,从MetaMask官网安装浏览器插件,创建并备份好助记词,确保钱包网络切换至“本地开发网络”(后续由Hardhat启动)。
本文以“简易投票系统”为例,实现以下功能:
在contracts目录下新建Voting.sol,编写如下代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Voting {
// 投票选项结构体
struct Option {
string name;
uint voteCount;
}
// 状态变量
string public votingTopic;
uint public votingDeadline;
mapping(address => bool) public hasVoted;
Option[] public options;
// 事件:投票时触发,方便前端监听
event Voted(address voter, string option);
// 构造函数:部署时初始化投票议题和选项
constructor(string memory _topic, string[] memory _optionNames) {
votingTopic = _topic;
votingDeadline = block.timestamp 1 days; // 投票截止时间:部署后1天
for (uint i = 0; i < _optionNames.length; i ) {
options.push(Option(_optionNames[i], 0));
}
}
// 投票函数
function vote(uint _optionIndex) public {
require(block.timestamp < votingDeadline, "Voting has ended");
require(!hasVoted[msg.sender], "You have already voted");
require(_optionIndex < options.length, "Invalid option index");
hasVoted[msg.sender] = true;
options[_optionIndex].voteCount ;
emit Voted(msg.sender, options[_optionIndex].name);
}
// 查询票数
function getVoteCount(uint _optionIndex) public view returns (uint) {
require(_optionIndex < options.length, "Invalid option index");
return options[_optionIndex].voteCount;
}
// 获取所有选项
function getOptions() public view returns (string[] memory, uint[] memory) {
string[] memory optionNames = new string[](options.length);
uint[] memory voteCounts = new uint[](options.length);
for (uint i = 0; i < options.length; i ) {
optionNames[i] = options[i].name;
voteCounts[i] = options[i].voteCount;
}
return (optionNames, voteCounts);
}
}
votingTopic(议题)、votingDeadline(截止时间)、hasVoted(记录投票地址)、options(投票选项数组); require校验投票时间、重复投票、选项有效性; Voted事件用于前端监听投票行为,提升交互体验。 在终端执行:
npx hardhat compile
成功后,artifacts目录会生成编译后的ABI(应用二进制接口)和字节码,这是合约与交互的“桥梁”。

在test目录下新建voting.test.js,使用Chai测试框架验证合约逻辑:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Voting Contract", function () {
let votingContract;
let owner, voter1, voter2;
beforeEach(async function () {
[owner, voter1, voter2] = await ethers.getSigners();
const Voting = await ethers.getContractFactory("Voting");
votingContract = await Voting.deploy("Best Crypto?", ["Bitcoin", "Ethereum"]);
await votingContract.waitForDeployment();
});
it("Should initialize with correct topic and options", async function () {
expect(await votingContract.votingTopic()).to.equal("Best Crypto?");
const [options, _] = await votingContract.getOptions();
expect(options).to.deep.equal(["Bitcoin", "Ethereum"]);
});
it("Should allow voting before deadline", async function () {
await votingContract.connect(voter1).vote(0); // 投票给Bitcoin
expect(await votingContract.getVoteCount(0)).to.equal(1);
expect(await votingContract.hasVoted(voter1.address)).to.equal(true);
});
it("Should reject duplicate votes", async function () {
await votingContract.connect(voter1).vote(0);
await expect(votingContract.connect(voter1).vote(0)).to.be.revertedWith("You have already voted");
});
it("Should reject voting after deadline", async function () {
// 快进时间(Hardhat支持时间快进)
await ethers.provider.send("evm_increaseTime", [2 * 24 * 60 * 60]); // 增加2天
await ethers.provider.send("evm_mine", []);
await expect(votingContract.connect(voter1).vote(0)).to.be.revertedWith("Voting has ended");
});
});
执行以下命令启动本地测试节点(默认Hardhat Network)并运行测试:
npx hardhat test
若所有测试通过,说明合约逻辑正确,可进入部署阶段。
在scripts目录下新建deploy.js:
async function main() {
const Voting = await ethers.getContractFactory("Voting");
const votingContract = await Voting.deploy("Best Crypto?", ["Bitcoin", "Ethereum"]);
await votingContract.waitForDeployment();
console.log("Voting contract deployed to:", votingContract.target);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
执行部署命令:
npx hardhat run scripts/deploy.js --network localhost
成功后,终端会输出合约地址(如0x5FbDB2315678afecb367f032d93F642f64180aa3),此时合约已部署到Hardhat本地网络。
免责声明:本文为转载,非本网原创内容,不代表本网观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
如有疑问请发送邮件至:bangqikeconnect@gmail.com