跳到主要内容

Web3 热门面试题

使用说明

本篇共 60 道题。

Web3 面试要把链上不可逆状态、钱包信任边界和前端体验结合起来。回答重点是交易生命周期、签名语义和安全,而不是只会调用 SDK。

Q1: 区块链解决什么问题?

答案

它让多个互不完全信任的参与者通过共识维护可验证、按规则演进的共享状态,不依赖单一数据库管理员。

代价是吞吐、延迟、成本、隐私和治理复杂度。已有可信中心的普通业务不一定需要上链。

  • 区块链通过共识和密码学让互不信任的参与者共同维护可验证状态,适合数字资产、跨组织结算和无需单方控制的规则执行。
  • 代价是吞吐、延迟、费用、隐私和不可逆性。普通中心化 CRUD 若已有可信运营方,数据库通常更便宜、简单且可治理。

Q2: 以太坊账户有哪些类型?

答案

传统模型中有外部拥有账户(EOA)和合约账户。EOA 由私钥签名发起交易;合约账户由代码控制,执行由交易或其他合约调用触发。

账户抽象进一步让智能账户具备更灵活的验证、批处理和恢复能力,但依赖具体标准和基础设施。

  • EOA 由私钥控制,可主动签名并发起交易;合约账户由代码控制,只有收到调用时执行逻辑。传统合约账户本身没有私钥。
  • 账户抽象把验证、批处理、代付和恢复能力放进智能账户,但仍要通过入口合约或协议规则触发执行。

Q3: 钱包连接时前端实际获得了什么?

答案

通常获得用户授权暴露的账户和当前链,并可通过 Provider 请求签名或发送交易;前端不会也不应获得私钥。

连接不等于登录,也不等于用户授权任意操作。账户、链和权限变化都要监听并更新 UI。

  • 连接钱包通常只请求用户授权公开地址和当前链信息,DApp 不会也不应拿到私钥。后续签名或交易都由钱包弹窗让用户确认。
  • 地址不是永久登录态;要监听账户、链和断开事件,并通过签名挑战建立服务端会话,不能把“已连接”直接等同于“已认证”。

Q4: EIP-1193 Provider 的作用是什么?

答案

它定义 DApp 与钱包 Provider 的通用请求和事件接口,例如 request、账户变化和链变化,让应用不直接依赖某个钱包实现。

调用仍可能被用户拒绝、链不支持或 Provider 断开,必须处理错误和重试边界。

  • EIP-1193 把钱包能力统一成 request({ method, params }),并定义 accountsChanged、chainChanged、connect、disconnect 等事件。
  • Provider 负责 RPC 与事件,不等同于 Signer 或完整客户端。前端要处理用户拒绝、未授权、链不支持和多个钱包 Provider 竞争。

Q5: 一笔以太坊交易的生命周期是什么?

答案

DApp 构造交易,钱包展示并签名,节点接收后进入交易池,被区块打包后获得确认,后续仍可能因链重组变化。

前端应展示待签名、已提交、确认中、成功/失败和被替换状态,不能发送后立即当成最终成功。

  • 前端构造并模拟交易,钱包估算费用和让用户签名;交易广播到节点后进入 mempool,被验证者打包进区块,再等待若干确认。
  • 期间可能被拒签、替换、丢弃、revert 或链重组。UI 应使用状态机展示“等待签名、已广播、已确认、失败”,并用 hash 支持恢复查询。

Q6: Gas 和 EIP-1559 费用怎么理解?

答案

Gas 衡量执行和存储消耗,实际费用由使用量与每单位价格决定。EIP-1559 引入基础费和优先费,用户设置最大愿付费用,未用完的上限不会简单全部收走。

估算可能因链上状态和合约分支变化失败,前端要展示估算、余额和失败原因。

  • Gas 衡量 EVM 执行和存储资源;用户支付的费用约为 gasUsed × effectiveGasPrice。EIP-1559 下费用由会销毁的 base fee 与给验证者的 priority fee 组成。
  • maxFeePerGas 是用户上限,不代表一定花满;估算前应先模拟,并区分执行失败消耗的 Gas 与钱包余额不足。

Q7: Nonce 有什么作用?

答案

Nonce 是每个地址的交易序号计数器,从 0 开始,每发送一笔交易递增 1。它有两个核心作用:

  1. 防止重放攻击:同一个签名的交易不能被重复执行
  2. 保证交易顺序:节点按 nonce 顺序处理交易

如果 nonce 不连续(比如跳过了 nonce=5 直接发 nonce=6),后面的交易会在 mempool 中等待,直到 nonce=5 的交易被执行或超时。

