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