今天需要尝试部署一个智能合约(Smart Contract),使用的是 Fuel 平台。本文将不探讨 Fuel 的底层逻辑,注重在从 0 配置环境到将智能合约部署到测试链上的过程和心得。

使用的开发环境是:

  • Ubuntu 22.04(在 Parallel Desktop 上运行)
  • Mac OS M2 Chip

前期准备

首先需要安装几个必备工具件,截至我写这篇文章的时候,他们的版本号如下所示:

  1. Rust - 1.75.0 (82e1608df 2023-12-21)
  2. Cargo - 1.75.0 (1d8b05cdd 2023-11-20)
  3. Fuel 工具链 - 0.20.0
  4. Cargo Generate(后续测试用) - 0.19.0

安装 Rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装 Fuel Toolchain

curl --proto '=https' --tlsv1.2 -sSf https://install.fuel.network/fuelup-init.sh | sh

安装 Cargo Generate

Cargo Generate 是为了后续我们需要测试我们写的代码,因为智能合约更像是一种后端,而且是没有终端输出(Console)的后端系统(至少目前我的认知是这样)。所以我们需要编写测试样例,来确保我们写的智能合约能够顺利的执行。

我们在终端中执行以下的指令来安装 cargo-generate。

cargo install cargo-generate

当然,在安装 cargo generate 的过程中我们遇到了很多问题,因此我也写了一篇文章 [How-To Fix] Error while installing cargo-generate 来解决该问题,可以移步阅读。

创建 Sway 项目

你们可以根据自己的理解去探索整个流程,自主探索是创新的必要条件

在你自己常用的工作目录下创建一个目录(这是因为我们后面如果需要构建前端界面的话,一些指令需要相对路径,别担心,后续我们会表明出来可以自己 DIY 的点),然后进入该目录:

mkdir fuel-project # fuel-project 可以改成您自己想要的名称
cd fuel-project

然后创建一个智能合约项目:

forc new counter-project # counter-project 可以改成您自己想要的项目名称,这里是因为接下来我们需要创建一个计数器智能合约,所以姑且叫这个名字

这时候会发现我们的项目目录中会出现几个文件,主要是 main.swForc.toml。其中,Forc.toml 相当于 NodeJS 中的 package.json 文件,用于管理我们的项目的依赖。main.sw 是我们主要的智能合约源码。

编写智能合约

打开 main.sw 删除里面出现的所有代码然后复制粘贴以下代码。这个代码将实现一个计数器,这个计数器的数据将会保存在链上,而整个区块链就是我们的数据库。是的,这听起来超级酷!别担心,我们将会在这个示例代码中详细的写出整个程序的代码及注释。

contract; // 声明我们写的代码是一份智能合约

// 创建一个 storage,用来保存我们的计数器。
storage {
// 定义一个 计数器
// 数据类型是 u64(等效于 unsigned int-64)
// 初始化 0 给该变量
counter: u64 = 0,
}

// 创建一个 ABI,理解成一个 Interface
// ABI 用来定义我们的 Contract 中有多少个函数
abi Counter {
// fn 用来定义一个函数,这里我们定义一个 increment(增量)函数。
#[storage(read, write)]
fn increment();

// 定义一个 count 函数,返回一个 u64 的值
#[storage(read)]
fn count() -> u64;
}

// 我们刚刚定义了各种函数在 ABI 中,现在将其实现,也就是写函数
// 这里能理解为,我们将 Counter 以 Contract 的形式实现
impl Counter for Contract {
// 首先实现读取计数器的函数
// 我们需要照抄 storage 的权限、函数名以及返回值
#[storage(read)]
fn count() -> u64 {
// 这个就是读取 storage 中 counter 的值
storage.counter.read();
}
}

知识点:

  • ABI 可以理解为一个接口(Interface),它应该被保存在一个独立的文件中,这里为了简易起见,我们将其直接写在我们的源码中。
  • #storage[(read, write)] 这里暂时不明白 storage 的作用,姑且觉得是可以从 storage 访问数据
    • read 和 write 用于设定我们对 storage 的操作权限
  • storage.counter.read(); 其实是一个隐式返回值,return 关键字被隐式编写了,可以省略 return 关键字

