You are on page 1of 42

TRUFFLE 使用教程

Truffle为以太坊提供了一个世界一流的开发环境、测试框架和资产管道(pipeline),旨在使以太
坊开发更容易。 使用Truffle,你会得到:

● 内置智能合约编译,链接,部署和二进制字节码管理。
● 针对快速迭代开发的自动化合约测试。
● 可脚本化,可扩展的部署和迁移框架。
● 网络管理,用于部署到任意数量的公共和私有网络。
● 使用ERC190标准,使用EthPM和NPM进行包安装管理。
● 用于直接合约通信的交互式控制台。
● 支持持续集成的可配置构建管道。
● 外部脚本运行程序可以在Truffle环境中执行脚本。

Truffle入门

安装

命令
npm install -g truffle

环境要求

● NodeJS 5.0+
● Windows,Linux,或Mac OS X

Truffle需要以太坊客户端支持,客户端支持几乎所有标准的JSON RPC API。对于开发来说,


有很多版本可供选择,有些更合适开发。 我们将在下一节中详细讨论它们。

Windows用户的建议
如果你是Windows用户,我们推荐你使用Powershell或Git BASH来安装和使用Truffle框架。
这两个shell环境提供了比标注命令行更方便的一些特性。
如果你必须使用命令行,可以看​这里​的关于如何配置Truffle的讨论。

选择一个以太坊客户端
有许多的以太坊客户端可以选择。我们推荐在开发和部署时使用不同客户端。

当开发时
● EtherumJS TestRPC

当开发基于Truffle的应用时,我们推荐使用​EthereumJS TestRPC​。它是一个完整的
运行在内存中的区块链,仅仅存在于你开发设备上。它在执行交易时是实时返回的,
而不等待默认的出块时间,这样你可以快速验证你新写的代码,当出现错误时,也能
即时反馈给你。它同时还是一个支持自动化测试的功能强大的客户端。Truffle充分利
用它的特性,能将测试运行时间提速近90%。

当部署到实际网络时
● Geth (go-ethereum)
● WebThree(cpp-ethereum)
● More

对此有许多官方和非官方的以太坊客户端可供选择。最好使用TestRPC客户端充分测
试后,再使用这些客户端。这些是完整的客户端实现,包括挖矿,网络,区块及交易
的处理,Truffle可以在不需要额外配置的情况下发布到这些客户端。

当部署到私有网络时

私有网络使用相同的技术,但具有不同的配置。 因此,您可以配置上述任何
Ethereum客户端运行私有网络,并以完全相同的方式进行部署。

创建一个项目

创建工程目录
在任何你想放你的工程的地方创建目录:
$mkdir myproject

初始化你的工程
接下来,通过下面的命令初始化一个Truffle工程:
$cd myproject
$truffle init

完成后,你将拥有如下目录:
● contract/ - Truffle默认的合约文件存放地址。
● migrations/ - 存放部署脚本文件.
● test/ - 用来测试应用和合约的测试文件
● truffle.js - Truffle的配置文件

默认工程:METACOIN
truffle init会默认创建一个构建在以太坊内的代币demo应用。我们可以使用这个工程来进行快
速的学习,或者你也可以删除这些文件来创建一个你自己的工程。

高级初始化
默认情况下,truffle init为您创建一个简单的项目,以便您熟悉基于Solidity的智能合约的编写
,编译和部署。 但是,如果您想在构建基于以太坊的Web应用程序时标记一个刺,我们建议
您改用以下命令:
$ truffle init webpack

这将下载并安装此​项目​的副本,该项目集成了Truffle和Webpack,为您提供Webpack的Web
开发能力和Truffle的以太坊开发。

这个包是即将发布的第一个官方支持的版本。 请留意后续进展!  

编译合约

合约位置
所有合约应该位于​./contracts​目录。目录中默认有一个Solidity合约文件的例子和一个
Solidity库文件的例子,均以​.sol​结尾作为示例。尽管库文件和合约有点区别,但为简
单起见,当前均称之为合约。

命令
要编译您的合约,简单的运行:
truffle compile
Truffle默认只编译自上次编译后被修改过的文件,减少不必要的编译。如果你想编译全部文件
,可以使用--compile-all选项。
truffle compile --compile-all

编译目录
编译的输出位于​./build/contracts​目录。如果目录不存在会自动创建。这些编译文件
对于Truffle框架能否正常工作至关重要。你不应该在正常的编译或发布以外手动修改
这些文件,因为所有修改都会合约编译和部署覆盖。

依赖
你可以通过使用​import​来声明依赖。Truffle将会按正确顺序依次编译合约,并把所有
的依赖发给编译器。依赖可以通过两种方式指定:

1. 通过文件名导入依赖

要从单独的文件导入合约,只需编写以下命令,其中AnotherContact.sol是和当前合同
相关的合约文件。 这将使得AnotherContract.sol中的所有合约都可用于当前源文件。

import​ ​"./AnotherContract.sol"​;
Solidity还允许其他导入语法。 有关更多信息,请参阅其导入文档。

2. 从外部包中导入合约

Truffle支持依赖包安装,工具是​EthPM​或NPM,使用下面的语法从依赖包中导入合约
,其中​somepackage​代表一个通过NPM或ethPM安装的包,​/SomeContract.sol​代表那个包
提供的Solidity源文件路径。

import​ ​"somepackage/SomeContract.sol"​;
请注意,Truffle在搜索从NPM安装的软件包之前,将首先从EthPM中搜索安装的软件包,如
果命名冲突,将使用通过EthPM安装的软件包。
有关如何使用Truffle的软件包管理功能的更多信息,请分别参见Truffle的NPM和EthPM文档。

运行迁移合约

迁移是由一些Javascript文件组成,用来协助发布合约到以太坊网络。这些文件负责对部署任
务进行分阶段,这些文件支持部署需求随时间可变。 随着你的项目的发展,可以创建新的迁
移脚本更新合约到区块链上。迁移的历史记录是通过一个特殊的Migrations合约实现的,在下
面会详细介绍。

命令
执行迁移,使用下述命令:
truffle migrate
这个命令会执行所有的位于migrations目录内的迁移脚本。如果你之前的迁移是成功执行的。
truffle migrate仅会执行新创建的迁移。如果没有新的迁移脚本,这个命令不同执行任何操
作。可以使用选项--reset来重新执行全部迁移脚本。对于本地测试环境,请确保在运行迁移之
前先安装TestRPC并运行。

迁移脚本文件
一个样例文件如下:
文件名:4_example_migration.js
var​ MyContract = artifacts.require(​"MyContract"​);
module​.exports = ​function​(deployer) {
​// deployment steps
deployer.deploy(MyContract);
};
需要注意的是文件名以数字开头,一个描述性的后缀结尾。数字前缀是必须的,用于记录移植
是否成功。后缀仅是为了提高可读性,以方便理解。
1. ARTIFACTS.REQUIRE()
在迁移开始时,我们告诉Truffle我们要通过artifacts.require()方法与哪些合约进行交互。 这
种方法类似于Node的require,但在我们的例子中,它特意返回一个合约抽象,我们可以在我
们的部署脚本的其余部分中使用它。 指定的名称不必与您的合约源文件的文件名相匹配。 相
反,它应该匹配该源文件中的合约定义的名称。在同一个源文件中指定两个合约的示例:
文件名:​./contracts/Contracts.sol​:

contract ContractOne {
​// ...
}

