cryptozombies11-TronBox

第1章:介绍

如果你有前端开发的背景,那么你可能已经熟悉那些能让网页开发者的生活更简单的工具——如 Webpack、Gulp 或 Browserify。

如果你有 Solidity 的背景,并且之前已经将智能合约部署到 Ethereum,那么你可能已经熟悉使用 Truffle 或 Hardhat。

但是,什么工具可以用来将你的智能合约部署到 TRON 网络呢?

TronBox

TronBox 是一个智能合约开发平台,它允许你测试、编译并将智能合约部署到 TRON 网络。TronBox 旨在让开发者的生活更轻松,并且具备了许多有用的功能:

  • 简单的智能合约编译
  • 自动生成 ABI
  • 集成的智能合约测试
  • 支持多个网络

TronWeb

TronWeb 提供了一个无缝的开发体验,受 Ethereum 的 Web3 启发。TRON 团队从 Web3 中汲取核心思想,并在此基础上进行扩展,以解锁 TRON 区块链的功能。

假设你的计算机上已经安装了 Node.js,接下来我们将安装 TronBox 和 TronWeb,并使其全局可用。

开始使用

首先,你需要创建一个新目录并安装 TronBox 和 TronWeb。

测试一下

让我们从基础开始。在右侧的终端中,使用 mkdir 命令创建一个名为 CryptoZombies-TRON 的目录。然后,在同一行中使用 cd 命令将其设置为当前目录。

有几种方式可以在同一行中输入多个 shell 命令。为了简单起见,我们使用 &&。这确保了第二个命令只有在第一个命令成功完成后才会执行。以下示例将名为 example 的目录设置为当前目录并列出其内容:cd example && ls

安装 TronBox 并使其全局可用。输入 npm install 命令,后面加上 -g 标志和包名(tronbox)。

最后,你需要通过输入 npm install 命令并加上 -g 标志和包名(tronweb)来全局安装 TronWeb。

mkdir CryptoZombies-TRON && cd CryptoZombies-TRON
npm install -g tronbox
npm install -g tronweb

第2章:初始化你的项目

现在你已经安装了 TronBoxTronWeb,让我们通过运行 tronbox init 命令来初始化一个新项目。这个命令会创建一组文件夹和配置文件,结构如下:

1
2
3
4
5
6
7
8
9
├── contracts
├── Migrations.sol
├── migrations
├── 1_initial_migration.js
├── 2_deploy_contracts.js
└── test
sample-env
tronbox-config.js
tronbox.js

contractsmigrationstests… 这些看起来有点复杂😟

正如你在本章中将看到的,这个目录结构其实是非常直观的,并且带来了几个重要的好处。让我们来仔细看看:

  • contracts:这是 TronBox 期望找到你所有智能合约的地方。为了保持代码的组织性,你甚至可以创建嵌套的文件夹,比如 contracts/tokens
  • migrations:这个目录包含了一些 JavaScript 文件,提供了所有必要的信息和指令,供 TronBox 部署智能合约。
  • test:这是你应该放置单元测试的地方,测试文件可以是 JavaScript 或 Solidity 文件。记住,一旦合约部署到区块链上,就无法修改,所以在部署前测试你的智能合约非常重要。

现在,我们已经了解了这些目录和文件的作用,接下来让我们看看使用标准化目录结构的好处。

  • 首先,如果你更改了这些目录的名称或移动了文件,TronBox 将无法正常工作。
  • 其次,使用标准的目录结构和代码约定,可以让其他开发者更容易理解你的代码。

动手实践

为了初始化一个新项目,运行 tronbox init 命令。

为了确保 tronbox init 命令确实创建了上述所有的文件和文件夹,可以运行 tree 命令。它将以树形结构打印出你的目录结构。

问题一: solidity开发中,migrate是什么意思,是部署合约的意思吗

Solidity 开发 中,migrate 确实是与 部署合约 密切相关的一个概念,但它不仅仅是简单的部署合约的意思。我们通常会在 TruffleTronBox 等框架中看到 migrate 操作,它代表着将智能合约从本地环境部署到区块链网络上的过程,并且通常涉及多个步骤,如迁移、初始化和更新合约。

