npm package: @li-shi-chen/metanode-wallet-sdk
Description: A lightweight wallet SDK for Web3 applications
Author: MetaNode Team
Latest version: 1.0.2

# 轻量级钱包SDK教学课件

## 📚 课程概览

### 课程目标
- 理解现代Web3钱包连接的核心概念
- 学习如何构建一个轻量级的钱包SDK
- 掌握React Context模式在复杂状态管理中的应用
- 了解多钱包支持和链切换的实现原理

### 项目介绍
这是一个轻量级的钱包连接SDK，作为RainbowKit的简化替代方案，提供了：
- 🔌 多钱包支持（MetaMask、Coinbase、WalletConnect）
- 🔗 多链支持和链切换功能
- 🎨 灵活的UI组件设计
- ⚡ 轻量级、无多余依赖

---

## 📁 第一章：项目架构设计

### 1.1 项目目录结构
```
src/wallet-sdk/
├── components/          # UI组件
│   ├── ConnectButton.tsx    # 连接按钮主组件
│   ├── WalletButton.tsx     # 单个钱包按钮
│   └── WalletModal.tsx      # 钱包选择弹窗
├── connectors/          # 钱包连接器
│   ├── metamask.ts          # MetaMask连接器
│   ├── coinbase.ts          # Coinbase连接器
│   └── walletconnect.ts     # WalletConnect连接器
├── providers/           # 上下文提供者
│   └── WalletProvider.tsx   # 核心状态管理
├── icons/              # 图标组件
├── types.ts            # TypeScript类型定义
└── utils.ts            # 工具函数
```

### 1.2 架构设计原则

#### 🎯 关注点分离
- **组件层**: 纯UI展示逻辑
- **连接器层**: 特定钱包的连接逻辑
- **状态层**: 全局状态管理
- **工具层**: 通用函数和类型

#### 🔄 可扩展性
- 新钱包连接器易于添加
- 支持自定义主题和样式
- 链配置完全可定制

---

## 🔧 第二章：核心类型系统

### 2.1 基础接口设计

```typescript
// 钱包接口 - 定义每个钱包的核心属性
interface Wallet {
  id: string;              // 唯一标识符
  name: string;            // 显示名称
  icon: React.FC;          // 图标组件
  connector: () => Promise<any>; // 连接函数
  description?: string;    // 描述信息
  installed?: boolean;     // 是否已安装
  downloadUrl?: string;    // 下载链接
}

// 链配置接口 - 支持多链环境
interface Chain {
  id: number;              // 链ID
  name: string;            // 链名称
  rpcUrl: string;          // RPC节点地址
  currency: {              // 原生代币信息
    name: string;
    symbol: string;
    decimals: number;
  };
  blockExplorer?: {        // 区块浏览器（可选）
    name: string;
    url: string;
  };
}
```

### 2.2 状态管理类型

```typescript
// 钱包状态 - 核心连接状态
interface WalletState {
  address: string | null;     // 用户地址
  chainId: number | null;     // 当前链ID
  isConnecting: boolean;      // 连接中状态
  isConnected: boolean;       // 连接状态
  ensName: string | null;     // ENS域名
  error: Error | null;        // 错误信息
}

// Context值 - 组合状态和方法
interface WalletContextValue extends WalletState {
  connect: (walletId: string) => Promise<void>;
  disconnect: () => Promise<void>;
  switchChain: (chainId: number) => Promise<void>;
  openModal: () => void;
  closeModal: () => void;
  isModalOpen: boolean;
  provider: any;
  chains: Chain[];
}
```

---

## ⚡ 第三章：状态管理核心 - WalletProvider

### 3.1 React Context模式的应用

#### 🎯 为什么选择Context?
1. **避免属性钻井**: 深层组件直接访问钱包状态
2. **集中式状态**: 统一管理连接状态和方法
3. **性能优化**: 减少不必要的re-render

