read.cash Log in
@vcckvv

智能合約教學(2) 和Hello World合約互動的Web3網頁 這次要來寫一個網頁程式來和前一篇發布的合約進行互動,這裡假設看的人已經對HTML和JavaScript有一定的了解,所以只會談論連結MetaMask帳戶、和合約互動的程式碼的部份 首先先實際操作一下完成的網頁:https://vcckvv.github.io/SmartContract/HelloWorld/

沒有MetaMask錢包的人要先安裝MetaMask錢包瀏覽器外掛,因為這個網頁是預設和MetaMask進行連結 第一次連進去應該會要求帳戶的連線,由於這個程式沒有寫切換到goerli測試鏈的功能,所以記得要手動切換到goerli測試鏈上,另外如果沒goerli的幣的話,可以先去水龍頭網站領取:https://goerlifaucet.com/ 按下getValue的按鈕會執行合約中的get()函數,也就是取得合約中value的資料,然後顯示在左邊的輸入框上 按下setValue的按鈕則會執行合約中的set()函數,會把左邊的輸入框中的文字當作輸入參數來執行,按下後會跳出MetaMask的交易確認,接著網頁會等到交易上鏈後再自動執行get()函數,由此顯示出我們的set()函數操作有正確改變合約中value的資料 值得注意的是,如果有多個人在同時間測試的話,value會被設定成最後一個人修改的資料,這個時候就要注意一下合約的交易情況: https://goerli.etherscan.io/address/0x8abb77ff22f8966729603bd49c5c049c4294efd5 接下來看一下完整網頁程式:https://github.com/vcckvv/SmartContract/blob/gh-pages/HelloWorld/index.html ---------------------------------------------------- <html> <head> <title>Hello World</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <input id="showValue" readonly></input><button onclick="onGetValueBtn()">getValue</button><br> <input id="newValue" ></input><button onclick="onSetValueBtn()">setValue</button> </body> <script src="https://cdn.ethers.io/lib/ethers-5.1.umd.min.js"></script> <script> // The Contract interface const abi = [{"constant": false,"inputs": [{"name": "v","type": "string"}],"name": "set","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [],"name": "get","outputs": [{"name": "","type": "string"}],"payable": false,"stateMutability": "view","type": "function"},{"inputs": [],"payable": false,"stateMutability": "nonpayable","type": "constructor"}]; const contractAddress = "0x8abb77ff22f8966729603bd49c5c049c4294efd5"; let address = null; let provider = null; let contract = null; let contractWithSigner = null; async function init(){ try{ let response = await window.ethereum.request({method: 'eth_requestAccounts'}); address = response[0]; provider = new ethers.providers.Web3Provider(window.ethereum); contract = new ethers.Contract(contractAddress, abi, provider); contractWithSigner = contract.connect(provider.getSigner() ); }catch(err){ alert(err.message); } } init(); async function onGetValueBtn(){ try{ let currentValue = await contract.get(); document.querySelector('#showValue').value=currentValue; }catch(err){ alert(err.message); } } async function onSetValueBtn(){ try{ let newValue=document.querySelector('#newValue').value; let tx = await contractWithSigner.set(newValue); alert(tx.hash); document.querySelector('#showValue').value=""; await tx.wait(); onGetValueBtn(); }catch(err){ if(err.code==4001){//reject alert("User rejected tx."); } else{ alert(err.message); } } } </script> </html> -------------------------------- 由於MetaMask沒辦法在打開本機上的HTML檔時開啟功能,所以在測試的時候要放到伺服器上來進行測試,我一般都是在自己電腦上建立一台網頁伺服器,然後放在上面進行測試比較方便,使用的軟體是Http File Server:https://www.rejetto.com/hfs/ ---------------------------------------------------- 這次使用的函數庫是ethers.js,個人的使用經驗上感覺非常便利,這裡只需要簡單地引用這個js檔就能使用: <script src="https://cdn.ethers.io/lib/ethers-5.1.umd.min.js"></script> ---------------------------------------------------- const abi = [{"constant": false,"inputs": [{"name": "v","type": "string"}],"name": "set","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [],"name": "get","outputs": [{"name": "","type": "string"}],"payable": false,"stateMutability": "view","type": "function"},{"inputs": [],"payable": false,"stateMutability": "nonpayable","type": "constructor"}]; 這串abi代表合約的函數介面資料,之前在Remix進行編譯時(Compile HelloWorld.sol),編譯完介面底下會出現一個ABI的按鈕,按下去就會把產生的ABI資料複製起來,然後就可以貼到程式碼這邊

要注意之後在處理多個contract的情況時,要在上面的CONTRACT下拉表中選對發布的contract,才能得到正確的ABI ---------------------------------------------------- let response = await window.ethereum.request({method: 'eth_requestAccounts'}); address = response[0]; provider = new ethers.providers.Web3Provider(window.ethereum); contract = new ethers.Contract(contractAddress, abi, provider); contractWithSigner = contract.connect(provider.getSigner() ); 這一段就是進行MetaMask的帳戶連結,然後產生provider物件,provider物件基本功能應該是和RPC Server相連線,由此和合約進行互動 RPC Server的網址就是MetaMask設定的區塊鏈的資料中的RPC URL,如果不是商業用的RPC Server也許會有些流量限制,不過測試的時候應該不用考慮那麼多,直接用預設的免費RPC Server就好 產生provider物件之後再產生contract和contractWithSigner物件,這兩個後面和合約互動時會用到 ---------------------------------------------------- let currentValue = await contract.get(); document.querySelector('#showValue').value=currentValue; 要執行合約中讀取性質的函數的方法很簡單,像是我們要執行合約中的get(),就只需要寫成contract.get()就可以了,要注意這個動作會動用到網路,所以是非同步函數,因此要用await去等待值回傳回來 而在JavaScript使用到await的話,函數就要加上async,代表這個函數裡面有非同步的指令,而我們這裡的函數都有用到,所以都有加上async ---------------------------------------------------- let newValue=document.querySelector('#newValue').value; let tx = await contractWithSigner.set(newValue); alert(tx.hash); document.querySelector('#showValue').value=""; await tx.wait(); onGetValueBtn(); 而要執行合約中寫入性質的函數的方法也很簡單,像是我們要執行合約中的set(),就只需要寫成`contractWithSigner.set()`然後再填入輸入的參數就可以了 執行這個函數後回傳的是交易資料tx,我們可以用`await tx.wait()`來等待交易上鏈完成,然後繼續其他的動作,這裡是執行`onGetValueBtn()`用來確定我們的值有真的在鏈上被修改掉 值得一提的是,如果在MetaMask中把pending的交易啟用加速功能(需要多給費用),那麼即使交易上鏈完成,這裡的tx.wait()也沒辦法回傳繼續行動,這個也不能說算是MetaMask的bug,因為本質上算是換成不同的交易 ---------------------------------------------------- 和合約互動的基本功能這樣就完成了,不過還是有些東西沒有提到,像是切換區塊鏈、偵測帳號切換、取得帳號餘額,這些之後會再加進來講解,這裡只寫一些最基本的功能 下一篇要來解說怎麼製作自己的ERC20代幣,之後還會弄個水龍頭網站,敬請期待~

No comments yet

Log in to join in Reading is open to everyone. Replying needs an account.