Web3-Web3.js核心操作:Metamask、合約調用、事件訂閱全指南

Web3-Web3.js核心操作:Metamask、合約調用、事件訂閱全指南

我們做了Solidity的合約代碼,但是合約僅僅是一個后端邏輯;我們想要讓用戶來操作你的邏輯還需要做一個基本的網頁。如果要做一個基本的網頁,我們就要使用到以太坊基金發布的JavaScript庫–Web3.js

什么事Web3.js

以太坊網絡是由節點組成的,每一個節點都包含了區塊鏈的一份拷貝。當你想要調用一份智能合約的一個方法,你需要從其中一個節點中查找并告訴它:

  1. 智能合約的地址
  2. 你想調用的方法,以及
  3. 你想傳入那個方法的參數

以太坊節點只能識別一種叫做 *JSON-RPC* 的語言。這種語言直接讀起來并不好懂。當你你想調用一個合約的方法的時候,需要發送的查詢語句將會是這樣的:

{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","gas":"0x76c0","gasPrice":"0x9184e72a000","value":"0x9184e72a","data":"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}

幸運的是 Web3.js 把這些令人討厭的查詢語句都隱藏起來了, 所以你只需要與方便易懂的 JavaScript 界面進行交互即可。

你不需要構建上面的查詢語句,在你的代碼中調用一個函數看起來將是這樣:

CryptoZombies.methods.createRandomZombie("Vitalik Nakamoto 🤔").send({ from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", gas: "3000000" })

準備工作

// 用 NPM
npm install web3// 用 Yarn
yarn add web3// 用 Bower
bower install web3// ...或者其他。

你可以從 github 直接下載壓縮后的 .js 文件 然后包含到你的項目文件中:

<script language="javascript" type="text/javascript" src="web3.min.js"></script>

Web3提供者

現在項目中有了Web3.js, 我們需要初始化它然后和區塊鏈對話。首先我們需要 *Web3 Provider*.

要記住,以太坊是由共享同一份數據的相同拷貝的 節點 構成的。 在 Web3.js 里設置 Web3 的 Provider(提供者) 告訴我們的代碼應該和 哪個節點 交互來處理我們的讀寫。這就好像在傳統的 Web 應用程序中為你的 API 調用設置遠程 Web 服務器的網址。

你可以運行你自己的以太坊節點來作為 Provider。 不過,有一個第三方的服務,可以讓你的生活變得輕松點,讓你不必為了給你的用戶提供DApp而維護一個以太坊節點— *Infura*.

Infura

Infura 是一個服務,它維護了很多以太坊節點并提供了一個緩存層來實現高速讀取。你可以用他們的 API 來免費訪問這個服務。 用 Infura 作為節點提供者,你可以不用自己運營節點就能很可靠地向以太坊發送、接收信息。

你可以通過這樣把 Infura 作為你的 Web3 節點提供者:

var web3 = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));

不過,因為我們的 DApp 將被很多人使用,這些用戶不單會從區塊鏈讀取信息,還會向區塊鏈 入信息,我們需要用一個方法讓用戶可以用他們的私鑰給事務簽名。

注意: 以太坊 (以及通常意義上的 blockchains )使用一個公鑰/私鑰對來對給事務做數字簽名。把它想成一個數字簽名的異常安全的密碼。這樣當我修改區塊鏈上的數據的時候,我可以用我的公鑰來 證明 我就是簽名的那個。但是因為沒人知道我的私鑰,所以沒人能偽造我的事務。

加密學非常復雜,所以除非你是個專家并且的確知道自己在做什么,你最好不要在你應用的前端中管理你用戶的私鑰。

不過幸運的是,你并不需要,已經有可以幫你處理這件事的服務了: *Metamask*.

Metamask

Metamask 是 Chrome 和 Firefox 的瀏覽器擴展, 它能讓用戶安全地維護他們的以太坊賬戶和私鑰, 并用他們的賬戶和使用 Web3.js 的網站互動(如果你還沒用過它,你肯定會想去安裝的——這樣你的瀏覽器就能使用 Web3.js 了,然后你就可以和任何與以太坊區塊鏈通信的網站交互了)

