多钱包
Dapp 链接多钱包,指的是在初始化链接时提供多个钱包选项,由用户选择使用。
技术层面的实现思路
- 提供列表选项
- 根据选择的结果初始化 provider
- 基于 provider 的 API 对接业务逻辑
- 通过 disconnect + 重新选择完成钱包切换
Bitget Wallet 嗅探
- App 内置浏览器:
var isBitgetWallet = navigator.userAgent.indexOf('BitKeep') > 0
- 桌面版浏览器:
var isBitgetWallet = window.bitkeep
如何开发 DApp 链接多个钱包
在开发 DApp 时,允许用户选择并连接多个钱包是一项关键功能。这里将详细讲解如何在不使用第三方钱包连接库的方法下实现这一功能,以及如何使用第三方钱包连接库的方法。
不使用第三方钱包连接库的方法
1. 配置钱包列表
首先,我们需要配置一个支持的钱包列表。这可以通过简单的 JavaScript 对象来实现:
js
const walletList = [
{
name: 'MetaMask',
provider: window.ethereum,
check: () => window.ethereum && window.ethereum.isMetaMask,
},
{
name: 'Bitget Wallet',
provider: window.bitkeep,
check: () => window.bitkeep && window.bitkeep.isBitKeep,
},
// 其他钱包可以在这里添加
];
const walletList = [
{
name: 'MetaMask',
provider: window.ethereum,
check: () => window.ethereum && window.ethereum.isMetaMask,
},
{
name: 'Bitget Wallet',
provider: window.bitkeep,
check: () => window.bitkeep && window.bitkeep.isBitKeep,
},
// 其他钱包可以在这里添加
];
2. 检测钱包是否安装
接下来,我们需要检测用户是否安装了这些钱包。可以通过遍历钱包列表并调用每个钱包的check
方法:
js
function detectWallets() {
return walletList.filter(wallet => wallet.check());
}
function detectWallets() {
return walletList.filter(wallet => wallet.check());
}
3. 管理连接钱包状态
为了管理连接状态,我们可以使用 React 的useState
钩子来创建一个状态变量:
js
const [connectedWallet, setConnectedWallet] = useState(null);
const [connectedWallet, setConnectedWallet] = useState(null);
4. 连接钱包
用户选择一个钱包后,我们可以使用对应的钱包的provider
来连接钱包:
js
async function connectWallet(wallet) {
if (!wallet.provider) {
alert('请安装钱包扩展');
return;
}
try {
const accounts = await wallet.provider.request({ method: 'eth_requestAccounts' });
if(accounts?.[0]){
// 连接成功一般钱包会返回账户信息,这里也可以将账户保存下来,ji
setConnectedWallet(wallet);
}
} catch (error) {
console.error('连接钱包失败:', error);
}
}
async function connectWallet(wallet) {
if (!wallet.provider) {
alert('请安装钱包扩展');
return;
}
try {
const accounts = await wallet.provider.request({ method: 'eth_requestAccounts' });
if(accounts?.[0]){
// 连接成功一般钱包会返回账户信息,这里也可以将账户保存下来,ji
setConnectedWallet(wallet);
}
} catch (error) {
console.error('连接钱包失败:', error);
}
}
5. 调用钱包 API
连接钱包后,我们可以使用钱包的provider
来调用钱包 API,例如获取账户地址:
js
async function getAccount() {
if (!connectedWallet) return null;
const accounts = await connectedWallet.provider.request({ method: 'eth_accounts' });
return accounts[0];
}
async function getAccount() {
if (!connectedWallet) return null;
const accounts = await connectedWallet.provider.request({ method: 'eth_accounts' });
return accounts[0];
}
6. 断开连接钱包
1. 监听钱包断开事件
以 Bitget Wallet 为例,可以监听disconnect
事件(也可监听accountsChanged
返回空数组[]
作为断开依据):
js
useEffect(() => {
if (connectedWallet) {
connectedWallet.provider.on('disconnect', handleDisconnect);
}
// 清理事件监听器
return () => {
if (connectedWallet) {
connectedWallet.provider.removeListener('disconnect', handleDisconnect);
}
};
}, [connectedWallet]);
function handleDisconnect() {
console.log('Wallet disconnected');
disconnectWallet();
}
useEffect(() => {
if (connectedWallet) {
connectedWallet.provider.on('disconnect', handleDisconnect);
}
// 清理事件监听器
return () => {
if (connectedWallet) {
connectedWallet.provider.removeListener('disconnect', handleDisconnect);
}
};
}, [connectedWallet]);
function handleDisconnect() {
console.log('Wallet disconnected');
disconnectWallet();
}
2. 断开连接的逻辑
我们需要定义断开连接时的逻辑,例如清除状态和通知用户:
js
function disconnectWallet() {
setConnectedWallet(null);
setAccount(null);
alert('钱包已断开连接');
}
function disconnectWallet() {
setConnectedWallet(null);
setAccount(null);
alert('钱包已断开连接');
}
7. 重置页面
为了确保页面状态一致,我们可以在断开连接钱包时重置页面:
js
function resetPage() {
disconnectWallet();
// 其他页面重置逻辑
}
function resetPage() {
disconnectWallet();
// 其他页面重置逻辑
}
重置页面后,用户可以重新链接其他的钱包,链接流程与以上一致,至此也就完成了多个钱包链接的切换动作。
使用第三方钱包连接库的方法
为了简化钱包连接的实现,我们可以使用第三方库,一般通过三方库可方便的管理钱包链接和断开链接的动作。
这里以Web3Modal
为例,给出示例代码和文档说明。
1. 使用 Web3Modal
Web3Modal 是一个流行的钱包连接库,支持多种钱包。
安装
js
npm install web3modal wagmi @tanstack/react-query
npm install web3modal wagmi @tanstack/react-query
示例代码
js
import { defaultWagmiConfig } from '@web3modal/wagmi/react/config'
mport React, { useEffect } from 'react'
import {
createWeb3Modal,
useWeb3Modal,
useWeb3ModalEvents,
useWeb3ModalState,
useWeb3ModalTheme
} from '@web3modal/wagmi/react'
import { WagmiProvider } from 'wagmi'
import { arbitrum, mainnet } from 'wagmi/chains'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
// 0. 初始化 queryClient for WAGMIv2
const queryClient = new QueryClient()
// 1. 获取 projectId
const projectId = import.meta.env.VITE_PROJECT_ID
if (!projectId) {
throw new Error('VITE_PROJECT_ID is not set')
}
// 2. 配置 wagmiConfig
const wagmiConfig = defaultWagmiConfig({
chains: [mainnet, arbitrum],
projectId,
metadata: {
name: 'Web3Modal React Example',
description: 'Web3Modal React Example',
url: '',
icons: []
}
})
// 3. 创建 modal
createWeb3Modal({
wagmiConfig,
projectId,
themeMode: 'light',
themeVariables: {
'--w3m-color-mix': '#00DCFF',
'--w3m-color-mix-strength': 20
}
})
export default function App() {
// 4. Use modal hook
const { walletProvider } = useWeb3ModalProvider()
const event = useWeb3ModalEvents();
useEffect(() => {
if (
event.data.event === "DISCONNECT_SUCCESS" ||
(event.data.event === "MODAL_CLOSE" && !event.data.properties.connected)
) {
// 参考不使用三方库断开钱包链接的方法,断开之后重新链接钱包即可完成切换钱包的动作
handleDisconnect()
}
}, [event]);
useEffect(() => {
// 管理钱包链接状态
if (walletProvider) {
setConnectedWallet(walletProvider)
} else {
setConnectedWallet(null)
}
}, [walletProvider])
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<w3m-button />
<w3m-network-button />
<w3m-connect-button />
<w3m-account-button />
...
</QueryClientProvider>
</WagmiProvider>
);
}
import { defaultWagmiConfig } from '@web3modal/wagmi/react/config'
mport React, { useEffect } from 'react'
import {
createWeb3Modal,
useWeb3Modal,
useWeb3ModalEvents,
useWeb3ModalState,
useWeb3ModalTheme
} from '@web3modal/wagmi/react'
import { WagmiProvider } from 'wagmi'
import { arbitrum, mainnet } from 'wagmi/chains'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
// 0. 初始化 queryClient for WAGMIv2
const queryClient = new QueryClient()
// 1. 获取 projectId
const projectId = import.meta.env.VITE_PROJECT_ID
if (!projectId) {
throw new Error('VITE_PROJECT_ID is not set')
}
// 2. 配置 wagmiConfig
const wagmiConfig = defaultWagmiConfig({
chains: [mainnet, arbitrum],
projectId,
metadata: {
name: 'Web3Modal React Example',
description: 'Web3Modal React Example',
url: '',
icons: []
}
})
// 3. 创建 modal
createWeb3Modal({
wagmiConfig,
projectId,
themeMode: 'light',
themeVariables: {
'--w3m-color-mix': '#00DCFF',
'--w3m-color-mix-strength': 20
}
})
export default function App() {
// 4. Use modal hook
const { walletProvider } = useWeb3ModalProvider()
const event = useWeb3ModalEvents();
useEffect(() => {
if (
event.data.event === "DISCONNECT_SUCCESS" ||
(event.data.event === "MODAL_CLOSE" && !event.data.properties.connected)
) {
// 参考不使用三方库断开钱包链接的方法,断开之后重新链接钱包即可完成切换钱包的动作
handleDisconnect()
}
}, [event]);
useEffect(() => {
// 管理钱包链接状态
if (walletProvider) {
setConnectedWallet(walletProvider)
} else {
setConnectedWallet(null)
}
}, [walletProvider])
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<w3m-button />
<w3m-network-button />
<w3m-connect-button />
<w3m-account-button />
...
</QueryClientProvider>
</WagmiProvider>
);
}