← 返回 MegaWallet 工作文档

MEGAWALLET · WORKING DOCUMENT 07

钱包授权机制通识分享

原始文件 · my-history/7.SharePermission.md

面向产品和技术团队。覆盖 EIP-2255 权限提案生态、行业落地现状、标准 EOA 权限流程,以及一张多维场景对比矩阵。


第一部分:权限提案全景 — 标准在哪,行业在哪

1. 标准 EOA 权限相关提案:从哪来,为什么会有

按时间线梳理,这些提案的出现是一个逐步“补洞”的过程:

2015 — EIP-191:签名消息标准 最早期,链上只有裸交易签名。191 定义了 personal_sign 的消息前缀格式(\x19Ethereum Signed Message:\n),让签名消息和交易签名区分开,防止 DApp 骗用户签一条消息实际上是一笔交易。

2017 — EIP-1193:Provider 接口标准 DApp 和钱包之间需要一个通信协议。1193 定义了 window.ethereum 这个 provider 对象的标准接口 — request()、事件机制(accountsChangedchainChanged)等。这是整个 DApp ↔ 钱包交互的基础管道,后面所有提案都跑在这个协议上。

2018 — EIP-712:结构化数据签名 191 只能签纯文本,用户看到的是一串 hex。712 让 DApp 可以提交结构化的类型化数据,钱包以人类可读的方式展示字段和值。这对 DeFi 场景(permit 签名、订单签名)至关重要。

2018 — EIP-1102:显式账户请求 在 1102 之前,DApp 可以直接读 eth_accounts 拿到用户地址,无需任何授权。这是一个隐私问题 — 用户打开任何 DApp 页面,地址就暴露了。1102 要求 DApp 必须调 eth_requestAccounts 显式请求,钱包弹窗让用户确认。这是第一个“权限”概念的引入。

2019 — EIP-2255:通用权限框架 MetaMask 团队把 1102 的思路泛化。在 2255 的视角下,eth_requestAccounts 等同于 wallet_requestPermissions({ eth_accounts: {} }) — 请求 eth_accounts 这个 restricted method 的权限。2255 定义了:

设计愿景很大:用户可以设定交易限额、限制交互合约、设置自动登出计时器。但实际上 caveat 只定义了 { type: string, value: any } 的开放接口,没有指定任何标准 caveat 类型,规范中的 filterResponserequiredMethods 都只是举例。这个“完全开放”的设计本意是前向可扩展,结果变成了谁也不去扩展它。

2021 — EIP-3085 / EIP-3326:网络管理 多链生态爆发后,DApp 需要引导用户切换到正确的链。3326 定义了 wallet_switchEthereumChain,3085 定义了 wallet_addEthereumChain(添加用户钱包里没有的链)。这些是非受限方法,但钱包仍弹窗确认 — 添加恶意 RPC 可能导致钓鱼。

2023 — EIP-6963:多钱包发现 1193 定义的 window.ethereum 是全局单例,多钱包安装时互相覆盖(后装的赢)。6963 让每个钱包通过事件广播自己的存在,DApp 监听后展示选择列表。解决了“我装了 MetaMask 和 Rabby,但 DApp 只看到一个”的问题。

它们之间的关系:

EIP-1193(通信管道)
  └─ EIP-1102(账户请求)→ EIP-2255(通用权限框架 + caveat)
  └─ EIP-3085 / 3326(网络管理)
  └─ EIP-191 → EIP-712(签名标准演进)
EIP-6963(发现层,解决 1193 的单例问题)

1193 是地基,所有交互跑在上面。1102 → 2255 是权限层的演进。3085/3326 补了多链场景。191 → 712 是签名能力的演进。6963 修了 1193 的多钱包缺陷。

3. 行业现状:MetaMask 是天花板,其他钱包连这层都没做

钱包 EIP-2255 权限系统 实际 caveat 使用 差异化方向
MetaMask 完整实现(PermissionController + restricted/unrestricted 方法分类 + MIP-2 撤销) restrictReturnedAccounts(过滤返回账户) 权限架构、Snaps 插件、Delegation Toolkit
Rabby 未实现,兼容 MetaMask 接口 交易模拟/预览、合约风险分析、自动链切换
OKX Wallet 未实现,兼容 MetaMask 接口 多链覆盖、一站式交易所+钱包、内置 DEX/NFT
Rainbow 未实现,兼容 MetaMask 接口 NFT 展示、极简 UX、链上 Approval 管理界面
Coinbase Wallet 未实现,兼容 MetaMask 接口 法币入金、与 Coinbase 交易所无缝衔接