Q8: 普通消息签名和交易签名有什么区别?

答案

交易签名授权链上状态变更并可能花费资产;消息签名证明账户控制权或表达某项链下意图,不直接发送交易。

消息同样可能授权登录、订单或资产操作,必须展示域名、链、nonce、过期和清晰语义,不能让用户盲签乱码。

  • 交易签名包含链 ID、Nonce、目标、金额、Gas 和 calldata,广播后会改变链上状态;消息签名只证明地址对某段字节签过名。
  • 消息签名也可能授权登录、订单或 Permit,不能当作无害操作。UI 要展示域、用途、过期时间和 Nonce,避免盲签。

Q9: EIP-712 解决什么问题?

答案

它定义结构化数据签名,把域和字段编码成可验证摘要,让钱包更可能向用户展示可读内容,并通过 domain separator 限定应用、链和合约上下文。

它改善可读性和重放边界,但业务仍要校验 nonce、deadline 和具体字段。

  • EIP-712 对 domain 和结构化字段做类型化哈希,钱包能展示可读数据,并用 chainId、verifyingContract 等域信息防止跨应用重放。
  • 前后端必须使用完全一致的类型顺序和值编码;还要包含业务 Nonce 和 deadline,服务端验证签名者、域和是否已使用。

Q10: SIWE 登录流程是什么?

答案

服务端生成一次性 nonce,前端构造带域名、URI、链和时间的登录消息,用户签名,服务端恢复地址并校验所有字段后建立普通会话。

Nonce 必须一次性并过期,防止重放;登录后的授权仍由服务端会话和权限系统完成。

  • 服务端生成包含域名、URI、chainId、Nonce、签发/过期时间的 SIWE 消息,钱包签名后服务端恢复地址并核对所有字段,再创建普通会话。
  • Nonce 必须一次性且短期有效,验证成功后立即消费;登录后的权限仍由服务端控制,不能只信客户端传来的地址。

Q11: ABI 在合约交互中有什么作用?

答案

ABI 描述函数、事件和参数类型,SDK 根据它编码 calldata、解码返回值和日志。

ABI 必须与目标合约和代理实现兼容。前端不能因为类型生成成功就假设调用一定成功,仍要模拟、处理 revert 和链错误。

  • ABI 描述函数、事件、参数和错误类型,客户端据此编码 calldata、解码返回值与日志。合约地址相同但 ABI 错误会导致调用失败或错误解释数据。
  • 代理合约还涉及实现升级和 ABI 版本。前端应把地址、链 ID、ABI 与发布版本绑定,并对自定义 error 做可读映射。

Q12: approveallowance 有什么安全风险?

答案

ERC-20 approve 授予 spender 在额度内转走代币的权力,无限授权会扩大合约被攻击或地址配置错误的损失。

前端应显示资产、spender 和额度,优先最小授权并提供管理入口;不要把“授权”文案伪装成普通登录。

  • ERC-20 的 approve 允许 spender 在额度内转走资产,无限授权会把未来余额也暴露给合约;授权从非零改非零还存在竞态风险。
  • 优先精确额度、先模拟并展示 spender,支持撤销;可用 Permit 减少交易,但签名本身仍是授权,必须校验域、Nonce 和截止时间。

Q13: 为什么交易前要模拟?

答案

模拟在当前链状态下执行调用,可提前发现 revert、估算结果和部分资产变化,减少用户支付失败 Gas 或签署危险交易。

模拟结果不是未来保证,因为状态会变化,也不能覆盖所有恶意合约行为。仍需地址、参数和风险提示。

  • eth_call 或专用模拟服务在指定区块状态上执行交易但不落链,可提前发现 revert、余额不足和滑点,并解码返回与事件。
  • 模拟结果不是最终保证,因为从模拟到打包期间状态会变化。关键参数仍要设 minOut、deadline 等链上保护,UI 明确模拟区块与限制。

Q14: 事件日志如何用于前端数据查询?

答案

合约通过 event 写入可索引日志,前端或索引服务按区块和 topic 查询,再构建业务视图。

日志不是合约可直接读取的存储,且会受重组影响。索引器需要确认数、去重、回滚和断点续扫。

  • 合约用 event 把可索引主题和数据写入日志,前端可按地址、topic 和区块范围查询,再用 ABI 解码。日志适合历史事件,不等于当前状态数据库。
  • 大范围 RPC 查询容易限流,应使用 Indexer/Subgraph,并按 blockNumber、transactionIndex、logIndex 唯一排序;链重组时还要回滚未最终确认日志。

