8.3 开始使用truffle-contract

在学习truffle之前,学习truffle-contract很重要,因为truffle-contract与truffle密切相关。Truffle测试、truffle中与合约交互的代码、部署代码等都是使用truffle-contract编写的。

truffle-contract API是一个JavaScript和Node.js库,它使以太坊智能合约的处理变得容易。到目前为止,我们已经使用了web3.js部署和调用智能合约 函数,这没问题,但是truffle-contract的目标是更容易操作以太坊智能合约。下面是truffle-contract的一些功能,这些功能 使truffle-contract在处理智能合约时优于web3.js:

·同步交易,优化了控制流(交易在直到确定被挖出之前都不会停止)。

·基于约定的API。再没有“回调地狱”。在ES6和async/await上都可以用。

·默认交易数值,例如from address或者gas。

·返回每个同步的日志、交易收据和交易哈希。

在学习truffle-contract之前,需要知道它不允许使用存储以太坊节点之外的账户签署交易,也就是说,它 没有类似于sendRawTransaction的东西。truffle-contract API假设DApp中的每个用户各自运行以太坊节点,且其账户都存储在那个节点中。事实上,DApp应该这样运行,因为如果DApp的每个客户端开始让用 户创建和管理账户,那么用户管理这么多账户就成了问题。开发人员为他们创建的每个客户端每次都要开发一个钱包manager也是很痛苦的。现在的问题是客 户端怎样才能知道用户在哪里以及用什么格式存储了账户。所以从概率角度出发,优选假设用户将账户存储在个人节点上,而且为了管理账户,它们使用以太坊钱包 应用的东西。因为以太坊节点中存储的账户由以太坊节点自身签名,所以就不再需要sendRawTransaction了。每个用户需要有各自的节点,而不 能分享节点,因为解锁一个账户时,对使用它的所有人都是开放的,这将使用户能盗窃其他人的以太币和用他人的账户进行交易。

如果所使用的App要求用户包含自己的节点,并在该节点中管理账户,那就需要确保只有本地应用才能对该节点进行JSON-RPC调用,而不能让所有人都能调用。还要确保用户不会长期解锁账户,只要不需要账户,就应当立即锁定。

如果应用要求有创建和签署原始交易功能,则可以使用truffle-contract开发和测试智能合约。在应用中可以与合约交互,就像我们之前做的。

8.3.1 安装和导入truffle-contract

在写本书时,truffle-contract API的最新版本是1.1.10。在导入truffle-contract之前,需要先导入web3.js,因为需要创建一个提供方处理truffle- contract API,这样truffle-contract将内部使用该提供方进行JSON-RPC调用。

在Node.js app中安装truffle-contract,只需在app目录中运行如下代码:

image.png

然后使用如下代码导入:

image.png

在浏览器中使用truffle-contract时,会在https://github.com/trufflesuite/truffle-contract 仓库发现dist目录中的浏览器分配。

在HTML中,可以使用如下命令将它存入队列:

image.png

这样将会有一个可用的TruffleContract全局变量。


8.3.2 建立测试环境

在开始学习truffle-contract API之前,需要建立测试环境,这将有助于我们在学习的同时测试代码。

首先,运行testrpc--networkId 10命令,即运行代表network ID 10的ethereumjs-testrpc节点。出于开发目的,我们随机选取了network ID 10,但是用户可以随意选择任何其他网络ID。只需要确保不是1,因为主网总是用于真实的App,而不是用于开发和测试。

然后创建一个HTML文件,放入如下代码:

image.png

下载web3.min.js和truffle-contract.min.js。truffle-contract浏览器在https://github.com/trufflesuite/truffle-contract/tree/master/dist 创建内容。

8.3.3 truffle-contract API

