Day 02 - Solana 钱包集成
日期: 第2天
目标: 集成 Solana 钱包连接功能
📋 任务清单
- Solana Wallet Adapter 安装配置
- WalletProvider 上下文创建
- 连接钱包按钮组件
- 钱包连接状态管理
💻 实现步骤
1. 安装依赖
npm install @solana/wallet-adapter-react @solana/wallet-adapter-react-ui @solana/wallet-adapter-wallets @solana/wallet-adapter-base
npm install @solana/web3.js bs58
2. 创建 WalletProvider
src/contexts/SolanaWalletProvider.tsx
'use client'
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
import { WalletAdapterNetwork } from '@solana/wallet-adapter-base';
import { PhantomWalletAdapter, SolflareWalletAdapter } from '@solana/wallet-adapter-wallets';
import { WalletModalProvider } from '@solana/wallet-adapter-react-ui';
import { useMemo } from 'react';
require('@solana/wallet-adapter-react-ui/styles.css');
export function SolanaWalletProvider({ children }: { children: React.ReactNode }) {
const network = WalletAdapterNetwork.Mainnet;
const endpoint = useMemo(() => {
return process.env.NEXT_PUBLIC_RPC_URL || 'https://api.mainnet-beta.solana.com';
}, []);
const wallets = useMemo(
() => [
new PhantomWalletAdapter(),
new SolflareWalletAdapter(),
],
[]
);
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
{children}
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
}
3. 连接钱包按钮组件
src/app/[locale]/components/buttons/ConnectButton.tsx
'use client'
import { useWallet } from '@solana/wallet-adapter-react';
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';
import { FC } from 'react';
export const ConnectButton: FC = () => {
const { connected, publicKey, wallet } = useWallet();
return (
<div className="flex items-center gap-4">
<WalletMultiButton style={{
backgroundColor: '#3B82F6',
padding: '8px 16px',
borderRadius: '8px',
color: 'white',
fontWeight: 'bold',
}} />
{connected && publicKey && (
<div className="text-sm text-gray-600">
{wallet?.adapter.name}: {publicKey.toBase58().slice(0, 8)}...
</div>
)}
</div>
);
};
export default ConnectButton;
4. 在 Layout 中使用 Provider
src/app/[locale]/layout.tsx
import { SolanaWalletProvider } from '@/contexts/SolanaWalletProvider';
import ConnectButton from './ components/buttons/ConnectButton';
export default function RootLayout({
children,
params: { locale }
}: {
children: React.ReactNode;
params: { locale: string };
}) {
return (
<html lang={locale}>
<body>
<SolanaWalletProvider>
<nav className="flex justify-between items-center p-4 bg-gray-100">
<h1 className="text-2xl font-bold">Geng</h1>
<ConnectButton />
</nav>
<main>{children}</main>
</SolanaWalletProvider>
</body>
</html>
);
}
5. 自定义钱包连接 Hook
src/app/[locale]/hooks/useWalletConnection.ts
'use client'
import { useWallet } from '@solana/wallet-adapter-react';
import { useWalletModal } from '@solana/wallet-adapter-react-ui';
import { useCallback } from 'react';
export const useWalletConnection = () => {
const { connected, publicKey, signTransaction, sendTransaction } = useWallet();
const { setVisible } = useWalletModal();
const ensureConnected = useCallback(async () => {
if (!connected) {
setVisible(true);
return false;
}
return true;
}, [connected, setVisible]);
const getAddress = useCallback(() => {
return publicKey?.toBase58() || null;
}, [publicKey]);
return {
connected,
publicKey,
address: getAddress(),
signTransaction,
sendTransaction,
ensureConnected,
};
};
⚠️ 常见问题
问题1:钱包未连接时应用崩溃
现象:
Error: Cannot read properties of undefined (reading 'toBase58')
原因: 没有正确的条件检查,访问 publicKey 时未确认连接
解决方案:
const { connected, publicKey } = useWallet();
// ❌ 错误做法
const address = publicKey.toBase58(); // publicKey 可能为 null
// ✅ 正确做法
if (!connected || !publicKey) {
return <div>Please connect your wallet first</div>;
}
const address = publicKey.toBase58();
问题2:网络切换后钱包断开连接
现象: 用户手动切换网络后应用功能异常
原因: RPC 端点与钱包网络不同步
解决方案:
const [network, setNetwork] = useState(WalletAdapterNetwork.Mainnet);
const endpoint = useMemo(() => {
if (network === WalletAdapterNetwork.Devnet) {
return 'https://api.devnet.solana.com';
}
return 'https://api.mainnet-beta.solana.com';
}, [network]);
// 提供网络切换接口
const switchNetwork = useCallback((newNetwork: WalletAdapterNetwork) => {
setNetwork(newNetwork);
// 触发钱包重新连接
disconnect();
}, []);
📊 验证清单
- 应用启动时显示连接钱包按钮
- 点击按钮能打开钱包选择界面
- 连接成功后显示钱包地址
- 断开连接后界面恢复初始状态
- 切换钱包能正常工作
🧪 测试代码
// pages/test/wallet.tsx
'use client'
import { useWallet } from '@solana/wallet-adapter-react';
import { useWalletConnection } from '@/app/[locale]/hooks/useWalletConnection';
export default function WalletTest() {
const { connected, publicKey } = useWallet();
const { address, ensureConnected } = useWalletConnection();
const handleTest = async () => {
const isConnected = await ensureConnected();
if (isConnected) {
console.log('Wallet connected:', address);
}
};
return (
<div className="p-8 space-y-4">
<p>Connected: {String(connected)}</p>
<p>Address: {address}</p>
<button
onClick={handleTest}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Test Connection
</button>
</div>
);
}
📝 总结
✅ 集成了 Solana Wallet Adapter
✅ 创建了钱包连接 UI 组件
✅ 实现了安全的钱包状态管理
✅ 解决了常见的连接问题
下一步: 项目上下文和工具函数(Day 03)
时间估计: 2-3 小时
难度等级: ⭐⭐ 中等
关键词: Solana, Wallet Adapter, 钱包连接