與智能合約交互
概述
為了與合約交互而向Ethereum網(wǎng)絡(luò)編寫原始請求包斑,那么您很快就會意識到流礁,編寫這些請求是笨重而麻煩的涕俗。
同樣,你可能會發(fā)現(xiàn)管理每個請求的狀態(tài)非常復(fù)雜崇棠。幸運的是咽袜,Truffle 為您解決了這種復(fù)雜性,使得與合約的交互變得輕而易舉枕稀。
讀寫數(shù)據(jù)
Ethereum網(wǎng)絡(luò)對向網(wǎng)絡(luò)寫入數(shù)據(jù)和從網(wǎng)絡(luò)讀取數(shù)據(jù)進行了區(qū)分询刹,這種區(qū)分在如何編寫應(yīng)用程序中起著重要作用。
通常萎坷,寫入數(shù)據(jù)稱為交易( transaction )凹联,而讀取數(shù)據(jù)稱為調(diào)用( call )。事務(wù)和調(diào)用的處理方式非常不同哆档,具有以下特征蔽挠。
交易 (Transactions)
交易從根本上改變了網(wǎng)絡(luò)的狀態(tài)。交易可以是簡單到將以太幣發(fā)送到另一個帳戶瓜浸,也可以是復(fù)雜到執(zhí)行合約函數(shù)或向網(wǎng)絡(luò)添加新合約澳淑。
交易的定義特性是它寫入(或更改)數(shù)據(jù)。交易的運行成本很高插佛,稱為“gas”杠巡,交易的處理需要時間。
當(dāng)您通過交易執(zhí)行合約的功能時雇寇,您不能接收該函數(shù)的返回值氢拥,因為交易沒有立即處理。一般來說锨侯,通過交易執(zhí)行的函數(shù)不會返回值;
它們將返回一個交易id嫩海。所以總的來說,交易(Transactions)特性如下:
- 消費 gas (以太幣 ether)
- 修改網(wǎng)絡(luò)狀態(tài)
- 不能馬上執(zhí)行
- 不會暴露一個返回值(之返回一個交易id)
調(diào)用 (Calls)
調(diào)用正好相反。調(diào)用可以是在網(wǎng)絡(luò)中執(zhí)行代碼囚痴,不會永久的改變數(shù)據(jù)(狀態(tài))叁怪。調(diào)用是免費的,它的特性就是讀取數(shù)據(jù)深滚。
當(dāng)你使用 call 調(diào)用一個合約中的一份方法時骂束,函數(shù)會馬上返回〕审铮總的來說 Calls 的特性:
- 免費(不消耗 gas)
- 不改變網(wǎng)絡(luò)狀態(tài)
- 馬上執(zhí)行
- 會暴露一個返回值
選擇使用 Tranaction 或 Call 很簡單展箱,取決于你是讀取數(shù)據(jù)還是寫入數(shù)據(jù)。
合約抽象簡介
合約抽象是使用 Javascript 與 Ethereum 合約交互的 bread 和 butter 蹬昌。
簡而言之混驰,合約抽象是一種包裝代碼,它使與合約的交互變得容易,從而讓你忘記了在引擎下執(zhí)行的許多引擎和齒輪栖榨。
Truffle通過 Truffle-contract 模塊使用它自己的合約抽象昆汹,下面描述的就是這個合約抽象。
為了理解合約抽象的作用婴栽,我們首先需要一個合約例子监署。我們將使用MetaCoin合約合砂,通過 truffle unbox 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;
}
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];
}
}
除了構(gòu)造函數(shù)(sendCoin访敌、getBalanceInEth和getBalance)之外,該契約還有三個方法轰枝。
這三個方法都可以作為 transaction 或 call 執(zhí)行捅彻。
現(xiàn)在讓我們看看 Truffle 提供給我們的名為 MetaCoin 的 Javascript 對象,
這是在 truffle console 中的:
// 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合同已部署版本的地址。
執(zhí)行合約方法
使用合約抽象你可以很容易的在以太坊網(wǎng)絡(luò)中調(diào)用合約诚撵。
創(chuàng)建一個交易
MetaCoin 中有三個方法可以執(zhí)行缭裆。如果你自己看你會發(fā)現(xiàn) setCoin
方法是用來在網(wǎng)絡(luò)中發(fā)送幣的。
這個方法需要修改網(wǎng)絡(luò)中的狀態(tài)寿烟。
我們調(diào)用 setCoin 方法時候我們使用 transaction 調(diào)用澈驼。在下面的例子中將會轉(zhuǎn)賬10個 Meta 幣,
就是說要永久的修改網(wǎng)絡(luò)狀態(tài):
var account_one = "0x1234..."; // 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.
})
上面的代碼有一些有趣的地方:
- 我們直接調(diào)用合約抽象的
setCoin
方法韧衣。這個調(diào)用將會返回一個 transactin 結(jié)果。 - 當(dāng)交易成功執(zhí)行以后购桑,回調(diào)方法才會執(zhí)行畅铭。這就讓生命周期管理改變的簡單,你不需要自己檢查交易狀態(tài)勃蜘。
- 我們將對象作為第三個參數(shù)傳遞給sendCoin硕噩。注意,在我們的Solidity合約中缭贡,sendCoin函數(shù)沒有第三個參數(shù)炉擅。
上面看到的是一個特殊的對象,它總是作為最后一個參數(shù)傳遞給一個函數(shù)阳惹,該函數(shù)允許您編輯交易的特定細(xì)節(jié)谍失。
在這里,我們設(shè)置了from address確保該交易來自account_one莹汤。
創(chuàng)建一個調(diào)用 call
繼續(xù)使用 MetaCion 例子快鱼,注意 getBalance
函數(shù)是從網(wǎng)絡(luò)讀取數(shù)據(jù)的一個很好的候選函數(shù)。
它不需要做任何更改,因為它只返回傳遞給它的地址的 Meta Coin 余額抹竹。讓我們試一試:
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.
})
這里有意思的地方:
- 我們執(zhí)行了
call
方法线罕,告訴以太坊網(wǎng)絡(luò)我們不會修改網(wǎng)絡(luò)中的數(shù)據(jù)。 - 我們接收到了一個返回值窃判,而不是一個交易id钞楼。
警告:
我們把返回值轉(zhuǎn)換為數(shù)字,因為這個demo中數(shù)字很小袄琳。通常給如果如果你要轉(zhuǎn)換一個超大整形需要
使用 JavaScript 提供 ```BigNumber``` 庫询件,否則會報錯或者拋出異常。
捕獲事件
你的合約可以觸發(fā)事件跨蟹,你可以通過這些事件獲得更多的了解你的合約在做什么雳殊。
處理事件的最簡單方法是處理觸發(fā)事件的交易的結(jié)果對象,如下所示:
var account_one = "0x1234..."; // 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.
});
處理交易結(jié)果
當(dāng)我們創(chuàng)建交易時窗轩,將獲得一個結(jié)果對象夯秃,該對象將提供關(guān)于交易的大量信息。
具體來說痢艺,有以下信息:
-
result.tx
(string) - 交易哈希值 -
result.log
(array) - 解碼后的事件 -
result.receipt
(object)- 交易收據(jù)
更多信息請查看 truffle-contract
的說明仓洼。
向網(wǎng)絡(luò)中添加一個新的合約
如果您已經(jīng)有一個合約地址,您可以創(chuàng)建一個新的合約抽象來表示該地址的合約堤舒。
var instance = MetaCoin.at("0x1234....");
給合約轉(zhuǎn)賬
你可能只想直接發(fā)送以太幣 ether 到合約色建,或者觸發(fā)合約的回退功能。
你可以使用以下兩個選項之一來實現(xiàn)這一點:
方式一:
直接向合約發(fā)送一個交易使用 instance.sendTransaction()
舌缤。這就像所有可用的合約實例函數(shù)一樣箕戳,
和 web3.eth.sendTransaction
方法相同,但是 web3.eth.sendTransaction
沒有回調(diào)国撵。
如果你沒有申明 to
陵吸,它將會被自動填充。
instance.sendTransaction({...}).then(function(result){
// same transction result object as above.
});
方式二:
直接發(fā)送以太幣也有簡寫:
instance.send(web3.toWei(1, "ether")).then(function(result){
// some result oject as above
});