#### 🔄 Context设计模式
```typescript
// 1. 创建Context
const WalletContext = createContext<WalletContextValue>(defaultValue);

// 2. 提供Provider
export const WalletProvider: React.FC<WalletProviderProps> = ({ 
  children, chains, wallets, autoConnect = true 
}) => {
  // 状态管理逻辑
  return (
    <WalletContext.Provider value={contextValue}>
      {children}
      <WalletModal /> {/* 模态框组件 */}
    </WalletContext.Provider>
  );
};

// 3. 自定义Hook
export const useWallet = (): WalletContextValue => {
  const context = useContext(WalletContext);
  if (!context) {
    throw new Error('useWallet must be used within WalletProvider');
  }
  return context;
};
```

### 3.2 核心功能实现

#### 🔌 连接功能
```typescript
const connect = async (walletId: string): Promise<void> => {
  try {
    const wallet = walletsMap[walletId];
    if (!wallet) throw new Error(`Wallet ${walletId} not found`);

    setState(prev => ({ ...prev, isConnecting: true, error: null }));

    // 调用具体钱包的连接器
    const { address, chainId, provider, signer } = await wallet.connector();

    setState({
      address, chainId,
      isConnecting: false,
      isConnected: true,
      ensName: null,
      error: null,
    });

    // 持久化连接信息
    setStorageItem('lastConnectedWallet', walletId, 24);
    setIsModalOpen(false);
  } catch (error) {
    setState(prev => ({
      ...prev,
      isConnecting: false,
      error: error as Error,
    }));
  }
};
```

#### 🔄 链切换功能
```typescript
const switchChain = async (chainId: number): Promise<void> => {
  const targetChain = chains.find(chain => chain.id === chainId);
  if (!targetChain) {
    throw new Error(`Chain ${chainId} not supported`);
  }
  
  try {
    // 尝试切换到目标链
    await window.ethereum.request({
      method: 'wallet_switchEthereumChain',
      params: [{ chainId: `0x${chainId.toString(16)}` }],
    });
  } catch (switchError: any) {
    // 如果链未添加，则添加该链
    if (switchError.code === 4902) {
      await window.ethereum.request({
        method: 'wallet_addEthereumChain',
        params: [{
          chainId: `0x${chainId.toString(16)}`,
          chainName: targetChain.name,
          rpcUrls: [targetChain.rpcUrl],
          nativeCurrency: targetChain.currency,
          blockExplorerUrls: targetChain.blockExplorer 
            ? [targetChain.blockExplorer.url] : undefined,
        }],
      });
    }
  }
};
```

### 3.3 事件监听和响应

#### 📡 钱包事件处理
```typescript
useEffect(() => {
  const handleAccountsChanged = (event: CustomEvent<{accounts: string[]}>) => {
    const [newAddress] = event.detail.accounts;
    setState(prev => ({
      ...prev,
      address: newAddress || null,
      isConnected: !!newAddress,
    }));
  };

  const handleChainChanged = (event: CustomEvent<{chainId: number}>) => {
    setState(prev => ({
      ...prev,
      chainId: event.detail.chainId,
    }));
  };

  // 注册事件监听器
  window.addEventListener('wallet_accountsChanged', handleAccountsChanged);
  window.addEventListener('wallet_chainChanged', handleChainChanged);
  
  return () => {
    // 清理事件监听器
    window.removeEventListener('wallet_accountsChanged', handleAccountsChanged);
    window.removeEventListener('wallet_chainChanged', handleChainChanged);
  };
}, []);
```

---

## 🔌 第四章：钱包连接器详解

### 4.1 连接器设计模式

#### 🎯 统一接口
所有钱包连接器都遵循相同的接口：
```typescript
const connector = async (): Promise<{
  provider: any;
  signer: any;
  address: string;
  chainId: number;
}> => {
  // 具体连接逻辑
};
```

### 4.2 MetaMask连接器实现

