Wagmi
Wagmi 通过优先考虑开发者体验、性能、功能覆盖和稳定性,使应用程序开发者能够专注于为以太坊构建高质量、高性能的体验。它为开发者提供直观的构建块来创建他们的以太坊应用程序。
本节将介绍如何将 Bitget Wallet
和 Bitget Wallet Lite
集成到 Wagmi 系统中。
快速开始
1.安装依赖
pnpm add wagmi
2.配置链和提供者
import { configureChains, allChains } from 'wagmi'
import { alchemyProvider } from 'wagmi/providers/alchemy'
import { publicProvider } from 'wagmi/providers/public'
// 使用 Alchemy 提供者配置链和提供者。
// 两个流行的提供者是 Alchemy (alchemy.com) 和 Infura (infura.io)
const { chains, provider } = configureChains(allChains, [
alchemyProvider(),
publicProvider(),
])
3.创建 BitgetWalletConnector
import { InjectedConnector } from 'wagmi/connectors/injected'
class BitgetWalletConnector extends InjectedConnector {
constructor({ chains = [], options_ = {} }) {
const options = {
name: 'BitgetWallet',
...options_,
}
super({ chains, options })
this.id = 'BitgetWallet'
this.ready =
typeof window != 'undefined' &&
!!this.findProvider(window?.bitkeep?.ethereum)
}
async getProvider() {
if (typeof window !== 'undefined') {
// TODO: Fallback to `ethereum#initialized` event for async injection
// https://github.com/BitKeep/detect-provider#synchronous-and-asynchronous-injection=
this.provider = window.bitkeep?.ethereum
}
return this.provider
}
getReady(ethereum) {
if (!ethereum.isBitKeep || !ethereum) return
// Brave tries to make itself look like BitKeep
// Could also try RPC `web3_clientVersion` if following is unreliable
if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) return
if (ethereum.isTokenPocket) return
if (ethereum.isTokenary) return
return ethereum
}
findProvider(ethereum) {
if (ethereum?.providers) return ethereum.providers.find(this.getReady)
return this.getReady(ethereum)
}
}
4.创建 BitgetWalletLiteConnector
import { InjectedConnector } from 'wagmi/connectors/injected'
import { OmniConnect } from '@bitget-wallet/omni-connect'
class BitgetWalletLiteConnector extends InjectedConnector {
constructor({ chains = [], options_ = {} }) {
const options = {
name: 'BitgetWallet',
...options_,
}
super({ chains, options })
this.id = 'BitgetWallet'
this.ready =
typeof window != 'undefined' &&
!!this.findProvider(window?.bitkeep?.ethereum)
this.omni = new OmniConnect({
metadata: {
name: options_.dappName,
iconUrl: options_.dappIconUrl,
url: options_.dappUrl,
privacyPolicyUrl: '',
termsOfUseUrl: '',
},
namespace: {
eip155: {
chains: chains,
// methods: ["eth_sign", "personal_sign"],
// events: ["accountsChanged", "chainChanged"],
},
},
})
}
async getProvider() {
if (typeof window !== 'undefined') {
// TODO: Fallback to `ethereum#initialized` event for async injection
// https://github.com/BitKeep/detect-provider#synchronous-and-asynchronous-injection=
this.provider = this.omni?.eip155?.provider
}
return this.provider
}
getReady(ethereum) {
if (!ethereum.isBitKeep || !ethereum) return
// Brave tries to make itself look like BitKeep
// Could also try RPC `web3_clientVersion` if following is unreliable
if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) return
if (ethereum.isTokenPocket) return
if (ethereum.isTokenary) return
return ethereum
}
findProvider(ethereum) {
if (ethereum?.providers) return ethereum.providers.find(this.getReady)
return this.getReady(ethereum)
}
}
5.设置客户端
import { createClient } from 'wagmi'
import { MetaMaskConnector } from 'wagmi/connectors/metaMask'
import BitgetWalletConnector from './connectors/BitgetWalletConnector'
const mainnetIds = [1, 10, 137, 42161] // BitgetWallet 目前只支持主网网络
// 设置客户端
const client = createClient({
autoConnect: true,
connectors: [
new MetaMaskConnector({ chains }),
new BitgetWalletConnector({
chains: chains.filter((v) => mainnetIds.includes(v.id)),
options: {
name: 'BitgetWallet',
icon: './assets/bitkeep-icon.png',
},
}),
new BitgetWalletConnector({
chains: chains.filter((v) => mainnetIds.includes(v.id)),
options: {
name: 'BitgetWallet',
iconUrl: './assets/bitkeep-icon.png',
dappName: 'your dappName',
dappIconUrl: 'your dappIconUrl',
dappUrl: 'your dappUrl',
},
}),
],
provider,
})
在 React 应用中使用
import {
WagmiConfig,
useConnect,
useAccount,
useDisconnect,
useSignMessage,
useNetwork,
useSwitchNetwork,
} from 'wagmi'
import { verifyMessage } from 'ethers/lib/utils'
const { isConnected, connector } = useAccount()
const { connect, connectors, error, isLoading, pendingConnector } = useConnect()
const { disconnect } = useDisconnect()
export function NetworkSwitcher() {
const { chain } = useNetwork()
const { chains, error, isLoading, pendingChainId, switchNetwork } =
useSwitchNetwork()
if (!chain) return null
return (
<div>
<div>
连接到 {chain.name ?? chain.id}
{chain.unsupported && ' (不支持)'}
</div>
{switchNetwork && (
<div>
{chains
.filter((x) => x.id !== chain.id)
.map((x) => (
<button
disabled={!switchNetwork || x.id === pendingChainId}
key={x.id}
onClick={() => switchNetwork(x.id)}
>
切换到 {x.name ?? x.id}
{isLoading && x.id === pendingChainId && ' (切换中)'}
</button>
))}
</div>
)}
<div>{error && error.message}</div>
</div>
)
}
function Profile() {
const { address, isConnected } = useAccount()
const { data, error, isLoading, signMessage } = useSignMessage({
message: 'gm wagmi frens',
})
const { disconnect } = useDisconnect()
if (isConnected) {
return (
<div>
<div>连接到 {address}</div>
<button onClick={() => signMessage()}>签名消息</button>
<button onClick={() => disconnect()}>断开连接</button>
<NetworkSwitcher />
</div>
)
}
return (
<div>
{connectors.map((connector) => (
<button
disabled={!connector.ready}
key={connector.id}
onClick={() => connect({ connector })}
>
{connector.name}
{!connector.ready && ' (不支持)'}
{isLoading && connector.id === pendingConnector?.id && ' (连接中)'}
</button>
))}
{error && <div>{error.message}</div>}
</div>
)
}
function App() {
return (
<WagmiConfig client={client}>
<Profile />
</WagmiConfig>
)
}
连接钱包
使用 Hook
import { useConnect, useAccount } from 'wagmi'
function ConnectWallet() {
const { connect, connectors, isPending } = useConnect()
const { address, isConnected } = useAccount()
if (isConnected) {
return <div>连接到 {address}</div>
}
return (
<div>
{connectors.map((connector) => (
<button
key={connector.id}
onClick={() => connect({ connector })}
disabled={isPending}
>
连接到 {connector.name}
</button>
))}
</div>
)
}
获取账户信息
import { useAccount, useBalance, useEnsName } from 'wagmi'
function Account() {
const { address, isConnected } = useAccount()
const { data: balance } = useBalance({
address,
})
const { data: ensName } = useEnsName({
address,
})
if (!isConnected) return <div>未连接</div>
return (
<div>
<div>地址: {ensName || address}</div>
<div>
余额: {balance?.formatted} {balance?.symbol}
</div>
</div>
)
}
发送交易
import { useSendTransaction, useAccount } from 'wagmi'
import { parseEther } from 'viem'
function SendTransaction() {
const { address } = useAccount()
const { sendTransaction, isPending } = useSendTransaction()
const handleSend = () => {
sendTransaction({
to: '0x...',
value: parseEther('0.01'),
})
}
return (
<button onClick={handleSend} disabled={isPending}>
{isPending ? '发送中...' : '发送 0.01 ETH'}
</button>
)
}
合约交互
读取合约
import { useReadContract } from 'wagmi'
const erc20Abi = [
{
name: 'balanceOf',
type: 'function',
stateMutability: 'view',
inputs: [{ name: 'account', type: 'address' }],
outputs: [{ name: '', type: 'uint256' }],
},
] as const
function TokenBalance() {
const { address } = useAccount()
const { data: balance } = useReadContract({
address: '0x...',
abi: erc20Abi,
functionName: 'balanceOf',
args: [address],
})
return <div>代币余额: {balance?.toString()}</div>
}
写入合约
import { useWriteContract } from 'wagmi'
function TransferToken() {
const { writeContract, isPending } = useWriteContract()
const handleTransfer = () => {
writeContract({
address: '0x...',
abi: erc20Abi,
functionName: 'transfer',
args: ['0x...', parseEther('1')],
})
}
return (
<button onClick={handleTransfer} disabled={isPending}>
{isPending ? '转账中...' : '转账 1 代币'}
</button>
)
}
签名消息
import { useSignMessage } from 'wagmi'
function SignMessage() {
const { signMessage, isPending } = useSignMessage()
const handleSign = () => {
signMessage({
message: 'Hello from Bitget Wallet!',
})
}
return (
<button onClick={handleSign} disabled={isPending}>
{isPending ? '签名中...' : '签名消息'}
</button>
)
}
网络切换
import { useSwitchChain } from 'wagmi'
function SwitchNetwork() {
const { switchChain, chains } = useSwitchChain()
return (
<div>
{chains.map((chain) => (
<button
key={chain.id}
onClick={() => switchChain({ chainId: chain.id })}
>
切换到 {chain.name}
</button>
))}
</div>
)
}
监听事件
import { useWatchContractEvent } from 'wagmi'
function EventListener() {
useWatchContractEvent({
address: '0x...',
abi: erc20Abi,
eventName: 'Transfer',
onLogs: (logs) => {
console.log('新的转账事件:', logs)
},
})
return <div>监听转账事件...</div>
}
断开连接
import { useDisconnect } from 'wagmi'
function DisconnectWallet() {
const { disconnect } = useDisconnect()
return <button onClick={() => disconnect()}>断开连接</button>
}
完整示例
import { useAccount, useConnect, useDisconnect, useBalance } from 'wagmi'
function WalletConnection() {
const { address, isConnected } = useAccount()
const { connect, connectors } = useConnect()
const { disconnect } = useDisconnect()
const { data: balance } = useBalance({ address })
if (isConnected) {
return (
<div>
<div>地址: {address}</div>
<div>
余额: {balance?.formatted} {balance?.symbol}
</div>
<button onClick={() => disconnect()}>断开连接</button>
</div>
)
}
return (
<div>
{connectors.map((connector) => (
<button key={connector.id} onClick={() => connect({ connector })}>
连接到 {connector.name}
</button>
))}
</div>
)
}
注意事项
- 版本兼容性: 确保使用兼容的 wagmi 和 viem 版本
- 错误处理: 使用
error
状态处理连接和交易错误 - 性能优化: 合理使用 React Query 的缓存机制
- 类型安全: 充分利用 TypeScript 的类型推断
相关资源
Last updated on