contract ContractTwo {
​// ...
}
如果只使用​ContractTwo​,​artifacts.require()​只需要如下:
var​ ContractTwo = artifacts.require(​"ContractTwo"​);
2. MODULE.EXPORTS

所有迁移必须通过​module.exports​语法导出一个函数。 每个迁移导出的函数都应该接受一个
deployer​对象作为其第一个参数。 该对象在部署过程中实现为部署智能合约提供了明确的语
法,也为执行部署提供更一般的功能,例如保存已部署的合约以备后面使用。 ​deployer​对象
是用于分段部署任务的主要接口,其API在本页底部进行了说明。

您的迁移功能也可以接受其他参数。 参见下面的例子。

初始移植
Truffle需要一个移植合约来实现移植功能。这个合约必须包括指定的接口,但你可以随便修改
合约。对大多数工程来说,这个合约会在第一次迁移时进行的第一次部署,后续都不会再更
新。通过truffle init创建一个全新工程时,你会获得一个默认的合约。
文件名:contracts/Migration.sol
contract Migrations {
address public owner;
​// A function with the signature `last_completed_migration()`,
returning a uint, is required.
uint public last_completed_migration;
modifier restricted() {
​if​ (msg.sender == owner) _
}
​function​ ​Migrations​() {
owner = msg.sender;
}
​// A function with the signature `setCompleted(uint)` is required.
​function​ ​setCompleted​(uint completed) ​restricted​ {
last_completed_migration = completed;
}
​function​ ​upgrade​(address new_address) ​restricted​ {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
你必须在你第一次部署自己的合约时,部署这个合约。可以使用如下方式来创建一次迁移。
文件名:migrations/1_initial_migrations.js
module​.exports = ​function​(deployer) {
​// Deploy the Migrations contract as our only task
deployer.deploy(Migrations);
};
由此,你可以接着创建递增的数字前缀来部署其它合约。
部署器(deployer)
你的迁移文件会使用部署器来分阶段部署任务。所以,你可以按一定顺序排列发布任务,他们
会按正确顺序执行。
// Stage deploying A before B
deployer.deploy(A);
deployer.deploy(B);
或者,部署器上的每个函数Promise使用。将部署任务做成一个队列,依赖于前一个合约的执
行情况。
// Deploy A, then deploy B, passing in A's newly deployed address
deployer.deploy(A).then(​function​() {
​return​ deployer.deploy(B, A.address);
});
如果你觉得这样清晰,你也可以选择实现一个Promise链。关于部署的API,在后面进行说
明。

网络相关
可以根据发布到的网络的具体情况进行不同的部署流程。这是一个高级特性,你先看​网络​的相
关内容后,再继续。
要实现不同条件的不同部署步骤,迁移代码中需要第二个参数network。示例如下:

module​.exports = ​function​(deployer, network) {


​// Add demo data if we're not deploying to the live network.
​if​ (network != ​"live"​) {
deployer.exec(​"add_demo_data.js"​);
}else{
// different steps
}
}

可用账号
迁移也通过以太坊客户端和web3提供者返回一个帐户列表,供您在部署期间使用。 和
web3.eth.getAccounts()返回的帐户列表完全相同。

部署器API
部署器有许多的可用函数,用来简化部署流程。
1. DEPLOYER.DEPLOY(CONTRACT, ARGS...)
部署一个指定的合约,第一参数是合约对象,后面是一些可选的构造器参数。
这个函数适用于单例合约,它只会在你的dapp中创建一个这个合约的实例(单例)。函数会在部
署后设置合约的地址(如:​Contract.address​ 将等于新的部署地址),它将会覆盖之前存储的
地址。
你也可以传入一个合约数组,或数组的数组来加速多合约的部署。
请注意,在调用​deploy​之前,您将需要首先部署和链接您的合同依赖的任何库。 有关详细信
息,请参阅下面的链接功能。
例子:
// 部署单个合约,不带参数
deployer.deploy(A);

// 部署单个合约,带参数
deployer.deploy(A, arg1, arg2, ...);

// 部署多个合约,一些带参数,一些不带参数.
// 比写3次 `deployer.deploy()` 语句更快, 就像部署器执行单个合约的批量执行.
deployer.deploy([
[A, arg1, arg2, ...],
B,
[C, arg1]
]);

// 外部依赖的例子:
//
// 该例中, 我们的依赖有一个地址 当部署到实时网络时, 不能用于开发网络和测试网络.
// 当部署实时网络时,我们希望用那个地址, 但在测试和开发网络中,我们需要部署一个
自己的版本. 我们可以简单的使用 `overwrite` 字段,而不是写一堆条件.
deployer.deploy(SomeDependency, {overwrite: ​false​});
2. DEPLOYER.LINK(LIBRARY, DESTINATIONS)
联接到一个已经发布的库到一个或多个合约。​destinations​可以是一个合约或多个合约组成
的一个数组。如果目标合约并不依赖这个库,部署器会忽略掉这个合约。
// Deploy library LibA, then link LibA to contract B, then deploy B.
deployer.deploy(LibA);
deployer.link(LibA, B);
deployer.deploy(B);

// Link LibA to many contracts


deployer.link(LibA, [B, C, D]);
3. DEPLOYER.THEN(FUNCTION() {...})
就像Promise一样,运行任意的部署步骤。 在迁移过程中使用此功能来调用特定的合约函数来
添加,编辑和重组合约数据。
例子:
var​ a, b;
deployer.then(​function​() {
​// Create a new version of A
​return​ A.new();
}).then(​function​(instance) {
a = instance;
​// Get the deployed instance of B
​ eturn​ B.deployed():
r
}).then(​function​(instance) {
b = instance;
​// Set the new instance of A's address on B via B's setA() function.
​return​ b.setA(a.address);
});

测试你的合约

框架
Truffle标配了一个自动化测试框架,使您的合同测试变得轻而易举。 该框架允许您以两种不
同的方式编写简单易管理的测试:

● 在Javascript中,像执行应用一样从外界执行合约。
● 在Solidity中,在裸机场景下执行合约。
两种风格的测试都有其优缺点。接下来的两章将分别讨论。

位置
所有的测试文件应置于./tests目录。Truffle只会运行以.js,.es,.es6,.sol和.jsx结尾的测试文
件,其它的都会被忽略。

命令
运行所有的测试,执行:
$ truffle test

或者指定路径
$ truffle test ./path/to/test/file.js

干净的环境
运行测试文件时,Truffle提供了一个干净的环境。 当对TestRPC运行测试时,Truffle将使用
TestRPC的高级快照功能来确保测试文件不会彼此共享状态。 当与其他以太坊客户端一起运
行时,在执行每个测试文件之前,Truffle都会重新部署所有迁移,以确保您有一套新的合约进
行测试。

速度和可靠性考虑
在运行自动化测试时,EthereumJS TestRPC比其他客户端要快得多。 此外,TestRPC还包
含特殊功能,Truffle把测试运行时(runtime)加速了近90%。 作为一般工作流程,我们建议您
在正常开发和测试期间使用TestRPC,然后在准备部署到生产或生产网络的同时,在
go-etheteum客户端或其他官方客户端上再测试一次。

用JavaScript编写测试用例

Truffle使用Mocha测试框架和Chai的断言机制来为您提供一个坚实的框架,在该框架中编写
Javascript测试。 让我们来看看Truffle如何建立在Mocha之上,以便让您的合同测试变得轻而
易举。
注意:如果您不熟悉Mocha单元测试,请继续阅读​Mocha文档​。

使用CONSTRACT() 替代 DESCRIBE()
结构上,您的测试应该与Mocha的测试基本保持不变:您的测试应该存在于./test目录中,以.
js扩展名结尾,并且包含Mocha能够自动识别并运行的代码。 Truffle测试与Mocha测试不同之
处在于contract()函数:该函数的作用与describe()完全相同,除了它能够使用Truffle的干净环
境(clean-room)功能。它的工作原理如下:
● 在运行每个contract()函数之前,您的合约将重新部署到运行的以太坊客户端,以便其
中的测试以干净的合约状态运行。
● contract()函数提供您以太坊客户端提供的可用于编写测试的帐户列表。
由于Truffle基于Mocha,所以每当Truffle干净环境功能不必要时,仍然可以使用describe()
运行正常的Mocha测试。

在测试中使用合约抽象
合约抽象是和Javascript进行合约交互的基础。因为Truffle无法检测您在测试中需要与哪些合
约进行交互,您需要明确指定这些合约。您可以通过使用artifacts.require()方法(由Truffle提
供的方法)来执行此操作,该方法允许您为特定的Solidity合约请求可用的合约抽象。如下面
的示例所示,您可以使用此抽象来确保您的合约正常工作。

有关使用合约抽象的更多信息,请参阅下面的合约交互章节。
使用ARTIFACTS.REQUIRE()
在测试中使用artifacts.require()与在迁移中使用它们完全相同。有关详细用法,请参阅迁移合
约部分中的artifacts.require()文档。

例子
这是一个由truffle init提供给您的示例测试。请注意使用contract()函数,用于指定可用的以太
坊帐户的帐户数组,以及我们使用artifacts.require()直接与我们的合约进行交互。

文件:./test/metacoin.js
// Specifically request an abstraction for MetaCoin
var​ MetaCoin = artifacts.require(​"MetaCoin"​);
contract(​'MetaCoin'​, ​function​(accounts) {
it(​"should put 10000 MetaCoin in the first account"​, f ​ unction​() {
​return​ MetaCoin.deployed().then(​function​(instance) {
​return​ instance.getBalance.call(accounts[0]);
}).then(​function​(balance) {
assert.equal(balance.valueOf(), 10000, ​"10000 wasn't in the first
account"​);
});
});
it(​"should send coin correctly"​, ​function​() {
​var​ meta;

​// Get initial balances of first and second account.


​var​ account_one = accounts[0];
​var​ account_two = accounts[1];

​var​ account_one_starting_balance;
​var​ account_two_starting_balance;
​var​ account_one_ending_balance;
​var​ account_two_ending_balance;

​var​ amount = 10;

​return​ MetaCoin.deployed().then(​function​(instance) {
meta = instance;
​return​ meta.getBalance.call(account_one);
}).then(​function​(balance) {
account_one_starting_balance = balance.toNumber();
​return​ meta.getBalance.call(account_two);
}).then(​function​(balance) {
account_two_starting_balance = balance.toNumber();
​return​ meta.sendCoin(account_two, amount, {from: account_one});
}).then(​function​() {
​return​ meta.getBalance.call(account_one);
}).then(​function​(balance) {
account_one_ending_balance = balance.toNumber();
​return​ meta.getBalance.call(account_two);
}).then(​function​(balance) {
account_two_ending_balance = balance.toNumber();

assert.equal(account_one_ending_balance,
account_one_starting_balance - amount, ​"Amount wasn't correctly taken
from the sender"​);
assert.equal(account_two_ending_balance,
account_two_starting_balance + amount, ​"Amount wasn't correctly sent to
the receiver"​);
});
});
});

测试输出如下:

Using network ​'development'​.

Contract: MetaCoin
✓ should put 10000 MetaCoin ​in​ the first account
✓ should send coin correctly

2 passing (113ms)

高级

Truffle可以查看Mocha的配置,所以你可以修改Mocha的行为,详细请看工程配置章节。

用Solidity编写测试用例

Solidity测试合约与JavaScript测试用例一起作为.sol文件。 当Truffle测试运行时,它们将作为
单独的测试套件包含在每个测试合约中。 这些合约保留了JavaScript测试的所有好处:即每个
测试套件的干净环境,直接访问您部署的合约,以及导入任何合约依赖关系的能力。 除了这
些功能之外,Truffle的Solidity测试框架还加入了以下建议:

● Solidity测试不应延伸至任何合约(如Test合约)。 这使您的测试尽可能的最小化,并
且可以完全控制您编写的合约。
● 任何断言库都不应该考虑Solidity测试。 Truffle为您提供了默认断言库,但您可以随时
更改此库,以满足您的需要。
● 您应该可以对任何以太坊客户端运行您的Solidity测试。

例子

看一个Solidity测试的例子,是truffle init 命令自动生成的:


​ truffle/Assert.sol"​;
import​ "
import​ "​ truffle/DeployedAddresses.sol"​;
import​ " ​ ../contracts/MetaCoin.sol"​;
contract TestMetacoin {
​function​ ​testInitialBalanceUsingDeployedContract​() {
MetaCoin meta = MetaCoin(DeployedAddresses.MetaCoin());

uint expected = 10000;

Assert.equal(meta.getBalance(tx.origin), expected, ​"Owner should


have 10000 MetaCoin initially"​);
}

​function​ ​testInitialBalanceWithNewMetaCoin​() {
MetaCoin meta = ​new​ MetaCoin();

uint expected = 10000;

Assert.equal(meta.getBalance(tx.origin), expected, ​"Owner should


have 10000 MetaCoin initially"​);
}
}

输出如下:

$ truffle test
Compiling ConvertLib.sol...
Compiling MetaCoin.sol...
Compiling truffle/Assert.sol
Compiling truffle/DeployedAddresses.sol
Compiling ../test/TestMetacoin.sol...

TestMetacoin
✓ testInitialBalanceUsingDeployedContract (61ms)
✓ testInitialBalanceWithNewMetaCoin (69ms)

2 passing (3s)

测试结构

为了更好的理解测试内部的流程, 来看一下更多的细节。
断言
断言功能如Assert.equal(),由Truffle/Assert.sol库提供。 这是默认的断言库,但是您可以包括
自己的断言库,只要库能够触发正确的断言事件,松散地与Truffle的测试运行器集成在一起。
您可以在Assert.sol中找到所有可用的断言功能。

部署地址
您部署的合约的地址(即部署的合约,作为迁移的一部分)可通过Truffle/
DeployedAddresses.sol库获得。 这是由Truffle提供,并在每个套件运行之前重新编译和重新
链接,以提供您使用Truffle干净环境的测试。 该库为所有部署的合约提供了函数,格式如下

DeployedAddresses.<contract name>();
这将返回一个地址,然后您可以使用该地址来访问该合约。有关使用情况,请参阅上面的示例
测试。

为了使用部署的合约,您必须将合约代码导入到测试套件中。代码:import
“../contracts/MetaCoin.sol”;在例子中。此导入与./test目录中存在的测试合约相关,并且它将
在测试目录之外,以便找到MetaCoin合约。然后使用该合约将地址转换为MetaCoin类型。

测试合约名称
所有测试合约必须以测试开始,使用大写字母T.该合约将测试辅助者和项目合约(即被测合
约)区分开来,让测试者知道哪些合约代表测试套件。

测试功能名称
与测试合同名称一样,所有测试功能必须以小写test开始。每个测试函数都作为一个单独的交
易执行,按照测试文件的出现顺序(就像你的Javascript测试)一样。断言函数由Truffle/
Assert.sol提供触发事件,测试运行者进行评估以确定测试结果。断言函数返回一个布尔值,
表示断言的结果,您可以使用它来从早期测试中返回,以防止执行错误(即TestRPC将暴露
的错误)。

测试钩子之前/之后
有许多测试钩子,如下面的示例所示。这些钩子是beforeAll,beforeEach,afterAll和
afterEach之后,它们和由Mocha在Javascript测试中提供的钩子相同。您可以使用这些挂钩在
每个测试之前和之后执行设置和拆卸操作,或在每个套件运行之前和之后执行。像测试功能一
样,每个钩子都是作为单个交易执行的。请注意,一些复杂的测试将需要执行大量的设置,可
能会溢出单个事务的gas限制; 您可以通过创建许多具有不同后缀的钩子来解决这个限制,如
下例所示:
import​ ​"truffle/Assert.sol"​;

contract TestHooks {
uint someValue;

​function​ ​beforeEach​() {
someValue = 5;
}

​function​ ​beforeEachAgain​() {
someValue += 1;
}

​function​ ​testSomeValueIsSix​() {
uint expected = 6;

Assert.equal(someValue, expected, ​"someValue should have been 6"​);


}
}

该测试合同还显示您的测试功能和钩子功能,具有相同的合约状态。您可以在测试之前设置合
约数据,在测试期间使用该数据,然后重置它以准备下一个数据。请注意,就像您的
Javascript测试一样,您的下一个测试功能将继续之前运行的测试功能的状态。

高级功能
Solidity测试带有一些高级功能,让您可以测试Solidity中的特定用例。

异常抛出测试
您可以轻松测试您的合约是否应该throw(即,对于使用throw的合约来表示预期的错误)。这
个话题首先由客座作家Simon de la Rouviere撰写,题目是他在“​Truffle Solidity测试中的throw
测试​”教程。

测试以太币交易
您还可以测试您的合约对收到以太币的反应,并在Solidity中对该交互进行脚本化。为此,您
的Solidity测试应该具有返回一个名为initialBalance的uint的公共函数。这可以直接写为函数或
公共变量,如下所示。当测试合约部署到网络中时,truffle将从您的测试帐户将指定数量的以
太币发送到您的测试合约。然后,您的测试合约在测试中就可以使用以太币在合约内用脚本进
行以太币交互。请注意,initialBalance是可选的,不是必须的。
​ truffle/Assert.sol"​;
import​ "
import​ "​ truffle/DeployedAddresses.sol"​;
import​ " ​ ../contracts/MyContract.sol"​;

contract TestContract {
​// Truffle will send the TestContract one Ether after deploying the
contract.
public uint initialBalance = 1 ether;

​function​ ​testInitialBalanceUsingDeployedContract​() {
MyContract myContract = MyContract(DeployedAddresses.MyContract());

​// perform an action which sends value to myContract, then assert.


myContract.send(...);
}
​function​ () {
​// This will NOT be executed when Ether is sent. \o/
}
}

请注意,Truffle是以不执行回退功能的方式将以太币发送到您的测试合约,因此您仍然可以使
用Solidity测试中的回退函数进行高级测试。

合约交互

介绍
如果您自己编写以太坊网络的原始请求和您的合同交互,您很快就会意识到编写这些请求是笨
重和繁琐的。同样,您可能会发现,管理每个请求的状态也很复杂。幸运的是,Truffle为您处
理这种复杂性,使您与合约交互变得轻而易举。

数据读写
以太坊网络区分了将数据写入网络和从中读取数据,这种区别在编写应用程序方面起着重要的
作用。一般来说,写入数据称为交易(transaction),而读取数据称为调用(call)。交易和调用的
处理方式截然不同,具有以下特点。

交易
交易从根本上改变了网络的状态。一个交易可以像发送以太币到另一个帐户一样简单,也可以
像执行合约功能一样复杂,或者向网络创建新合约一样复杂。交易的字面定义是一个交易可以
写入(或更改)数据。交易花费以太币运行,被称为“gas”,交易需要时间处理。当您通过交
易执行合约函数时,您不能接收该函数的返回值,因为交易不会立即处理。通常,通过交易执
行的函数将不会返回值;他们将返回一个交易ID。所以总结一下,交易是:

● 花费gas(以太币)
● 更改网络的状态
● 不立即处理
● 不会暴露一个返回值(只有一个交易ID)。

CALLS
另一方面,调用则完全不一样。调用可以用于在网络上执行代码,尽管不会永久更改数据。调
用可以自由运行,它们的定义特征是它们读取数据。当您通过调用执行合约函数时,您将立即
收到返回值。总而言之,调用是:

● 是免费的(不要花费gas)
● 不会更改网络的状态
● 会被立即处理
● 会暴露一个返回值(hooray!)
选择与决定使用交易还是调用,依据很简单:要读取数据还是写入数据。

抽象介绍
合约抽象是Javascript和以太坊合约交互的面包和黄油。简而言之,合约抽象是一层代码封装
,可以轻松地与合约进行交互,从而让您不必关心合约调用细节。Truffle通过Truffle抽象模块
使用自己的合约抽象,下面描述了这个合约抽象。

为了介绍合约抽象的有用性,我们以truffle init生成的MetaCoin合约为例进行介绍。
pragma solidity ^0.4.2;

import​ ​"./ConvertLib.sol"​;

// This is just a simple example of a coin-like contract.


// It is not standards compatible and cannot be expected to talk to
other
// coin/token contracts. If you want to create a standards-compliant
// token, see: https://github.com/ConsenSys/Tokens. Cheers!

contract MetaCoin {
mapping (address => uint) balances;

event Transfer(address indexed _from, address indexed _to, uint256


_value);

​function​ ​MetaCoin​() {
balances[tx.origin] = 10000;
}

​ unction​ ​sendCoin​(address receiver, uint amount) ​returns​(bool


f
sufficient) {
​if​ (balances[msg.sender] < amount) ​return​ ​false​;
balances[msg.sender] -= amount;
balances[receiver] += amount;
Transfer(msg.sender, receiver, amount);
​return​ ​true​;
}

​function​ ​getBalanceInEth​(address addr) ​returns​(uint){


​return​ ConvertLib.convert(getBalance(addr),2);
}

​function​ ​getBalance​(address addr) ​returns​(uint) {


​return​ balances[addr];
}
}

这个合约提供了 3钟方法​sendCoin​, ​getBalanceInEth​, 和 ​getBalance​,都可以通过一个交


易或调用来执行。
现在看一下Truffle提供的JavaScript对象MetaCoin,在Truffle控制台上生成:
/ Print the deployed version ​of​ MetaCoin.
// Note that getting the deployed version requires a promise, hence the
.then.
MetaCoin.deployed().then(​function​(instance) {
​console​.log(instance);
});

// outputs:
//
// Contract
// - address: "0xa9f441a487754e6b27ba044a5a8eb2eec77f6b92"
// - allEvents: ()
// - getBalance: ()
// - getBalanceInEth: ()
// - sendCoin: ()
// ...
这个抽象包括和合约综合完全相同的函数,还包括一个地址,指向已部署的MetaCoin合约。

执行合约函数
你可以使用抽象在以太坊网络上很容易执行合约函数。

执行一笔交易
我们可以执行MetaCoin合约的三个函数。 如果您分析每一个,您将看到sendCoin是唯一旨在
更改网络的函数。 sendCoin的目标是将一些Meta硬币从一个帐户“发送到下一个”,这些更改
应该永久保存。

调用sendCoin时,我们将其作为交易执行。 在以下示例中,我们将从一个帐户向另一个帐户
发送10 Meta硬币,以一种持续更改网络的方式:
​ 0x1234..."​; /
var​ account_one = " ​ / an address
var​ account_two = "​ 0xabcd..."​; /​ / another address

var​ meta;
MetaCoin.deployed().then(​function​(instance) {
meta = instance;
​return​ meta.sendCoin(account_two, 10, {from: account_one});
}).then(​function​(result) {
​ / If this callback is called, the transaction was successfully
/
processed.
alert(​"Transaction successful!"​)
}).catch(​function​(e) {
​// There was an error! Handle it.
})

以上代码有一些有趣的事情:

● 我们直接调用了抽象的sendCoin函数。这将导致默认情况下的交易(即写入数据)而
不是调用。
● 当交易成功时,在处理交易之前,回调函数不会被触发。这使得处理变得简单,意味
着您不必自己检查交易的状态。
● 我们将一个对象作为第三个参数传递给sendCoin。请注意,我们的Solidity合同中的
sendCoin函数没有第三个参数。您上面看到的是一个特殊对象,​可以随时作为最后一
个参数传递给一个函数​,让您可以编辑有关交易的具体细节。在这里,我们设置from
地址确保此事务来自account_one。

执行一个调用
继续使用MetaCoin,注意getBalance函数是从网络中读取数据的绝佳选择。它不需要进行任
何更改,因为它只是返回传递给它的地址的MetaCoin余额。让我们来看一下:
var​ account_one = ​"0x1234..."​; ​// an address

var​ meta;
MetaCoin.deployed().then(​function​(instance) {
meta = instance;
​return​ meta.getBalance.call(account_one, {from: account_one});
}).then(​function​(balance) {
​// If this callback is called, the call was successfully executed.
​// Note that this returns immediately without any waiting.
​// Let's print the return value.
​console​.log(balance.toNumber());
}).catch(​function​(e) {
​// There was an error! Handle it.
})
这里有什么好处

我们必须明确地执行.call()函数,让Ethereum网络知道我们不打算持续任何更改。
我们收到一个返回值,而不是成功的交易ID。 请注意,由于以太坊网络可以处理非常大的数
字,因此我们将获得一个BigNumber对象,然后将其转换为一个数字。
警告​:我们将返回值转换为数字,因为在这个例子中数字很小。 但是,如果您尝试转换比
Javascript支持的最大整数还要大的BigNumber,则可能会遇到错误或意外行为。

捕获事件
您的合约可以触发事件,事件允许被捕获,可以更好得观察您的合同正在做什么。 处理事件
的最简单的方法是处理触发事件的交易结果对象,如下所示:
​ 0x1234..."​; /
var​ account_one = " ​ / an address
var​ account_two = "​ 0xabcd..."​; /​ / another address

var​ meta;
MetaCoin.deployed().then(​function​(instance) {
meta = instance;
​return​ meta.sendCoin(account_two, 10, {from: account_one});
}).then(​function​(result) {
​// result is an object with the following values:
​//
​// result.tx => transaction hash, string
​// result.logs => array of decoded events that were triggered
within this transaction
​// result.receipt => transaction receipt object, which includes gas
used

​ / We can loop through result.logs to see if we triggered the Transfer


/
event.
​for​ (​var​ i = 0; i < result.logs.length; i++) {
​var​ log = result.logs[i];

​if​ (log.event == ​"Transfer"​) {


​// We found the event!
​break​;
}
}
}).catch(​function​(err) {
​// There was an error! Handle it.
});

添加一个新合约到网络
上面的所有情况,我们都使用一个已部署好的合约抽象,现在我们部署自己的合约,使用​.
new()​函数:
MetaCoin.new().then(​function​(instance) {
​// Print the new address
​console​.log(instance.address);
}).catch(​function​(err) {
​// There was an error! Handle it.
});
指定账户地址使用合约
如果你已经有一个合约的地址,你可以在那个地址上创建一个新的抽象,代表那个合约。
var​ instance = MetaCoin.at(​"0x1234..."​);

发送以太币到合约
您可能只想将以太币直接发送给合约,或触发合约的回退函数。 您可以使用以下两个选项之
一来执行此操作。

选项1:通过instance.sendTransaction()将交易直接发送到合约。 像所有可用合约实例函数一
样,并且具有与web3.eth.sendTransaction相同的API,但没有回调。 如果没有指定,to值将
自动填写。
nstance.sendTransaction({...}).then(​function​(result) {
​// Same transaction result object as above.
});
选项2:直接发送以太币:
instance.send(web3.toWei(1, ​"ether"​)).then(​function​(result) {
​// Same result object as above.
});

进一步阅读
Truffle提供的合约抽象包含了丰富的工具,可以轻松地与合约进行交互。 查看​Truffle合约文档
,了解技巧和见解。

通过EthPM安装包

ETHPM
EthPM是以太坊的新的包注册表。 它符合ERC190规范,用于发布和使用智能合约包,并得
到了许多不同的以太坊开发工具的广泛支持。 为了表示我们的支持,我们将以太坊包注册表
直接集成到Truffle中。

安装包
从EthPM安装软件包和通过NPM安装软件包几乎一样简单。 您可以直接运行以下命令:
$ truffle install <package name>

也可以指定安装包的具体版本:
$ truffle install <package name>@<version>

安装依赖
在工程里面可以定义一个ethpm.json文件,其中包括将项目固定到特定的依赖和版本上。 要
安装ethpm.json文件中列出的所有依赖项,请运行:
$ truffle install

有关ethpm.json文件的更多详细信息,请参阅下面的软件包配置。

运行安装的合约
已安装的软件包将被放置在项目文件夹中的installed_contracts目录中。 如果没有
installed_contracts目录存在,会自动创建。 您对待这个文件夹,应该像使用NPM对待
node_modules文件夹 - 也就是说,除非你知道你在做什么,否则你不应该编辑内容。:)

安装的包可以在测试中使用,迁移包和solidity合约文件是通过 import或require 该包和合约名


字实现。 例如,以下Solidity合约将从拥有的包导入owned.sol文件:
pragma solidity ^0.4.2;

import​ ​"owned/owned.sol"​;

contract MyContract is owned {


​// ...
}

类似的,下面的迁移文件可以从ens包中使用ENS.sol合约

文件:​./migrations/2_deploy_contracts.js
var​ ENS = artifacts.require(​"ens/ENS.sol"​);
var​ MyContract = artifacts.require(​"MyContract.sol"​);

module​.exports = ​function​(deployer) {
​// Only deploy ENS if there's not already an address already.
​// i.e., don't deploy if we're using the canonical ENS address,
​// but do deploy it if we're on a test network and ENS doesn't exist.
deployer.deploy(ENS, {overwrite: ​false​}).then(​function​() {
​return​ deployer.deploy(MyContract, ENS.address);
});
};
请注意,在上面的迁移中,我们使用ens包并根据ENS是否已经设置了地址来有条件地部署
ENS合约。这是部署者提供给您的一个奇特的技巧,更容易地根据当前存在的网络修改迁
移。在这种情况下,如果我们在ropsten网络上运行我们的迁移,则迁移将不会部署ENS合约
,因为(在撰写本文时)roptsen已存在规范的ENS合约 - 我们不能部署我们自己的ENS合
约。但是,如果我们正在针对不同的网络或测试网络运行我们的迁移,那么我们将要部署
ENS合同,以便我们有一个依赖合同来处理。

发布你自己的包
发布自己的软件包与安装一样简单,但像NPM一样,需要更多的配置。

ROPSTEN,ROPSTEN,ROPSTEN
以太坊包目前存在于Ropsten测试网络上。要发布到注册表,我们需要设置我们自己的
Ropsten配置,因为我们将进行需要签名的交易。

在这个例子中,我们将使用Infura发布软件包,以及truffle-hdwallet-provider NPM模块和一个
12个字的​分层确定性(Hierarchical Deterministic)​钱包助记符,代表了Ropsten网络上的
以太坊地址。首先,通过NPM在您的项目目录中安装truffle-hdwallet-provider:
$ npm install truffle-hdwallet-provider --save

编辑配置文件,添加​ropsten​网络配置,包括12个字的硬件钱包助记符。
文件:​truffle.js
var​ HDWalletProvider = ​require​(​"truffle-hdwallet-provider"​);

// 12-word mnemonic
var​ mnemonic = ​"opinion destroy betray ..."​;

module​.exports = {
networks: {
development: {
host: ​"localhost"​,
port: 8545,
network_id: ​"*"​ ​// Match any network id
},
ropsten: {
provider: ​new​ HDWalletProvider(mnemonic,
"https://ropsten.infura.io/"​),
network_id: 3 ​// official id of the ropsten network
}
}
};
包的配置
像NPM一样,EthPM的配置选项可以在一个名为ethpm.json的JSON文件中找到。 此文件位
于Truffle配置旁边,并提供了Truffle发布的包所需的所有信息。 您可以在配置部分中看到完整
的可用选项列表。
文件:​ethpm.json
{
​"package_name"​: ​"adder"​,
​"version"​: ​"0.0.3"​,
​"description"​: ​"Simple contract to add two numbers"​,
​"authors"​: [
​"Tim Coulter <tim.coulter@consensys.net>"
],
​"keywords"​: [
​"ethereum"​,
​"addition"
],
​"dependencies"​: {
​"owned"​: ​"^0.0.1"
},
​"license"​: ​"MIT"
}

命令
一定配置完成, 瞬间就可以发布:
$ truffle publish

你会看到类似于下面的输出,确定你的包被成功发布:
$ truffle publish
Gathering contracts...
Finding publishable artifacts...
Uploading sources and publishing to registry...
+ adder@0.0.3

发布之前
当使用一个网络比如默认的development网络时,配置是匹配任何以太坊客户端(比如
TestRPC)的,您必须具有围绕您私有网络的工件。 在发布包之前,请考虑运行以下命令来
删除任何无关的网络工件:
$ truffle networks --clean
更多信息请查看看​命令参考​。

通过NPM安装包

包管理

Truffle集成了标准npm工具,并在工程目录下创建node_modules目录,如果存在包的话。这
意味着你可以通过npm使用和分发合约,DApp,使能以太坊的库,使得你的代码可以提供给
他人使用,也可以是用他人的代码。

包布局
使用Truffle创建的项目默认具有特定的布局,使能它们就可以作为包来使用。这种布局不是必
需的,但是是常规惯例 - 或“事实上的标准”,那么通过NPM分发合同和dapps将变得更加容
易。

Truffle包中最重要的目录如下:

/contracts
/ build(其中包括 /build/contract,由Truffle创建)
第一个目录是您的合约目录,其中包括您的原始Solidity合约。第二个目录是构建目录,更具
体地说是/build/contracts,它以.json文件的形式保存构建工件。在您的包中包含原始合约将允
许其他人的Solidity代码中导入这些合约。同样,包括您的.json构建工件将允许其他人与
JavaScript中的合约无缝交互,可以用于dapps,脚本和迁移。

使用包
在您自己的项目中使用包时,请注意,有两个地方可能有兴趣使用他人的合同代码:在您的合
约内和您的Javascript代码(迁移和测试)中。以下提供了每种情况的示例,并讨论了充分利
用其他合约和构建工件的技术。

安装
对于这个例子,我们将使用例如Truffle Library,该库提供了一个部署到Morden测试网络的简
单名字注册表。为了使用它作为依赖,我们必须首先通过npm将其安装在我们的项目中:

$ cd my_project
$ npm install-truffle-library
请注意,上述最后一个命令下载该包并将其放在my_project/node_modules目录中,这对于下
面的示例很重要。有关使用npm安装软件包的帮助,请参阅npm文档。

在合约中使用
要在合约中使用包里面的合约,和Solidity的import语句一样简单。当导入路径不是明确的相对
或绝对路径时,这对于Truffle意味着将从特定的命名包中查找文件。考虑使用上面提到的实例
Truffle库的这个例子:
import​ ​"example-truffle-library/contracts/SimpleNameRegistry.sol"​;

由于路径没有以./开头,所以Truffle知道在项目的node_modules目录中查找
example-truffle-library文件夹。 从那里,它提供了要求的合约路径。

在JAVASCRIPT代码中使用
要在Javascript代码中与包中的合约交互,您只需要使用该包的.json文件,然后使用
Truffle-constract模块将其转换为可用的抽象:
var​ contract = ​require​(​"truffle-contract"​);
var​ data =
require​(​"example-truffle-library/build/contracts/SimpleNameRegistry.json
"​);
var​ SimpleNameRegistry = contract(data);
要使用这些抽象,请参阅与您的合同交互部分了解更多详细信息。

包的部署地址
有时您希望您的合约与该包中已部署的合约进行交互。 由于部署的地址存在于该包的.json文
件中,所以您必须执行额外的步骤才能将这些地址合并到合约中。 为此,让您的合同接受依
赖合约的地址,然后使用迁移。 以下是项目中存在的示例合同以及迁移示例:

合同:MyContract.sol
import​ ​"example-truffle-library/contracts/SimpleNameRegistry.sol"​;

contract MyContract {
SimpleNameRegistry registry;
address public owner;

​function​ ​MyContract​ {
owner = msg.sender;
}

​// Simple example that uses the deployed registry from the package.
​function​ ​getModule​(bytes32 name) ​returns​ (address) {
​return​ registry.names(name);
}

​// Set the registry if you're the owner.


​function​ ​setRegistry​(address addr) {
​if​ (msg.sender != owner) ​throw​;

registry = SimpleNameRegistry(addr);
}
}

迁移: 3_hook_up_example_library.js
// Note that artifacts.require takes care of creating an abstraction for
us.
var​ SimpleNameRegistry =
artifacts.require(​"example-truffle-library/contracts/SimpleNameRegistry.
sol"​);

module​.exports = ​function​(deployer) {
​// Deploy our contract, then set the address of the registry.
deployer.deploy(MyContract).then(​function​() {
​return​ MyContract.deployed();
}).then(​function​(deployed) {
​return​ deployed.setRegistry(SimpleNameRegistry.address);
});
};

发布之前

当使用配置为匹配任何Ethereum客户端(即TestRPC)的默认开发网络的网络时,您必须处
理您不想发布的网络工件。 在发布包之前,请考虑运行以下命令来删除任何无关的网络工件

$ truffle networks --clean


更多信息请查看看​命令参考​。

使用控制台

背景

有时,为了测试和调试目的,或者手动执行交易,交互式地处理合同是很好的。 Truffle为您
提供了一种简单的方法,可以随时通过交互式控制台完成此操作,您的合约。
命令

$ truffle ​console
除非使用指定网络,否则将加载使用develop网络的控制台。 您可以使用--network选项覆盖此
选项。 请参阅“网络”章节中的更多详细信息以及命令参考章节。

当您加载控制台时,您会立即看到如下输出:

$ truffle ​console
truffle(development)>
该提示符告诉你当前运行的Truffle控制台使用的是develop网络。

特性

控制台提供了Truffle命令行工具中提供的所有功能。 例如,您可以在控制台中键入migrate
-reset,并将它解释为与从控制台外面运行的 truffle migrate --reset 相同。 Truffle的控制台还
具有以下功能:

● 可以操作所有可用的已编译合约。
● 在每个命令(如migrate --reset)之后,合约被重新设置,以便您立即开始使用新分配
的地址和二进制文件。
● 以太坊客户端上也可以使用web3库。
● 自动处理所有命令的回复,并打印结果,对于简单命令无需使用.then()。 例如,
truffle(​default​)> MyContract.at(​"0xabcd..."​).getValue.call();
5

编写外部脚本

背景

你可能经常需要外部脚本和合约进行交互,Truffle提供了一种简单的方法,根据您所需的网络
引导您的合约,并根据您的项目配置自动连接到以太坊客户端。
命令

要运行外部脚本,请执行以下操作:

$ truffle exec <path/to/file.js>

文件结构

为使外部脚本正确的运行,Truffle期望他们导出一个单个参数的回调函数:
module​.exports = ​function​(callback) {
​// perform actions
}

您可以在此脚本中执行任何操作,只要在脚本完成时调用回调函数即可。 回调接受一个错误
作为其第一个也是唯一的参数。 如果提供错误,执行将停止,并返回一个非零退出码。

使用构建流水线

构建流水线
Truffle1.0.和2.0使用标准的面向web应用程序的默认构建系统(这里“构建”的意思是转换代码
为HTML,JavaScript,和CSS),该构建系统已被提取到自身模块中,使Truffle的可用性和
可扩展性适用于所有类型的应用程序。
Truffle可以配置为和任何构建系统紧密集成。要配置自定义构建系统,请参阅高级构建过程章
节以获取更多详细信息。

命令

构建系统配置好后,构建你的应用程序,请运行:

$ truffle build
如果你尝试在运行build之前,不首先配置过自定义构建过程, 将收到一个错误。
联系开发者

联系我们

Gitter

最好的联系方式是通过Gitter,在上面你可以向Truffle开发者和整个社区提问。

进阶

配置

位置

配置文件叫做truffle.js,位于项目目录的根目录。 该文件是一个Javascript文件,可以执行任
何代码来创建配置。 它必须导出一个表示项目配置的对象,如下面的示例。

解决WINDOWS上的命名冲突
在Windows上使用命令提示符时,默认的配置文件名与Truffle可执行文件可能会冲突。 如果
是这种情况,我们建议使用Windows PowerShell或Git BASH,因为这些shell没有这种冲突。
或者,您可以将配置文件重命名为truffle-config.js以避免此冲突。

举例
module​.exports = {
networks: {
development: {
host: ​"localhost"​,
port: 8545,
network_id: ​"*"​ ​// Match any network id
}
}
};
默认配置是单个开发网络的配置,在localhost:8545上运行。 还有很多其他配置选项,详细如
下。

一般选项

构建

构建应用程序的配置,如果您的应用程序需要与Truffle紧密集成。大多数用户可能不需要配置
此选项。有关详细信息,请参阅高级构建过程部分。

网络

在迁移期间指定哪些网络用于部署,以及在与每个网络进行交互时具体的交易参数(如gas价
格,地址等)。在特定网络上编译和运行迁移时,合约工件将被保存并记录以备以后使用。当
合约抽象检测到您的以太坊客户端连接到特定网络时,他们将使用与该网络相关联的合约工件
来简化应用程序部署。网络通过以太坊的net_version RPC调用以及区块链URI来标识。

networks对象(如下所示)是由输入的网络名字指定的 并包含定义网络参数的对象。如果没
有网络配置,则需要networks 选项,否则Truffle将无法部署您的合约。由truffle init提供的默
认网络配置为您提供了一个可以匹配任何网络连接的开发网络 - 这在开发过程中非常有用,但
不适合于生产部署。要配置Truffle连接到其他网络,只需添加更多命名网络并指定相应的网络
ID。

网络名称方便用户操作,例如在特定网络上运行迁移:

$ truffle migrate --network live


例子:

networks: {
development: {
host: ​"localhost"​,
port: 8545,
network_id: ​"*"​ ​// match any network
},
live: {
host: ​"178.25.19.88"​, ​// Random IP for example purposes (do not use)
port: 80,
network_id: 1, ​// Ethereum public network
​// optional config values:
​// gas
​// gasPrice
​// from - default address to use for any transaction Truffle makes
during migrations
​// provider - web3 provider instance Truffle should use to talk to
the Ethereum network.
​// - if specified, host and port are ignored.
}
}
对于任何网络, 如果没有指定交易选项, 将使用下面的默认值:

● gas: Gas上限. 默认值是 4712388.


● gasPrice: Gas价格. 默认值是100000000000 (100 Shannon).
● from: 迁移期间的发送地址. 默认值是以太坊客户端第一个可用账户.
● provider: 默认web3接口提供者: ​new
Web3.providers.HttpProvider("http://<host>:<port>")

Mocha

MochaJS测试框架的配置选项。 该配置预期的对象在Mocha文档中详细说明。

例子:

mocha: {
useColors: ​true
}

EthPM配置

该配置在可选的ethpm.js文件中,和truffle.js文件放在一起。

包名字

待发布的包名字。必须是EthPM注册表中唯一的。例:

package_name: ​"adder"
版本号
包的版本。使用这个​semver​规范。例:

version: ​"0.0.3"

描述

包的文本描述。例:

description: ​"Simple contract to add two numbers"

作者

推荐以下的格式:

authors: [
​"Tim Coulter <tim.coulter@consensys.net>"
]

关键词

描述该包的一组关键词:

keywords: [
​"ethereum"​,
​"addition"
],

依赖

一组EthPM包的依赖,版本号使用这个​semver​规范。例:

dependencies: {
​"owned"​: ​"^0.0.1"​,
​"erc20-token"​: ​"1.0.0"
}
许可

该包的使用许可,严格使用下面的格式:

license: ​"MIT"​,

网络和App部署

背景

即使是最小的项目也将在至少两个区块链中进行交互:一个在开发人员机器上,如
EthereumJS TestRPC,另一个代表网络是最终部署应用程序的网络(这可能是公共的以太坊
网络,或者私人联盟网络)。 Truffle提供了一个系统,用于管理每个网络的编译和部署工件
,并最终简化应用程序部署。

配置

有关详细信息,请参阅配置部分。

指定一个网络

大多数Truffle命令将根据指定的网络而有所不同,并使用该网络的合约和配置。您可以使用
--network选项指定网络,如下所示:

$ truffle migrate --network live


在这个例子中,Truffle将在“实时(live)”网络上运行您的迁移,就像这个​示例​的配置,和公以太
坊公有链相关联。

建立工件

如“编译合约”部分所述,构建工件将作为.json文件存储在./build/contracts目录中。当您编译合
约或使用特定网络运行迁移时,Truffle将更新这些.json文件,以便它们包含与该网络相关的信
息。当这些工件稍后使用(例如在您的前端或通过Truffle合约的应用程序中)时,它们将自动
检测以太坊客户端连接到哪个网络,并相应地使用正确的合约工件。

应用部署

运行时(runtime)的网络由合约工件自动检测,这意味着您只需要部署应用程序或前端一次。当
您的应用程序运行时,运行的以太坊客户端将确定使用哪些工件,这将使您的应用程序非常灵
活。例如,如果您要将Web应用程序部署到http://mydapp.io,您可以使用您最喜欢的钱包浏
览器(如MetaMask或Mist)导航到该地址,您的dapp就可以正常工作,而不管以太坊网络连
接的钱包浏览器。如果钱包浏览器连接到实时网络,您的dapp将使用在实时网络上部署的合
约。如果在Ropsten上,合约将部署到Ropsten。

构建过程

自定义构建过程

为了和Truffle紧密集成,Truffle允许你自定义一个构建流水线,用于引导和配置应用程序。
Truffle提供三种集成方法,如下所述。

运行一个外部命令

如果你想用Truffle运行一个外部命令来随时触发一个构建,只要简单的在你的项目配置中包含
如下的选项:

module​.exports = {
​// 在每次构建中将运行 `webpack` 命令.
​//
​// 下面的环境变量将被设置,当运行命令:
​// WORKING_DIRECTORY: 项目的根目录
​// BUILD_DESTINATION_DIRECTORY: 构建结果存放位置 (对 `truffle serve`很重
要)
​// BUILD_CONTRACTS_DIRECTORY: 构建的合约文件根目录 (.sol.js)
build: ​"webpack"
}
请注意,您将获得足够的环境变量,以便与Truffle进行整合。
提供自定义功能

您还可以提供如下的自定义构建功能。 请注意,您可以获得项目与Truffle紧密集成的大量信
息。

build: ​function​(options, callback) {


​// 构建时,这里执行自定义的工作. `options` 包含下面的值:
​//
​// working_directory: 项目根目录
​// contracts_directory: .sol 文件的根目录
// destination_directory: truffle期望的构建结果存放位置(用于 `truffle
serve`)
}
}

创建一个自定义模块

您还可以创建一个实现构建器界面的模块或对象(比如,一个包含像上述的build函数的对
象)。 方便与truffle紧密集成并发布一个包给其他人使用。

以下是使用Truffle的默认构建器的示例:

var​ DefaultBuilder = ​require​(​"truffle-default-builder"​);


module​.exports = {
build: ​new​ DefaultBuilder(...) ​// specify the default builder
configuration here.
}

引导你的应用程序

无论构建的应用程序是在浏览器中运行,还是使用命令行工具,JavaScript库或本地移动应用
程序,使用相同的方式引导你的合约,并且遵循相同的流程来使用部署的合约工件。

配置构建工具或应用程序时,需要执行以下步骤:
1)将您的所有合约工件放到构建的pipeline/application中。这包括./build/contracts目录中的
所有.json文件。

2)通过truffle-condtract将这些.json合约工件转化为易于使用的合约抽象。

3)通过Web3提供者提供合约抽象。在浏览器中,该提供者可能来自Metamask或Mist,但它
也可能是您配置的自定义提供者。指向Infura或任何其他以太坊客户端。

4)使用你的合约吧!

在Node中,这很容易做到。我们来看一个例子,展示了执行上述步骤的“最纯粹”的方法,因
为它存在于任何构建过程或工具之外。

// Step 1: Get a contract into my application


var​ json = ​require​(​"./build/contracts/MyContract.json"​);

// Step 2: Turn that contract into an abstraction I can use


var​ contract = ​require​(​"truffle-contract"​);
var​ MyContract = contract(json);

// Step 3: Provision the contract with a web3 provider


MyContract.setProvider(​new
Web3.providers.HttpProvider(​"http://localhost:8545"​));

// Step 4: Use the contract!


MyContract.deployed().then(​function​(deployed) {
​return​.deployed.someFunction();
});

所有构建过程和合约引导将遵循这种模式。 设置自己的自定义构建过程的关键是确保您正在
使用所有的合约工件并正确设置合约抽象。

命令参考

使用方法
$ truffle [command] [options]
可用命令

build
执行构建(如果存在)
$ truffle build

有关详细信息,请参阅构建应用程序部分。

console

运行一个带有命令和合约抽象的控制台

$ truffle ​console
一旦控制台启动,您就可以通过命令行使用合约,就像在代码使用一样。 此外,这里列出可
在控制台中使用所有Truffle命令。

可选参数:

● - -network name:指定要使用的网络。
● --verbose-rpc:在Truffle和RPC之间通信记录。

有关详细信息,请参阅使用控制台部分。

compile

智能编译您的合约。除非另有说明,否则只会编译自上次编译以来发生变更的合约。

$ truffle compile
可选参数:

● --all:编译所有合约,而不是智能选择。
● --network name:指定要使用的网络,保存着该网络具体的工件。

create contract

创建新合约的辅助手段。名字必须是驼峰命名格式。
$ truffle create contract MyContract

create migration

创建新迁移的辅助手段。名字必须是驼峰命名格式。

$ truffle create migration MyContract

create test

支持一个合约的新测试的辅助手段。名字必须是驼峰命名格式。

$ truffle create test MyTest

exec
在truffle环境中执行一个Javascript文件。包括web3,根据指定的网络(如果有的话)设置默
认提供者,并在执行脚本时将合约作为全局对象。脚本必须导出Truffle可以运行的函数。有关
详细信息,请参阅“编写外部脚本”部分。

$ truffle exec /path/to/my/script.js


可选参数:

-- network name:指定要使用的网络,使用该网络特有的工件。

init

在当前工作目录中创建一个全新的应用程序。将添加默认合约,测试和前端配置。

$ truffle init

install

从以太坊包注册表安装一个包。
$ truffle install <package name> @ <version>
@version参数语法是可选的。有关详细信息,请参阅带有EthPM的软件包管理部分。

migrate

运行项目的迁移。有关详细信息,请参阅迁移部分。

$ truffle migrate
可选参数:

● --reset:重新运行所有迁移,而不是从上一次完成的迁移开始运行。
● -f number:从指定的迁移运行合约。
● --network name:指定要使用的网络,保存该网络的特定工件。
● --compile-all:编译所有合约,而不是智能地选择。
● --verbose-rpc:在truffle和RPC之间通信记录。

networks

显示所有网络上所有合约的部署地址,并可选择清理无关的网络工件。

$truffle networks
在发布软件包之前,请使用此命令查看是否有不需要发布的网络工件。没有指定任何选项,该
包将简单地输出当前工件状态。

可选参数:

● --clean:删除与指定网络不相关联的所有网络工件。

publish

发布一个包到以太坊包注册表。

$ truffle publish
所有参数都从项目的配置文件中获取。有关详细信息,请参阅使用EthPM的软件包管理部分。
serve

在http:// localhost:8080上给应用程序提供服务,并且根据需要重新构建和重新部署。像truffle
watch,添加了Web服务器组件。

$ truffle serve

可选参数:

-p port:指定要开放的端口。默认值为8080。

-- network name:指定要使用的网络,使用该网络特有的工件。

test

运行./test目录中的所有测试,或选择运行单个测试。

$ truffle test [​/ path /​ to / test / file]


可选参数:

● --compile-all:编译所有合同,而不是智能地选择。
● --verbose-rpc:在truffle和RPC之间通信记录。
● --network name:指定要使用的网络,使用该网络特有的工件。

version

显示版本号并退出。

$ truffle version

watch

观察合约、应用和配置文件的更改。当有变化时,如果需要,重新构建应用程序。

$ truffle watch

You might also like