#### 🦊 核心连接逻辑
```typescript
const connectMetaMask = async (): Promise<any> => {
  // 1. 检查安装状态
  if (!isMetaMaskInstalled()) {
    throw new Error('MetaMask is not installed');
  }

  // 2. 请求账户访问权限
  const accounts = await window.ethereum?.request({
    method: 'eth_requestAccounts',
  });

  // 3. 创建Provider和Signer
  const provider = new ethers.BrowserProvider(window.ethereum!);
  const signer = await provider.getSigner();
  const address = await signer.getAddress();
  const { chainId } = await provider.getNetwork();

  // 4. 设置事件监听
  setupEventListeners();

  return { provider, signer, address, chainId };
};
```

#### 📡 事件监听设置
```typescript
const setupEventListeners = () => {
  window.ethereum?.on('accountsChanged', (newAccounts: string[]) => {
    if (newAccounts.length === 0) {
      window.dispatchEvent(new CustomEvent('wallet_disconnected'));
    } else {
      window.dispatchEvent(new CustomEvent('wallet_accountsChanged', {
        detail: { accounts: newAccounts },
      }));
    }
  });

  window.ethereum?.on('chainChanged', (chainIdHex: string) => {
    const newChainId = parseInt(chainIdHex, 16);
    window.dispatchEvent(new CustomEvent('wallet_chainChanged', {
      detail: { chainId: newChainId },
    }));
  });
};
```

### 4.3 多钱包支持策略

#### 🔄 钱包检测逻辑
```typescript
// utils.ts
export const isMetaMaskInstalled = (): boolean => {
  return typeof window !== 'undefined' && 
         typeof window.ethereum !== 'undefined' && 
         window.ethereum.isMetaMask === true;
};

export const isCoinbaseInstalled = (): boolean => {
  return typeof window !== 'undefined' && 
         typeof window.ethereum !== 'undefined' && 
         window.ethereum.isCoinbaseWallet === true;
};
```

---

## 🎨 第五章：UI组件设计

### 5.1 ConnectButton组件架构

#### 🎛️ 多状态设计
```typescript
const ConnectButton: React.FC<ConnectButtonProps> = ({
  label = 'Connect Wallet',
  showBalance = true,
  chainStatus = 'full',      // 链显示模式
  accountStatus = 'full',    // 账户显示模式  
  size = 'md',              // 按钮尺寸
  className = '',
}) => {
  const {
    isConnected, address, chainId, 
    openModal, disconnect, switchChain
  } = useWallet();

  // 未连接状态：显示连接按钮
  if (!isConnected) {
    return (
      <button onClick={openModal} className={buttonStyles}>
        <Wallet size={iconSize} />
        {label}
      </button>
    );
  }

  // 已连接状态：显示网络选择器和账户信息
  return (
    <div className="flex items-center gap-2">
      <NetworkSelector />
      <AccountDisplay />
    </div>
  );
};
```

#### 🌐 网络选择器
```typescript
const NetworkSelector = () => {
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
  
  return (
    <div className="relative">
      <button onClick={() => setIsDropdownOpen(!isDropdownOpen)}>
        <Globe size={16} />
        {getNetworkName(chainId)}
        <ChevronDown size={16} />
      </button>
      
      {isDropdownOpen && (
        <div className="dropdown-menu">
          {chains.map((chain) => (
            <button
              key={chain.id}
              onClick={() => handleChainSwitch(chain.id)}
              className={chain.id === chainId ? 'active' : ''}
            >
              {chain.name}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};
```

### 5.2 响应式设计和主题

#### 🎨 Tailwind CSS集成
```typescript
const sizeClasses = {
  sm: 'text-sm px-3 py-1.5',
  md: 'text-base px-4 py-2', 
  lg: 'text-lg px-6 py-3',
};

const buttonStyles = `
  flex items-center justify-center gap-2 
  bg-blue-600 hover:bg-blue-700 
  text-white font-medium rounded-xl
  transition-colors duration-200
  ${sizeClasses[size]}
  ${className}
`;
```

