React.js开发DApp如何在Polygon链集成Metamask实现连接及资产查询
基于React + Polygon链的Metamask集成实现方案
本方案直接采用
ethers.js v6+ 原生Metamask Provider API实现,无多余第三方封装依赖,逻辑清晰可控,完全匹配你的需求。注:你问题中提及的ERC720应为笔误,下文按通用的ERC20同质化代币标准实现
前置依赖安装
仅需安装ethers.js即可,无需其他多余库:
npm install ethers # 或 yarn add ethers # 或 pnpm add ethers
核心功能分步实现
1. 全局常量配置
先定义Polygon链、ERC20相关的固定参数,后续全局复用:
// config/constants.js // Polygon主网配置,若用孟买测试网则CHAIN_ID改为0x13881 export const POLYGON_CONFIG = { chainId: '0x89', chainName: 'Polygon Mainnet', rpcUrls: ['https://polygon-rpc.com/'], nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 }, blockExplorerUrls: ['https://polygonscan.com/'] }; // ERC20代币最小ABI,仅包含查询余额所需的方法 export const ERC20_MIN_ABI = [ "function balanceOf(address owner) view returns (uint256)", "function decimals() view returns (uint8)", "function symbol() view returns (string)" ];
2. 封装Metamask专用自定义Hook
新建hooks/useMetamask.js封装所有核心逻辑,包含连接/断开、账户监听、余额查询功能:
import { useState, useEffect, useCallback } from 'react'; import { BrowserProvider, Contract, formatEther } from 'ethers'; import { POLYGON_CONFIG, ERC20_MIN_ABI } from '../config/constants'; export const useMetamask = () => { const [account, setAccount] = useState(null); const [chainId, setChainId] = useState(null); const [maticBalance, setMaticBalance] = useState('0'); const [provider, setProvider] = useState(null); // 校验是否已安装Metamask const hasMetamask = !!window.ethereum; // 切换到Polygon链,不存在则自动添加 const switchToPolygon = useCallback(async () => { if (!window.ethereum) return false; try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: POLYGON_CONFIG.chainId }], }); return true; } catch (switchError) { // 链不存在时添加链 if (switchError.code === 4902) { try { await window.ethereum.request({ method: 'wallet_addEthereumChain', params: [POLYGON_CONFIG], }); return true; } catch (addError) { console.error('添加Polygon链失败', addError); return false; } } console.error('切换Polygon链失败', switchError); return false; } }, []); // 连接Metamask const connect = useCallback(async () => { if (!hasMetamask) { alert('请先安装Metamask钱包'); return; } try { // 先切到Polygon链 const switchSuccess = await switchToPolygon(); if (!switchSuccess) return; // 请求账户授权 const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const currentAccount = accounts[0]; setAccount(currentAccount); // 初始化ethers provider const ethersProvider = new BrowserProvider(window.ethereum); setProvider(ethersProvider); // 获取MATIC余额 const balance = await ethersProvider.getBalance(currentAccount); setMaticBalance(formatEther(balance)); } catch (err) { console.error('连接Metamask失败', err); } }, [hasMetamask, switchToPolygon]); // 断开连接(仅清空前端状态,Metamask本身无主动断开API) const disconnect = useCallback(() => { setAccount(null); setChainId(null); setMaticBalance('0'); setProvider(null); }, []); // 查询指定ERC20代币余额 const getERC20Balance = useCallback(async (tokenAddress) => { if (!provider || !account) return '0'; try { const tokenContract = new Contract(tokenAddress, ERC20_MIN_ABI, provider); const [balance, decimals] = await Promise.all([ tokenContract.balanceOf(account), tokenContract.decimals() ]); return formatEther(balance, decimals); } catch (err) { console.error('查询ERC20余额失败', err); return '0'; } }, [provider, account]); // 监听账户切换、链切换事件 useEffect(() => { if (!window.ethereum) return; const handleAccountsChange = (accounts) => { if (accounts.length > 0) { setAccount(accounts[0]); } else { disconnect(); } }; const handleChainChange = (newChainId) => { setChainId(newChainId); if (newChainId !== POLYGON_CONFIG.chainId) { alert('请切换到Polygon链使用功能'); } }; window.ethereum.on('accountsChanged', handleAccountsChange); window.ethereum.on('chainChanged', handleChainChange); return () => { window.ethereum.removeListener('accountsChanged', handleAccountsChange); window.ethereum.removeListener('chainChanged', handleChainChange); }; }, [disconnect]); return { account, chainId, maticBalance, hasMetamask, isPolygonConnected: chainId === POLYGON_CONFIG.chainId, connect, disconnect, getERC20Balance }; };
3. 组件中调用示例
直接在业务组件中引入上面的Hook即可使用所有功能:
import { useState } from 'react'; import { useMetamask } from './hooks/useMetamask'; function App() { const { account, maticBalance, connect, disconnect, getERC20Balance } = useMetamask(); const [erc20Address, setErc20Address] = useState(''); const [erc20Balance, setErc20Balance] = useState('0'); const handleQueryERC20 = async () => { const balance = await getERC20Balance(erc20Address); setErc20Balance(balance); }; return ( <div style={{ padding: '2rem' }}> <h1>Polygon DApp Demo</h1> {!account ? ( <button onClick={connect}>连接Metamask钱包</button> ) : ( <div> <p>当前账户:{account}</p> <p>MATIC余额:{maticBalance}</p> <button onClick={disconnect}>断开连接</button> <div style={{ marginTop: '1rem' }}> <input placeholder="输入ERC20代币地址" value={erc20Address} onChange={(e) => setErc20Address(e.target.value)} style={{ width: '300px', marginRight: '0.5rem' }} /> <button onClick={handleQueryERC20}>查询ERC20余额</button> <p>ERC20余额:{erc20Balance}</p> </div> </div> )} </div> ); } export default App;
注意事项
- 所有链上操作前请先校验当前链是否为Polygon,避免在其他链上调用接口报错
- 生产环境可添加用户操作的错误提示,比如用户拒绝授权、拒绝切链等场景的友好提示
- ERC20代币地址需要是Polygon链上已部署的合约地址,否则查询会返回0
内容的提问来源于stack exchange,提问作者Abubillo Sanz
相关产品推荐
相关产品推荐