作為開發者,如果你想讓用戶從他們的瀏覽器里通過網站和你的DApp交互(就像我們在 CryptoZombies 游戲里一樣),你肯定會想要兼容 Metamask 的。

注意: Metamask 默認使用 Infura 的服務器做為 web3 提供者。 就像我們上面做的那樣。不過它還為用戶提供了選擇他們自己 Web3 提供者的選項。所以使用 Metamask 的 web3 提供者,你就給了用戶選擇權,而自己無需操心這一塊。

使用Metamask的Web3的提供者

Metamask 把它的 web3 提供者注入到瀏覽器的全局 JavaScript對象web3中。所以你的應用可以檢查 web3 是否存在。若存在就使用 web3.currentProvider 作為它的提供者。

這里是一些 Metamask 提供的示例代碼,用來檢查用戶是否安裝了MetaMask,如果沒有安裝就告訴用戶需要安裝MetaMask來使用我們的應用。

window.addEventListener('load', function() {// 檢查web3是否已經注入到(Mist/MetaMask)if (typeof web3 !== 'undefined') {// 使用 Mist/MetaMask 的提供者web3js = new Web3(web3.currentProvider);} else {// 處理用戶沒安裝的情況, 比如顯示一個消息// 告訴他們要安裝 MetaMask 來使用我們的應用}// 現在你可以啟動你的應用并自由訪問 Web3.js:startApp()})

你可以在你所有的應用中使用這段樣板代碼,好檢查用戶是否安裝以及告訴用戶安裝 MetaMask。

注意: 除了MetaMask,你的用戶也可能在使用其他他的私鑰管理應用,比如 Mist 瀏覽器。不過,它們都實現了相同的模式來注入 web3 變量。所以我這里描述的方法對兩者是通用的。

和合約對話

我們已經用 MetaMask 的 Web3 提供者初始化了 Web3.js。接下來就讓它和我們的智能合約對話。

Web3.js 需要兩個東西來和你的合約對話: 它的 地址 和它的 *ABI*

合約地址

在寫完智能合約后,我們需要去編譯合約然后把合約部署到以太坊。部署后的合約會生成一個合約地址,就是以太坊上的永久地址。

合約 ABI

另一個 Web3.js 為了要和你的智能合約對話而需要的東西是 *ABI*

ABI 意為應用二進制接口(Application Binary Interface)。 基本上,它是以 JSON 格式表示合約的方法,告訴 Web3.js 如何以合同理解的方式格式化函數調用。

當你編譯你的合約向以太坊部署時, Solidity 編譯器會給你 ABI,所以除了合約地址,你還需要把這個也復制下來。

因為我們這一課不會講述部署,所以現在我們已經幫你編譯了 ABI 并放在了名為cryptozombies_abi.js,文件中,保存在一個名為 cryptoZombiesABI 的變量中。

如果我們將cryptozombies_abi.js 包含進我們的項目,我們就能通過那個變量訪問 CryptoZombies ABI 。

var myContract = new web3js.eth.Contract(myABI, myContractAddress);

調用合約函數

Web3.js 有兩個方法來調用我們合約的函數: call and send.

Call

call 用來調用 viewpure 函數。它只運行在本地節點,不會在區塊鏈上創建事務。

復習: viewpure 函數是只讀的并不會改變區塊鏈的狀態。它們也不會消耗任何gas。用戶也不會被要求用MetaMask對事務簽名。

使用 Web3.js,你可以如下 call 一個名為myMethod的方法并傳入一個 123 作為參數:

myContract.methods.myMethod(123).call()
Send

send 將創建一個事務并改變區塊鏈上的數據。你需要用 send 來調用任何非 view 或者 pure 的函數。

注意: send 一個事務將要求用戶支付gas,并會要求彈出對話框請求用戶使用 Metamask 對事務簽名。在我們使用 Metamask 作為我們的 web3 提供者的時候,所有這一切都會在我們調用 send() 的時候自動發生。而我們自己無需在代碼中操心這一切,挺爽的吧。

使用 Web3.js, 你可以像這樣 send 一個事務調用myMethod 并傳入 123 作為參數:

myContract.methods.myMethod(123).send()

語法幾乎 call()一模一樣。

MetaMask賬戶

MetaMask 允許用戶在擴展中管理多個賬戶。

我們可以通過這樣來獲取 web3 變量中激活的當前賬戶:

var userAccount = web3.eth.accounts[0]

因為用戶可以隨時在 MetaMask 中切換賬戶,我們的應用需要監控這個變量,一旦改變就要相應更新界面。

我們可以通過 setInterval 方法來做:

var accountInterval = setInterval(function() {// 檢查賬戶是否切換if (web3.eth.accounts[0] !== userAccount) {userAccount = web3.eth.accounts[0];// 調用一些方法來更新界面updateInterface();}
}, 100);

這段代碼做的是,每100毫秒檢查一次 userAccount 是否還等于 web3.eth.accounts[0] (比如:用戶是否還激活了那個賬戶)。若不等,則將 當前激活用戶賦值給 userAccount,然后調用一個函數來更新界面。

發送事務

現在我們來看看用 send 函數來修改我們智能合約里面的數據。

相對 call 函數,send 函數有如下主要區別:

  1. send 一個事務需要一個 from 地址來表明誰在調用這個函數(也就是你 Solidity 代碼里的 msg.sender )。 我們需要這是我們 DApp 的用戶,這樣一來 MetaMask 才會彈出提示讓他們對事務簽名。

  2. send 一個事務將花費 gas

  3. 在用戶 send 一個事務到該事務對區塊鏈產生實際影響之間有一個不可忽略的延遲。這是因為我們必須等待事務被包含進一個區塊里,以太坊上一個區塊的時間平均下來是15秒左右。如果當前在以太坊上有大量掛起事務或者用戶發送了過低的 gas 價格,我們的事務可能需要等待數個區塊才能被包含進去,往往可能花費數分鐘。

    所以在我們的代碼中我們需要編寫邏輯來處理這部分異步特性。

我們的函數 send 一個事務到我們的 Web3 提供者,然后鏈式添加一些事件監聽:

  • receipt 將在合約被包含進以太坊區塊上以后被觸發,這意味著僵尸被創建并保存進我們的合約了。
  • error 將在事務未被成功包含進區塊后觸發,比如用戶未支付足夠的 gas。我們需要在界面中通知用戶事務失敗以便他們可以再次嘗試。

注意:你可以在調用 send 時選擇指定 gasgasPrice, 例如: .send({ from: userAccount, gas: 3000000 })。如果你不指定,MetaMask 將讓用戶自己選擇數值。

調用 Payable 函數

attack, changeName, 以及 changeDna 的邏輯將非常雷同,所以本課將不會花時間在上面。

實際上,在調用這些函數的時候已經有了非常多的重復邏輯。所以最好是重構代碼把相同的代碼寫成一個函數。(并對txStatus使用模板系統——我們已經看到用類似 Vue.js 類的框架是多么整潔)

我們來看看另外一種 Web3.js 中需要特殊對待的函數 — 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") })

訂閱事件

通過 Web3.js 和合約交互非常簡單直接——一旦你的環境建立起來, call 函數和 send 事務和普通的網絡API并沒有多少不同。

還有一點東西我們想要講到——訂閱合約事件

在 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);

看到了吧, 使用 eventindexed 字段對于監聽合約中的更改并將其反映到 DApp 的前端界面中是非常有用的做法。

查詢過去的事件

我們甚至可以用 getPastEvents 查詢過去的事件,并用過濾器 fromBlocktoBlock 給 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 更好的最新語法來監聽事件。

總結

Web3.js是以太坊官方JavaScript庫,簡化了與智能合約的交互過程。本文介紹了Web3.js的核心功能與使用方法:1) 通過Web3提供者(如Infura或Metamask)連接以太坊節點,2) 使用合約地址和ABI與智能合約對話,3) 區分call和send方法調用合約函數。重點講解了如何集成Metamask進行用戶賬戶管理,包括檢測賬戶變更和事務簽名。該指南為開發者提供了Web3.js的基本操作框架,包括初始化、合約交互和事件處理等關鍵環節。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/85023.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/85023.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/85023.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