migrate 的含义

  1. 迁移合约

    • 在 Truffle 或 TronBox 中,migrate 是一种将智能合约 从开发环境部署到区块链 上的操作。它不仅仅是将合约上传到区块链,还包括了合约部署的步骤,比如设置合约的初始状态、进行相关的资金分配等。

    • 这个过程可以理解为 合约的迁移,即将智能合约的代码从本地环境迁移到区块链网络(无论是测试网还是主网)。

  2. 合约部署的自动化

    • migrate 是通过 迁移脚本(例如 1_initial_migration.js2_deploy_contracts.js)自动完成的,这些脚本包含了如何将智能合约部署到指定网络的步骤。
    • 在 Truffle 中,迁移脚本 记录了每次合约部署的历史,并确保合约不会被重复部署,这对多次更新和升级智能合约非常重要。
  3. 初始化合约状态

    • 部署智能合约时,migrate 不仅仅是上传合约代码,还通常会对合约进行初始化操作,例如设置合约的初始参数、初始化合约的状态变量等。
    • 例如,部署一个 ZombieFactory 合约时,migrate 脚本可能会用来设定初始的合约状态,如创建初始的僵尸。

migrate 操作中的步骤

  1. 编译合约
    在执行 migrate 之前,通常会先编译合约。编译合约是将 Solidity 源代码转化为字节码和 ABI,确保智能合约可以在区块链上运行。

  2. 部署合约
    执行 migrate 命令时,Truffle 或 TronBox 会将编译后的合约字节码上传到区块链,并且执行 迁移脚本,根据脚本中的指令部署合约。

  3. 记录部署信息
    migrate 操作还会记录部署过程中的信息,比如合约地址、部署的区块信息等。这些信息通常保存在一个数据库或者文件中,以确保每次迁移都不会重复部署相同的合约,并且能够跟踪历史版本。

  4. 合约初始化
    一些智能合约需要在部署后进行初始化操作,例如设定初始的状态变量、合约地址等,migrate 操作也会处理这些初始化步骤。

例子:Truffle 中的 migrate

Truffle 中,migrate 操作通常会执行以下步骤:

  1. 编译合约

    1
    truffle compile
  2. 执行迁移

    1
    truffle migrate

    这将部署所有合约,并执行迁移脚本。如果是第一次部署,它会将所有的合约部署到网络中。如果是更新部署,它会根据迁移脚本更新已经部署的合约。

  3. 特定网络部署
    你也可以将合约部署到特定的网络,比如以太坊主网、测试网或本地网络:

    1
    truffle migrate --network ropsten

    这会将合约部署到 Ropsten 测试网,并执行相关的迁移脚本。

总结

  • migrate 是智能合约的 部署和迁移 过程,通常包括部署合约、初始化合约状态、以及管理合约的版本和历史记录。
  • 它并不仅仅是简单的部署合约,而是包括了合约的初始化和历史记录跟踪。
  • TruffleTronBox 等开发框架中,迁移脚本 记录了每次部署的步骤,确保合约在不同网络环境中的迁移过程是自动化的、可重复的。

第3章:迁移智能合约

太棒了,你刚刚初始化了你的新项目!在本章中,你将学习如何迁移你的智能合约,以便将它们部署到 TRON 网络。尽管 TRON 区块链与 EVM 兼容,但在将智能合约从以太坊迁移到 TRON 时,有一个小的区别需要注意——TRON 区块链的本地货币是 TRX

目前你所创建的智能合约使用的是 ETH,即以太坊区块链的本地货币,因此你需要从使用 ETH 单位改为使用 TRX

幸运的是,你只需要更新 ZombieHelper 智能合约中的一行代码。

动手实践

我们已经将你迄今为止编写的所有智能合约复制到 ./contracts 文件夹中。

ZombieHelper 智能合约的第一行声明了一个名为 levelUpFeeuint 变量,并将其值设置为 0.001 ether。将这个值替换为 26 trx

注意: 当你真正编译和部署智能合约时,可以根据需要选择合适的值。然而,由于我们的答案检查器非常基础,它只会接受这个答案作为正确答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

uint levelUpFee = 26 trx;

modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}

function withdraw() external onlyOwner {
address payable _owner = address(uint160(owner()));
_owner.transfer(address(this).balance);
}