现在来看truffle-contract API。基本上,truffle-contract有两个API,即合约抽象API和合约实例API。合约抽象API代表关于合约(或者库)的多种信息, 例如ABI、未接入的字节码、在多个以太坊网络中的地址(如果合约已部署)、对于多个以太坊网络它所依赖的库地址(如果已部署)和合约事件。抽象API是 对所有合约抽象都存在的一系列函数。合约实例代表一个特定网络中的已部署合约。实例API是对合约实例可用的API。它是以Solidity源文件中的可 用函数为基础动态创建的。特定合约的合约实例是从代表同一合约的合约抽象中创建的。

1.合约抽象API

合约抽象API是truffle-contract与web3.js的不同之处。其特点如下:

·它将根据连接的网络自动抓取默认数值,例如库地址、合约地址等,因此用户不需要每次换网络时修改源代码。

·可以选择只在特定网络中监听特定事件。

·把库实时接入合约的字节码变得容易。在学习了如何使用API之后,用户会发现其他优点。

在学习如何创建合约抽象及其方法之前,先写一个样本合约(代表合约抽象)。示例样本合约的代码如下:

image.png

该合约使用StringLib库把uint转换成bytes32,把bytes32转换成uint。String-Lib在主网络上的 0xcca8353a18e7ab7b3d094ee1f9ddc91bdf2ca6a4地址可用,但是在其他网络上,我们需要部署它以测试合约。在进行 下一步操作之前,使用Browser Solidity编译它,因为将需要ABI和字节码。

上述代码的执行过程如下:

1)创建一个provider。truffle-contract使用这个provider与节点通信。

2)为样本合约创建合约抽象。使用Truffle-contract函数创建合约抽象。该函数有一个对象,其中包含关 于合约的多种信息。该对象可以被称为artifacts对象。abi和unlinked_binary属性是必选项,其他属性是可选项。abi属性指向合 约的ABI,而unlinked_binary属性指向合约的未链接的二进制代码。

3)network属性表示不同网络中的合约涉及的各种信息。这里,在network ID 1中,StringLib相关程序被部署在0xcca8353a18e7ab7b3d094ee1f9ddc91bdf2ca6a4地址,所以在网络1部 署样本合约时,它会自动连接。在networks对象下面还可以设置address属性,表示该合约已经被部署到这个网络,这就是合约地址。在 networks对象中还有一个events对象,用于指明想获取的合约事件。events对象的key是事件主题,value是事件的ABI。

4)通过传送一个新的provider实例调用SampleContract对象的setProvider方法。这是 一种传送provider的方式,这样truffle-contract就能与该节点通信。truffle-contract API无法在全局设置provider,反而需要为每个合约抽象设置一个provider。该功能允许用户轻松接入多个网络并在其中工作。

5)调用SampleContract对象的detectNetwork方法。这是设置合约抽象当前代表的网络ID的 一种方式。也就是说,在对合约抽象进行全部操作期间,使用被映射到该网络ID的数值。该方法将自动检测节点连接到了哪个网络ID,并将自动设置这一网络 ID。如果想手动设置网络ID或者实时修改,可以使用SampleContract.setNetwork(network_id)。如果修改网络ID, 就要确保provider还指向同一个网络的节点,否则truffle-contract不能用正确的链接、地址和事件映射网络ID。

6)为SampleContract()生成的交易设置默认数值。该方法用于获取和设置(可选项)交易默认值。如果调用时不指定任何参数,则只返回一个表示当前默认值的对象;如果一个对象被作为参数传送,则将设置新的默认值。

7)为了创建合约抽象,对StringLib库进行同样的操作。

2.创建合约实例

合约实例代表在特定网络中已部署的合约。要使用合约抽象实例,我们需要创建一个合约实例。创建合约实例的方法有如下3种:

·SampleContract.new([arg1,arg2,...],[tx params])。该函数使用合约要求的任何constructor参数,并部署一个新的合约实例到合约抽象要用到的网络。最后一个实参是可选的,可以用 它传送包括来自地址的交易、gas上限和gas价格在内的交易参数。该函数返回一个承诺,在挖出交易时,该承诺归结为新部署的地址上合约抽象的一个新实 例。该方法不会对合约抽象代表的artifacts对象进行任何修改。在使用该方法前,确保它可以为要用到的网络发现字节码所依赖的库地址。