四色(定理/猜想)染色算法小軟件Version1.11

四色(定理/猜想)染色算法小軟件Version1.11 2025.6.16 開發者&#xff1a;路人甲/打醬油 增加了【自思肯普法】 為什么加上【自思】兩字&#xff1f;因為我也看不明英文的PDF的四色定理證明文檔&#xff0c;分什么成千上百種類來證明。我就是百度下&#xff0c;看相關介紹&am…

【Linux驅動開發 ---- 2_深入理解內核模塊】

Linux驅動開發 ---- 2_深入理解內核模塊 目錄 Linux驅動開發 ---- 2_深入理解內核模塊學習目標時間安排建議 理論學習1. 什么是內核模塊&#xff1f;2. 模塊加載與卸載3. 內核模塊開發基礎 實踐任務任務1&#xff1a;準備開發環境任務2&#xff1a;編寫簡單內核模塊任務3&#…

Linux SUID提權 案例以及知識點提高分析

目錄 &#x1f4da; Linux SUID 提權原理與紅隊實踐 &#x1f680; 概述 &#x1f6e0;? SUID 提權原理 核心機制 技術棧 &#x1f50d; 案例背景&#xff1a;sudo -l 與 .monit 腳本 案例信息 腳本內容 &#x1f9ea; 提權步驟分解 &#x1f4cb; 1. 檢查 sudo 權限…