#### 🌙 深色模式支持
```css
/* 自动适配深色模式 */
.wallet-button {
  @apply bg-white dark:bg-gray-800 
         border-gray-200 dark:border-gray-700
         text-gray-900 dark:text-white
         hover:bg-gray-50 dark:hover:bg-gray-750;
}
```

---

## 🛠️ 第六章：工具函数和实用功能

### 6.1 地址处理工具

#### ✂️ 地址截断
```typescript
export const truncateAddress = (address: string): string => {
  if (!address) return '';
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
};
```

#### 💰 余额格式化
```typescript
export const formatEther = (wei: bigint): string => {
  const ether = ethers.formatEther(wei);
  const num = parseFloat(ether);
  
  if (num < 0.0001) return '< 0.0001 ETH';
  if (num < 1) return `${num.toFixed(4)} ETH`;
  if (num < 1000) return `${num.toFixed(3)} ETH`;
  
  return `${(num / 1000).toFixed(2)}K ETH`;
};
```

### 6.2 本地存储管理

#### 💾 持久化工具
```typescript
export const setStorageItem = <T>(
  key: string, 
  value: T, 
  expiryHours: number = 24
): void => {
  const item = {
    value,
    expiry: Date.now() + (expiryHours * 60 * 60 * 1000),
  };
  localStorage.setItem(key, JSON.stringify(item));
};

export const getStorageItem = <T>(key: string): T | null => {
  const itemStr = localStorage.getItem(key);
  if (!itemStr) return null;

  const item = JSON.parse(itemStr);
  if (Date.now() > item.expiry) {
    localStorage.removeItem(key);
    return null;
  }

  return item.value;
};
```

### 6.3 网络工具

#### 🌐 网络名称映射
```typescript
export const getNetworkName = (chainId: number): string => {
  const networks: Record<number, string> = {
    1: 'Ethereum',
    5: 'Goerli',
    11155111: 'Sepolia',
    137: 'Polygon',
    80001: 'Mumbai',
    56: 'BSC',
    97: 'BSC Testnet',
  };
  
  return networks[chainId] || `Chain ${chainId}`;
};
```

---

## 🔧 第七章：项目配置和构建

### 7.1 开发环境配置

#### 📦 核心依赖
```json
{
  "dependencies": {
    "ethers": "^6.14.3",        // 以太坊交互库
    "lucide-react": "^0.344.0", // 图标库
    "react": "^19.0.0",         // React框架
    "wagmi": "^2.15.4"          // Web3 React Hooks
  },
  "devDependencies": {
    "tailwindcss": "^3.4.1",   // CSS框架
    "typescript": "^5.5.3",     // TypeScript
    "vite": "^5.4.2",           // 构建工具
    "terser": "^5.44.1"         // 代码压缩
  }
}
```

#### ⚙️ TypeScript配置
```json
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  }
}
```

### 7.2 构建和部署

#### 🚀 双模式构建系统

本项目支持两种构建模式：
1. **SDK库模式** - 构建可发布的npm包
2. **演示网页模式** - 构建展示SDK功能的静态网站

#### 📦 构建脚本说明

```json
{
  "scripts": {
    "dev": "vite",                          // 开发服务器（SDK演示）
    "dev:demo": "vite --mode demo",         // 演示网页开发模式
    "build": "npm run build:sdk && npm run build:demo",  // 构建所有
    "build:sdk": "vite build --mode lib",   // 构建SDK库
    "build:demo": "vite build --mode demo", // 构建演示网页
    "build:lib": "npm run build:sdk && npm run build:types", // 构建SDK+类型
    "preview": "vite preview",              // 预览SDK构建
    "preview:demo": "vite preview --outDir docs" // 预览演示网页
  }
}
```