·SampleContract.at(address)。该函数用于创建合约抽象的一个新实例——代表传入的地址上 的合约。它返回一个“thenable(则可能)”对象(对于反向兼容性来说还不是一个实际的承诺)。确保代码存在于所要用到的网络中的特定地址之后,该 函数将解析一个合约抽象实例。

·SampleContract.deployed()。该函数和at()类似,但地址是从artifacts对象中检索的。像at()一样,deployed()是合理的。在确保代码存在于所要用到的网络中的特定地址之后,该函数将解析一个代表已部署的合约的合约实例。

下面部署合约并获取样本合约的实例。在network ID 10中,需要首先使用new()部署StringLib库,然后把StringLib库的已部署地址添加到StringLib抽象,再把 StringLib抽象接入SampleContract抽象,最后使用new()部署样本合约以获取样本合约的一个实例。但是在network ID 1中,只需要部署SampleContract并获取其实例,因为已经在那里部署了StringLib。全部相关代码如下:

上述代码的执行过程如下:

image.png

1)检测网络ID。如果网络ID是10,则部署合约和库;如果网络ID是1,则只部署合约。

2)在network ID 10中,部署StringLib合约并获取其合约实例。

3)更新StringLib抽象,这样就知道了它所代表的当前网络的合约地址。更新抽象的界面类似于直接更新artifacts对象。如果连接到network ID 1,则将重写已经设置好的StringLib地址。

4)把已部署的StringLib接入SampleContract抽象。接入会更新链接,并把库里的事件复制到SampleContract抽象的当前网络。库可以被多次接入,并将重写它们之前的链接。

5)部署SampleContract到当前网络。

6)更新SampleContract抽象,以便在当前网络中存储合约地址,这样以后可以使用deployed()来获取实例。

7)在network ID 1中,只部署SampleContract即可。

8)这时可以修改节点连接的网络并重启应用,应用将相应做出修改。例如,在开发人员的机器上,应用将连接到开发网络; 在生产服务端上,应用将连接到主网络。显然,用户可能不希望每次运行前面的文件都部署合约,所以实际上,只要合约被部署了,就可以更新artifacts 对象,而且可以在代码中检查是否已经部署了合约。如果没有部署,只有这时部署它。在合约部署完成后,用户可以在数据库或者文件中存储artifacts对 象并编写代码自动更新(而非手动更新)。

3.合约实例API

源Solidity合约不同,每个合约实例也是不同的,且API是动态创建的。下面是合约实例的不同API:

·allEvents。这是一个合约实例函数,在当前网络ID下,当合约artifacts对象中匹配事件签名的合约 引发一个事件时,就激活这个回调函数。用户还可以用eventname-specific函数抓取特定事件,而非所有事件。在前面的合约中,要抓取 ping事件,可以使用SampleContract_Instance.ping(function(e,r){})。

·send。该函数用于向合约发送以太币。它有两个实参:第一个实参是要转账的wei数量;第二个实参是可选项对象,该对象用于设置交易的from,即发送以太币的地址。该调用返回一个承诺,且该承诺在挖出交易时解析交易细节。

·可以使用SampleContract.functionName()或者 SampleContract.functionName.call()调用合约的任何方法。前者发送交易,后者则只调用EVM上的方法,且修改并不持 续。两种方法都返回一个承诺。在第一种情况下,承诺解析交易结果,也就是说,一个对象包含交易哈希、日志和交易收据。在第二种情况下,承诺解析方法调用的 返回值。两种方法都有函数实参,且最后一个实参为可选项,它是一个设置交易的from、gas和value等的对象。


来源:我是码农,转载请保留出处和链接!

本文链接:http://www.54manong.com/?id=541

'); (window.slotbydup = window.slotbydup || []).push({ id: "u3646208", container: s }); })();
'); (window.slotbydup = window.slotbydup || []).push({ id: "u3646147", container: s }); })();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值