# DApp建立: 查詢區塊與交易資訊 ## 查詢區塊與交易資訊 在區塊鏈中 最重要的其中一個核心理念就是 **"不可篡改性"** 自然會很希望能查詢到 **所期待的歷史資訊** 因此透過查詢能獲得區塊鏈資訊 亦是**去中心化應用程序(DApp)中非常重要的一環** 這篇將以下重點進行敘述: 1. 專案規劃 2. 透過 web3.js 獲取資料 3. 透過 ethers.js 獲取資料 4. 連線至 乙太坊 主網 5. 最終呈現畫面 **另外可參考 GitHub 專案位置:** https://github.com/weiawesome/dapp_website ## 專案規劃 ### 佈局規劃 ![](https://hackmd.io/_uploads/r1_Y5jZbp.png) * 標題 ```寫下本頁面的目標``` * 選擇要連接的網路 ```可以自己填寫要連接的乙太坊網路``` * 區塊查詢區域 ```各種查詢區塊資訊的方式 (左 web3.js 右 ethers.js)``` * 交易查詢區域 ```各種查詢交易資訊的方式 (左 web3.js 右 ethers.js)``` ### 架構規劃 ``` ├── src │ ├── app │ ├── pages │ │ ├── get_information.tsx │ ├── style │ │ ├── get_information.css │ ``` "src/app/pages/get_information.tsx" 邏輯與基本布局 "src/app/style/get_information.css" 對上述畫面的修飾 **因此 最後畫面會顯示在 /get_information 這個路由下** ### 區塊 與 交易 資料呈現 ```typescript // 區塊的資訊 class BlockInfo{ // 區塊高度 BlockHeight:number // 區塊哈希值 BlockHash:string // 前一區塊哈希值 PreviousHash:string } // 交易的資訊 class TransactionInfo{ // 交易所在區塊的區塊高度 BlockHeight:number // 交易哈希值 Hash:string // 交易發起人地址 From:string // 交易接收人地址 To:string // 交易金額 Amount:string // 交易手續費 Fee:string } ``` * **區塊 與 交易** 由於二者完整資訊過多 不方便作為呈現 因此就**選擇數個屬性作為展示的效果** ## 透過 web3.js 獲取資料 ### 區塊方面 ```typescript // 獲取當前最新區塊高度 const getBlockHeightWeb3=()=>{ const url=urlRef.current?.value.toString() const web3 = new Web3(url); web3.eth.getBlockNumber() .then(blockNumber => { console.log('最新區塊高度:', blockNumber); setLastBlockNumberWeb3(Number(blockNumber)); }) .catch(error => { console.error('獲取區塊高度時發生錯誤:', error); }); } // 從區塊高度尋找區塊 const getBlockByHeightWeb3=()=>{ const url=urlRef.current?.value.toString() const web3 = new Web3(url); const blockNumber=Number(heightRef.current?.value); web3.eth.getBlock(blockNumber).then(blockInfo => { if (blockInfo) { let tmp=new BlockInfo(); tmp.BlockHeight=Number(blockInfo.number); tmp.BlockHash=blockInfo.hash; tmp.PreviousHash=blockInfo.parentHash; setBlockByHeightWeb3(tmp); console.log('區塊資訊:', blockInfo); } else { alert(`找不到區塊號 ${blockNumber} 的資訊。`); } }).catch(error => { alert('發生錯誤:'+ error); }); } // 從區塊哈希尋找區塊 const getBlockByHashWeb3=()=>{ const url=urlRef.current?.value.toString() const web3 = new Web3(url); const blockHash=hashRef.current?.value; web3.eth.getBlock(blockHash,true).then(blockInfo => { if (blockInfo) { let tmp=new BlockInfo(); tmp.BlockHeight=Number(blockInfo.number); tmp.BlockHash=blockInfo.hash; tmp.PreviousHash=blockInfo.parentHash; setBlockByHashWeb3(tmp); console.log('區塊資訊:', blockInfo); } else { alert(`找不到區塊號 ${blockHash} 的資訊。`); } }).catch(error => { alert('發生錯誤:'+ error); }); } ``` * 獲取最新區塊高度 ```其實就是獲取目前區塊鏈的長度``` * 從區塊高度尋找區塊 ```可以直接根據區塊高度尋找到相對應的區塊資訊 並更新相關資訊``` * 從區塊哈希尋找區塊 ```可以直接根據區塊哈希尋找到相對應的區塊資訊 並更新相關資訊``` ### 交易方面 ```typescript // 從交易哈希獲取交易相關資訊 const getTransactionWeb3=()=>{ const url=urlRef.current?.value.toString() const web3 = new Web3(url); const transactionHash = transactionHashRef.current?.value.toString(); web3.eth.getTransaction(transactionHash) .then(transactionInfo => { if (transactionInfo) { console.log('交易資訊:', transactionInfo); let tmp=new TransactionInfo(); tmp.From=transactionInfo.from; tmp.To=transactionInfo.to!; tmp.Amount=formatEther(transactionInfo.value); tmp.Fee=formatEther(transactionInfo.gasPrice); tmp.BlockHeight=Number(transactionInfo.blockNumber); tmp.Hash=transactionInfo.hash; setTransactionWeb3(tmp); } else { alert(`找不到交易哈希 ${transactionHash} 的資訊。`); } }).catch(error => { alert('發生錯誤:'+ error); }); } ``` * 從交易哈希獲取交易相關資訊 ```從交易哈希去尋找相符合的交易 並設定相關資訊``` ## 透過 ethers.js 獲取資料 ### 區塊方面 ```typescript // 獲取最新區塊高度 const getBlockHeightEthers=()=>{ const url=urlRef.current?.value.toString() const provider = new ethers.JsonRpcProvider(url); provider.getBlockNumber().then(blockNumber => { setLastBlockNumberEthers(blockNumber); }).catch((e)=>{ alert("error: "+e); }); } // 從區塊高度尋找區塊 const getBlockByHeightEthers=()=>{ const url=urlRef.current?.value.toString() const provider = new ethers.JsonRpcProvider(url); const blockNumber=Number(heightRef.current?.value); provider.getBlock(blockNumber).then(blockInfo => { if (blockInfo) { let tmp=new BlockInfo(); tmp.BlockHeight=blockInfo.number; tmp.BlockHash=blockInfo.hash; tmp.PreviousHash=blockInfo.parentHash; setBlockByHeightEthers(tmp); console.log('區塊資訊:', blockInfo); } else { alert(`找不到區塊號 ${blockNumber} 的資訊。`); } }).catch(error => { alert('發生錯誤:'+ error); }); } // 從區塊哈希尋找區塊 const getBlockByHashEthers=()=>{ const url=urlRef.current?.value.toString() const provider = new ethers.JsonRpcProvider(url); const blockHash=hashRef.current?.value; provider.getBlock(blockHash).then(blockInfo => { if (blockInfo) { let tmp=new BlockInfo(); tmp.BlockHeight=blockInfo.number; tmp.BlockHash=blockInfo.hash; tmp.PreviousHash=blockInfo.parentHash; setBlockByHashEthers(tmp); console.log('區塊資訊:', blockInfo); } else { alert(`找不到區塊號 ${blockHash} 的資訊。`); } }).catch(error => { alert('發生錯誤:'+ error); }); } ``` * 獲取最新區塊高度 ```可以直接獲得最新區塊的區塊高度 即獲得區塊鏈長度``` * 從區塊高度尋找區塊 ```透過指定區塊高度 獲得相對應的資訊 並設置相關變數`` * 從區塊哈希尋找區塊 ```透過指定區塊哈希 獲得相對應的資訊 並設置相關變數`` ### 交易方面 ```typescript // 從交易哈希獲取交易相關資訊 const getTransactionEthers=()=>{ const url=urlRef.current?.value.toString(); const provider = new ethers.JsonRpcProvider(url); const transactionHash=transactionHashRef.current?.value.toString(); provider.getTransaction(transactionHash).then(transactionInfo => { if (transactionInfo) { console.log('交易資訊:', transactionInfo); let tmp=new TransactionInfo(); tmp.From=transactionInfo.from; tmp.To=transactionInfo.to; tmp.Amount=formatEther(transactionInfo.value); tmp.Fee=formatEther(transactionInfo.gasPrice); tmp.BlockHeight=transactionInfo.blockNumber; tmp.Hash=transactionInfo.hash; setTransactionEthers(tmp); } else { alert(`找不到交易哈希 ${transactionHash} 的資訊。`); } }).catch(error => { alert('發生錯誤:'+ error); }); } ``` * 從交易哈希獲取交易相關資訊 ```從交易哈希去尋找相符合的交易 並設定相關資訊``` ## 連線至 乙太坊 主網 如果想要連接到以太坊主網(Ethereum Mainnet) 可以使用 以太坊節點提供者 或 自己運行的以太坊節點 以太坊節點提供者 可以使用 Infura 以下為介紹如何使用 Infura ### 註冊與登入 **Infura 官方網站:** https://www.infura.io/zh ![](https://hackmd.io/_uploads/B1iyN3-bp.png) 上圖取自於(https://www.infura.io/zh)網頁畫面 * **點選 Sign In 進行登入** ![](https://hackmd.io/_uploads/rkcNV2ZZT.png) 上圖取自於(https://www.infura.io/zh)網頁登入畫面 * **註冊與登入細節便不贅述** ### 進入主畫面 並創建 API-KEY ![](https://hackmd.io/_uploads/S1i1B2Zbp.png) 上圖取自於(https://www.infura.io/zh)網頁主畫面 * **選擇創建新API-KEY (CREATE NEW API KEY)** ![](https://hackmd.io/_uploads/S1pGHhbZa.png) 上圖取自於(https://www.infura.io/zh)網頁API-KEY創建畫面 * **選擇 Web3 API 並隨意命名** ![](https://hackmd.io/_uploads/B1nsHnWZT.png) 上圖取自於(https://www.infura.io/zh)網頁API-KEY畫面 * **下方 Ethereum 選擇 MAINNET 並且複製後方URL** ### 接下來就可以透過以上 URL 進行查詢區塊鏈資訊的互動 ## 最終呈現畫面 ### 標題 與 URL填寫位置 ![](https://hackmd.io/_uploads/B11rBi-Wa.png) ### 區塊查詢區域 ![](https://hackmd.io/_uploads/rkxGBibW6.png) ### 交易查詢區域 ![](https://hackmd.io/_uploads/r159_oWbp.png) ### 執行步驟 #### 1. 填入 URL ![](https://hackmd.io/_uploads/B1qwKnWZp.png) * 填入剛剛上方獲得的 URL #### 2. 查詢最新區塊高度 ![](https://hackmd.io/_uploads/ryMftn-ba.png) **乙太坊區塊鏈連覽器:** https://etherscan.io/blocks ![](https://hackmd.io/_uploads/Sy9ytnbZp.png) * 可透過區塊鏈連覽器 檢查資訊是否正確 * 確實與最新區塊資訊符合 #### 3. 查詢該高度區塊 詳細資料 ![](https://hackmd.io/_uploads/SkLJih-W6.png) **區塊鏈連覽器(對應區塊資料):** https://etherscan.io/block/18314410 ![](https://hackmd.io/_uploads/Hkwbj2bWT.png) 資料取自於(https://etherscan.io/block/18314410) * 資料確實相符合 不論是 區塊高度、區塊哈希、前一區塊哈希 #### 4. 根據區塊哈希查詢該個區塊 ![](https://hackmd.io/_uploads/SJNas3Wbp.png) * 確實也與上圖資訊符合 #### 5. 查詢交易 **透過以下交易為例子:** ![](https://hackmd.io/_uploads/HkuLC3b-6.png) 圖片與資料取自於(https://etherscan.io/tx/0xa674e253eec4ebf4cfd4c83f708fd32cc5e3c938a0e1d6b7ab4b15452847d1c5) * 將交易哈希進行複製 透過交易哈希進行查詢 ![](https://hackmd.io/_uploads/B1tE0nWbT.png) ![](https://hackmd.io/_uploads/HJyaAhbZa.png) 圖片與資料取自於(https://etherscan.io/tx/0xa674e253eec4ebf4cfd4c83f708fd32cc5e3c938a0e1d6b7ab4b15452847d1c5) * 確實資料都相符合 地址、金額、手續費 ``` 備註: 在此處也可以使用 Ganache 模擬區塊鏈 相對資訊可能也沒有現實區塊鏈麼複雜 也比較容易觀察 如果要使用 Ganache 模擬區塊鏈 網路 則 URL 部分直接填入 HTTP://127.0.0.1:7545 即可 (根據自己設定可能會有所不同) ``` ## 結言 現在可以輕鬆地拿到區塊鏈上所有資料 這就是**區塊鏈的公開性** 正是因為透過這樣的特性 可以衍伸出許多有趣的應用 希望透過這篇能夠理解 1. 透過 web3.js 查詢 區塊鏈資訊 2. 透過 ethers.js 查詢 區塊鏈資訊 3. 使用 Infura 乙太坊節點提供者 建立連線 4. 將結果資料與區塊鏈瀏覽器資訊作對應 ## 下回預告 嗚嗚嗚 這下還能觀察區塊鏈上所有資訊了 資訊是看透透了 不過比起看資訊 更想創建資訊!!! 虛擬貨幣 最基礎的就是交易 那那 可以直接**進行交易**嗎? 感覺超酷的吧 ~~~ ### 下回 "DApp建立: 執行交易"