You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 01:15:08