跳转到内容

连接钱包

连接钱包

本页内容正在整理中,欢迎贡献

概述

钱包连接是所有 DApp 的入口。现代 DApp 需要支持多种钱包(MetaMask、Coinbase Wallet、WalletConnect 协议钱包等)并提供流畅的用户体验。本文介绍几种主流连接方案的实现方法。

主要内容

方案选择

方案支持钱包开发成本推荐场景
RainbowKit20+新 React 项目首选
ConnectKit20+更简洁的 UI 风格
Web3Modal v320+非 React 框架
原生 window.ethereumMetaMask 等注入钱包轻量级场景

RainbowKit(推荐)

Terminal window
npm install @rainbow-me/rainbowkit wagmi viem @tanstack/react-query
// 完整配置
import { getDefaultConfig, RainbowKitProvider } from "@rainbow-me/rainbowkit";
import { mainnet, sepolia, optimism, arbitrum, base } from "wagmi/chains";
const config = getDefaultConfig({
appName: "My DApp",
projectId: "从 cloud.walletconnect.com 获取",
chains: [mainnet, sepolia, optimism, arbitrum, base],
// 可选:自定义支持的钱包
});
// 自定义钱包列表
import {
metaMaskWallet,
coinbaseWallet,
walletConnectWallet,
rainbowWallet,
okxWallet,
} from "@rainbow-me/rainbowkit/wallets";
import { connectorsForWallets } from "@rainbow-me/rainbowkit";
const connectors = connectorsForWallets(
[
{
groupName: "推荐",
wallets: [metaMaskWallet, coinbaseWallet, rainbowWallet],
},
{
groupName: "更多",
wallets: [walletConnectWallet, okxWallet],
},
],
{ appName: "My DApp", projectId: "..." }
);

账户状态管理(wagmi Hooks)

import {
useAccount,
useConnect,
useDisconnect,
useBalance,
useChainId,
useSwitchChain,
} from "wagmi";
function WalletInfo() {
const { address, isConnected, isConnecting, chain } = useAccount();
const { disconnect } = useDisconnect();
const { data: balance } = useBalance({ address });
const chainId = useChainId();
const { switchChain } = useSwitchChain();
if (!isConnected) return <ConnectButton />;
return (
<div>
<p>地址: {address}</p>
<p>余额: {balance?.formatted} {balance?.symbol}</p>
<p>当前网络: {chain?.name}</p>
<button onClick={() => switchChain({ chainId: 11155111 })}>
切换到 Sepolia
</button>
<button onClick={() => disconnect()}>断开连接</button>
</div>
);
}

处理网络切换

import { useChainId, useSwitchChain } from "wagmi";
import { mainnet } from "wagmi/chains";
function NetworkGuard({ children }: { children: React.ReactNode }) {
const chainId = useChainId();
const { switchChain, isPending } = useSwitchChain();
if (chainId !== mainnet.id) {
return (
<div>
<p>请切换到以太坊主网</p>
<button
onClick={() => switchChain({ chainId: mainnet.id })}
disabled={isPending}
>
{isPending ? "切换中..." : "切换网络"}
</button>
</div>
);
}
return <>{children}</>;
}

签名消息(Sign-In with Ethereum)

import { useSignMessage } from "wagmi";
function SignInButton() {
const { signMessage, isPending, data: signature } = useSignMessage();
const handleSignIn = () => {
signMessage({
message: `登录 My DApp\n\n时间戳: ${Date.now()}\n地址: ${address}`,
});
};
return (
<button onClick={handleSignIn} disabled={isPending}>
{isPending ? "签名中..." : "用钱包登录"}
</button>
);
}

EIP-712 结构化数据签名

import { useSignTypedData } from "wagmi";
const { signTypedData } = useSignTypedData();
signTypedData({
domain: {
name: "My DApp",
version: "1",
chainId: 1,
verifyingContract: CONTRACT_ADDRESS,
},
types: {
Order: [
{ name: "buyer", type: "address" },
{ name: "amount", type: "uint256" },
{ name: "deadline", type: "uint256" },
],
},
primaryType: "Order",
message: {
buyer: address,
amount: parseEther("1.0"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
},
});

最佳实践

  1. 始终检查网络 —— 确保用户在正确的链上再允许交互
  2. 优雅处理拒绝 —— 用户拒绝签名时给出清晰提示
  3. 显示交易状态 —— 展示 pending / success / error 状态
  4. 支持移动端 —— 确保 WalletConnect 二维码可正常显示
  5. 缓存连接状态 —— 页面刷新后自动重连

深入阅读