Q15: 为什么链上数据需要确认数?

答案

新区块可能因共识重组被替换,确认越多,交易被回滚的概率通常越低。不同链和业务金额需要不同最终性标准。

UI 可以先展示“已上链/确认中”,高价值业务等待足够确认后再发放不可逆权益。

  • 新区块可能因分叉被替换,交易进入一个区块只代表暂时被包含。等待更多后续区块会降低被重组掉的概率。
  • 所需确认数取决于链的最终性和业务价值;UI 可以先显示“已上链”,达到阈值后再标记“最终确认”,同时监听 replaced/reorg。

Q16: Viem/Wagmi 相比直接调用 Provider 有什么价值?

答案

Viem 提供类型化的链和合约客户端;Wagmi 在 React 中管理连接、查询、缓存和交易状态,减少重复边界代码。

框架不能替代对链、签名和错误的理解。配置链、Transport、缓存键和账户变化仍需正确处理。

  • Viem 提供类型安全的 RPC、ABI 推导和客户端分层;Wagmi 在其上封装 React hooks、钱包连接、缓存和状态同步。
  • 相比直接调用注入 Provider,它们统一多钱包、多链、错误与请求生命周期。但关键交易参数和状态机仍需业务显式设计,不能完全依赖 hooks 默认行为。

Q17: ERC-20、ERC-721 和 ERC-1155 的区别是什么?

答案

对比维度ERC-721ERC-1155
每个合约只能管理一种 NFT 集合可以管理多种代币(FT + NFT 混合)
批量转账不支持,每次只能转一个 NFT原生支持 safeBatchTransferFrom
Gas 消耗转 10 个 NFT 需要 10 笔交易批量转 10 个只需 1 笔交易
元数据每个 token 独立的 tokenURI通过 URI 模板 {id} 替换统一管理
适用场景每个代币有独立身份的场景(艺术品、PFP)游戏道具、门票等需要混合管理的场景

Q18: 什么是账户抽象?

答案

账户抽象让账户验证逻辑可编程,可支持多签、社交恢复、批量操作、会话密钥和代付 Gas,改善钱包体验。

它引入 Bundler、Paymaster、EntryPoint/智能账户等组件及新的信任边界,前端要解释失败和费用来源。

  • 账户抽象让智能账户自定义签名验证、批量调用、社交恢复、Session Key 和 Gas 代付,改善助记词与原生币门槛。
  • 实现上通常涉及 UserOperation、Bundler、Paymaster 和 EntryPoint。前端要展示代付边界、部署状态和失败原因,并防止 Session Key 权限过大。

Q19: 多链 DApp 的主要难点是什么?

答案

同一资产/合约在不同链地址和最终性不同,RPC、Gas 代币、区块浏览器和桥接风险也不同。

配置要按 chain ID 隔离,交易前强校验目标链;不要只靠名称判断资产。跨链是异步多阶段流程,需要状态跟踪和恢复。

  • 要管理 chainId、地址、RPC、区块时间、确认策略、原生币和合约版本;同一资产在不同链也可能是桥接表示。
  • 切链前校验钱包支持,所有缓存键带 chainId,交易链接按链生成。跨链流程是多阶段异步状态机,还要处理桥接最终性和目标链失败。

Q20: DApp 前端最重要的安全原则有哪些?

答案

不接触私钥,签名前展示真实域、链、合约、方法、资产和额度;合约地址来自可信配置;防 XSS、依赖投毒和钓鱼替换。

交易模拟、允许列表和风险提示是辅助,不能承诺绝对安全。高风险操作支持硬件钱包、撤销和异常监控。

  • 永远不接触私钥或助记词;签名和交易前把合约、方法、资产、额度、链和预期结果清楚展示,并默认最小授权。
  • 依赖与前端部署要防供应链和域名劫持,关键配置签名或校验;所有输入不可信,交易先模拟,异常链/地址和高价值操作增加二次确认。

Q21: Web2 和 Web3 的核心区别是什么?

答案

Web2 是平台拥有数据的读写互联网,用户创造内容但平台掌握数据所有权和分发权(如微信文章、抖音视频)。Web3 是用户拥有数据和资产的去中心化互联网,通过区块链技术实现:

  • 身份:Web2 用邮箱/手机号登录(依赖平台),Web3 用钱包签名(自主身份)
  • 数据:Web2 数据存在中心化数据库,Web3 数据存在区块链/IPFS 上
  • 资产:Web2 中你的"资产"(如游戏装备)实际上归平台所有,Web3 中链上资产真正属于你
  • 信任:Web2 依赖平台信誉,Web3 依赖代码和密码学("Code is Law")