S參數與串擾-信號完整性分析

S參數與串擾: 四端口網絡S參數中&#xff0c;仍表示反射&#xff0c;表示信號的傳輸。根據S參數的定義&#xff0c;和兩個參數的含義為 當只有端口1有正弦信號激勵源時&#xff0c;從端口3和端口4出來的正弦信號只能是互連結構內部耦合過來的&#xff0c;因此表示的是近端串擾…

Android OkHttp 框架超時設置詳解

OkHttp 提供了四種不同的超時設置&#xff0c;每種針對網絡請求的不同階段&#xff1a; 1. callTimeout (調用超時) 作用&#xff1a;控制整個調用從開始到結束的總時間&#xff0c;包括所有重定向和重試 默認值&#xff1a;0 (不超時) 場景&#xff1a;當你希望限制整個請求…

如何讓LLM通過反思來提升生成sql的正確率 - 以Gemini生成sql為例

什么是Agent Reflection 通常指 “智能體反思”&#xff0c;即讓 AI 系統通過自我反思機制優化決策或任務處理過程&#xff0c;類似人類通過復盤改進策略。 創建一個 SQL Agent 導入相關的庫 import openai import pandas as pd import sqlite3 from util.get_schema impor…

數字IC學習筆記(二)

數字IC學習筆記&#xff08;二&#xff09; 宏定義&#xff0c;異步FIFO, 時鐘來源&#xff0c;復位信號 文章目錄 數字IC學習筆記&#xff08;二&#xff09;1. define宏定義的使用2&#xff0c;異步FIFO原理3&#xff0c;時鐘來源4&#xff0c;復位參考資料 1. define宏定義的…

日志輸出功能

當程序運行出現問題時&#xff0c;日志記錄是一種非常有用的工具&#xff0c;它可以幫助我們追蹤和定位問題。在 MicroPython 中&#xff0c;可以使用 log 模塊來記錄程序運行中的信息。本文將介紹 log 模塊的使用方法和常見功能。 日志級別 log.DEBUG 常量&#xff0c;用來…

【JVM】- 類加載與字節碼結構1