function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}

function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level = zombies[_zombieId].level.add(1);
}

function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) onlyOwnerOf(_zombieId) {
zombies[_zombieId].name = _newName;
}

function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) onlyOwnerOf(_zombieId) {
zombies[_zombieId].dna = _newDna;
}

function getZombiesByOwner(address _owner) external view returns(uint[] memory) {
uint[] memory result = new uint[](ownerZombieCount[_owner]);
uint counter = 0;
for (uint i = 0; i < zombies.length; i++) {
if (zombieToOwner[i] == _owner) {
result[counter] = i;
counter++;
}
}
return result;
}

}

第4章:编译源代码

现在你已经将智能合约迁移到 TRON 网络,接下来你将学习如何编译它们。

你问,为什么我们需要编译呢?

TRON 虚拟机不能直接运行你编写的 Solidity 源代码。因此,你必须使用一个编译器,将你的源代码“翻译”成机器可读的字节码。虚拟机然后执行这些字节码,并完成你的智能合约所要求的操作。

当你编译智能合约时,TronBox 会为每个合约在 ./build/contracts/ 目录下创建一个单独的 JSON 文件。这些文件包含了 TronBox 部署智能合约所需的所有信息:ABI、字节码和编译器版本。请注意,JSON 文件的名称并不反映源文件的名称,而是智能合约的名称。

这些文件统称为 “artifacts”(工件)。它们是 TronBox 内部工作的重要组成部分,并在智能合约成功部署中起着关键作用。不要编辑这些文件,否则 TronBox 可能会停止正常工作。

说了这么多,接下来让我们好好利用编译器的力量吧!

测试一下

在编译智能合约之前,确保你正在使用 Solidity 编译器的 0.5.18 版本。为此,打开 tronbox.js 文件并在文本编辑器中修改 compilers 部分,设置为如下内容:

1
2
3
4
5
compilers: {
solc: {
version: '0.5.18'
}
}
  1. 现在你准备好编译智能合约了。在右侧的框中,输入 tronbox compile 命令。

  2. 接下来,确保构建工件已创建,通过输入 tree ./build/contracts 命令来查看输出。它应该看起来像这样:

1
2
3
4
5
6
7
8
9
10
./build/contracts
├── ERC721.json
├── Migrations.json
├── Ownable.json
├── SafeMath.json
├── ZombieAttack.json
├── ZombieFactory.json
├── ZombieFeeding.json
├── ZombieHelper.json
└── ZombieOwnership.json

第5章:账户和私钥

太棒了!你已经成功编译了智能合约。

不过,在你能够部署合约之前,还有一些事情需要完成。

TRON 区块链由多个账户组成,你可以把它们想象成像银行账户一样。每个账户有一个 TRX(TRON 区块链的货币)余额,你可以像银行账户之间进行电汇转账一样,发送和接收 TRX 付款。每个 TRON 区块链上的账户都有一个地址,这是一个唯一的标识符,指向该账户。

我们不会深入探讨地址的细节,但目前你只需要理解,一个地址是由特定用户(或智能合约)拥有的,并且用户通过一种叫做 私钥 的东西来证明他们的身份。

TRON 使用公钥/私钥对来数字签名交易。你发送的任何交易都必须由与你的账户关联的私钥签名,而公钥可以从签名中推导出来,并与账户进行匹配,从而确保没有人可以伪造你的账户的交易。

加密学很复杂,因此除非你是加密学专家,否则应该使用经过充分测试和审查的加密库,而不是自己编写。

幸运的是,TronWeb 库提供了一个函数,可以通过几步简单的操作创建一个私钥😊。你只需要做以下几件事:

  1. 创建一个 JavaScript 源文件。

  2. 导入你已经安装的 TronWeb 模块。在 JavaScript 中,导入模块的语法如下:

    1
    const myModule = require('mymodule')
  3. 调用 TronWebutils.accounts.generateAccount() 函数,并将其输出打印到控制台。

动手实践

我们已经创建了一个空文件,名为 ./scripts/generate-private-key.js。现在让我们填写它。

  1. 第一行应该声明一个名为 tronWeb 的常量,并将其设置为 require('tronweb')
  2. 第二行应该调用 console.log 函数,打印 tronWeb.utils.accounts.generateAccount() 函数返回的值。

