diff --git a/basic/78-wallet-connect/docs/guides/installation.md b/basic/78-wallet-connect/docs/guides/installation.md new file mode 100644 index 000000000..eb16342be --- /dev/null +++ b/basic/78-wallet-connect/docs/guides/installation.md @@ -0,0 +1,191 @@ +# WalletConnect 安装和使用指南 + +## 安装 + +在 DApp 中使用 WalletConnect 时,可以安装 WalletConnect JavaScript 客户端库: + +```bash +npm install @walletconnect/client +``` + +对于钱包提供方,需要集成 WalletConnect SDK(提供 iOS、Android 和 Web 版本)。 + +## 基本使用 + +### 步骤 1:初始化 WalletConnect 客户端 + +在 JavaScript/TypeScript 代码中,导入并初始化 WalletConnect 客户端: + +```javascript +import WalletConnect from "@walletconnect/client"; +import QRCodeModal from "@walletconnect/qrcode-modal"; + +// 创建 WalletConnect 客户端实例 +const connector = new WalletConnect({ + bridge: "https://bridge.walletconnect.org", // 必填项 + qrcodeModal: QRCodeModal, +}); +``` + +### 步骤 2:检查连接并创建会话 + +如果没有已建立的连接,则创建一个新会话,提示用户连接钱包。 + +```javascript +// 检查是否已连接 +if (!connector.connected) { + // 创建新会话 + await connector.createSession(); +} + +// 监听会话事件 +connector.on("connect", (error, payload) => { + if (error) { + throw error; + } + + // 连接成功 + const { accounts, chainId } = payload.params[0]; +}); + +connector.on("session_update", (error, payload) => { + if (error) { + throw error; + } + + // 更新的账户和链ID + const { accounts, chainId } = payload.params[0]; +}); + +connector.on("disconnect", (error, payload) => { + if (error) { + throw error; + } + + // 已断开连接 +}); +``` + +### 步骤 3:发送交易 + +连接成功后,即可与区块链进行交互。例如,发送一笔交易: + +```javascript +// 设置交易详情 +const tx = { + from: accounts[0], // 用户地址 + to: "0xRecipientAddress", + value: "0xAmountInWei", // 金额,单位为 wei + data: "0x", // 可选数据 +}; + +// 发送交易请求 +const result = await connector.sendTransaction(tx); +``` + +## 高级配置 + +### 多链支持 + +要支持多个区块链网络,可以在初始化时配置: + +```javascript +const connector = new WalletConnect({ + bridge: "https://bridge.walletconnect.org", + qrcodeModal: QRCodeModal, + chainId: 1, // 默认以太坊主网 + supportedChainIds: [1, 56, 137] // 支持的链ID:以太坊、BSC、Polygon +}); +``` + +### 自定义配置 + +```javascript +const connector = new WalletConnect({ + bridge: "https://your-custom-bridge.com", + qrcodeModal: QRCodeModal, + clientMeta: { + name: "Your DApp", + description: "Your DApp Description", + url: "https://your-dapp.com", + icons: ["https://your-dapp.com/icon.png"] + } +}); +``` + +## 错误处理最佳实践 + +### 连接错误处理 + +```javascript +connector.on("connect", (error, payload) => { + if (error) { + console.error("连接错误:", error); + // 实现重试逻辑 + return; + } + // 连接成功处理 +}); + +// 自定义错误处理 +try { + await connector.createSession(); +} catch (error) { + if (error.message.includes("User rejected")) { + console.log("用户拒绝连接"); + } else { + console.error("创建会话失败:", error); + } +} +``` + +### 交易错误处理 + +```javascript +try { + const result = await connector.sendTransaction(tx); +} catch (error) { + if (error.message.includes("insufficient funds")) { + console.error("余额不足"); + } else if (error.message.includes("gas")) { + console.error("Gas 费用估算失败"); + } else { + console.error("交易失败:", error); + } +} +``` + +## 安全性考虑 + +### 数据加密 + +- 确保所有敏感数据在传输前进行加密 +- 不要在客户端存储私钥或助记词 +- 使用安全的通信协议(HTTPS) + +### 交易安全 + +- 在发送交易前进行金额和地址验证 +- 实现交易确认对话框 +- 显示详细的交易信息供用户确认 + +### 会话管理 + +- 定期检查会话状态 +- 实现自动断开连接的超时机制 +- 在用户离开应用时清理会话数据 + +## 性能优化 + +- 使用 WebSocket 保持连接状态 +- 实现重连机制 +- 缓存常用数据 +- 批量处理交易请求 + +## 常见问题 + +- **二维码未显示**:请确保 `QRCodeModal` 正确导入并配置。 +- **连接问题**:请检查桥接 URL,默认桥接为 `"https://bridge.walletconnect.org"`,也支持自托管桥接。 +- **断开连接**:检查网络状态,确保 WebSocket 连接正常。 +- **交易失败**:验证账户余额、Gas 费用设置是否合理。 +- **兼容性问题**:确认钱包版本是否支持当前协议版本。 diff --git a/basic/78-wallet-connect/docs/introduction/overview.md b/basic/78-wallet-connect/docs/introduction/overview.md new file mode 100644 index 000000000..3a23f903a --- /dev/null +++ b/basic/78-wallet-connect/docs/introduction/overview.md @@ -0,0 +1,445 @@ +# WalletConnect 概述 + +## 简介 + +**WalletConnect** 是一个开放协议,允许用户将加密钱包安全地连接到去中心化应用(DApps),并支持多区块链网络的交互。用户只需扫描二维码或使用深度链接,即可与 DApp 进行交互,而无需暴露私钥,提供了安全、便捷的体验。 + +WalletConnect 通过加密消息在钱包与 DApp 之间传递信息,确保安全性。此外,WalletConnect 还支持多链和多会话连接,已广泛应用于 DeFi 和 NFT 平台等 Web3 领域。 + +## 功能 + +- **多链支持**:WalletConnect v2 支持跨多个区块链网络的连接,用户可以在单一会话中管理多链资产。 +- **安全消息传递**:所有通信均加密,确保钱包和 DApp 之间的交互安全。 +- **灵活的会话管理**:支持多会话,用户可以轻松在不同 DApps 和区块链网络之间切换。 +- **简单的集成方式**:DApp 和钱包开发者可以使用 WalletConnect SDK 快速集成,并支持广泛的钱包连接。 +- **跨平台兼容**:支持 iOS、Android 和 Web,确保用户在各种设备上都能访问。 + +## 技术架构 + +### 核心组件 + +1. **Bridge Server**: + - 作为中继服务器,负责在钱包和 DApp 之间传递加密消息 + - 采用分布式架构,提高可用性和性能 + - 支持 WebSocket 长连接,实现实时通信 + + 示例配置: + ```javascript + const bridge = new WalletConnectBridge({ + url: "wss://bridge.walletconnect.org", + pingInterval: 15000, + maxAttempts: 5 + }); + ``` + +2. **Client SDK**: + - 提供多语言支持(JavaScript、Swift、Kotlin等) + - 封装核心协议逻辑,简化开发流程 + - 内置安全机制和错误处理 + + JavaScript 示例: + ```javascript + import WalletConnect from "@walletconnect/client"; + + const connector = new WalletConnect({ + bridge: "https://bridge.walletconnect.org", + qrcodeModal: QRCodeModal, + chainId: 1 + }); + + // 监听连接事件 + connector.on("connect", (error, payload) => { + if (error) { + console.error(error); + return; + } + const { accounts, chainId } = payload.params[0]; + console.log("Connected to", accounts[0]); + }); + ``` + +3. **协议层**: + - 定义标准消息格式和通信流程 + - 实现跨平台兼容性 + - 支持协议版本升级和向后兼容 + + 消息格式示例: + ```json + { + "id": 1234567890, + "jsonrpc": "2.0", + "method": "eth_sendTransaction", + "params": [{ + "from": "0x...", + "to": "0x...", + "value": "0x..." + }] + } + +### 连接流程 + +1. DApp 创建连接请求并生成二维码 +2. 用户使用钱包扫描二维码 +3. 建立加密通道 +4. 进行身份验证和授权 +5. 开始安全通信 + +## 安全机制 + +### 加密方案 + +- **非对称加密**:使用 X25519 密钥交换 +- **对称加密**:采用 AES-256-CBC 加密消息内容 +- **消息认证**:使用 HMAC-SHA256 确保消息完整性 + +### 安全特性 + +1. **零信任架构**: + - 所有通信均端到端加密 + - Bridge Server 无法解密消息内容 + - 每个会话使用唯一的密钥对 + +2. **权限控制**: + - 细粒度的操作授权 + - 支持会话过期和主动断开 + - 可限制特定链和方法的访问 + +## 应用场景 + +### DeFi 应用 + +- 去中心化交易 +- 流动性提供 +- 收益耕作 +- 借贷平台 + +### NFT 交易 + +- 市场交易 +- 铸造操作 +- 拍卖参与 +- 版税管理 + +### GameFi + +- 游戏资产管理 +- 道具交易 +- 成就奖励 +- 跨游戏互操作 + +## 性能优化 + +### 连接优化 + +1. **快速重连**: + - 缓存会话信息 + - 支持自动重连 + - 优化重连流程 + + 重连实现示例: + ```javascript + class ConnectionManager { + constructor() { + this.reconnectAttempts = 0; + this.maxAttempts = 5; + this.baseDelay = 1000; // 1秒 + } + + async reconnect() { + if (this.reconnectAttempts >= this.maxAttempts) { + throw new Error("重连次数超过限制"); + } + + // 使用指数退避策略 + const delay = this.baseDelay * Math.pow(2, this.reconnectAttempts); + await new Promise(resolve => setTimeout(resolve, delay)); + + try { + // 尝试恢复缓存的会话 + const session = localStorage.getItem("walletconnect"); + if (session) { + await connector.connect(JSON.parse(session)); + } else { + await connector.createSession(); + } + this.reconnectAttempts = 0; + return true; + } catch (error) { + this.reconnectAttempts++; + return this.reconnect(); + } + } + } + ``` + +2. **网络适应**: + - 动态超时调整 + - 自动重试机制 + - 网络状态监控 + + 网络适应示例: + ```javascript + class NetworkMonitor { + constructor() { + this.baseTimeout = 5000; + this.maxTimeout = 30000; + this.latencyHistory = []; + } + + // 计算动态超时时间 + calculateTimeout() { + if (this.latencyHistory.length === 0) { + return this.baseTimeout; + } + + // 使用最近5次请求的平均延迟 + const recentLatencies = this.latencyHistory.slice(-5); + const avgLatency = recentLatencies.reduce((a, b) => a + b, 0) / recentLatencies.length; + + // 动态超时 = 平均延迟 * 3 + 标准差 + const timeout = (avgLatency * 3) + this.calculateStdDev(recentLatencies); + return Math.min(Math.max(timeout, this.baseTimeout), this.maxTimeout); + } + + // 计算标准差 + calculateStdDev(values) { + const avg = values.reduce((a, b) => a + b, 0) / values.length; + const squareDiffs = values.map(value => Math.pow(value - avg, 2)); + const avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / squareDiffs.length; + return Math.sqrt(avgSquareDiff); + } + + // 记录请求延迟 + recordLatency(latency) { + this.latencyHistory.push(latency); + if (this.latencyHistory.length > 10) { + this.latencyHistory.shift(); + } + } + } + ``` + +### 资源管理 + +- 优化内存使用 +- 减少网络请求 +- 高效的状态同步 + +资源管理示例: +```javascript +// 内存优化 +class ResourceManager { + constructor() { + this.subscriptions = new Map(); + this.messageQueue = []; + this.maxQueueSize = 100; + this.processingInterval = 1000; // 1秒处理一次队列 + } + + // 批量处理消息队列 + processMessageQueue() { + setInterval(() => { + const batch = this.messageQueue.splice(0, 10); + if (batch.length > 0) { + this.sendBatchMessage(batch); + } + }, this.processingInterval); + } + + // 清理过期订阅 + cleanupSubscriptions() { + const now = Date.now(); + for (const [key, sub] of this.subscriptions) { + if (now - sub.lastActive > 5 * 60 * 1000) { // 5分钟无活动 + sub.unsubscribe(); + this.subscriptions.delete(key); + } + } + } + + // 状态同步优化 + async syncState() { + const lastSync = localStorage.getItem("lastSync"); + const now = Date.now(); + + // 增量同步 + if (lastSync) { + const changes = await this.getStateChangesSince(lastSync); + this.applyStateChanges(changes); + } else { + // 全量同步 + const state = await this.getFullState(); + this.setState(state); + } + + localStorage.setItem("lastSync", now.toString()); + } + + // 性能指标监控 + measurePerformance() { + return { + memoryUsage: performance.memory?.usedJSHeapSize || 0, + activeSubscriptions: this.subscriptions.size, + queueLength: this.messageQueue.length, + processingLatency: this.calculateProcessingLatency() + }; + } +} +``` + +## 最佳实践 + +### 开发建议 + +1. **错误处理**: + - 实现完整的错误处理逻辑 + - 提供用户友好的错误提示 + - 记录详细的错误日志 + + 错误处理示例: + ```javascript + connector.on("error", (error) => { + // 网络错误处理 + if (error.message.includes("network")) { + showNetworkError(); + attemptReconnect(); + return; + } + + // 会话过期处理 + if (error.message.includes("session")) { + clearSession(); + requestNewConnection(); + return; + } + + // 通用错误处理 + console.error("WalletConnect错误:", error); + showUserFriendlyError(error); + }); + + function showUserFriendlyError(error) { + const errorMessages = { + "user_rejected": "用户拒绝了请求", + "chain_not_supported": "当前钱包不支持该链", + "invalid_parameters": "无效的交易参数" + }; + + const message = errorMessages[error.code] || "连接出现问题,请稍后重试"; + displayError(message); + } + ``` + +2. **用户体验**: + - 显示连接状态和进度 + - 提供清晰的操作引导 + - 实现平滑的断开重连 + + 状态管理示例: + ```javascript + const ConnectionState = { + CONNECTING: "connecting", + CONNECTED: "connected", + DISCONNECTED: "disconnected", + ERROR: "error" + }; + + function updateConnectionUI(state) { + const stateMessages = { + [ConnectionState.CONNECTING]: "正在连接钱包...", + [ConnectionState.CONNECTED]: "钱包已连接", + [ConnectionState.DISCONNECTED]: "请连接钱包", + [ConnectionState.ERROR]: "连接出错" + }; + + // 更新UI状态 + updateStatusText(stateMessages[state]); + updateConnectButton(state); + updateLoadingIndicator(state === ConnectionState.CONNECTING); + } + ``` + +### 安全建议 + +- 定期更新 SDK 版本 +- 实施请求频率限制 +- 验证所有用户输入 +- 实现会话超时机制 + +安全实践示例: +```javascript +// 请求频率限制 +const rateLimiter = { + requests: {}, + limit: 5, + interval: 60000, // 1分钟 + + checkLimit(method) { + const now = Date.now(); + const recentRequests = this.requests[method] || []; + + // 清理过期请求 + this.requests[method] = recentRequests.filter( + time => now - time < this.interval + ); + + if (this.requests[method].length >= this.limit) { + throw new Error("请求过于频繁,请稍后再试"); + } + + this.requests[method].push(now); + } +}; + +// 输入验证 +function validateTransactionParams(params) { + const required = ["to", "value"]; + for (const field of required) { + if (!params[field]) { + throw new Error(`缺少必要参数: ${field}`); + } + } + + // 验证地址格式 + if (!/^0x[a-fA-F0-9]{40}$/.test(params.to)) { + throw new Error("无效的接收地址"); + } + + // 验证金额 + if (!/^[0-9]+$/.test(params.value)) { + throw new Error("无效的转账金额"); + } +} + +// 会话超时检查 +const SESSION_TIMEOUT = 30 * 60 * 1000; // 30分钟 +function checkSessionTimeout() { + const lastActivity = connector.lastActivityTime; + if (Date.now() - lastActivity > SESSION_TIMEOUT) { + connector.killSession(); + return false; + } + return true; +} +``` + +## 未来发展 + +### 技术路线 + +1. **协议升级**: + - 支持更多加密算法 + - 优化消息传输效率 + - 增强安全性能 + +2. **功能扩展**: + - 支持更多区块链网络 + - 提供更丰富的API + - 增加开发者工具 + +### 生态建设 + +- 扩大合作伙伴网络 +- 建立开发者社区 +- 提供更多学习资源 +- 举办技术研讨会 diff --git a/basic/78-wallet-connect/v2/multichain-example/.env.example b/basic/78-wallet-connect/v2/multichain-example/.env.example new file mode 100644 index 000000000..a1af2b6e5 --- /dev/null +++ b/basic/78-wallet-connect/v2/multichain-example/.env.example @@ -0,0 +1,5 @@ +# WalletConnect Project ID from https://cloud.walletconnect.com +REACT_APP_PROJECT_ID=your_project_id_here + +# Infura API Key for multiple chain support +REACT_APP_INFURA_KEY=your_infura_key_here \ No newline at end of file diff --git a/basic/78-wallet-connect/v2/multichain-example/README.md b/basic/78-wallet-connect/v2/multichain-example/README.md new file mode 100644 index 000000000..cd21824a2 --- /dev/null +++ b/basic/78-wallet-connect/v2/multichain-example/README.md @@ -0,0 +1,89 @@ +# WalletConnect 多链交互示例 + +这个示例项目展示了如何使用 WalletConnect v2 实现多链交互功能,包括跨链资产查看、多链钱包连接和链间切换等功能。 + +## 功能特点 + +- 支持多个区块链网络的同时连接 +- 实时显示多链资产余额 +- 支持在不同链之间无缝切换 +- 展示跨链交易历史 +- 集成多链交易签名功能 + +## 技术栈 + +- React.js - 前端框架 +- wagmi - 以太坊 React Hooks 库 +- web3modal - Web3钱包连接组件 +- ethers.js - 区块链交互库 +- viem - 现代以太坊开发工具包 + +## 支持的网络 + +- Ethereum Mainnet +- Polygon +- Arbitrum +- Optimism +- Binance Smart Chain + +## 开始使用 + +1. **安装依赖** + ```bash + yarn install + ``` + +2. **环境配置** + - 复制 `.env.example` 到 `.env` + - 在 [WalletConnect Cloud](https://cloud.walletconnect.com) 注册并获取项目ID + - 在 [Infura](https://infura.io) 注册并获取API密钥 + - 更新 `.env` 文件中的配置 + +3. **启动开发服务器** + ```bash + yarn start + ``` + +## 项目结构 + +``` +├── src/ +│ ├── components/ # React组件 +│ ├── hooks/ # 自定义Hooks +│ ├── config/ # 配置文件 +│ ├── utils/ # 工具函数 +│ └── services/ # 区块链服务 +``` + +## 主要功能演示 + +1. **多链钱包连接** + - 支持同时连接多个区块链网络 + - 显示每个网络的连接状态 + - 提供网络切换功能 + +2. **资产管理** + - 查看多链资产余额 + - 显示代币价格和市值 + - 支持资产转账功能 + +3. **交易功能** + - 发送跨链交易 + - 查看交易历史 + - 交易状态追踪 + +## 测试 + +```bash +# 运行单元测试 +yarn test + +# 运行E2E测试 +yarn test:e2e +``` + +## 参考资源 + +- [WalletConnect v2 文档](https://docs.walletconnect.com/2.0) +- [Wagmi 文档](https://wagmi.sh) +- [Web3Modal 文档](https://docs.walletconnect.com/2.0/web3modal/about) diff --git a/basic/78-wallet-connect/v2/multichain-example/package.json b/basic/78-wallet-connect/v2/multichain-example/package.json new file mode 100644 index 000000000..b6633f258 --- /dev/null +++ b/basic/78-wallet-connect/v2/multichain-example/package.json @@ -0,0 +1,40 @@ +{ + "name": "multichain-example", + "version": "1.0.0", + "private": true, + "dependencies": { + "@wagmi/core": "^1.4.5", + "@web3modal/ethereum": "^2.7.1", + "@web3modal/react": "^2.7.1", + "ethers": "^5.7.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "5.0.1", + "viem": "^1.18.1", + "wagmi": "^1.4.5" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} \ No newline at end of file diff --git a/basic/78-wallet-connect/v2/multichain-example/public/index.html b/basic/78-wallet-connect/v2/multichain-example/public/index.html new file mode 100644 index 000000000..32eba196d --- /dev/null +++ b/basic/78-wallet-connect/v2/multichain-example/public/index.html @@ -0,0 +1,20 @@ + + +
+ + + + + + + +已连接地址: {address}
+当前网络: {selectedChain?.name || '未知网络'}
+余额: {balance?.formatted || '0'} {balance?.symbol}
+ +请连接钱包
+{supportedChain.name}
++ Chain ID: {supportedChain.id} +
+