#### ⚙️ Vite配置（双模式）
```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';

export default defineConfig(({ mode }) => {
  const isLib = mode === 'lib';
  
  return {
    plugins: [react()],
    
    build: isLib ? {
      // SDK库模式配置
      lib: {
        entry: resolve(__dirname, 'src/wallet-sdk/index.ts'),
        name: 'MetaNodeWalletSDK',
        fileName: (format) => `metanode-wallet-sdk.${format}.js`,
        formats: ['es', 'umd', 'cjs']
      },
      rollupOptions: {
        external: ['react', 'react-dom', 'react/jsx-runtime', 'ethers'],
        output: {
          globals: {
            'react': 'React',
            'react-dom': 'ReactDOM',
            'react/jsx-runtime': 'React',
            'ethers': 'ethers'
          }
        }
      },
      outDir: 'dist',
      minify: 'terser'
    } : {
      // 演示网页模式配置
      outDir: 'docs',
      rollupOptions: {
        output: {
          manualChunks: {
            'vendor': ['react', 'react-dom'],
            'highlight': ['highlight.js']
          }
        }
      },
      minify: 'terser'
    }
  };
});
```

#### 📁 构建输出目录

- **dist/** - SDK库文件（npm发布）
  - `metanode-wallet-sdk.es.js` - ES模块版本
  - `metanode-wallet-sdk.umd.js` - UMD版本（浏览器）
  - `metanode-wallet-sdk.cjs.js` - CommonJS版本
  
- **docs/** - 演示网页文件（可部署到GitHub Pages）
  - `index.html` - 主页
  - `assets/` - 静态资源

#### 🚀 快速开始

```bash
# 安装依赖
npm install

# 开发模式（演示网页）
npm run dev

# 构建SDK库
npm run build:sdk

# 构建演示网页
npm run build:demo

# 一键构建所有
npm run build

# 预览构建结果
npm run preview:demo
```

---

## 🎯 第八章：使用示例和最佳实践

### 8.1 基础使用

#### 🏗️ 应用设置
```tsx
// App.tsx
import { WalletProvider } from './wallet-sdk';
import { metaMaskWallet, coinbaseWallet } from './wallet-sdk/connectors';

const chains = [
  {
    id: 1,
    name: 'Ethereum',
    rpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key',
    currency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
  },
  {
    id: 11155111,
    name: 'Sepolia',
    rpcUrl: 'https://sepolia.infura.io/v3/your-project-id',
    currency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
  },
];

const wallets = [metaMaskWallet, coinbaseWallet];

function App() {
  return (
    <WalletProvider chains={chains} wallets={wallets}>
      <YourApp />
    </WalletProvider>
  );
}
```

#### 🎛️ 组件使用
```tsx
// 在任何子组件中
import { useWallet } from './wallet-sdk';
import ConnectButton from './wallet-sdk/components/ConnectButton';

function MyComponent() {
  const { isConnected, address, chainId } = useWallet();

  return (
    <div>
      <ConnectButton 
        label="连接钱包"
        size="lg"
        chainStatus="name"
        accountStatus="full"
      />
      
      {isConnected && (
        <div>
          <p>地址: {address}</p>
          <p>链ID: {chainId}</p>
        </div>
      )}
    </div>
  );
}
```

### 8.2 高级用法

#### 🔧 自定义连接器
```typescript
// 创建新的钱包连接器
import { Wallet } from './wallet-sdk/types';

const customWallet: Wallet = {
  id: 'custom-wallet',
  name: 'Custom Wallet',
  icon: CustomIcon,
  connector: async () => {
    // 自定义连接逻辑
    const provider = await connectToCustomWallet();
    const signer = provider.getSigner();
    const address = await signer.getAddress();
    const network = await provider.getNetwork();
    
    return {
      provider,
      signer, 
      address,
      chainId: Number(network.chainId),
    };
  },
  installed: checkCustomWalletInstallation(),
  downloadUrl: 'https://custom-wallet.com/download',
};
```

#### 🎨 自定义主题
```tsx
const customTheme = {
  accentColor: '#6366f1',
  accentColorForeground: '#ffffff',
  borderRadius: '12px',
  fontStack: 'system-ui, sans-serif',
};

<WalletProvider 
  chains={chains} 
  wallets={wallets}
  theme={customTheme}
>
  <App />
</WalletProvider>
```

---

## 🚀 第九章：性能优化和最佳实践

### 9.1 性能优化策略

#### ⚡ 组件优化
```typescript
// 使用React.memo避免不必要的重渲染
const ConnectButton = React.memo<ConnectButtonProps>(({ 
  label, size, showBalance 
}) => {
  // 组件逻辑
});

// 使用useMemo缓存计算结果
const walletsMap = useMemo(() => {
  return wallets.reduce((acc, wallet) => {
    acc[wallet.id] = wallet;
    return acc;
  }, {} as Record<string, Wallet>);
}, [wallets]);

// 使用useCallback缓存函数引用
const handleConnect = useCallback(async (walletId: string) => {
  await connect(walletId);
}, [connect]);
```

#### 🔄 状态更新优化
```typescript
// 批量状态更新
setState(prev => ({
  ...prev,
  isConnecting: false,
  isConnected: true,
  address: newAddress,
  chainId: newChainId,
}));

// 避免深层对象比较
const contextValue = useMemo(() => ({
  ...state,
  connect,
  disconnect,
  switchChain,
  openModal,
  closeModal,
  isModalOpen,
  provider,
  chains,
}), [state, connect, disconnect, switchChain, /* 其他依赖 */]);
```

### 9.2 错误处理最佳实践

#### 🛡️ 错误边界
```tsx
class WalletErrorBoundary extends React.Component {
  constructor(props: any) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('钱包连接错误:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <div>钱包连接出现错误，请刷新页面重试</div>;
    }

