摘要:如果當(dāng)前在以太坊上有大量掛起事務(wù)或者用戶發(fā)送了過低的價格,我們的事務(wù)可能需要等待數(shù)個區(qū)塊才能被包含進(jìn)去,往往可能花費數(shù)分鐘。
接上篇 Web3.js,這節(jié)課繼續(xù)學(xué)習(xí)Web3.js 的相關(guān)知識。一、發(fā)送事務(wù)
這下我們的界面能檢測用戶的 MetaMask 賬戶,并自動在首頁顯示它們的僵尸大軍了,有沒有很棒?
現(xiàn)在我們來看看用 send 函數(shù)來修改我們智能合約里面的數(shù)據(jù)。
相對 call 函數(shù),send 函數(shù)有如下主要區(qū)別:
1、send 一個事務(wù)需要一個 from 地址來表明誰在調(diào)用這個函數(shù)(也就是你 Solidity 代碼里的 msg.sender )。 我們需要這是我們 DApp 的用戶,這樣一來 MetaMask 才會彈出提示讓他們對事務(wù)簽名。
2、send 一個事務(wù)將花費 gas
3、在用戶 send 一個事務(wù)到該事務(wù)對區(qū)塊鏈產(chǎn)生實際影響之間有一個不可忽略的延遲。這是因為我們必須等待事務(wù)被包含進(jìn)一個區(qū)塊里,以太坊上一個區(qū)塊的時間平均下來是15秒左右。如果當(dāng)前在以太坊上有大量掛起事務(wù)或者用戶發(fā)送了過低的 gas 價格,我們的事務(wù)可能需要等待數(shù)個區(qū)塊才能被包含進(jìn)去,往往可能花費數(shù)分鐘。
所以在我們的代碼中我們需要編寫邏輯來處理這部分異步特性。
生成一個僵尸我們來看一個合約中一個新用戶將要調(diào)用的第一個函數(shù): createRandomZombie.
作為復(fù)習(xí),這里是合約中的 Solidity 代碼:
function createRandomZombie(string _name) public { require(ownerZombieCount[msg.sender] == 0); uint randDna = _generateRandomDna(_name); randDna = randDna - randDna % 100; _createZombie(_name, randDna); }
這是如何在用 MetaMask 在 Web3.js 中調(diào)用這個函數(shù)的示例:
function createRandomZombie(name) { // 這將需要一段時間,所以在界面中告訴用戶這一點 // 事務(wù)被發(fā)送出去了 $("#txStatus").text("正在區(qū)塊鏈上創(chuàng)建僵尸,這將需要一會兒..."); // 把事務(wù)發(fā)送到我們的合約: return CryptoZombies.methods.createRandomZombie(name) .send({ from: userAccount }) .on("receipt", function(receipt) { $("#txStatus").text("成功生成了 " + name + "!"); // 事務(wù)被區(qū)塊鏈接受了,重新渲染界面 getZombiesByOwner(userAccount).then(displayZombies); }) .on("error", function(error) { // 告訴用戶合約失敗了 $("#txStatus").text(error); }); }
我們的函數(shù) send 一個事務(wù)到我們的 Web3 提供者,然后鏈?zhǔn)教砑右恍┦录O(jiān)聽:
receipt 將在合約被包含進(jìn)以太坊區(qū)塊上以后被觸發(fā),這意味著僵尸被創(chuàng)建并保存進(jìn)我們的合約了。
error 將在事務(wù)未被成功包含進(jìn)區(qū)塊后觸發(fā),比如用戶未支付足夠的 gas。我們需要在界面中通知用戶事務(wù)失敗以便他們可以再次嘗試。
注意:你可以在調(diào)用 send 時選擇指定 gas 和 gasPrice, 例如: .send({ from: userAccount, gas: 3000000 })。如果你不指定,MetaMask 將讓用戶自己選擇數(shù)值。實戰(zhàn)演練
我們添加了一個div, 指定 ID 為 txStatus — 這樣我們可以通過更新這個 div 來通知用戶事務(wù)的狀態(tài)。
1、在 displayZombies下面, 復(fù)制粘貼上面 createRandomZombie 的代碼。
2、我們來實現(xiàn)另外一個函數(shù) feedOnKitty:
調(diào)用 feedOnKitty 的邏輯幾乎一樣 — 我們將發(fā)送一個事務(wù)來調(diào)用這個函數(shù),并且成功的事務(wù)會為我們創(chuàng)建一個僵尸,所以我們希望在成功后重新繪制界面。
在 createRandomZombie 下面復(fù)制粘貼它的代碼,改動這些地方:
a) 給其命名為 feedOnKitty, 它將接收兩個參數(shù) zombieId 和 kittyId
b) #txStatus 的文本內(nèi)容將更新為: "正在吃貓咪,這將需要一會兒..."
c) 讓其調(diào)用我們合約里面的 feedOnKitty 函數(shù)并傳入相同的參數(shù)
d) #txStatus 里面的的成功信息應(yīng)該是 "吃了一只貓咪并生成了一只新僵尸!"
index.html
二、調(diào)用Payable函數(shù)CryptoZombies front-end
attack, changeName, 以及 changeDna 的邏輯將非常雷同,所以本課將不會花時間在上面。
實際上,在調(diào)用這些函數(shù)的時候已經(jīng)有了非常多的重復(fù)邏輯。所以最好是重構(gòu)代碼把相同的代碼寫成一個函數(shù)。(并對txStatus使用模板系統(tǒng)——我們已經(jīng)看到用類似 Vue.js 類的框架是多么整潔)
我們來看看另外一種 Web3.js 中需要特殊對待的函數(shù) — payable 函數(shù)。
升級回憶一下在 ZombieHelper 里面,我們添加了一個 payable 函數(shù),用戶可以用來升級:
function levelUp(uint _zombieId) external payable { require(msg.value == levelUpFee); zombies[_zombieId].level++; }
和函數(shù)一起發(fā)送以太非常簡單,只有一點需要注意: 我們需要指定發(fā)送多少 wei,而不是以太。
啥是 Wei?一個 wei 是以太的最小單位 — 1 ether 等于 10^18 wei
太多0要數(shù)了,不過幸運(yùn)的是 Web3.js 有一個轉(zhuǎn)換工具來幫我們做這件事:
// 把 1 ETH 轉(zhuǎn)換成 Wei web3js.utils.toWei("1", "ether");
在我們的 DApp 里, 我們設(shè)置了 levelUpFee = 0.001 ether,所以調(diào)用 levelUp 方法的時候,我們可以讓用戶用以下的代碼同時發(fā)送 0.001 以太:
CryptoZombies.methods.levelUp(zombieId) .send({ from: userAccount, value: web3js.utils.toWei("0.001","ether") })實戰(zhàn)演練
在 feedOnKitty 下面添加一個 levelUp 方法。代碼和 feedOnKitty 將非常相似。不過:
1、函數(shù)將接收一個參數(shù), zombieId
2、在發(fā)送事務(wù)之前,txStatus 的文本應(yīng)該是 "正在升級您的僵尸..."
3、當(dāng)它調(diào)用合約里的levelUp時,它應(yīng)該發(fā)送"0.001" ETH,并用 toWei 轉(zhuǎn)換,如同上面例子里那樣。
4、成功之后應(yīng)該顯示 "不得了了!僵尸成功升級啦!"
5、我們 不 需要在調(diào)用 getZombiesByOwner 后重新繪制界面 — 因為在這里我們只是修改了僵尸的級別而已。
index.html
三、訂閱事件CryptoZombies front-end
如你所見,通過 Web3.js 和合約交互非常簡單直接——一旦你的環(huán)境建立起來, call 函數(shù)和 send 事務(wù)和普通的網(wǎng)絡(luò)API并沒有多少不同。
還有一點東西我們想要講到——訂閱合約事件
監(jiān)聽新事件如果你還記得 zombiefactory.sol,每次新建一個僵尸后,我們會觸發(fā)一個 NewZombie 事件:
event NewZombie(uint zombieId, string name, uint dna);
在 Web3.js里, 你可以 訂閱 一個事件,這樣你的 Web3 提供者可以在每次事件發(fā)生后觸發(fā)你的一些代碼邏輯:
cryptoZombies.events.NewZombie() .on("data", function(event) { let zombie = event.returnValues; console.log("一個新僵尸誕生了!", zombie.zombieId, zombie.name, zombie.dna); }).on("error", console.error);
注意這段代碼將在 任何 僵尸生成的時候激發(fā)一個警告信息——而不僅僅是當(dāng)前用用戶的僵尸。如果我們只想對當(dāng)前用戶發(fā)出提醒呢?
使用indexed為了篩選僅和當(dāng)前用戶相關(guān)的事件,我們的 Solidity 合約將必須使用 indexed 關(guān)鍵字,就像我們在 ERC721 實現(xiàn)中的Transfer 事件中那樣:
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
在這種情況下, 因為_from 和 _to 都是 indexed,這就意味著我們可以在前端事件監(jiān)聽中過濾事件.
cryptoZombies.events.Transfer({ filter: { _to: userAccount } }) .on("data", function(event) { let data = event.returnValues; // 當(dāng)前用戶更新了一個僵尸!更新界面來顯示 }).on("error", console.error);
看到了吧, 使用 event 和 indexed 字段對于監(jiān)聽合約中的更改并將其反映到 DApp 的前端界面中是非常有用的做法。
查詢過去的事件我們甚至可以用 getPastEvents 查詢過去的事件,并用過濾器 fromBlock 和 toBlock 給 Solidity 一個事件日志的時間范圍("block" 在這里代表以太坊區(qū)塊編號):
cryptoZombies.getPastEvents("NewZombie", { fromBlock: 0, toBlock: "latest" }) .then(function(events) { // events 是可以用來遍歷的 `event` 對象 // 這段代碼將返回給我們從開始以來創(chuàng)建的僵尸列表 });
因為你可以用這個方法來查詢從最開始起的事件日志,這就有了一個非常有趣的用例: 用事件來作為一種更便宜的存儲。
若你還能記得,在區(qū)塊鏈上保存數(shù)據(jù)是 Solidity 中最貴的操作之一。但是用事件就便宜太多太多了。
這里的短板是,事件不能從智能合約本身讀取。但是,如果你有一些數(shù)據(jù)需要永久性地記錄在區(qū)塊鏈中以便可以在應(yīng)用的前端中讀取,這將是一個很好的用例。這些數(shù)據(jù)不會影響智能合約向前的狀態(tài)。
舉個栗子,我們可以用事件來作為僵尸戰(zhàn)斗的歷史紀(jì)錄——我們可以在每次僵尸攻擊別人以及有一方勝出的時候產(chǎn)生一個事件。智能合約不需要這些數(shù)據(jù)來計算任何接下來的事情,但是這對我們在前端向用戶展示來說是非常有用的東西。
Web3.js事件和MetaMask上面的示例代碼是針對 Web3.js 最新版1.0的,此版本使用了 WebSockets 來訂閱事件。
但是,MetaMask 尚且不支持最新的事件 API (盡管如此,他們已經(jīng)在實現(xiàn)這部分功能了, 點擊這里 查看進(jìn)度)
所以現(xiàn)在我們必須使用一個多帶帶 Web3 提供者,它針對事件提供了WebSockets支持。 我們可以用 Infura 來像實例化第二份拷貝:
var web3Infura = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws")); var czEvents = new web3Infura.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress);
然后我們將使用 czEvents.events.Transfer 來監(jiān)聽事件,而不再使用 cryptoZombies.events.Transfer。我們將繼續(xù)在課程的其他部分使用 cryptoZombies.methods。
將來,在 MetaMask 升級了 API 支持 Web3.js 后,我們就不用這么做了。但是現(xiàn)在我們還是要這么做,以使用 Web3.js 更好的最新語法來監(jiān)聽事件。
放在一起來添加一些代碼監(jiān)聽 Transfer 事件,并在當(dāng)前用戶獲得一個新僵尸的時候為他更新界面。
我們將需要在 startApp 底部添加代碼,以保證在添加事件監(jiān)聽器之前 cryptoZombies 已經(jīng)初始化了。
1、在 startApp()底部,為 cryptoZombies.events.Transfer 復(fù)制粘貼上面的2行事件監(jiān)聽代碼塊
2、復(fù)制監(jiān)聽 Transfer 事件的代碼塊,并用 _to: userAccount 過濾。要記得把 cryptoZombies 換成 czEvents 好在這 里使用 Infura 而不是 MetaMask 來作為提供者。
3、用 getZombiesByOwner(userAccount).then(displayZombies); 來更新界面
index.html
CryptoZombies front-end
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/24152.html
摘要:這些天,為了錄制以太坊開發(fā)實戰(zhàn)課程,我準(zhǔn)備把文檔全部翻譯一下并做適當(dāng)?shù)难a(bǔ)充,目前版本已經(jīng)翻譯完成,歡迎大家前往查閱。 這些天,為了錄制以太坊DAPP開發(fā)實戰(zhàn)課程,我準(zhǔn)備把web3文檔全部翻譯一下(并做適當(dāng)?shù)难a(bǔ)充),目前web3.js 0.20.x 版本 已經(jīng)翻譯完成,歡迎大家前往查閱。 這里還幾個實用DEMO,供大家參考: 使用web3.js API在頁面中轉(zhuǎn)賬 web3.js 0....
摘要:這些天,為了錄制以太坊開發(fā)實戰(zhàn)課程,我準(zhǔn)備把文檔全部翻譯一下并做適當(dāng)?shù)难a(bǔ)充,目前版本已經(jīng)翻譯完成,歡迎大家前往查閱。 這些天,為了錄制以太坊DAPP開發(fā)實戰(zhàn)課程,我準(zhǔn)備把web3文檔全部翻譯一下(并做適當(dāng)?shù)难a(bǔ)充),目前web3.js 0.20.x 版本 已經(jīng)翻譯完成,歡迎大家前往查閱。 這里還幾個實用DEMO,供大家參考: 使用web3.js API在頁面中轉(zhuǎn)賬 web3.js 0....
摘要:本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接與智能合約交互實戰(zhàn)原文已更新,請讀者前往原文閱讀寫在前面在最初學(xué)習(xí)以太坊的時候,很多人都是自己創(chuàng)建以太坊節(jié)點后,使用與之交互。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:Web3與智能合約交互實戰(zhàn)原文已更新,請讀者前往原文閱讀 寫在前面 在最初學(xué)習(xí)以太坊的時候,很多人都是自己創(chuàng)建以太坊節(jié)點后,使用geth與之交互。這種使用命令行交互的方法雖然讓很多程序員...
摘要:首先我們需要要記住,以太坊是由共享同一份數(shù)據(jù)的相同拷貝的節(jié)點構(gòu)成的。你可以運(yùn)行你自己的以太坊節(jié)點來作為。在你部署智能合約以后,它將獲得一個以太坊上的永久地址。如果你還記得第二課,在以太坊上的地址是。 通過前邊的學(xué)習(xí),DApp 的 Solidity 合約部分就完成了。現(xiàn)在我們來做一個基本的網(wǎng)頁好讓你的用戶能玩它。 要做到這一點,我們將使用以太坊基金發(fā)布的 JavaScript 庫 —— ...
摘要:本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接控制臺使用及使用實戰(zhàn)原文已更新,請讀者前往原文閱讀在開發(fā)以太坊去中心化應(yīng)用,免不了和以太坊進(jìn)行交互,那就離不開。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:Geth控制臺使用及 Web3.js 使用實戰(zhàn)原文已更新,請讀者前往原文閱讀 在開發(fā)以太坊去中心化應(yīng)用,免不了和以太坊進(jìn)行交互,那就離不開Web3。Geth 控制臺(REPL)實現(xiàn)了所有的web3 A...
閱讀 2849·2021-11-22 14:45
閱讀 2998·2021-09-10 11:26
閱讀 3368·2021-09-07 10:18
閱讀 2267·2019-08-30 14:08
閱讀 679·2019-08-29 12:22
閱讀 1440·2019-08-26 13:48
閱讀 2661·2019-08-26 10:24
閱讀 1207·2019-08-23 18:35