确保安装好了 Truffle Framework 和 Ganache CLI.
$ sudo npm install -g truffle
$ sudo npm install -g ganache-cli
// SPDX-License-Identifier: MIT
pragma solidity > 0.4.21;
contract Storage {
mapping (string => string) private _store;
function addData(string memory key, string memory value) public {
require(bytes(_store[key]).length == 0);
_store[key] = value;
}
function removeData(string memory key) public returns (string memory) {
require(bytes(_store[key]).length != 0);
string memory prev = _store[key];
delete _store[key];
return prev;
}
function changeData(string memory key, string memory newValue) public {
require(bytes(_store[key]).length != 0);
_store[key] = newValue;
}
}
solidity 0.5.0版本更新后,string后面要加上memory,否则编译会报错。
你可以能已经看到了在运行 truffle init
时生成的 Migrations.sol
和 1_initial_migration.js
。
初始迁移合约一般不需要修改,他们是跟踪部署在区块链上的地址。当然也可以按照自己的需要修改 Migrations.sol
合约文件,进行一些高级的迁移管理,但需要保留truffle init
命令创建的接口。
1_initial_migration.js
迁移文件,仅仅是说明如何把 Migrations.sol
合约部署到对应的链上。
1_initial_migration.js
迁移文件名,前面的序号,代表着 truffle migrate
运行迁移文件的顺序,1
表示第一个运行的迁移文件(从 1 开始)。 我们可以创建其他的迁移文件: 2_mycontract_migration.js ,在每个合约部署完成,Truffle 会把迁移序号保存到 Migrations 合约的 last_completed_migration
假设 migrations
就这两个迁移文件,truffle migrate
运行时实际会发生 4 笔交易:
运行 1_initial_migration.js
进行部署
把序号 1 写入到合约 Migrations
运行 2_mycontract_migration.js
进行部署
把序号 2 写入到合约Migrations
last_completed_migration
表示的是最后部署的迁移,之后再加入其它的迁移文件:3_yourcontract.js
时, 运行truffle migrate
时 Truffle 会首先读取 last_completed_migration
状态变量,参看之前部署到了哪些,再部署比 last_completed_migration
序号大的(所有)迁移文件,这样就可以保证不会重复部署。
注意,如果修改一个已有的合约,需要重新部署的话,直接运行 truffle migrate
是不会自动部署的,需要新加(或修改)一个更高序号的迁移文件,再运行 truffle migrate
。
truffle migrate
可以接一个 -f
序号 来强制从一个序号开始执行迁移(此时会忽略 last_completed_migration的值
)。 例如: truffle migrate -f 2
会从第 2 个迁移文件开始部署。
有了这些知识,让我们写下我们的第一个迁移文件 2_deploy_contracts.js, 代码如下:
// 获取对应的合约文件
var Storage = artifacts.require("./Storage.sol");
// JavaScript export
module.exports = function(deployer) {
// deployer 是用来部署
// 部署
deployer.deploy(Storage);
}
编写迁移就这么简单。
为了运行迁移脚本,请在终端中运行以下命令:
truffle migrate
不过这时候,会得到一个错误:
Error: No network specified. Cannot determine current network.
意思是 Truffle 找不到要部署到的网络,这时可以在命令行终端打开一个新的 tab 运行 ganache-cli
来启动一个模拟的测试区块链,启动后,有会类似输出:
/TruffleTest$ ganache-cli
Ganache CLI v6.12.2 (ganache-core: 2.13.2)
Available Accounts
==================
(0) 0x36eC0bC604780Da01da74fdc21062090342422F0 (100 ETH)
(1) 0x5df8Ff0fC05Fa2d0eDE36a14c22958652b5e2396 (100 ETH)
(2) 0xEB20b6e8844A32c50F1149Df54f6AAC55754b248 (100 ETH)
(3) 0x8Bb575F18d54dc570449E14A3A453555628e597F (100 ETH)
(4) 0xf29Bae551395fd286345518262A197950Ffb3903 (100 ETH)
(5) 0xEfd420BB71682725fCEf27a48D88dfc04d20c171 (100 ETH)
(6) 0xC07026932a7C29C3505Ec28baf2c372177849f8a (100 ETH)
(7) 0x4d219b1490B21849BFdADC071B18E761863B9AB1 (100 ETH)
(8) 0xF33e7CeaA8825c90cAFb332EA3A0448fFEE242E7 (100 ETH)
(9) 0xd0Ffeb7caA16025e713EabF1761a9bb431406322 (100 ETH)
Private Keys
==================
(0) 0x627e7ad2299bf54679de34746714d4559b7dd463d4c869d37a540d8f0f00fba4
(1) 0x0fb839122b11abe05bb8783578a74d0a1ac11ea314b200050b3e394ab3e6119c
(2) 0x51a4f0d22d1ecad211a580944152f03b8ea7fdf13b5e22def7fbec17bb0750bb
(3) 0x8d95d6d02882d591beddaa46a8f59910db588b7ef3c5cc43ee58dd9b67fc93ac
(4) 0x9dd25620de22ce96616aa933066a2f240f09819beed9cb611e0004034fc61bff
(5) 0x5e4d89ce7885cb5f645eee49a8b844c29edb4447b0e7bfa9449583a412e8aa06
(6) 0x79d5edde2f9cf0c2e0d572cfc74ee2b6b2efe4883bcb9d095f27bfd72cc106c2
(7) 0xd9c62a643dc16531a4e22c50491af696f30a029422d9170cd2776ac6f8303621
(8) 0x9e0d415bb50dbc5f095a001f0a642bc069560cbc37b229b0d2960c90719dc664
(9) 0xb214a0f61c14119b47decdfb59124cb2f6ffe33960ac2af08b45a52da6e766bf
HD Wallet
==================
Mnemonic: entire exhaust spoon client ladder used harsh hobby cabbage swing cave where
Base HD Path: m/44'/60'/0'/0/{account_index}
Gas Price
==================
20000000000
Gas Limit
==================
6721975
Call Gas Limit
==================
9007199254740991
Listening on 127.0.0.1:8545
现在已经建立了一个私有区块链,它运行在localhost:8545
上。
现在让我们配置 Truffle 以便部署合约到该网络。
打开 truffle-config.js
文件,加入以下内容:
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*"
}
}
};
上面配置的含义是把编译的合约部署到 localhost:8545
所在的网络上。
现在运行 truffle migrate
, 得到以下输出:
/TruffleTest$ truffle migrate
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'development'
> Network id: 1628492486749
> Block gas limit: 6721975 (0x6691b7)
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0x8a16df1dde1da9624236320f31505e1149b1401ea3229347f830fe074690f8d7
> Blocks: 0 Seconds: 0
> contract address: 0xeaFd135174544015f10edCC4C38494f2967EC72D
> block number: 1
> block timestamp: 1628492566
> account: 0x36eC0bC604780Da01da74fdc21062090342422F0
> balance: 99.99616114
> gas used: 191943 (0x2edc7)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00383886 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00383886 ETH
2_deploy_contracts.js
=====================
Deploying 'Storage'
-------------------
> transaction hash: 0x6c34134d752cf425413d27fe1d102a05afb0e684655f6d5826cf4efa60648202
> Blocks: 0 Seconds: 0
> contract address: 0x7c91Ba1Fa7395B94909021Fe186fc172F0b1a71D
> block number: 3
> block timestamp: 1628492566
> account: 0x36eC0bC604780Da01da74fdc21062090342422F0
> balance: 99.98386434
> gas used: 572502 (0x8bc56)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.01145004 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.01145004 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.0152889 ETH
可以看到
truffle migrate
部署合约时使用的账户是ganache-cli
预置的10个账户的第一个账户:0x36eC0bC604780Da01da74fdc21062090342422F0
Truffle 将合约迁移到网络并保存了构件(artifacts)。 在构建目录(build/contracts/)的 Storage.json文件中,通过检查 networks 对象来检查它是否正确。 您应该看到类似以下内容:
"networks": {
"1628492486749": {
"events": {},
"links": {},
"address": "0x7c91Ba1Fa7395B94909021Fe186fc172F0b1a71D",
"transactionHash": "0x6c34134d752cf425413d27fe1d102a05afb0e684655f6d5826cf4efa60648202"
}
},
1628492486749
是网络的 ID,以太坊网络主网和测试网都有固定的 ID(主网是 1)。
address
是部署合约的地址。
transactionHash
是部署合约的交易hash。
在后面会看到如何使用。
Truffle 迁移的真正亮点是能进行多个合约的编译、部署和跟踪(几乎所有区块链项目都是这样)。
迁移文件不仅允许我们使用单个命令部署多个合约,还允许我们调用合约的函数,如获取这些函数的返回值并将其传递给后续合约。
我们在 contracts
目录下,添加一个新合约 InfoManager.sol
, 代码如下:
pragma solidity >0.4.21;
import "./Storage.sol";
contract InfoManager {
Storage private _dataStore;
uint private _lastAdded;
constructor(Storage dataStore) public {
_dataStore = dataStore;
}
function addData(string memory key, string memory value) public {
require((now - 1 days) > _lastAdded);
_dataStore.addData(key, value);
}
}
可以看出,InfoManager
依赖 Storage
合约,不仅如此,InfoManager
的构造函数还需要 Storage
合约作为参数。
重新修改一下 2_deploy_contracts
.js 让它可以完成 InfoManager
的部署:
var Storage = artifacts.require("./Storage.sol");
var InfoManager = artifacts.require("./InfoManager.sol");
module.exports = function(deployer) {
// 部署 Storage
deployer.deploy(Storage)
// 等待、直到合约部署完成
.then(() => Storage.deployed())
// 传递 Storage 合约地址,部署 InfoManager 合约
.then(() => deployer.deploy(InfoManager, Storage.address));
}
部署的语法是:
...
deployer.deploy(`ContractName`, [`constructor params`]) // 返回一个 promise
...
因为 deploy(...)
返回一个 promise, 我们可以按自己喜欢的方式处理它,不过要注意的是在部署文件里,还不支持 async
。
我们还可以在部署合约后自定义调用函数。例如,迁移还可以这样:
deployer.deploy(Storage)
.then(() => Storage.deployed())
.then((instance) => {
instance.addData("Hello", "world")
}).then(() => deployer.deploy(InfoManager, Storage.address));
这样在部署 InfoManager
之前,先给Storage
添加一条数据。
这个技巧很有用,因为有时相互依赖的合约可能需要在构造函数的之外写入。
还可以根据所处的网络不同,进行不同的部署。这对于在开发阶段使用模拟链的数据而在主网上线时使用已部署的主网合约作为输入参数到合约中都非常有用。
通过导出函数 module.exports
扩展一个参数 network
:
module.exports = function(deployer, network) {
if (network == "live") {
// do one thing
} else if (network == "development") {
// do other thing
}
}
module.exports
默认函数还可以公开一个账号参数,这些账号是以太坊节点或钱包provider “暴露” 出来的,看看下面的例子:
module.exports = function(deployer, network, accounts) {
var defaultAccount;
if (network == "live") {
defaultAccount = accounts[0]
} else {
defaultAccount = accounts[1]
}
}
你还可以通过 deployer.link(...)
连接已经存在(部署)的库:
...
deployer.deploy(MyLibrary);
deployer.link(MyLibrary, MyContract);
deployer.deploy(MyContract);
...
通过使用上面的这些技术,可以把大部分区块链部署工作自动化,并减少开发去中心化应用程序涉及的大量重复工作。
相关内容:
TypeError: Data location must be “memory“ for parameter in function, but none was given.