许多情况下,我们想要与我们的合约进行交互,但是许多操作需要一些比较底层的技术积累,而且就算你自己写出来了(肯定不止一两条命令吧),你也会发现,管理这些命令也是相当麻烦的---比如你发现你的一条命令或者操作具有很好的扩展性,你打算扩展它,但是操蛋的是,你发现扩展后与其他的命令冲突...郁闷不!Truffle注意到了这个问题的复杂度,并整合相关技术来帮助大家。
以太坊认为将数据写入网络和读取数据是有区别的,并且他们也是这样做的。事实上,这种区别在你编写应用程序的过程中起着重要的作用。一般来说,编写数据称为 transaction,而读取数据则称为 call。transaction和call的处理方式非常不同,并且具有以下特征。
交易可以说从根本上改变了网络的状态,一条交易可以像发送以太到另一个帐户一样简单,也可以像执行合约功能或向网络添加新合约一样复杂。交易的特征是它写入(或更改)了数据。交易需要花费以太运行,我们称之为‘gas’,同时交易的完成也是需要的时间的(事实上,目前的去中心的交易花费时间都是比较长的,不过也有相关技术发展旨在解决该问题,比如闪电网络与IPFS等等)。另一方面,当你通过一笔交易来执行合约里面的某个函数时,你不能接收该函数的返回值,因为交易没有被立即处理。一般来说,通过交易执行的函数不会返回一个值;相反的,他们将返回一个事务id。所以总的来说,交易:
调用,对比于交易来说,非常不同。调用可以用于在网络上执行代码,但不会永久改变数据。与交易相比,调用是免费运行的,他们的特征就是读取数据。当你通过调用来执行合约函数时,你会立即收到返回值。所以总的来说,调用:
在事务和调用之间进行选择与决定是否要读取数据或写入数据一样简单。
这些来自Javascript的合约抽象,相当于在与以太坊合约交互的过程中撒上了黄油与面包。简而言之,抽象就是包装了一些代码,使得我们与合约的交互变得容易。打个比方,抽象使我们忘记在引擎盖下执行的发动机与许多齿轮。truffle通过truffle-contract模块使用自己的合约抽象,下面描述的就是我们所讲的合约抽象。
还是老话,是骡子是马,咱是不是需要牵出来遛一遛啊,别说的那么好,结果操作起来垃圾的要死。所以为了让大家更好的理解抽象,我们需要一个合约来解释。通过truffle unbox metacoin命令,我们可以利用现有的模板,创建一个MetaCoin合约。
首先在控制台输入命令:
mkdir metacoin
cd metacoin/
truffle unbox metacoin
就像下面这张图片一样:
如果出现我这张图片,就说明你这一步成功了,然后输入命令:
atom ./
就会跳转到我们的atom软件里面。如图:
在上面这张图片中,我们可以从左边看到整个metacoin的结构。
右边代码区域就是下面的代码(除了我翻译了英文注释,哈哈)
pragma solidity ^0.4.2;
import "./ConvertLib.sol";
// 这只是一个类似硬币的合约的简单例子。
// 它不是与标准兼容的,所以不要指望它能与以太产生交流。
// coin/token contracts. 如果你想创建一个符合标准的
// 通证, 参见: https://github.com/ConsenSys/Tokens. 感谢!
contract MetaCoin {
mapping (address => uint) balances;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
function MetaCoin() {
balances[tx.origin] = 10000;
}
function sendCoin(address receiver, uint amount) returns(bool 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];
}
}
sendCoin
,
getBalanceInEth
, and
getBalance
),没有构造函数(合约一般都会有构造函数),这三个方法都可以作为交易或者调用来执行。
(跳过compile与migrate阶段--这一部分就不讲了,打开truffle console)
通过truffle我们获得了一个称之为MetaCoin的Javascript对象
就像下面这段代码一样:
// 打印已经部署的MetaCoin“版本”
// 需要注意的是,我们要获得MetaCoin版本需要一个承诺,输入:
// 下面的instance就是我们的合约对象
MetaCoin.deployed().then(function(instance) {
console.log(instance);
});
// 将会输出:
//
// Contract
// - address: "0xa9f441a487754e6b27ba044a5a8eb2eec77f6b92"
// - allEvents: ()
// - getBalance: ()
// - getBalanceInEth: ()
// - sendCoin: ()
// ...
利用这些抽象,你可以很方便的在以太坊网络(事实上,除了主网以外的网络同样也可以)中执行合约包含的方法。
我们知道在MetaCoin合约里面我们有三个方法可以执行。如果你仔细分析一下,你就会发现是sendCoin唯一写入数据、改变数据的方法。这个方法的目的是发送Meta币从一个账户到别的账户。
当我们调用方法时,我们将把这样的操作当做一笔交易。在接下来的示例中,我们将从一个账户发送10个Meta币到另一个账户,以一种持续改变网络的方式。
var account_one = "0x1234..."; // 一个地址
var account_two = "0xabcd..."; // 另一个地址
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// 如果这个回调函数被调用,表明交易成功。
alert("Transaction successful!")
}).catch(function(e) {
// 有一个错误!需要处理。
})
在上面的代码中有一些有趣的地方:
我们接着继续,注意getBalance函数是一个很好的从网络读取数据的方法。它不需要做任何更改,因为它只返回传递给它的地址的MetaCoin余额。那就让我们试试看吧:
var account_one = "0x1234..."; // 一个地址
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one, {from: account_one});
}).then(function(balance) {
// 如果这个回调函数被调用,则表明调用成功。
// 注意这个返回是立即、马上的,没有等待。
// 让我们打印返回值。
console.log(balance.toNumber());
}).catch(function(e) {
// 有一个错误!需要处理。
})
一些有趣的事情:
警告:我们把返回值转换成一个数字,因为在这个例子中,数字很小。但是,如果您试图转换一个大于Javascript支持的最大整数的BigNumber,您很可能会遇到错误或意外的提示。
你的合约可以触发事件,通过捕捉这些事件,我们可以更好的了解合约在做什么。处理事件的最简单方法—是处理触发事件的交易的结果对象,例如:
var account_one = "0x1234..."; // 一个地址
var account_two = "0xabcd..."; // 另一个地址
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// 结果是一个具有以下值的对象:
//
// result.tx => 交易的哈希,字符串。
// result.logs => 在此交易中触发的解码事件数组。
// result.receipt => 交易接受的对象,包括花费的gas。
// 我们可以对结果进行循环。查看是否触发了传输事件。
for (var i = 0; i < result.logs.length; i++) {
var log = result.logs[i];
if (log.event == "Transfer") {
// 我们发现了事件!
break;
}
}
}).catch(function(err) {
// 有一个错误!需要处理。
});
当你进行交易时,你会得到一个结果对象,它会给你提供有关交易的大量信息。具体来说,您可以得到以下内容:
result.tx
(string) - 交易哈希如果你想了解更多,请参见truffle-contract项目中的README。
在上述描述的情况中,我们一直在使用已经部署的合约抽象。我们可以使用.new()函数将自己的版本部署到网络中:
MetaCoin.new().then(function(instance) {
// 打印新地址
console.log(instance.address);
}).catch(function(err) {
// 有一个错误!需要处理。
});
如果你已经有了一个合同的地址,您可以创建一个新的抽象来表示该地址的合约。
var instance = MetaCoin.at("0x1234...");
你可能只想将以太直接发送到合约,或触发合约的 fallback function。你可以有两个选择来达成这一目标。
选择1:通过instance.sendTransaction()直接发送一个交易到合约。这就像所有可用的合约实例函数一样, 具有与web3.eth.sendTransaction相同的API,但没有回调。如果没有指定,将自动填充to值。
instance.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-contrac文档,了解相关提示、技巧和见解。
(官方的一点希冀)
想让这个页面变得更好吗? Edit here →