Q22: 区块链为什么不可篡改?

答案

区块链的不可篡改性源于哈希链接结构共识机制

  1. 哈希链接:每个区块头包含上一个区块头的哈希(parentHash)。篡改任一区块的数据会导致其哈希变化,从而使后续所有区块的 parentHash 失效,需要重新计算整条链
  2. 共识机制:在 PoS 中,攻击者需要控制全网 2/3 以上的质押 ETH 才能篡改已最终确认的区块,这在经济上几乎不可行
  3. 分布式验证:全球数千个节点同时存储和验证数据,单点篡改会被其他节点拒绝

Q23: 钱包的本质是什么?它实际存储了什么?

答案

钱包本质是一个密钥管理器,它存储的是私钥(或助记词),而不是加密货币或 Token。资产始终记录在区块链上,钱包只是用私钥证明"我有权操作这个地址上的资产"。类比:钱包是银行保险箱的钥匙,不是保险箱本身。

Q24: Ethers.js 中 Provider 和 Signer 的区别是什么?

答案

  • Provider 是区块链的只读连接,不关联任何账户,用于查询数据(余额、区块、合约 view 方法等)
  • Signer 关联一个以太坊账户(拥有私钥),可以签名消息和发送交易
  • 创建合约实例时传入 Provider 只能调用 view/pure 方法;传入 Signer 才能调用状态修改方法
  • BrowserProvider.getSigner() 从钱包获取 Signer,JsonRpcProvider 本身就是 Provider

Q25: DApp 如何维护钱包连接状态的一致性?

答案

钱包状态至少包括账号、链、连接器和授权状态,不能只在首次连接时读取一次。前端要订阅 Provider 的 accountsChangedchainChanged 和连接器生命周期事件,并在页面恢复、网络切换后重新校验。

实践要点:

  • 区分“曾经选择过连接器”和“当前钱包仍授权”,自动重连失败时回到可操作 UI。
  • 请求发送前再次确认账号与 Chain ID。
  • 切链、签名和交易要处理用户拒绝、钱包锁定和请求并发。
  • 多标签页可共享提示状态,但最终以 Provider 返回结果为准。
  • 清理旧 Provider 监听,避免切换连接器后重复响应。

Wagmi 等库能封装状态机,但业务仍要设计加载、错误和链不匹配状态。

Q26: 合约函数选择器和 Calldata 是怎样编码的?

答案

外部函数调用的 Calldata 通常由 4 字节函数选择器和 ABI 编码参数组成。选择器是规范化函数签名(如 transfer(address,uint256))的 Keccak-256 哈希前 4 字节。

静态类型按 32 字节槽编码;字符串、bytes 和动态数组通过偏移指向后续动态区域。前端一般交给 Viem/Ethers 等库编码,不应手工拼十六进制。

选择器只有 4 字节,理论上可能碰撞;合约审计和代理路由要考虑冲突。调试交易失败时,先用 ABI 解码 Calldata,确认地址、单位、链和函数重载。

Q27: EIP-1559 交易的实际 Gas 费如何计算?

答案

实际每单位 Gas 的价格为:

effectiveGasPrice = min(maxFeePerGas, baseFee + maxPriorityFeePerGas)

其中 baseFee 由协议根据上一个区块的 Gas 使用率自动调整,maxPriorityFeePerGas 是给验证者的小费,maxFeePerGas 是用户设定的上限。总费用 = effectiveGasPrice × gasUsed,多付的部分自动退还。

Q28: Permit 相比 approve 有什么价值和边界?

答案

Permit 让持币人通过链下签名授权额度,第三方可把签名提交到链上,常用于减少一次独立 approve 交易和改善 Gas/交互体验。

它并没有消除授权风险:

  • 签名必须绑定 owner、spender、value、nonce、deadline、chainId 和验证合约等域信息。
  • 不同 Token 的 Permit 标准和实现可能不同,前端不能假设全部支持。
  • 无限额度和长期 deadline 仍然危险。
  • 用户看到的是签名而非转账交易,更需要清晰展示授权对象、金额和过期时间。
  • 服务端/Relayer 要防重放并处理签名已使用、过期和链切换。

Q29: 什么是盲签?为什么它是 DApp 安全的核心问题?

答案

盲签是指用户在不理解签名内容的情况下确认签名操作。在早期钱包中,签名请求通常只显示一串十六进制哈希,用户无法判断自己在授权什么操作。

