以太坊作为全球领先的智能合约平台,开创了去中心化应用(Dapp)的新纪元,智能合约是以太坊的灵魂,它是在以太坊区块链上运行的自执行代码,能够自动执行合约条款,无需中介机构,确保了交易的透明、安全和不可篡改,本教程将带你一步步走进以太坊智能合约的世界,从基础概念到实践开发,助你迈出构建去中心化应用的第一步。
以太坊智能合约就像一个“数字化的自动售货机”,你预先设定好规则(代码),然后向机器(合约地址)投入价值(以太币或其他代币),机器就会根据预设规则自动执行相应的操作并给出结果(发放商品或触发其他操作)。
与传统合约相比,智能合约具有以下特点:
在开始编写智能合约之前,你需要准备以下开发环境:

安装 Node.js 和 npm:
安装代码编辑器:
安装 Ganache:

安装 Truffle:
npm install -g truffle安装 MetaMask:
让我们通过一个简单的投票合约来学习 Solidity 语言的基本语法和合约结构。
创建项目目录:
my-vote-dapp cd my-vote-dapp
初始化 Truffle 项目:

truffle init
这会创建一些基本文件夹和配置文件,如 contracts/(存放智能合约代码)、migrations/(部署脚本)、test/(测试文件)等。
编写智能合约代码:
contracts/ 目录下创建一个名为 Voting.sol 的文件。// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title Voting
* @dev 一个简单的投票合约,允许对多个选项进行投票。
*/
contract Voting {
// 定义一个候选人的结构体
struct Candidate {
uint id;
string name;
uint voteCount;
}
// 存储候选人的映射,键为候选人ID,值为Candidate结构体
mapping(uint => Candidate) public candidates;
// 存储投票者地址的映射,防止重复投票
mapping(address => bool) public voters;
// 候选人数量
uint public candidatesCount;
// 事件:当有人投票时触发
event VotedEvent(uint indexed candidateId, address voter);
// 构造函数:部署合约时初始化候选人
constructor(string[] memory candidateNames) {
candidatesCount = 0;
for (uint i = 0; i < candidateNames.length; i ) {
candidates[candidatesCount] = Candidate(candidatesCount, candidateNames[i], 0);
candidatesCount ;
}
}
// 投票函数
function vote(uint _candidateId) public {
// 检查候选人ID是否有效
require(_candidateId < candidatesCount, "Invalid candidate ID");
// 检查投票者是否已经投过票
require(!voters[msg.sender], "Already voted");
// 记录投票者
voters[msg.sender] = true;
// 增加候选人的票数
candidates[_candidateId].voteCount ;
// 触发投票事件
emit VotedEvent(_candidateId, msg.sender);
}
// 获取候选人信息
function getCandidate(uint _candidateId) public view returns (uint id, string memory name, uint voteCount) {
Candidate storage candidate = candidates[_candidateId];
return (candidate.id, candidate.name, candidate.voteCount);
}
} 代码解释:
SPDX-License-Identifier 和 pragma solidity:Solidity 合约的标准开头,指定许可证版本和编译器版本。contract Voting:定义一个名为 Voting 的合约。struct Candidate:定义候选人结构体,包含ID、姓名和票数。mapping:类似于哈希表,用于存储候选人信息和投票记录。constructor:合约部署时执行的构造函数,用于初始化候选人列表。vote:核心投票函数,包含权限检查和票数更新。getCandidate:获取指定候选人信息的视图函数(不消耗gas)。编译合约:
truffle compile
build/contracts/ 目录下会生成编译后的 JSON 文件(ABI 和字节码)。编写测试用例(可选但推荐):
在 test/ 目录下创建一个 JavaScript 测试文件,test/voting.test.js:
const Voting = artifacts.require("Voting");
contract("Voting", accounts => {
it("should initialize with the correct candidate names", async () => {
const votingInstance = await Voting.deployed();
const candidate0 = await votingInstance.getCandidate(0);
const candidate1 = await votingInstance.getCandidate(1);
assert.equal(candidate0[1], "Candidate 1", "First candidate name incorrect");
assert.equal(candidate1[1], "Candidate 2", "Second candidate name incorrect");
});
it("should allow a voter to vote", async () => {
const votingInstance = await Voting.deployed();
const voter = accounts[0];
await votingInstance.vote(0, { from: voter });
const candidate0 = await votingInstance.getCandidate(0);
assert.equal(candidate0[2], "1", "Vote count not incremented");
const voterStatus = await votingInstance.voters(voter);
assert.equal(voterStatus, true, "Voter status not updated");
});
}); 运行测试:
truffle test
配置网络:
truffle-config.js(或 truffle.js)中,配置 Ganache 的本地网络:module.exports = {
networks: {
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 7545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
// 可以添加其他测试网络,如 Ropsten, Rinkeby, Goerli 等
},
compilers: {
solc: {
version: "0.8.20", // 指定 Solidity 编译器版本
}
}
}; 编写迁移脚本:
migrations/ 目录下创建一个新的迁移脚本,2_deploy_contracts.js:const Voting
免责声明:本文为转载,非本网原创内容,不代表本网观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
如有疑问请发送邮件至:bangqikeconnect@gmail.com