    return this.props.children;
  }
}
```

#### ⚠️ 优雅错误处理
```typescript
const connect = async (walletId: string): Promise<void> => {
  try {
    setState(prev => ({ ...prev, isConnecting: true, error: null }));
    
    const wallet = walletsMap[walletId];
    if (!wallet) {
      throw new Error(`钱包 "${walletId}" 未找到`);
    }

    if (!wallet.installed) {
      throw new Error(`请先安装 ${wallet.name}`);
    }

    const result = await wallet.connector();
    // 成功处理逻辑
    
  } catch (error) {
    const errorMessage = error instanceof Error 
      ? error.message 
      : '连接失败，请重试';
      
    setState(prev => ({
      ...prev,
      isConnecting: false,
      error: new Error(errorMessage),
    }));
    
    // 可选：错误上报
    reportError(error, { walletId, userAgent: navigator.userAgent });
  }
};
```

### 9.3 安全考虑

#### 🔒 安全最佳实践
```typescript
// 1. 验证钱包地址格式
const isValidAddress = (address: string): boolean => {
  return /^0x[a-fA-F0-9]{40}$/.test(address);
};

// 2. 限制RPC调用频率
const rateLimitedRequest = throttle(async (method: string, params: any[]) => {
  return await provider.send(method, params);
}, 1000); // 1秒限制

// 3. 敏感数据不持久化
const sanitizeForStorage = (data: any) => {
  const { privateKey, mnemonic, ...safeData } = data;
  return safeData;
};

// 4. CSP安全头设置
// <meta http-equiv="Content-Security-Policy" 
//       content="default-src 'self'; connect-src 'self' https:;">
```

---

## 📊 第十章：测试和调试

### 10.1 单元测试

#### 🧪 组件测试
```typescript
// ConnectButton.test.tsx
import { render, fireEvent, screen } from '@testing-library/react';
import { WalletProvider } from '../providers/WalletProvider';
import ConnectButton from '../components/ConnectButton';

const TestWrapper = ({ children }: { children: React.ReactNode }) => (
  <WalletProvider chains={mockChains} wallets={mockWallets}>
    {children}
  </WalletProvider>
);

