以太坊作为全球领先的区块链平台,其核心魅力在于智能合约——一种运行在区块链上、自动执行合约条款的计算机程序,它允许开发者在去中心化的环境中构建各种应用,如去中心化金融(DeFi)、非同质化代币(NFT)、去中心化自治组织(DAO)等,本文将通过一个简单而实用的实例,带您一步步了解以太坊智能合约的开发流程。

开发环境准备

在开始编写智能合约之前,我们需要准备以下开发环境:

  1. Node.js 和 npm:JavaScript 运行时环境和包管理器,从 Node.js 官网 下载并安装 LTS 版本。
  2. Truffle Suite:一套流行的以太坊开发框架,包括 Truffle(开发环境、测试框架和构建工具)、Ganache(个人区块链,用于本地测试)和 Drizzle(与前端交互的库)。
    • 安装:npm install -g truffle
  3. MetaMask:一款浏览器插件钱包,用于与以太坊网络交互(测试网和主网),从 MetaMask 官网 下载并安装。
  4. 代码编辑器:如 VS Code,并安装 Solidity 插件以提供语法高亮和智能提示。

智能合约实例:一个简单的投票合约

我们将开发一个简单的投票合约,允许创建投票提案,并对提案进行投票,合约功能包括:

  • 创建者可以创建新的投票提案。
  • 地址可以投票,且每个地址只能投一次票。
  • 查询提案的描述、得票数以及投票状态。

创建项目结构

  1. 创建一个新的项目文件夹,voting-dapp
  2. 在终端中进入该文件夹,并初始化 Truffle 项目:
    truffle init

    这会创建以下标准目录结构:

    • contracts/:存放 Solidity 智能合约文件。
    • migrations/:存放部署脚本文件。
    • test/:存放测试脚本文件。
    • truffle-config.js:Truffle 配置文件。

编写智能合约

  1. contracts 目录下创建一个新的 Solidity 文件,命名为 Voting.sol
  2. 编写合约代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Voting {
    // 定义提案结构体
    struct Proposal {
        string description; // 描述
        uint voteCount;    // 得票数
    }
    // 存储提案的映射,键为提案名称,值为 Proposal 结构体
    mapping(string => Proposal) public proposals;
    // 存储已投票地址的集合,防止重复投票
    mapping(address => bool) public voters;
    // 提案名称数组
    string[] public proposalNames;
    // 投票是否已结束
    bool public votingEnded;
    // 事件:当新提案创建时触发
    event ProposalCreated(string proposalName);
    // 事件:当有人投票时触发
    event Voted(address voter, string proposalName);
    // 创建提案的函数,仅限合约创建者调用
    function createProposal(string memory _proposalName) public {
        require(!votingEnded, "Voting has ended");
        require(bytes(proposals[_proposalName].description).length == 0, "Proposal already exists");
        proposals[_proposalName] = Proposal({
            description: _proposalName,
            voteCount: 0
        });
        proposalNames.push(_proposalName);
        emit ProposalCreated(_proposalName);
    }
    // 投票函数
    function vote(string memory _proposalName) public {
        require(!votingEnded, "Voting has ended");
        require(bytes(proposals[_proposalName].description).length != 0, "Proposal does not exist");
        require(!voters[msg.sender], "You have already voted");
        voters[msg.sender] = true;
        proposals[_proposalName].voteCount++;
        emit Voted(msg.sender, _proposalName);
    }
    // 结束投票函数,仅限合约创建者调用
    function endVoting() public {
        // 在实际应用中,可能需要添加更复杂的权限控制,比如只有特定角色可以结束投票
        // 这里为了简化,假设任何调用者都可以结束,或者可以添加一个owner变量
        require(!votingEnded, "Voting has already ended");
        votingEnded = true;
    }
    // 重新开始投票函数(可选)
    function restartVoting() public {
        // 同样,这里简化权限控制
        require(votingEnded, "Voting has not ended yet");
        for (uint i = 0; i < proposalNames.length; i++) {
            proposals[proposalNames[i]].voteCount = 0;
        }
        delete voters;
        votingEnded = false;
    }
    // 获取所有提案名称
    function getProposalNames() public view returns (string[] memory) {
        return proposalNames;
    }
    // 获取特定提案的得票数
    function getVoteCount(string memory _proposalName) public view returns (uint) {
        return proposals[_proposalName].voteCount;
    }
}

代码解释:

  • SPDX-License-Identifierpragma solidity:Solidity 合约的标准开头,指定许可证和编译器版本。
  • struct Proposal:定义提案的数据结构,包含描述和得票数。
  • mapping(string => Proposal) public proposals随机配图