教程区块链区块链技术ch1414.2 钱包连接与账户管理

本页目录

钱包是用户进入 Web3 的唯一入口。连接质量直接决定用户留存——EIP-6963 修复了多年的"互斥钱包注入"问题,嵌入式钱包(Privy、Coinbase Smart Wallet)正在让 Web3 像 Web2 一样简单。


14.2.1 浏览器钱包的进化

历史:窗口注入的混乱

graph TD
    subgraph Old["2023 年之前"]
        A[window.ethereum] --> B[MetaMask]
        A --> C[Coinbase Wallet]
        A --> D[多个钱包互斥覆盖]
    end
    
    subgraph New["EIP-6963 之后"]
        E[event: eip6963:announceProvider] --> F[MetaMask]
        E --> G[Coinbase]
        E --> H[Phantom]
        E --> I[Rainbow]
        F -.-> J[选择窗口]
    end
    
    style Old fill:#ffebee
    style New fill:#e8f5e9

EIP-6963(2023 年 11 月,MetaMask 主导):每个钱包暴露 eip6963:announceProvider 事件,DApp 可以发现并列出所有已安装的钱包,而非猜测 window.ethereum 被谁覆盖。


14.2.2 连接协议的核心流程

typescript
/**
 * 钱包连接的 TypeScript 模拟
 * 展示 EIP-1102, EIP-6963 和现代连接模式
 */

// === 阶段 1: 传统注入(EIP-1102)
interface WindowEthereum {
  async request(args: { method: string; params?: any }): Promise<any>;
  on(event: string, handler: (args: any) => void): void;
  removeListener(event: string, handler: any): void;
  selectedAddress: string | null;
  isMetaMask?: boolean;
}

async function legacyConnect(): Promise<string[]> {
  // 检查 window.ethereum(已被覆盖风险)
  const eth = (window as any).ethereum as WindowEthereum;
  if (!eth) throw new Error("No wallet installed");
  
  // EIP-1102: 请求用户授权连接
  const accounts = await eth.request({
    method: "eth_requestAccounts",
    params: [],
  });
  // 返回已授权地址列表
  return accounts; // ["0x...", ...]
}

// === 阶段 2: EIP-6963 多钱包发现
interface EIP6963Provider {
  info: {
    uuid: string;           // 唯一标识
    name: string;           // 钱包名称
    icon: string;           // base64 icon
    rdns: string;           // 反向域名(如 com.metamask)
  };
  provider: WindowEthereum; // 实际 EIP-1193 provider
}

class WalletDiscovery {
  private providers: Map<string, EIP6963Provider> = new Map();
  
  discover(): Promise<EIP6963Provider[]> {
    return new Promise((resolve) => {
      const providers: EIP6963Provider[] = [];
      
      // 监听 announce 事件
      const onAnnounce = (event: any) => {
        const detail: EIP6963Provider = event.detail;
        if (!this.providers.has(detail.info.uuid)) {
          this.providers.set(detail.info.uuid, detail);
          providers.push(detail);
        }
      };
      
      window.addEventListener("eip6963:announceProvider", onAnnounce);
      
      // 主动请求所有钱包重新 announce
      setTimeout(() => {
        window.dispatchEvent(new Event("eip6963:requestProvider"));
      }, 100);
      
      // 3 秒后返回收集到的钱包
      setTimeout(() => {
        window.removeEventListener("eip6963:announceProvider", onAnnounce);
        resolve(providers);
      }, 3000);
    });
  }
  
  async connectByUUID(uuid: string): Promise<string[]> {
    const p = this.providers.get(uuid);
    if (!p) throw new Error("Wallet not discovered");
    return p.provider.request({ method: "eth_requestAccounts", params: [] });
  }
}

// 示例使用
async function multiWalletDemo() {
  const discovery = new WalletDiscovery();
  const wallets = await discovery.discover();
  console.log("发现钱包:", wallets.map(w => w.info.name));
  
  // 用户选择后连接
  const mm = wallets.find(w => w.info.rdns === "io.metamask");
  if (mm) {
    const accounts = await mm.provider.request({ method: "eth_requestAccounts" });
    console.log("MetaMask 地址:", accounts[0]);
  }
}

console.log("钱包发现机制已定义");

14.2.3 嵌入式钱包(Embedded Wallets)

类型代表流程门槛
浏览器扩展MetaMask安装 → 创建/导入助记词 → 备份
移动端Trust, 1inch安装 App → 创建
嵌入式(Web2 登录)Coinbase Smart Wallet, Privy, Dynamic邮箱/社交 → 链上账户自动创建

嵌入式钱包原理

graph LR
    User[用户] --> |"邮箱 / Google / X 登录"| Auth[auth.privy.io / Coinbase]
    Auth --> |"返回 JWT + 用户 ID"| KeyGen[分布式密钥派生]
    KeyGen --> |"EOA 或 智能钱包"| Account[链上账户]
    Account --> |"无需 seed phrase"| User
    
    style KeyGen fill:#e3f2fd

密钥的托管模式:

  • MPC(多方计算):密钥分片存于不同设备/节点,永远无法完整重建
  • Passkey / 生物识别:设备本地 HSM 签名,云端只存公钥
  • 账户抽象(ERC-4337):用户不管理 EOA,直接控制链上"智能钱包"

14.2.4 账户抽象与 Smart Wallet

typescript
/**
 * 账户抽象(ERC-4337)核心:用户的"账户"是一个智能合约
 */
interface SmartWallet {
  // 传统 EOA:address = keccak256(publicKey)[12:]
  // 4337 智能账户:address = CREATE2 部署的合约地址
  
  address: string;
  
  // 入口点合约(所有 4337 交易都通过它)
  entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
  
  // 验证逻辑可以是:签名、M-of-N 多签、社交恢复、Session Key...
  async validateUserOp(userOp: UserOperation): Promise<ValidationData>;
}

interface UserOperation {
  sender: string;           // 4337 智能账户地址
  nonce: bigint;
  initCode?: string;        // 首次部署账户的字节码
  callData: string;         // 实际调用
  signature: string;        // 签名(不验证 sender 地址,验证自定义逻辑)
  paymaster?: string;       // 可选的代付方
}

function calculate4337Address(
  factory: string,
  salt: bigint,
  initCodeHash: string,
): string {
  // 与 CREATE2 相同公式
  return "0x" + BigInt(
    "0x" + keccak256("0xFF" + factory + salt.toString(16).padStart(64, '0') + initCodeHash)
  ).toString(16).slice(-40).padStart(40, '0');
}

// 关键优势:
// 1. 用户无需知道"私钥"——可以邮箱+验证码控制账户
// 2. 批量交易(一次签名,10 次操作)
// 3. 社交恢复(3 个朋友 = 恢复账户)
// 4. 免 gas 支付(paymaster 代付)

console.log("ERC-4337 智能钱包:签名 = 任何可验证逻辑,不限于 secp256k1");

14.2.5 连接状态管理

graph TD
    A[未连接] --> |点击连接| B[请求中]
    B --> |用户确认| C[已连接]
    B --> |用户拒绝| D[拒绝]
    C --> |切换网络| E[切换中]
    C --> |断开| A
    C --> |账户切换| C
    E --> |成功| C
    E --> |不支持| F[错误]
    
    style A fill:#ffebee
    style C fill:#c8e6c9
    style D fill:#ffebee
    style F fill:#ffebee

> ← 14.1 前端库 | 前往 → 14.3 交易构造与追踪 |*

评论

0

评论加载中…

发表评论

0/2000