TokenPocket 文档原话:“eth_accounts 目前是唯一的权限,这个方法就是你现在需要的全部。” — 基本代表了除 MetaMask 外整个行业的态度。

4. 为什么大家都没做?

  1. DApp 侧不需要。 没有 DApp 会调 wallet_requestPermissions 请求复杂权限,也没有 DApp 读 wallet_getPermissions 检查 caveats。所有 DApp 就做一件事:eth_requestAccounts 拿地址,然后逐次调签名。
  2. EOA 的本质限制。 RPC 层的 caveat 只能过滤返回值或控制方法调用权,无法在链上强制执行交易限制。用户一旦点了签名确认,链上执行不受钱包管控。真正的约束必须在链上执行,而 EOA 没有可编程性。
  3. 竞争差异化方向不在这里。 各钱包把精力放在了交易安全(模拟/风险检测)、多链 UX、一站式功能上,而不是权限 caveats。
  4. MetaMask 做权限系统也是为未来铺路。 PermissionController 真正发挥价值的地方是 Snaps(插件需要严格权限隔离)和 Delegation Toolkit(7710/7715 需要钱包侧有成熟的权限基础设施)。如果不做 Snaps 也不做智能账户委托,EIP-2255 权限系统就是过度工程。

第二部分:标准 EOA 钱包的权限流程

用户从打开 DApp 到完成操作,经历的权限环节:发现 → 连接 → 逐次确认

  1. 钱包发现(EIP-6963) — DApp 通过事件广播发现多个钱包,用户选择 provider。无权限授予。
  2. 连接/暴露账户(EIP-1102 → EIP-2255) — DApp 调 eth_requestAccounts,钱包弹窗让用户选择暴露哪些地址。这是第一个真正的权限行为。
  3. 网络切换(EIP-3085 / EIP-3326)wallet_switchEthereumChain / wallet_addEthereumChain,非受限方法,但钱包仍弹窗确认。
  4. 签名请求personal_signeth_signTypedData_v4eth_sendTransaction,每次调用都弹窗,无预授权机制。
  5. Token 授权(ERC-20 approve) — 链上层面的权限,和钱包权限机制是两回事,但用户体验上混在一起。

本质上就是一个“看门人”模型 — 钱包守着 RPC 通道的大门,决定 DApp 能不能跟账户“说上话”。一旦 DApp 拿到说话的权利,后续每个操作就退化成逐次弹窗确认。


第三部分:边界场景对比矩阵

不列“正常流程”(连接弹窗长什么样、签名弹窗展示什么 — 这些打开就能看到)。只列那些状态不一致、时序冲突、通道差异下的刁钻表现。MegaWallet 列留空待填。

账户状态

场景 MegaWallet MetaMask OKX Wallet Rainbow Coinbase Wallet
钱包锁定时,DApp 调 eth_accounts(已有连接权限)→ 返回 [] 还是已授权地址?
用户切到 Account B(B 未被授权给该 DApp)→ emit B?emit []?还是不 emit、保持原授权账户?
eth_sendTransactionfrom 指定非当前活跃账户 → 拒绝、忽略 from、还是切过去?

网络状态

场景 MegaWallet MetaMask OKX Wallet Rainbow Coinbase Wallet
tx 带的 chainId 和钱包当前链不匹配 → 在当前链执行?拒绝?自动切?
钱包是“全网络可选”心智模型 → 连接时报给 DApp 的 chainId 是哪个?
两个 DApp 同时连接,分别期望不同链 → 全局单链互相影响,还是 per-DApp 隔离?

并发 / 时序

场景 MegaWallet MetaMask OKX Wallet Rainbow Coinbase Wallet
DApp 连续快速发多个签名请求 → 排队?覆盖?并行弹窗?
上一笔交易 pending 中,DApp 发新交易 → nonce 管理策略(自动递增?等待?)
wallet_switchEthereumChaineth_sendTransaction 几乎同时到达 → 竞态处理

硬件钱包

场景 MegaWallet MetaMask OKX Wallet Rainbow Coinbase Wallet
硬件钱包签名超时(用户长时间不操作设备)→ 超时时间?DApp 收到什么?
DApp 发 EIP-712 签名但设备固件不支持 → 提示开盲签?直接失败?软件端降级处理?
DApp 连发多笔交易 → 每笔都要设备确认?还是可以批量?