答案:

1
2
const tronWeb = require('tronweb')
console.log(tronWeb.utils.accounts.generateAccount())

第6章:生成私钥

在本章中,我们将向你展示如何生成私钥,并将其安全地传递给 TronBox 作为环境变量。

但在开始之前,我们先来了解一下测试网络的概念。

测试网络概念

简而言之,测试网络是开发者用来在类似生产环境中测试智能合约的网络。

多个 TRON 测试网络允许你在将合约部署到主网之前免费进行测试。这些测试网络使用与主网不同的共识算法,并且 TRX 是免费的,以鼓励充分的测试。

👉🏻 虽然测试非常重要,但它需要专门的一课来讲解——因此,我们在本章中只会集中讲解部署部分。

在本章中,你将使用 Shasta 网络。

tronbox.js 配置文件

TronBox 将其配置保存在一个名为 tronbox.js 的文件中。让我们看一下文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const port = process.env.HOST_PORT || 9090

module.exports = {
networks: {
mainnet: {
// 不要在这里放私钥:
privateKey: process.env.PRIVATE_KEY_MAINNET,
/*
创建一个 .env 文件(它必须被 git 忽略),并包含如下内容:

export PRIVATE_KEY_MAINNET=4E7FECCB71207B867C495B51A9758B104B1D4422088A87F4978BE64636656243

然后,通过以下命令运行迁移:

source .env && tronbox migrate --network mainnet
*/
userFeePercentage: 100,
feeLimit: 1000 * 1e6,
fullHost: '<https://api.trongrid.io>',
network_id: '1'
},
shasta: {
privateKey: process.env.PRIVATE_KEY_SHASTA,
userFeePercentage: 50,
feeLimit: 1000 * 1e6,
fullHost: '<https://api.shasta.trongrid.io>',
network_id: '2'
},
nile: {
privateKey: process.env.PRIVATE_KEY_NILE,
userFeePercentage: 100,
feeLimit: 1000 * 1e6,
fullHost: '<https://api.nileex.io>',
network_id: '3'
},
development: {
// 针对 trontools/quickstart docker 镜像
privateKey: 'da146374a75310b9666e834ee4ad0866d6f4035967bfc76217c5a495fff9f0d0',
userFeePercentage: 0,
feeLimit: 1000 * 1e6,
fullHost: '<http://127.0.0.1>:' + port,
network_id: '9'
},
compilers: {
solc: {
version: '0.5.18'
}
}
},
// solc 编译器优化
solc: {
// optimizer: {
// enabled: true,
// runs: 200
// },
// evmVersion: 'istanbul'
}
}

这个配置文件很简单:它指定了多个可以部署的网络。为了将合约部署到这些网络中的某一个,你必须设置相应的环境变量来提供私钥。在本章中,你将使用 Shasta 网络,这意味着你必须定义一个名为 PRIVATE_KEY_SHASTA 的环境变量。

通过使用环境变量来传递你的私钥给 TronBox,从安全角度来看是非常不错的,因为我们不推荐将像助记词或私钥这样的秘密存储在配置文件中,原因是配置文件经常会被推送到 GitHub 上,任何人都可以看到这些信息,导致你暴露在攻击风险中。

测试一下

  1. 通过运行 node scripts/generate-private-key.js 命令生成你的私钥。在右侧的框中,你将看到你的私钥。复制它,因为你在接下来的步骤中将需要它。请注意,由于我们终端的工作方式,你需要双击十六进制编码的字符串,然后按住鼠标按钮,同时按下 Ctrl+CCommand+C 来复制密钥。

  2. 创建一个名为 PRIVATE_KEY_SHASTA 的环境变量,并将其设置为右侧框中显示的私钥。为此,输入以下命令:

    1
    export PRIVATE_KEY_SHASTA=<YOUR_PRIVATE_KEY>

    <YOUR_PRIVATE_KEY> 替换为你复制的私钥。

注意:如果你在终端中运行此命令,你可能会在 = 前后输入或不输入空格。但是,由于我们的命令行解释器比较基础,除非你去掉空格,否则它不会认为你的答案正确。