盲签是 DApp 安全的核心问题,因为:

  1. 不可逆性:链上操作一旦执行无法撤回
  2. 资产直接暴露:签名可能直接导致资产被转移
  3. 用户认知不足:大多数用户不具备解读原始签名数据的能力

EIP-712 通过结构化、人类可读的签名格式来缓解盲签问题,钱包可以清晰展示签名的目标合约、链 ID、操作类型和具体参数。

Q30: 什么是 Chain ID?为什么它很重要?

答案

Chain ID 是区块链网络的唯一数字标识符(如以太坊主网是 1,Polygon 是 137)。它被写入交易签名中(EIP-155),用于防止跨链重放攻击。如果没有 Chain ID,在以太坊主网上签署的交易可以被拿到另一条 EVM 兼容链上重放执行。前端在发送交易前必须校验当前 Chain ID 是否与目标链一致。

Q31: MPC 钱包和传统 HD 钱包有什么区别?

答案

传统 HD(Hierarchical Deterministic)钱包基于一套助记词派生所有密钥,私钥完整存在于用户设备。MPC 钱包使用分布式密钥生成(DKG),私钥从始至终不以完整形式存在于任何单一设备。核心区别在于:

  • 密钥存在形式:HD 钱包有完整私钥;MPC 只有分片
  • 安全模型:HD 是单点风险(私钥/助记词泄露即完);MPC 是阈值安全(需攻破多方)
  • 恢复机制:HD 依赖助记词;MPC 支持社交恢复或云备份分片

Q32: 前端查询链上数据有哪些方式?

答案

方式优点缺点适用场景
RPC实时性最高查询能力有限余额、allowance
The Graph复杂查询、分页排序有索引延迟历史数据、统计
Etherscan API开箱即用中心化、速率限制交易历史、合约 ABI
Alchemy / MoralisAPI 丰富付费、厂商锁定NFT、Token 数据

Q33: ENS 的正向解析和反向解析有什么区别?

答案

  • 正向解析:将 ENS 名称(如 vitalik.eth)解析为以太坊地址。用于转账场景,用户输入域名代替地址
  • 反向解析:将以太坊地址解析为 ENS 名称。用于显示场景,让界面显示人类可读的名称而非长串地址

关键区别在于:正向解析只要域名所有者设置了地址记录即可生效;而反向解析需要地址所有者主动设置 Primary Name,且设置的名称必须正向解析回同一个地址(防止冒充)。

Q34: 前端如何与智能合约交互?

答案

前端通过 JSON-RPC 协议与区块链节点通信,通常借助 ethers.jsviem 等库封装底层调用。核心流程:

  1. 通过 window.ethereum(钱包注入的 Provider)或 RPC URL 创建 Provider
  2. 用合约地址 + ABI 创建合约实例
  3. 调用合约方法:读操作直接返回结果,写操作返回交易对象,需要等待确认

Q35: CREATECREATE2 生成合约地址的方式有什么区别?

答案

CREATE 的地址主要由部署者地址和部署 nonce 推导;CREATE2 由部署者、salt 和初始化代码 hash 推导,因此可以在部署前计算地址,适合确定性部署和部分反事实账户场景。

使用 CREATE2 要注意:

  • 初始化代码或构造参数变化会改变地址。
  • 同一部署者、salt 和 init code 组合不能在已有代码的地址上再次部署。
  • 地址可预测不代表该地址上的代码可信,前端仍要校验链、部署工厂和字节码。
  • 代理地址稳定也不代表实现逻辑不变,还要展示升级与权限风险。

前端应使用经过验证的 ABI 编码和 hash 工具,不要手写字符串拼接。

Q36: accountsChangedchainChanged 事件应该怎样处理?

答案

accountsChanged 表示钱包向当前站点暴露的账号列表变化;数组为空通常意味着授权断开或钱包锁定。chainChanged 表示当前链变化,Chain ID 通常是十六进制字符串,需要规范化后比较。

处理原则:

  • 取消或作废依赖旧账号、旧链的未完成请求和缓存。
  • 重新读取余额、授权、合约地址和功能开关,不只修改页面文本。
  • 新链不受支持时给出切链或添加网络操作,但不无限弹钱包请求。
  • 监听器只注册一次,并在连接器更换或卸载时移除。
  • 发交易前仍再次读取实际链和账号,事件不是唯一安全校验。

Q37: Solidity Custom Error 相比字符串 revert 有什么优势?前端怎样解析?

答案

Custom Error 用错误选择器和 ABI 编码参数表达失败,比长字符串通常更节省部署与执行 Gas,还能携带结构化上下文:

