钱包是用户进入 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评论加载中…