题目
一个 DApp 已经通过浏览器钱包拿到了 window.ethereum。为什么还需要单独配置公共 RPC?请说明 EIP-1193 Provider、viem Public Client 和 Wallet Client 各自负责什么,并给出生产环境中的分工方案。
考察目标
- 是否理解钱包 Provider 是受用户控制的能力入口,而不等于稳定的公共节点服务。
- 是否能区分只读请求与需要用户授权的签名、交易请求。
- 是否考虑隐私、RPC 限流、链一致性、故障降级和钱包断开等生产问题。
考察 EIP-1193 Provider、公共 RPC、Public Client 和 Wallet Client 的职责边界。
一个 DApp 已经通过浏览器钱包拿到了 window.ethereum。为什么还需要单独配置公共 RPC?请说明 EIP-1193 Provider、viem Public Client 和 Wallet Client 各自负责什么,并给出生产环境中的分工方案。
EIP-1193 Provider 是钱包暴露给 DApp 的最小请求与事件接口,主要用于访问用户授权的账户、签名、切链和发送交易。公共 RPC 用于区块、余额、日志、合约读取和交易回执等无需用户授权的链上查询。生产环境通常用独立 Public Client 连接可信 RPC 做读取,用 Wallet Client 通过钱包 Provider 做签名和写入,并在写操作前确认账户与链一致。
EIP-1193 只规定一个通用的 request 方法以及 connect、disconnect、chainChanged、accountsChanged、message 等事件。它是 DApp 与钱包或客户端之间的边界,不负责保证某个钱包实现了所有节点 RPC 方法。
浏览器钱包 Provider 具有以下特点:
Public Client 面向公共 JSON-RPC 方法,适合完成:
公共读取不应依赖用户先连接钱包。否则未连接用户无法浏览页面,钱包切链也可能让整个站点的读取数据突然切换到另一条链。
Wallet Client 持有或访问账户能力,负责:
浏览器钱包场景下,Wallet Client 通常使用 EIP-1193 Provider 作为自定义 Transport,把签名操作委托给钱包,而不是把私钥交给前端。
这种分工也有利于隐私控制:应用可以在不读取用户地址的情况下展示公共数据,只在用户主动连接后加载账户相关信息。
import { createPublicClient, createWalletClient, custom, http } from "viem";
import { mainnet } from "viem/chains";
// 公共读取不依赖钱包连接。
const publicClient = createPublicClient({
chain: mainnet,
transport: http(process.env.NEXT_PUBLIC_MAINNET_RPC_URL),
});
const blockNumber = await publicClient.getBlockNumber();
// 只有签名或写操作需要钱包 Provider。
const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum),
});
const [account] = await walletClient.requestAddresses();
实际项目还应检查 account 是否存在、钱包是否在目标链,并对环境变量缺失和 RPC 失败提供明确反馈。
window.ethereum 当成高可用公共 RPC,所有读取都依赖用户钱包。eth_getLogs 时,你会怎样处理?eth_sendTransaction 像普通 GET 请求一样自动重试?发现这道题有问题? 反馈此题