Truffle 以太坊智能合约开发

FreeGuideOnline 12阅读 2026-07-13

bash npm install -g truffle


验证安装是否成功:

```bash
truffle version

你还需要一个以太坊客户端或模拟网络用于部署和测试。本教程推荐使用 Ganache(一款个人区块链),它内置 GUI 和 CLI 两种形式,可快速创建本地测试网。

安装 Ganache CLI:

npm install -g ganache

启动 Ganache 后,会默认在 http://127.0.0.1:7545 启动一个预分配了测试以太的私有链。

3. 创建你的第一个 Truffle 项目

通过 Truffle 初始化一个新项目,它会生成标准的目录结构和配置文件。

mkdir my-dapp
cd my-dapp
truffle init

初始化后的文件夹结构如下:

  • contracts/:存放 Solidity 合约源文件。
  • migrations/:部署脚本,控制合约如何迁移到网络。
  • test/:存放测试文件(JavaScript 或 Solidity)。
  • truffle-config.js:项目配置文件,定义网络连接、编译器等。

4. 编写智能合约

contracts/ 目录下创建一个简单的存储合约 SimpleStorage.sol

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

contract SimpleStorage {
    uint256 private storedData;

    event DataStored(uint256 data);

    function set(uint256 x) public {
        storedData = x;
        emit DataStored(x);
    }

    function get() public view returns (uint256) {
        return storedData;
    }
}

5. 编译合约

所有合约都需要编译成以太坊虚拟机可理解的字节码。Truffle 使用内置的 Solidity 编译器进行编译。

在项目根目录执行:

truffle compile

成功编译后,会生成 build/contracts/ 文件夹,其中包含合约的 ABI、字节码等 JSON 制品。Truffle 会自动处理依赖关系,只重新编译发生变化的文件。

6. 配置网络与部署(迁移)

6.1 配置 truffle-config.js

打开 truffle-config.js,取消 development 网络的注释,并确保其指向 Ganache 的地址(默认为 localhost:7545)。

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*" // 匹配任何网络 id
    }
  },
  compilers: {
    solc: {
      version: "0.8.19" // 根据你的合约版本调整
    }
  }
};

6.2 创建迁移脚本

Truffle 通过 JavaScript 脚本来指导部署过程。在 migrations/ 目录下,已有一个初始迁移 1_initial_migration.js。我们需要为 SimpleStorage 创建第二个脚本,命名为 2_deploy_simple_storage.js

const SimpleStorage = artifacts.require("SimpleStorage");

module.exports = function (deployer) {
  deployer.deploy(SimpleStorage);
};

6.3 执行部署

确保 Ganache 正在运行,然后在终端执行:

truffle migrate

如果部署成功,你会看到类似输出:

1_initial_migration.js
======================
   Deploying 'Migrations'
   ----------------------
   > transaction hash: ...
   > contract address: ...
   ...
2_deploy_simple_storage.js
==========================
   Deploying 'SimpleStorage'
   -------------------------
   > contract address: ...

部署后的合约地址会被记录在 build/contracts/SimpleStorage.json 中,方便应用调用。

6.4 重新部署与重置

  • truffle migrate --reset:忽略已有的部署记录,强制从头运行所有迁移脚本。
  • truffle migrate --network <network_name>:指定部署到哪个网络。

7. 测试智能合约

自动化测试是确保合约按预期工作的关键。Truffle 支持两种测试方式:JavaScript 和 Solidity。

7.1 使用 JavaScript + Mocha 编写测试

test/ 目录下创建 simpleStorage.test.js

const SimpleStorage = artifacts.require("SimpleStorage");

contract("SimpleStorage", (accounts) => {
  it("should store the value 89", async () => {
    const simpleStorageInstance = await SimpleStorage.deployed();
    // 设置值
    await simpleStorageInstance.set(89, { from: accounts[0] });
    // 获取值
    const storedData = await simpleStorageInstance.get.call();
    assert.equal(storedData.toString(), "89", "The value 89 was not stored.");
  });

  it("should emit an event when setting a value", async () => {
    const simpleStorageInstance = await SimpleStorage.deployed();
    const tx = await simpleStorageInstance.set(42, { from: accounts[0] });
    expect(tx.logs[0].event).to.equal("DataStored");
    expect(tx.logs[0].args.data.toString()).to.equal("42");
  });
});

运行所有测试:

truffle test

7.2 使用 Solidity 编写测试

你也可以在 test/ 下创建 .sol 文件,Truffle 会将其作为合约进行部署和测试。这种方式的优势在于可以直接使用 assertrequire,但与真实链上状态隔离,适合单元逻辑验证。

pragma solidity ^0.8.0;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/SimpleStorage.sol";

contract TestSimpleStorage {
    function testInitialValue() public {
        SimpleStorage simple = SimpleStorage(DeployedAddresses.SimpleStorage());
        uint expected = 0;
        Assert.equal(simple.get(), expected, "Initial value should be 0");
    }
}

8. 与合约交互:Truffle Console

Truffle 内置了交互式控制台,非常适合快速调试和手动测试。

truffle console

进入控制台后,你可以直接使用 JavaScript 与已部署的合约交互:

let instance = await SimpleStorage.deployed()
await instance.set(2024)
let value = await instance.get()
value.toString() // 输出 "2024"

如果你需要脚本化交互,可以在项目根目录创建 scripts/Interact.js,然后使用 truffle exec 运行:

const SimpleStorage = artifacts.require("SimpleStorage");

module.exports = async function (callback) {
  const instance = await SimpleStorage.deployed();
  await instance.set(100);
  console.log("Value:", (await instance.get()).toString());
  callback(); // 结束脚本
};

执行:truffle exec scripts/Interact.js

9. 合约升级与可升级模式

Truffle 原生不包含可升级合约支持,但可与 OpenZeppelin 升级插件结合使用。通常的升级流程涉及代理合约和实现合约的分离。

不过,原生的迁移系统已经提供了一种合约替换的方案:当你修改合约并执行新的迁移脚本时,Truffle 会部署一个全新实例。旧合约的状态依旧保留在链上,但你的应用逻辑需要切换到新合约地址。对于简单的开发学习,这种方式已经足够。

10. 最佳实践与常见问题

  • 使用固定编译器版本:在 truffle-config.js 中明确指定 Solidity 版本,避免因编译器差异导致的问题。
  • 管理私钥安全:部署到公共测试网或主网时,不要将私钥硬编码在配置文件中。可使用 dotenv 加载环境变量,或配合 @truffle/hdwallet-provider 使用助记词。
  • Gas 优化:在迁移过程中可以设置 gasgasPrice 参数,避免交易失败。
  • 事件日志监听:在测试或 DApp 前端中,利用合约事件可以高效追踪状态变化。
  • 测试覆盖率:Truffle 可以通过插件 solidity-coverage 生成覆盖率报告,帮助发现未测试的代码分支。

11. 部署到公共测试网(Goerli/Sepolia)

当你在本地开发完成后,通常需要将合约部署到公共测试网,以便其他开发者或前端应用试用。

首先安装 hdwallet-provider

npm install @truffle/hdwallet-provider

truffle-config.js 中添加网络配置:

const HDWalletProvider = require('@truffle/hdwallet-provider');
const mnemonic = '你的助记词'; // 或使用 process.env.MNEMONIC

module.exports = {
  networks: {
    sepolia: {
      provider: () => new HDWalletProvider(mnemonic, `https://sepolia.infura.io/v3/你的项目ID`),
      network_id: 11155111, // Sepolia 网络 ID
      gas: 5500000,
      confirmations: 2,
      timeoutBlocks: 200,
      skipDryRun: true
    },
    // ... 其他网络
  }
};

随后执行:

truffle migrate --network sepolia