WalletConnect 模式

场景 MegaWallet MetaMask OKX Wallet Rainbow Coinbase Wallet
手机钱包 App 被系统杀掉 / 后台冻结 → DApp 发请求的表现和超时策略
WC 会话过期 → DApp 继续发请求 → 错误处理和自动重连策略
用户在手机端切了账户/链 → 桌面 DApp 是否通过 session update 感知?
namespace 协商:DApp 要求 eip155 + solana,钱包只支持 eip155 → 部分通过还是整体拒绝?

容错

场景 MegaWallet MetaMask OKX Wallet Rainbow Coinbase Wallet
gas 估算失败(合约 revert)→ 仍允许用户强制发送?还是直接拦截?
RPC 节点不可达 → 降级/重试/切备用节点策略

第四部分:关键设计争议点分析

矩阵里几个场景背后有根本性的架构分歧,不是简单的“A 还是 B”能讲清楚的。这里展开讨论每个争议点的本质、行业做法、以及对 MegaWallet 的影响。

1. 切账户时 DApp 感知策略:全局活跃 vs per-DApp 绑定

这是最核心的架构分歧,直接决定了账户状态相关场景的所有行为。

全局活跃账户模型(MetaMask): 钱包维护一个全局“当前账户”。DApp 看到的永远是这个当前账户(前提是有权限)。用户在钱包里切账户,所有已连接 DApp 都受影响。

per-DApp 绑定模型: 每个 DApp 有自己绑定的账户,钱包内部切账户不影响任何 DApp 连接。只有用户在 DApp 内主动操作(比如点“切换账户”)才会改变绑定关系。

MegaWallet 需要决定: 如果走 per-DApp 绑定,架构上更干净(DApp 连接是稳定的),但需要提供 DApp 内切换账户的交互入口。如果走全局活跃,兼容性最好(DApp 生态默认假设 MetaMask 行为),但要接受“切账户断连接”的体验代价。

2. 钱包锁定时的权限语义:连接 ≠ 解锁

eth_accounts 在钱包锁定时返回什么,本质上是在回答一个问题:连接权限是否依赖解锁状态?

返回 [](MetaMask): 锁定 = 不暴露任何信息。即使 DApp 之前连接过,锁定后也看不到地址。安全性最高,但 DApp 无法区分“从未连接”和“已连接但锁定”。

返回已授权地址: 连接权限是持久的,锁定只影响签名能力。DApp 可以继续展示用户地址和余额(只读),签名时再要求解锁。体验更连贯,但地址始终暴露。

折中方案: 返回 [],但提供 wallet_getPermissions 让 DApp 区分“未连接”和“已连接但锁定”,从而展示不同的 UI(“连接钱包” vs “解锁钱包”)。MetaMask 支持这个,但几乎没有 DApp 用。

3. 多 DApp 多链:全局单链 vs per-DApp 链隔离

用户同时打开 Uniswap(期望 Ethereum)和 PancakeSwap(期望 BSC),钱包怎么处理?

全局单链(MetaMask): 钱包只有一个当前链。DApp A 调 wallet_switchEthereumChain 切到 BSC,DApp B 也被影响 — 收到 chainChanged 事件,突然发现自己在 BSC 上了。

per-DApp 链隔离: 每个 DApp 会话维护独立的链状态。DApp A 切链不影响 DApp B。

4. eth_sendTransaction 的 from 字段冲突

DApp 发交易时指定的 from 和钱包当前活跃账户不一致。三种策略各有问题:

在 per-DApp 绑定模型下,这个问题天然缓解 — DApp 绑定的账户是稳定的,from 不一致的概率大幅降低。

5. 并发签名请求的队列策略

DApp(尤其是 DeFi 协议)可能快速连发多个签名请求(approve + swap、多步操作)。

串行队列(MetaMask): 一个弹窗处理完才弹下一个。安全但慢,用户要点 N 次确认。

并行弹窗: 同时展示多个确认弹窗。快但混乱,用户可能搞混顺序,误签。

批量确认: 将相关请求打包成一个确认界面。体验最好,但需要判断哪些请求“相关”,实现复杂。

这个决策和 nonce 管理策略强耦合 — 如果允许并行,nonce 必须预分配;如果串行,可以等上一笔确认后再取 nonce。