類文件結構 ClassFile {u4 magic;u2 minor_version;u2 major_version;u2 constant_pool_count;cp_info constant_pool[constant_pool_count-1];u2 access_flags;u2 this_class;u2 …

Android及Harmonyos實現圖片進度顯示效果

鴻蒙Harmonyos實現&#xff0c;使用ImageKnife自定義transform來實現圖片進度效果 import { Context } from ohos.abilityAccessCtrl; import { image } from kit.ImageKit; import { drawing } from kit.ArkGraphics2D; import { GrayScaleTransformation, PixelMapTransform…

linux 中的自動化之systemd

linux | 自動化之systemd linux 中的自動化之systemd 學習總是循序漸進的。 linux 中程序的自動化&#xff0c;包括早期手動啟動&#xff0c;查看啟動后的日志、分析啟動情況&#xff0c;再到后面封裝腳本(大致要求啟動后檢查是否掛了&#xff0c;掛了拉起&#xff0c;沒掛跳過…

【編譯工具】CodeRider 2.0:馭碼 CodeRider 2.0 全流程智能研發協作平臺深度技術測評報告

目錄 前言&#xff1a;CodeRider 2.0 簡介 &#xff08;1&#xff09;核心功能 &#xff08;2&#xff09;適用場景 &#xff08;3&#xff09;優勢 一、產品概述與技術架構 &#xff08;1&#xff09;產品定位與核心價值 &#xff08;2&#xff09;技術架構解析 &…

抓包 TCP 四次揮手報文

文章目錄 抓包 TCP 三次握手報文一、第一次揮手二、第二次揮手三、第三次揮手四、第四次揮手 抓包 TCP 三次握手報文 抓包 TCP 三次握手報文 一、第一次揮手 二、第二次揮手 三、第三次揮手 四、第四次揮手

KMP(Kotlin Multiplatform)發布Web版本亂碼

一、背景 最近用KMP嘗試運行在Android、iOS、desktop都成功了&#xff0c;網絡數據訪問也正常。 可是當運行wasmJs的時候遇到了2個較大的問題。 中文字體出現亂碼。 出現了跨域問題。 首先貼一下每個平臺的運行截圖&#xff1a; Android iOS Desktop 二、問題 當web跑起…

一個應用程序或移動網站項目提供最佳UI解決方案

一個應用程序或移動網站項目提供最佳UI解決方案 此套件是用大量的愛和辛勤工作制作的&#xff0c;為您的下一個應用程序或移動網站項目提供最佳解決方案。120個完全可編輯的界面&#xff0c;分為10個類別和2種文件格式&#xff0c;Photoshop和AI。簡單易用的結構將允許您以所…

Android studio打包生成jar包文件

Android studio打包生成jar包文件 一 項目配置1.修改 app/build.gradle2.修改 AndroidManifest.xml 二 打 Jar 包1.修改 app/build.gradle2.編譯生成 Jar 包 一 項目配置 1.修改 app/build.gradle 將com.android.application改成com.android.library注釋掉applicationId 2.…

JAVA類加載機制(jdk8)

三句話總結JDK8的類加載機制&#xff1a; 類緩存&#xff1a;每個類加載器對他加載過的類都有一個緩存。雙親委派&#xff1a;向上委托查找&#xff0c;向下委托加載。沙箱保護機制&#xff1a;不允許應用程序加載JDK內部的系統類。 JDK8的類加載體系 類加載器的核心方法 //…

更進一步深入的研究ObRegisterCallBack

引入 我們如果想hook對象的回調,在上篇文章里我們已經知道了對象回調函數存在一個列表里面&#xff0c;我們通過dt可以看見&#xff0c;這里他是一個LIST_ENTRY結構&#xff0c;但是實際調用的時候&#xff0c;這樣是行不通的&#xff0c;說明它結構不對 0: kd> dt _OBJEC…

Nginx-3 Nginx 的負載均衡策略

Nginx-3 Nginx 的負載均衡策略 Nginx 的負載均衡其實就是指將請求按照一定的策略轉發給服務集群中的一臺&#xff0c;提高了服務集群的可用性&#xff0c;解決數據流量過大、網絡負荷過重的問題。 AKF 擴展立方體 分為 3 個方向負載&#xff1a; x 軸&#xff1a;增加實例數…