describe('ConnectButton', () => {
  test('显示连接按钮当未连接时', () => {
    render(
      <TestWrapper>
        <ConnectButton label="连接钱包" />
      </TestWrapper>
    );
    
    expect(screen.getByText('连接钱包')).toBeInTheDocument();
  });

  test('点击按钮打开模态框', () => {
    render(
      <TestWrapper>
        <ConnectButton />
      </TestWrapper>
    );
    
    fireEvent.click(screen.getByRole('button'));
    expect(screen.getByText('选择钱包')).toBeInTheDocument();
  });
});
```

#### 🔧 Hook测试
```typescript
// useWallet.test.ts
import { renderHook, act } from '@testing-library/react';
import { useWallet } from '../providers/WalletProvider';

describe('useWallet', () => {
  test('初始状态正确', () => {
    const { result } = renderHook(() => useWallet(), {
      wrapper: TestWrapper,
    });

    expect(result.current.isConnected).toBe(false);
    expect(result.current.address).toBeNull();
  });

  test('连接钱包更新状态', async () => {
    const { result } = renderHook(() => useWallet(), {
      wrapper: TestWrapper,
    });

    await act(async () => {
      await result.current.connect('metamask');
    });

    expect(result.current.isConnected).toBe(true);
    expect(result.current.address).toBeTruthy();
  });
});
```

### 10.2 集成测试

#### 🔄 端到端测试
```typescript
// e2e/wallet-connection.spec.ts
import { test, expect } from '@playwright/test';

test('用户可以连接MetaMask钱包', async ({ page }) => {
  await page.goto('http://localhost:3000');
  
  // 点击连接按钮
  await page.click('[data-testid="connect-button"]');
  
  // 选择MetaMask
  await page.click('[data-testid="wallet-metamask"]');
  
  // 模拟MetaMask确认
  await page.evaluate(() => {
    window.ethereum.selectedAddress = '0x1234...';
    window.ethereum.chainId = '0x1';
  });
  
  // 验证连接状态
  await expect(page.locator('[data-testid="wallet-address"]')).toBeVisible();
});
```

### 10.3 调试工具

#### 🛠️ 开发调试
```typescript
// 开发环境调试工具
const DebugPanel = () => {
  const wallet = useWallet();
  
  if (process.env.NODE_ENV !== 'development') return null;
  
  return (
    <div className="fixed bottom-4 right-4 bg-black text-white p-4 rounded">
      <h3>钱包调试信息</h3>
      <pre>{JSON.stringify(wallet, null, 2)}</pre>
      <button onClick={() => wallet.disconnect()}>强制断开</button>
    </div>
  );
};

// 错误监控
const errorReporter = (error: Error, context: any) => {
  if (process.env.NODE_ENV === 'production') {
    // 发送到错误监控服务
    analytics.track('wallet_error', {
      error: error.message,
      stack: error.stack,
      context,
    });
  } else {
    console.error('钱包错误:', error, context);
  }
};
```

---

## 🎓 第十一章：课程总结和扩展

### 11.1 核心概念回顾

#### 🎯 学习要点
1. **架构设计**: 模块化、可扩展的SDK架构
2. **状态管理**: React Context模式的深度应用
3. **类型安全**: TypeScript在复杂项目中的使用
4. **用户体验**: 流畅的钱包连接和切换体验
5. **错误处理**: 优雅的错误处理和用户反馈

#### 🔄 设计模式总结
- **Provider模式**: 全局状态管理
- **策略模式**: 多钱包连接器设计
- **观察者模式**: 钱包事件监听
- **工厂模式**: 钱包实例创建

### 11.2 扩展方向

#### 🚀 功能扩展
```typescript
// 1. ENS域名解析
const resolveENS = async (address: string): Promise<string | null> => {
  try {
    const provider = new ethers.providers.JsonRpcProvider();
    return await provider.lookupAddress(address);
  } catch {
    return null;
  }
};

// 2. 多签钱包支持
interface MultiSigWallet extends Wallet {
  threshold: number;
  owners: string[];
  pendingTransactions: Transaction[];
}