error InsufficientBalance(uint256 available, uint256 required);

前端需要包含对应 Error 定义的 ABI,才能把 revert data 解码为错误名和参数。RPC、钱包或合约包装层可能把数据嵌套在不同错误字段中,因此解析要做兼容和回退。

对用户展示时把技术错误映射成可行动文案,未知错误保留交易哈希、链 ID 和原始错误用于排查,不把内部堆栈或敏感信息直接暴露。

Q38: Wagmi 的 Config 包含哪些核心配置?

答案

createConfig 的三个核心配置:

  1. chains:应用支持的区块链列表(如 mainnet、polygon)
  2. transports:每条链的 RPC 传输方式(http()webSocket()
  3. connectors:钱包连接器(injectedwalletConnectcoinbaseWallet

此外还支持 ssr(服务端渲染)、multiInjectedProviderDiscovery(自动发现注入式钱包)等可选配置。

Q39: 合约的 calltransaction 有什么区别?

答案

特性Call(读)Transaction(写)
Gas 消耗
钱包签名不需要需要
改变链上状态不能
返回值直接返回函数结果返回交易哈希(tx.hash
等待时间即时需等区块确认

在 ethers.js 中,调用 view/pure 函数会自动使用 call,调用其他函数会自动发送 transaction。也可以用 contract.transfer.staticCall(...) 强制以 call 模式模拟写操作,用于预检交易是否会成功。

Q40: Base Fee 是如何调整的?

答案

以太坊每个区块有一个目标 Gas 用量(Gas limit 的 50%)。如果实际用量超过 50%,Base Fee 上涨(最多 +12.5%);低于 50% 则下降(最多 -12.5%)。Base Fee 不归验证者所有,而是被直接销毁(burn),这也是 ETH 通缩的来源之一。

Q41: ERC-165 怎样用于检测合约支持的接口?

答案

ERC-165 通过 supportsInterface(bytes4 interfaceId) 让合约声明是否支持某个接口。接口 ID 通常由接口中函数选择器异或得到,NFT 应用可用它检测 ERC-721、ERC-1155 及扩展接口。

前端调用前仍要处理:

  • 目标地址可能不是合约。
  • 老合约或非标准实现可能不支持 ERC-165。
  • 恶意合约可以虚假声明,接口检测不能替代交易模拟和业务校验。
  • RPC 调用可能失败,应提供未知状态而不是直接判定“不支持”。

Q42: eth_signpersonal_signeth_signTypedData_v4 有什么区别?

答案

  • eth_sign:对任意哈希签名,攻击者可构造任何交易哈希让用户签名,极度危险。MetaMask 已默认禁用
  • personal_sign:签名前自动添加 "\x19Ethereum Signed Message:\n" 前缀,防止签名被冒充为交易。适用于登录验证等场景
  • eth_signTypedData_v4:EIP-712 结构化签名,包含 domain 信息和类型化数据,钱包可解析并展示人类可读内容。是最安全的签名方式

推荐:所有涉及链上操作的签名都使用 eth_signTypedData_v4,消息验证(如登录)使用 personal_sign,完全禁用 eth_sign

Q43: wallet_switchEthereumChainwallet_addEthereumChain 的区别是什么?

答案

  • wallet_switchEthereumChain:请求钱包切换到已存在的链。只需传入 chainId 参数。如果钱包中没有该链配置,会抛出错误码 4902。
  • wallet_addEthereumChain:请求钱包添加一条新链(并切换到该链)。需要传入完整的链配置:chainIdchainNamerpcUrlsnativeCurrencyblockExplorerUrls

标准做法是先尝试 switch,捕获到 4902 错误后再调用 add

Q44: 什么是 ERC-4337?它解决了什么问题?

答案

ERC-4337 是以太坊的账户抽象标准,将用户账户从 EOA 升级为智能合约钱包。它引入了一套链下 + 链上的基础设施(UserOperation → Bundler → EntryPoint → Smart Account),在不修改以太坊协议的前提下实现了:

  1. Gas 代付:Paymaster 代付 Gas,用户无需持有 ETH
  2. 批量交易:一次签名执行多笔操作
  3. 自定义签名验证:支持多签、社交恢复、生物识别等
  4. 会话密钥:临时授权,减少签名确认次数

Q45: 什么是 Multicall?解决了什么问题?

答案

Multicall 是一个部署在链上的聚合合约,它允许在一次 RPC 请求中批量执行多个合约的只读调用。核心解决的问题是 N 次 RPC 请求合并为 1 次,减少网络往返和 RPC 配额消耗。典型场景包括批量查询多个地址的 Token 余额、加载仪表盘数据等。viem 默认内置了 Multicall 支持,调用多个 readContract 时会自动聚合。

Q46: ENS 头像支持哪些格式?如何解析?

答案

ENS 头像支持三种格式:

  1. HTTPS URL:直接可用,如 https://example.com/avatar.png
  2. IPFS URI:需要通过 IPFS 网关转换,如 ipfs://QmHash -> https://gateway.pinata.cloud/ipfs/QmHash
  3. NFT URI:如 eip155:1/erc721:0xBC4C.../1234,需要调用 NFT 合约的 tokenURI 方法获取元数据,再从元数据中提取 image 字段

实际开发中,ethers.js 和 viem 内置了完整的头像解析逻辑,建议直接使用 resolver.getAvatar()getEnsAvatar()

Q47: 交易回执中的 statuslogs 和区块确认分别表示什么?

答案

回执 status 表示交易执行成功还是回滚;即使失败,交易也可能已经上链并消耗 Gas。logs 是成功执行路径产生的事件日志,前端可按 ABI 解析,但不能把“得到交易 hash”当成已经得到日志。

区块确认表示包含该交易的区块后又追加了多少区块,用于降低链重组的不确定性。确认数取决于链、金额和业务风险,不应写死通用数字。

前端状态通常分为签名中、已广播、待打包、已上链待确认、成功或失败。超时只表示暂时没观察到结果,不能直接判定链上失败;应允许按 hash 恢复查询。

Q48: 为什么 Gas 估算会失败或与实际消耗不同?

答案

eth_estimateGas 会在当前节点状态和指定交易参数下模拟执行。余额不足、授权缺失、条件会 revert、from/value/chain 错误或节点状态不同,都可能让估算失败。

估算值也不是最终费用保证:交易上链前状态可能变化,动态存储写入、外部调用和区块环境会影响实际 Gas。钱包通常会在估算上增加安全余量,但不能用无限 Gas 掩盖逻辑错误。

前端应先模拟并解析 revert,明确展示 Max Fee 与可能实际费用;合约逻辑本身要设置业务上限,防止不可控循环或外部调用放大成本。

Q49: 签名如何防止跨链、跨合约和重复使用?

答案

优先使用 EIP-712 结构化签名,并把验证域和消息约束完整:

  • Domain 包含 chainId、verifyingContract、name/version。
  • Message 包含用户、动作、业务对象、nonce、deadline 和必要金额。
  • 合约或服务端原子消费 nonce,拒绝重复签名。
  • 过期时间限制泄漏后的利用窗口。
  • UI 展示人类可理解的真实授权内容。

personal_sign 只有消息前缀,业务域需要自行编码且更难让钱包清晰展示。签名本身不说明调用方有权执行所有操作,验证端仍要检查当前状态和权限。

Q50: Ethers.js 和 Viem 该如何选择?

答案

场景推荐
新项目,使用 wagmiViem(wagmi 底层就是 Viem)
注重 TypeScript 类型安全Viem(ABI 类型自动推断)
已有项目使用 Ethers.jsEthers.js(迁移成本高)
快速原型、学习阶段Ethers.js(文档更丰富、社区示例更多)
关注包体积Viem(~35KB vs ~120KB)

Q51: useReadContractuseWriteContract 的区别是什么?

答案

特性useReadContractuseWriteContract
调用类型view / pure 函数状态修改函数
Gas 消耗不消耗消耗 Gas
钱包签名不需要需要用户签名确认
返回值合约函数的返回值交易哈希
底层TanStack QueryMutation
缓存自动缓存无缓存(每次是新交易)

Q52: 解释 ERC-20 的 approve + transferFrom 流程,为什么不直接用 transfer

答案

transfer 只能由 Token 持有者自己调用。但在 DEX 场景中,Swap 合约需要代替用户转移 Token,这就需要两步授权机制:

  1. 用户调用 approve(spenderAddress, amount):授权 DEX 合约可以从自己账户转移最多 amount 数量的 Token
  2. DEX 合约内部调用 transferFrom(userAddress, dexAddress, amount):DEX 代替用户完成转移

这种设计将"授权"和"执行"分离,用户始终保持对 Token 的控制权,只有被授权的合约才能转移用户的 Token。

Q53: 以太坊中如何替换或取消一笔 Pending 交易?

答案

同一账户在同一链上,可以发送一笔使用相同 nonce、费用更有竞争力的新交易。节点和打包者接受替代后,最终只会有一笔该 nonce 的交易被确认。

“加速”通常保留原目标和数据但提高费用;“取消”通常向自己发送 0 ETH 的空交易并使用相同 nonce。取消不是协议级撤回保证:原交易若已经被打包、替代费用不足或私有交易通道不同,都可能失败。

前端要从 Pending 队列选择正确 nonce,按 EIP-1559 调整费用,并在任一交易确认后把同 nonce 的其他候选标为被替代。

Q54: 前端处理 ERC-20 代币余额时,为什么不能直接用 Number 类型?

答案

两个原因:

  1. 精度丢失:JavaScript 的 Number 是 IEEE 754 双精度浮点数,最大安全整数是 2^53 - 1(约 9000 万亿)。而 ERC-20 余额的最小单位是 uint256,18 位小数的代币中 1 个代币就是 10^18,远超 Number.MAX_SAFE_INTEGER
  2. 浮点精度问题0.1 + 0.2 !== 0.3,在金额计算中不可接受

正确做法是始终使用 BigInt 处理链上原始值,只在展示时使用 ethers.formatUnits 转为字符串。

Q55: Permit 钓鱼是如何实现的?为什么特别难防范?

答案

Permit(ERC-2612)允许用户通过离线签名授权代币转移,无需链上 approve 交易。攻击者利用这一点:

  1. 创建钓鱼页面诱导用户签署 Permit 签名
  2. 获取用户签名后,攻击者自行提交到链上
  3. 调用 transferFrom 转走用户代币

特别难防范的原因:

  • Permit 是离线操作,不产生链上交易,区块浏览器无记录
  • revoke.cash 等工具无法检测已签署但未提交的 Permit
  • 攻击者可以选择最佳时机提交签名
  • 签名本身看起来可能像正常操作

防护手段:仔细检查 EIP-712 签名中的 spender 地址、value 金额和 deadline 时间。

Q56: 如何在前端管理多链的合约地址?

答案

推荐使用合约地址映射表:以 Chain ID 为 key,合约名称为二级 key,地址为值。配合一个 getContractAddress(chainId, name) 工具函数,在运行时根据当前链动态获取合约地址。对于使用 Wagmi 的项目,也可以利用 Viem 的 multicall 和 Wagmi 的 useReadContract 直接传入对应链的地址。所有地址应集中管理在一个配置文件中,避免硬编码分散在各组件里。

Q57: Bundler 在 ERC-4337 中扮演什么角色?

答案

Bundler 是 ERC-4337 架构中的链下角色,类似于传统以太坊的矿工/验证者。它的职责包括:

  1. 收集 UserOperation:维护一个 UserOp 内存池(类似 Mempool)
  2. 打包交易:将多个 UserOp 打包为一笔真实的以太坊交易
  3. 提交上链:调用 EntryPoint 合约的 handleOps 方法执行所有 UserOp
  4. Gas 估算:模拟执行 UserOp 以估算 Gas

Bundler 的经济激励来源于 UserOp 中的 Gas 费差价。

Q58: The Graph 的 Subgraph 是如何工作的?

答案

开发者在 subgraph.yaml 中声明监听的合约和事件,在 schema.graphql 中定义实体模型,在 mapping.ts(AssemblyScript)中编写事件到实体的映射逻辑。部署后,Graph Node 持续监听链上新区块,执行 Mapping 处理器将数据写入 PostgreSQL,前端通过 GraphQL API 查询,支持分页、排序、过滤等复杂操作。

Q59: DApp 中如何优雅展示用户地址?

答案

遵循优先级策略:

  1. 优先显示 ENS 名称(如 vitalik.eth
  2. 无 ENS 时截断地址(如 0xd8dA...6045),保留前后各 4 位
  3. 头像使用 ENS Avatar,无头像时使用 Blockie 或 Jazzicon 生成确定性头像
  4. 鼠标悬停时显示完整地址(tooltip)
  5. 点击地址可复制到剪贴板

Q60: 智能合约钱包相比 EOA 能提供哪些能力?

答案

智能合约钱包把授权规则写进合约,可支持多签、社交恢复、批量调用、限额、会话密钥、Gas 代付和可编程验证;EOA 主要由单个私钥直接控制并签署协议交易。

代价是合约漏洞、升级与治理风险、部署/执行 Gas、链上兼容和恢复流程复杂。账户抽象可以改善 User Operation、Bundler 与 Paymaster 体验,但最终安全仍取决于钱包实现和验证策略。

前端必须识别签名与交易能力差异,不能假设所有账户都支持同一种 personal_sign、nonce 或 Gas 支付流程。