Web3 钱包连接:MetaMask 与 DApp 交互(React)

1. 核心概念
  • MetaMask:浏览器扩展钱包,提供以太坊账户管理和区块链交互接口。
  • DApp:去中心化应用,通过智能合约与区块链交互。
  • 交互流程
    graph LR
      A[DApp前端] -->|请求连接| B[MetaMask]
      B -->|授权账户| A
      A -->|调用合约| C[以太坊网络]
    

2. 环境准备

安装必要依赖:

npm install ethers @metamask/detect-provider

3. 连接钱包实现
步骤 1:检测 MetaMask 提供者
import { useState, useEffect } from 'react';
import { ethers } from 'ethers';

const useMetaMask = () => {
  const [account, setAccount] = useState(null);
  const [provider, setProvider] = useState(null);

  useEffect(() => {
    const initProvider = async () => {
      if (window.ethereum) {
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        setProvider(provider);
        
        // 检查是否已连接
        const accounts = await provider.listAccounts();
        if (accounts.length > 0) setAccount(accounts[0]);
      }
    };
    initProvider();
  }, []);

  return { account, provider, connectWallet };
};

步骤 2:连接钱包函数
const connectWallet = async () => {
  if (!window.ethereum) {
    alert('请安装MetaMask!');
    return;
  }
  
  try {
    const accounts = await window.ethereum.request({ 
      method: 'eth_requestAccounts' 
    });
    setAccount(accounts[0]);
  } catch (error) {
    console.error('连接失败:', error);
  }
};

4. UI 集成示例
function WalletButton() {
  const { account, connectWallet } = useMetaMask();

  return (
    <button onClick={connectWallet}>
      {account ? `已连接: ${account.slice(0,6)}...` : '连接MetaMask'}
    </button>
  );
}

5. 合约交互示例

调用 ERC-20 代币余额:

const getTokenBalance = async (tokenAddress) => {
  if (!provider || !account) return;
  
  const contract = new ethers.Contract(
    tokenAddress,
    ['function balanceOf(address) view returns (uint256)'],
    provider
  );
  
  const balance = await contract.balanceOf(account);
  return ethers.utils.formatUnits(balance, 18); // 假设18位小数
};

6. 事件监听

处理账户切换:

useEffect(() => {
  if (!window.ethereum) return;
  
  const handleAccountsChanged = (accounts) => {
    setAccount(accounts[0] || null);
  };
  
  window.ethereum.on('accountsChanged', handleAccountsChanged);
  return () => window.ethereum.removeListener('accountsChanged', handleAccountsChanged);
}, []);

7. 安全注意事项
  1. 交易确认:关键操作需用户二次确认
    const sendTransaction = async () => {
      await window.ethereum.request({
        method: 'eth_sendTransaction',
        params: [{ from: account, to: '0x...', value: '0x...' }]
      });
    };
    

  2. 网络验证:检查是否正确网络
    const chainId = await window.ethereum.request({ method: 'eth_chainId' });
    if (chainId !== '0x1') alert('请切换到主网!');
    

8. 完整组件示例
export default function DAppInterface() {
  const { account, provider } = useMetaMask();
  const [balance, setBalance] = useState(0);

  useEffect(() => {
    if (account && provider) {
      provider.getBalance(account).then(bal => {
        setBalance(ethers.utils.formatEther(bal));
      });
    }
  }, [account, provider]);

  return (
    <div>
      <WalletButton />
      {account && (
        <div>
          <p>ETH 余额: ${balance}$</p>
          <button onClick={sendTransaction}>发送交易</button>
        </div>
      )}
    </div>
  );
}

9. 常见问题解决
问题 解决方案
连接无响应 检查 MetaMask 弹窗是否被浏览器拦截
交易失败 确保账户有足够 ETH 支付 gas 费
网络错误 使用 window.ethereum.request({ method: 'wallet_switchEthereumChain' }) 切换网络

最佳实践

  • 使用 @metamask/sdk 优化移动端兼容性
  • 关键操作添加加载状态提示
  • 在用户断开钱包后清理本地缓存数据

通过此实现,React DApp 可安全连接 MetaMask,实现账户管理、资产查询和链上交易功能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