// 3. NFT头像支持
const fetchNFTAvatar = async (address: string): Promise<string | null> => {
  // 获取用户的NFT头像
  const nfts = await fetchUserNFTs(address);
  return nfts.find(nft => nft.isAvatar)?.image || null;
};

// 4. 交易历史
const useTransactionHistory = (address: string) => {
  const [transactions, setTransactions] = useState([]);
  
  useEffect(() => {
    if (address) {
      fetchTransactionHistory(address).then(setTransactions);
    }
  }, [address]);
  
  return transactions;
};
```

#### 🌍 生态集成
```typescript
// 1. DeFi协议集成
const useTokenBalance = (tokenAddress: string) => {
  const { provider, address } = useWallet();
  const [balance, setBalance] = useState('0');
  
  useEffect(() => {
    if (provider && address && tokenAddress) {
      const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
      contract.balanceOf(address).then(setBalance);
    }
  }, [provider, address, tokenAddress]);
  
  return balance;
};

// 2. IPFS集成
const uploadToIPFS = async (file: File): Promise<string> => {
  const formData = new FormData();
  formData.append('file', file);
  
  const response = await fetch('/api/ipfs/upload', {
    method: 'POST',
    body: formData,
  });
  
  const { hash } = await response.json();
  return `ipfs://${hash}`;
};

// 3. Layer 2 支持
const bridgeToL2 = async (amount: string, l2ChainId: number) => {
  const bridgeContract = new ethers.Contract(
    BRIDGE_ADDRESS, 
    BRIDGE_ABI, 
    signer
  );
  
  return await bridgeContract.depositETH(l2ChainId, {
    value: ethers.parseEther(amount),
  });
};
```

### 11.3 实际项目应用

#### 🏗️ 企业级应用
```typescript
// 1. 权限管理
const useRoleBasedAccess = (requiredRole: string) => {
  const { address } = useWallet();
  const [hasAccess, setHasAccess] = useState(false);
  
  useEffect(() => {
    if (address) {
      checkUserRole(address, requiredRole).then(setHasAccess);
    }
  }, [address, requiredRole]);
  
  return hasAccess;
};

// 2. 审计日志
const auditLogger = {
  logWalletConnection: (address: string, walletType: string) => {
    console.log(`用户 ${address} 通过 ${walletType} 连接`);
    // 发送到审计服务
  },
  
  logTransactionSignature: (txHash: string, address: string) => {
    console.log(`用户 ${address} 签署交易 ${txHash}`);
    // 记录关键操作
  },
};

// 3. 合规检查
const complianceCheck = async (address: string): Promise<boolean> => {
  const sanctions = await checkSanctionsList(address);
  const kyc = await checkKYCStatus(address);
  
  return !sanctions && kyc;
};
```

---

## 🎯 课程作业和实践

### 练习1: 基础集成
在你的React项目中集成钱包SDK，实现：
- 钱包连接/断开
- 网络切换
- 余额显示

### 练习2: 自定义连接器
创建一个新的钱包连接器（如Phantom钱包），包括：
- 安装检测
- 连接逻辑
- 事件监听

### 练习3: 高级功能
扩展SDK功能：
- 添加交易签名功能
- 实现消息签名
- 集成ENS域名解析

### 练习4: UI定制
定制ConnectButton组件：
- 自定义样式主题
- 添加动画效果
- 实现暗黑模式切换

---

## 📚 推荐资源

### 文档和工具
- [Ethers.js文档](https://docs.ethers.org/)
- [WalletConnect文档](https://docs.walletconnect.com/)
- [MetaMask开发者文档](https://docs.metamask.io/)
- [Wagmi文档](https://wagmi.sh/)

### 社区和教程
- [以太坊开发者资源](https://ethereum.org/developers/)
- [Web3前端开发指南](https://web3.guide/)
- [DApp开发最佳实践](https://consensys.github.io/smart-contract-best-practices/)

---

**感谢参与本课程！希望这个轻量级钱包SDK项目能帮助你更好地理解Web3前端开发的核心概念和实践技巧。** 🚀