iT邦幫忙

1

Web3 DApp 開發 (二):整合 MetaMask 錢包與交易狀態管理

  • 分享至 

  • xImage
  •  

如果我們想要開發一個真正的 DApp(例如讓使用者把代幣轉給別人,或是參與我們前幾天寫的拍賣合約),我們就必須發起一筆「會改變區塊鏈狀態的交易」。發起交易需要使用者的「私鑰」來進行數位簽章。

在 Web3 的資安鐵則中,DApp 網頁絕對不可以要求使用者輸入私鑰。我們必須透過一個安全的第三方代理人來協助簽署交易,而這個代理人就是目前全世界最多人使用的瀏覽器擴充錢包——MetaMask。今天,我們就來實作前端網頁與 MetaMask 的串接!

瀏覽器裡的橋樑:window.ethereum
當使用者在瀏覽器(如 Chrome)安裝了 MetaMask 擴充功能後,MetaMask 會在所有的網頁環境中偷偷注入一個全域變數:window.ethereum。

這個變數就是我們前端網頁與錢包溝通的唯一橋樑。我們可以透過它來詢問使用者:「嘿,可以讓我連線你的錢包嗎?」或是「請幫我簽署這筆轉帳交易」。

實戰步驟一:連線 MetaMask 並取得 Signer
在昨天我們使用的是唯讀的 JsonRpcProvider,今天我們要改用 Ethers.js v6 專門用來對接瀏覽器錢包的 BrowserProvider。

讓我們寫一段 JavaScript 函數來觸發錢包連線:
'''
const { ethers } = require("ethers");

async function connectWallet() {
// 1. 檢查使用者的瀏覽器有沒有安裝 MetaMask (即是否存在 window.ethereum)
if (typeof window.ethereum === 'undefined') {
alert('請先安裝 MetaMask 錢包!');
return null;
}

try {
    // 2. 建立 BrowserProvider,將 window.ethereum 包裝成 Ethers 支援的格式
    const provider = new ethers.BrowserProvider(window.ethereum);

    // 3. 向 MetaMask 發出連線請求,這會跳出 MetaMask 的授權視窗
    // 使用者點擊「連線」後,我們才能拿到他的地址
    const accounts = await provider.send("eth_requestAccounts", []);
    console.log("成功連線帳戶:", accounts[0]);

    // 4. 取得 Signer (簽署者物件)
    // Signer 代表了這個連線的帳戶,我們後續發送交易都要靠它
    const signer = await provider.getSigner();
    return signer;

} catch (error) {
    console.error("使用者拒絕連線或發生錯誤:", error);
}

}
'''

實戰步驟二:發送交易與狀態管理 (Transaction Lifecycle)
拿到 signer 後,我們就可以來呼叫合約裡需要花費 Gas 的函數了(例如 transfer)。

在傳統 Web2 裡,API 打過去只要等幾百毫秒就會收到成功或失敗的回覆。但在 Web3 裡,一筆交易被發送到網路上,到真正被礦工打包進區塊(Mined),通常需要十幾秒甚至幾分鐘的時間。

如果這段時間網頁畫面沒有任何回饋,使用者一定會以為當機了,然後瘋狂連點按鈕,最後導致發送了重複的交易並浪費大量 Gas 費。因此,嚴謹的「交易狀態管理」是 DApp 開發的重中之重。
'''
// 假設我們已經準備好合約地址與 ABI
const contractAddress = "0xYourTokenContractAddressHere";
const tokenABI = [
"function transfer(address to, uint256 amount) returns (bool)"
];

async function executeTransfer(signer, toAddress, amount) {
// 建立可以發送交易的 Contract 物件 (這次傳入的是 signer 而不是唯讀的 provider)
const tokenContract = new ethers.Contract(contractAddress, tokenABI, signer);

try {
    console.log("請在 MetaMask 中確認交易...");
    
    // 1. 轉換代幣單位 (假設是 18 位小數)
    const parsedAmount = ethers.parseUnits(amount.toString(), 18);

    // 2. 發送交易!這時 MetaMask 會彈出視窗要求使用者點擊「確認」
    const tx = await tokenContract.transfer(toAddress, parsedAmount);

    // --- 此時交易已發送至節點的交易池 (Pending 狀態) ---
    console.log(`交易已送出!交易雜湊值 (Tx Hash): ${tx.hash}`);
    console.log("正在等待礦工打包,請稍候...");
    // 實務上這裡會在網頁 UI 顯示一個 Loading 動畫

    // 3. 等待交易被寫入區塊鏈 (Mined)
    const receipt = await tx.wait();

    // --- 此時交易已確認 ---
    console.log("交易成功寫入區塊鏈!", receipt);
    alert('轉帳成功!');

} catch (error) {
    // 處理使用者在 MetaMask 按下「拒絕」,或是合約執行失敗 (Revert) 的狀況
    if (error.code === 'ACTION_REJECTED') {
        console.log("使用者取消了交易");
    } else {
        console.error("交易失敗:", error);
    }
}

}
'''

這兩天的教學中,我們利用 Ethers.js 補齊了 DApp 的最後一塊拼圖:讀取鏈上狀態與發送簽署交易。

身為未來的資安從業人員與開發者,我們現在不僅懂得如何從零刻出區塊鏈底層、如何寫出防禦重入攻擊的智慧合約,更能打造出與之互動的 Web3 前端介面。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言