构建智能合约

请确保你现在是处于你的项目目录下的。然后运行以下指令来构建我们的智能合约

forc build

这时候项目中会自动创建 Forc.lock 和 构建好的位于 ./out/debug/ 下的可执行(在区块链中执行)文件。

如果在这个过程中出现问题,多半是因为上方的代码有问题,需要进行调整。请发邮件和报错信息给我知道。或者参考官方文档 进行调整。

测试智能合约

前面我们已经下载好了测试工具 cargo-generate 接下来运行这段指令以创建测试文件:

cargo generate --init fuellabs/sway templates/sway-test-rs --name counter-contract # counter-contract 

执行完成后,会在 test/ 目录下创建一个 harness.rs 文件,这是我们的自动化测试代码。在代码的末端添加以下代码,用于测试我们的计数器

#[tokio::test]
async fn test_increment() {
let (instance, _id) = get_contract_instance().await;

// 增加计数器
instance.methods().increment().call().await.unwrap();

// 获取当前计数器的数值
let result = instance.methods().count().call().await.unwrap();

// 断言当前数值为 1
// 因为我们在 storage 创建的计数器数值初始值为 0,计数过程为 + 1
assert_eq!(result.value, 1);
}

接下来在终端执行以下指令进行测试。

如果顺利的话,底下会出现类似 test ... ok 的字段。如果在这个过程中

部署合约

根据官方文档说明,我们也可以通过 Rust SDK 或 TypeScript SDK 进行操作,这里我们选择和官方相同的 forc。其他方法请参考官方文档进行学习。

创建 / 导入钱包

如果你们已经拥有钱包,可以直接通过以下指令进行钱包导入,然后通过助记词(mnemonic)来导入钱包。

forc wallet import

如果尚未拥有电子钱包,可以通过以下指令来创建一个新钱包。

forc wallet new

创建 / 刷新钱包账号

在创建 / 导入钱包之后,会发现钱包是空的(就算你是导入钱包,通过 forc wallet accounts 也无法列出你所拥有的钱包账户。这是因为 forc wallet 会将钱包缓存在本地中,而导入钱包并不会刷新本地缓存。这时候我们需要执行以下指令来创建 / 刷新钱包账号

forc wallet account new

获取测试代币

faucet 领取测试代笔,输入你的钱包地址,就可以获得 0.5 ETH 进行测试

发布到测试网

最后一步了!我们可以通过以下指令将合约部署到测试网上,在这之前我们需要配置 fuelup 工具链,使其默认使用 beta-4 作为工具链

fuelup toolchain install beta-4
fuelup default beta-4

# 发布智能合约
forc deploy --testnet

执行完这个指令之后,会出现 Contract deploy-to-beta-4 Deployed! 字眼。接下来就能去区块链上查看自己的合约是否部署成功啦!鉴于官方给出的 Network Explorer 还是很烂,所以建议直接用该 Explorer 查询自己的钱包是否有交易记录

Network Explorer 链接点此

嗯是的,那个 Explorer 烂的很,只显示一个钱包最早的 10 条记录,没错!是最早的……

总结和不足

在开发过程中,经常会出现很多“玄学”的报错,代码完全相同,但就是在 cargo test 的过程中总是出错。以下是几个根据实际情况判断的可能发生的问题,还未经验证:

  1. 由于我们开发环境是运行于 MacOS 上的 Ubuntu 虚拟机,而我们的计算机架构是 ARM64,可能在构建的时候会出现问题。
  2. 国内网络环境问题
  3. 开发者技术问题,Programmers are also a human and can make mistake。程序员也是人,也会犯错。可能是 Fuel 开发者的问题,期待后续的修复。

后续需要再尝试几次构建,然后才能更加熟练的去编程及部署智能合约。不过基于现在还是在 Testnet 测试网的过程,所以不建议花费太长时间专研某种语言。更重要的是了解整个区块链的开发技术。