答案:

1
2
3
node scripts/generate-private-key.js
export PRIVATE_KEY_SHASTA=<YOUR_PRIVATE_KEY>

第7章:迁移

太棒了!这部分比较复杂——实际上,将智能合约部署到 Shasta 网络以及 TRON 主网接下来的过程将变得非常简单。为了实现这一点,TronBox 依赖于迁移(migrations)。

迁移听起来可能像是涉及到很多东西在移动,但实际上,迁移不过是一个 JavaScript 文件,告诉 TronBox 如何修改智能合约的状态:

  • 第一个迁移部署了当前形式的 ZombieOwnership 智能合约。
  • 其他的迁移将部署代码的新版本,添加新功能或修复漏洞。

简而言之,迁移提供了一种便捷的方式来跟踪你对代码所做的更改。

如果你想部署多个合约,则必须为每个合约创建一个单独的迁移文件。迁移始终按顺序执行——1, 2, 3,依此类推。

总的来说,最简单的形式,迁移就是一个用 JavaScript 编写的部署脚本。

说到这,当你运行 tronbox init 命令时,TronBox 会创建两个基本的迁移文件:

  • 第一个文件(01_initial_migration.js)部署了一个名为 Migrations.sol 的特殊合约,用来追踪你对代码所做的更改。它的工作原理是将更改的历史记录保存在链上。因此,你永远不可能再次部署相同的代码。
1
2
3
4
5
var Migrations = artifacts.require("./Migrations.sol");

module.exports = function(deployer) {
deployer.deploy(Migrations);
};
  • 第二个文件(./migrations/02_deploy_contracts.js)只是一个空壳,你将在本章中使用它来部署 ZombieOwnership 智能合约。
1
2
3
4
5
// var MyContract = artifacts.require("./MyContract.sol");

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

创建迁移

为了理解迁移是如何工作的,我们来仔细看看 ./contracts/2_deploy_contracts.js 文件。

首先,注意到里面的指令是被注释掉的。这样,TronBox 确保在你编辑文件内容之前,它不会做任何事情。

第一行指定了 TronBox 将要部署的智能合约的名称——MyContract.sol

接下来,它导出一个接受一个名为 deployer 的对象作为参数的函数。这个对象作为开发者和 TronBox 部署引擎之间的接口。

我们已经将 ./contracts/2_deploy_contracts.js 文件重命名为 ./contracts/2_zombie_ownership.js。现在轮到你实践本章所学的内容了😉。

测试一下

取消注释第一行,并确保它声明一个名为 ZombieOwnershipContract 的变量,并将其设置为… 好吧,你明白了。为了简化,修改 ./contracts/2_zombie_ownership.js 文件为如下内容:

1
2
3
4
5
var ZombieOwnershipContract = artifacts.require("./ZombieOwnershipContract.sol");

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

第8章:部署到 Shasta 测试网络

在本章中,我们将把智能合约部署到 Shasta 测试网络,但在进行部署之前,需要做一些准备工作。

获取一些 TRX

确保你的账户中有足够的 TRX。获取用于测试的 TRX 最简单的方式是通过一个叫做水龙头(faucet)服务。我们推荐使用 Shasta Faucet。按照说明操作,几分钟后,你的地址将会获得一些 TRX。

测试一下

现在一切准备就绪,是时候部署到 Shasta 测试网络了。为此,在右侧的终端中运行以下命令:

1
tronbox migrate --reset --network shasta

注意迁移是按顺序执行的😉。

如果你对 --reset 标志感到好奇,它的作用是“告诉” TronBox 重新运行所有迁移。如果你没有指定此标志,并且之前的迁移已经成功执行,那么 tronbox migrate 命令只会运行新创建的迁移。

如果一切顺利,你将看到类似于右侧的输出。

第9章:部署到 TRON 主网

部署到主网其实并不复杂,但请确保先对智能合约进行充分的测试!

一旦智能合约经过测试,你只需要运行 tronbox migrate 命令,指定要迁移到主网即可。别忘了,你需要支付 gas 费用!我们相信你能够顺利完成。

测试一下

要将合约部署到 TRON 主网,请在右侧的框中输入以下命令:

1
tronbox migrate --network mainnet