3、智能合约基础:Solidity语言入门、Remix IDE使用、智能合约编译与部署、与DAO相关的合约模式

好,咱们进入正题。这一章是DAO开发的地基。说白了,不懂智能合约,后面所有关于DAO的治理、投票、金库管理都是空中楼阁。我个人习惯把这一章叫做「从零到一写合约」,因为我会带你亲手写一个能跑的合约,再把它部署到链上。

你想想看,DAO本质上就是一组智能合约的集合。所以,咱们得先把单个合约玩明白。

3.1 Solidity语言入门:别怕,它没那么玄乎

Solidity是啥?它是以太坊上写智能合约的主流语言。语法上跟JavaScript有点像,但又有自己的脾气。我第一次接触时也觉得别扭,尤其是那个address类型和msg.sender,当时老搞混。

咱们先看一个最简单的合约骨架:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleDAO {
    // 状态变量
    address public owner;
    uint256 public proposalCount;

    // 构造函数
    constructor() {
        owner = msg.sender;
    }

    // 修饰符
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    // 事件
    event ProposalCreated(uint256 indexed id);

    // 函数
    function createProposal() public onlyOwner {
        proposalCount++;
        emit ProposalCreated(proposalCount);
    }
}

这里有几个关键点,我重点说一下:

  • 状态变量:存在链上的数据,永久保存。比如ownerproposalCount
  • 构造函数:部署时只执行一次。我习惯在这里初始化管理员地址。
  • 修饰符onlyOwner这种,用来做权限控制。DAO里到处都是这玩意儿。
  • 事件:链上日志,前端可以监听。嗯,调试时特别好用。
我的小技巧: 写合约时,先把事件定义好。我在项目中吃过亏,合约写完了才发现忘了加事件,又得重新部署。事件就像程序的「呼吸」,一定要有。

3.2 Remix IDE使用:浏览器里的开发利器

别急着装什么VSCode插件。刚开始,我强烈建议你用Remix IDE。它就是个网页版IDE,打开浏览器就能用。地址是 remix.ethereum.org,你搜一下就有。

为什么推荐它?因为零配置。你不需要装Node.js、不需要配Hardhat,直接写代码、编译、部署一条龙。我记得我第一次在Remix上部署合约时,点一下按钮就成功了,那种感觉——嗯,很爽。

基本操作流程:

  1. 左侧文件浏览器,新建一个.sol文件。
  2. 把上面的代码粘贴进去。
  3. 切换到「Solidity Compiler」标签,点编译。
  4. 切换到「Deploy & Run Transactions」标签,选环境(比如JavaScript VM,这是本地模拟链)。
  5. 点「Deploy」,搞定。
注意: 用JavaScript VM部署的合约,刷新页面就没了。它只适合测试逻辑。真要上链,得选「Injected Provider」连MetaMask。

3.3 智能合约编译与部署:从字节码到链上

编译,就是把Solidity代码变成字节码。Remix里点一下编译按钮,底下会出现ABI和Bytecode。ABI是接口描述,前端调用合约时需要它。Bytecode就是部署到链上的二进制代码。

部署时,你会遇到一个概念叫Gas。每次操作都要付燃料费。我刚开始总是不理解,为什么改个数字也要花钱?后来想通了——你是在占用全球计算机的资源,当然要付费。

部署流程其实就三步:

步骤 操作 说明
1 编译 生成ABI和Bytecode
2 签名 用私钥签署交易
3 广播 发送到网络,等待确认

在Remix里,这些步骤被简化了。你只需要点一下「Deploy」,剩下的它帮你搞定。但你要理解背后发生了什么——你的钱包签名了一笔交易,矿工把它打包进区块,然后你的合约就有了一个地址。

核心概念: 合约地址一旦生成,不可更改。所以部署前一定要仔细检查代码。我曾经因为少写了一个require,导致合约上线后被人钻了空子。嗯,那晚我睡得特别晚。

3.4 与DAO相关的合约模式:投票、治理、金库

好,前面都是基础。现在咱们聊聊DAO里常用的合约模式。说白了,DAO就是一群人通过合约做决策。那合约得支持哪些功能?

3.4.1 投票模式

最简单的投票,就是每个人投一票,按票数决定。但DAO里通常用代币加权投票——你持有的代币越多,投票权重越大。

核心代码大概长这样:

contract SimpleVote {
    mapping(address => uint256) public votes;
    uint256 public totalVotes;

    function vote(uint256 _amount) public {
        // 假设用户有代币,这里简化处理
        votes[msg.sender] += _amount;
        totalVotes += _amount;
    }

    function getVotePower(address _voter) public view returns (uint256) {
        return votes[_voter];
    }
}

当然,真实项目里不会这么简单。你会用到ERC20Votes这种标准实现,它内置了委托投票和快照功能。我建议你直接继承OpenZeppelin的合约,别自己造轮子。

3.4.2 治理模式

治理就是提案的创建、投票、执行流程。典型的模式叫Compound Governor。它把治理拆成几个阶段:

  • 提案创建:任何人(或满足条件的人)可以发起提案。
  • 投票期:持币人投票,通常持续几天。
  • 执行期:投票通过后,等待一段时间才能执行。
  • 执行:调用目标合约的函数。

为什么要等执行期?为了防止恶意提案被立即执行。嗯,这是安全设计上的一个经典考量。

3.4.3 金库模式

DAO的钱,通常放在一个多签钱包时间锁合约里。时间锁的意思是,任何操作都要延迟一段时间才能执行。比如,治理投票通过了,但资金要等48小时后才能转出。

我参与过一个项目,金库用的是Gnosis Safe。它支持多签——比如5个多签人里,3个人同意才能转账。这种模式在DAO里非常常见。

避坑指南: 我曾经把一个时间锁的延迟设成了1秒,觉得反正有投票把关。结果被攻击者利用闪电贷,在1秒内完成了提案和执行。后来我把延迟改成了24小时。记住:时间锁的延迟,至少得大于一个区块确认时间,建议24小时以上。

3.5 小结:动手写一个迷你DAO合约

说了这么多,不如动手试试。我建议你打开Remix,写一个包含以下功能的合约:

  1. 一个owner,部署时设置。
  2. 一个propose函数,只有owner能调用。
  3. 一个vote函数,任何人都能投票(每人一票)。
  4. 一个execute函数,投票通过后执行。

别怕写错。写合约就像学骑车,摔几次就熟了。我第一次写的投票合约,连require都没加,结果一个人能投一万次。嗯,那是我最难忘的一次debug。

下一章,咱们会深入讲ERC20代币与DAO的关系。你会看到,代币不只是用来炒的,它还是DAO里的投票权。准备好了吗?