(八)web3面试第一弹

(八)web3面试第一弹

viEcho Lv5

1. 私有(private)、内部(internal)、公共(public)、外部(external)函数之间的区别?

private:只能在 当前合约 内部被调用(包括 this 也不能)。继承合约无法访问。编译器级别限制。
internal:只能在 当前合约及其派生合约(继承)中访问,相当于 protected。
public:既可以从合约内部直接调用,也会为外部生成 ABI,可被外部交易或其他合约通过 call 调用(生成 getter)。
external:只能从合约外部或通过 this.func() 从合约内部以外部调用方式调用(会把参数作为 calldata 传入)。当参数是大型数组/字符串时,external 比 public 更省 gas(避免内存拷贝)。
小结:访问域 + 调用方式(internal/public 可以内部直接调用;external 更适合外部调用/节省拷贝)。

2. 一个合约大小(部署后字节码)大约可以有多大?

EVM 对合约字节码大小有限制:最大代码大小 24,576 bytes(约 24 KB),由 EIP-170 在 Spurious Dragon 引入。
超出会部署失败或抛出 out-of-gas/错误。
常用应对:启用编译器优化、把逻辑拆分成库/代理/多合约、使用 delegatecall 或工厂模式。

3.create 和 create2 的区别?

CREATE(原始):新合约地址由部署者地址 + 部署者 nonce(transaction/creation count)决定(不可预测且随 nonce 变化)。

CREATE2(EIP-1014):地址由 keccak256(0xff ++ deployingAddress ++ salt ++ keccak256(init_code)) 决定,部署者可控制 salt 与 init_code,因此可以预计算/确定目标地址,方便“预言式部署 / 可替换部署 / 工厂 + 代理” 等模式。

注意:CREATE2 能预测地址,但如果相同地址已存在合约,后续部署会失败或覆盖策略要谨慎设计(安全风险与冲突)。

4.Solidity 0.8.0 版本中对运算符(算术)的重大变化?

默认开启算术检查:所有整型算术运算(加减乘等)在溢出/下溢时会 revert(之前需要 SafeMath)。若需要旧的环绕行为,可用 unchecked { … }。此外 ABI coder v2 默认启用等改动。

功能 ABI coder v1 ABI coder v2
嵌套 struct 支持 ❌ No ✅ Yes
多维动态数组 ❌ No ✅ Yes
Struct 作为 external 参数 ❌ No ✅ Yes
Struct 作为返回值 ❌ No ✅ Yes
编码更严格、安全 ❌ 较弱 ✅ 强
默认启用 ❌ 需要 pragma ✅ 默认

小结:从 0.8.x 开始 ABI coder v2 默认启用,它允许 struct 嵌套、复杂动态数组、更安全的编码方式,并全面支持复杂类型作为参数和返回值,大幅提升了 Solidity 的数据处理能力。

5. 代理(proxy)需要哪种特殊的 call 才能工作?

delegatecall:代理模式(如透明代理、Universal Upgradeable Proxy)通常使用 delegatecall 将调用上下文(storage、msg.sender)委托给实现合约(logic/implementation)。delegatecall 使被调用合约在代理合约的 storage 中操作,从而实现可升级逻辑。

额外说明:代理的 ABI/存储槽布局必须严格匹配(避免存储冲突),常用专用槽存放 implementation 地址(EIP-1967 / EIP-1822 等标准)。

6.在 EIP-1559 之前,如何计算以太坊交易的美元成本?

⏳ EIP-1559 之前的计算,Gas Price是一个由用户设定的单一数值,整个交易费用会全额支付给矿工。
那时的美元成本计算公式更为直接:
交易成本(美元) = Gas Used × Gas Price × ETH/USD 价格
这里的 Gas Price 完全由市场供需决定,用户需要通过“拍卖”的方式与其他用户竞争,出价越高,交易被打包的概率越大。

EIP-1559 之后的计算
现在,Gas Price 不再是单一数值,而是由一个固定部分和一个可选部分组成,即:
实际Gas价格 (Effective Gas Price) = 基础费用 (Base Fee) + 优先费 (Priority Fee)

基础费用 (Base Fee):这是由网络自动计算的强制性费用,它会根据上一个区块的使用情况自动升降,目标是维持50%的区块空间利用率,最关键的是,这部分费用会被永久销毁(Burn)。
优先费 (Priority Fee):这是你为了激励验证者(矿工的继任者)优先打包你的交易而自愿支付的“小费”

所以,现在的美元成本计算公式为:
交易成本(美元) = Gas Used × (Base Fee + Priority Fee) × ETH/USD 价格

7.在区块链上跨区块创建随机数的挑战是什么?

可预见/可操控性:矿工/验证者能观察到交易并操控区块属性(timestamp、blockhash、nonce等),从而在出块时选择是否包含交易或改变区块属性来影响随机数结果。
延迟性:跨区块随机种子(如使用前一区块 hash)可能在短期内被操控或受重组影响。
解决思路:使用链下或链上阈值签名 VRF(如 Chainlink VRF)、提交揭示(commit-reveal)或加密的多方计算(MPC)。

8.荷兰拍卖(Dutch auction)和英式拍卖(English auction)的区别?

英式拍卖(English):从低价开始,竞价者逐步加价,直到无人加价,最高出价者得标(常见于 eBay、传统拍卖)。
荷兰拍卖(Dutch):从高价开始,价格逐步下降,直到第一个接受当前价格的出价者得标(快速成交,常用于花卉市场或某些 token 发售)。
在智能合约中实现上,荷兰拍卖常用时间衰减价格函数;英式拍卖要处理最高价、保证金、撤回等。

9.ERC-20 的 transfer 和 transferFrom 之间有什么区别?

transfer(to, amount):msg.sender 向 to 转账(直接扣自己账户)。
transferFrom(from, to, amount):允许 msg.sender(通常是合约或第三方)从 from 的余额转账到 to,但前提是 from 已通过 approve(msg.sender, allowance) 授权足够的额度。常用于去中心化交易所、代付等场景。

10.对于地址白名单(whitelist),使用映射(mapping)还是数组更好,为什么?

mapping(address => bool):通常更好。
理由:查询 O(1)、gas 低:判断地址是否在白名单只需 mapping[addr]。容易增删:设置 true/false 即可。

数组:添加简单但 查询 O(n)(查找耗 gas),删除麻烦(需要移动元素或标记),不适合大量/频繁判断场景。
小结:若你需要频繁做存在性检查,优先 mapping;若你需要枚举所有白名单地址(导出),可以额外维护数组,但注意同步成本。

11.为什么不使用 tx.origin 进行身份验证?

安全风险(钓鱼/重入委托攻击):tx.origin 是交易的原始发起者(EOA),攻击者可以用中间合约诱导受害者签名交易,使 tx.origin 仍为受害者,而 msg.sender 是攻击合约,从而绕过 tx.origin 基于身份的验证。
推荐:使用 msg.sender 做访问控制(并结合 require、多签或 EIP-2771/签名验证等更安全方案)。

12.以太坊使用什么哈希函数?

以太坊 EVM 广泛使用 Keccak-256(通常称为 keccak256,与标准化的 SHA3-256 有细微差别—历史实现差异)。keccak256 用于地址计算、签名回退、存储哈希等。

13.1个ETH相当于多少 gwei?1 个 ETH 相当于多少 wei?

1 ETH = 1,000,000,000 gwei(即 10⁹ gwei)。
1 ETH = 1,000,000,000,000,000,000 wei(即 10¹⁸ wei)。
常用单位关系:1 gwei = 10^9 wei,1 ether = 10^9 gwei = 10^18 wei。

14. assert 和 require 有什么区别?

require(condition, “msg”):用于检查函数输入、外部调用结果、条件不满足时回退并返还剩余 gas(EVM 会 revert 并可返回错误字符串)。
常用于用户输入校验与外部条件。

assert(condition):用于检测不应该发生的内部错误、不可达分支或程序错误(invariant)。失败时会触发 panic(消耗剩余 gas,表示严重错误,不建议用于常规输入校验)。

简单判断:外部可控条件用 require;内部不变量或编译器/逻辑错误用 assert。

15. 什么是闪电贷(Flash Loan)?

闪电贷:在单笔交易内借入资产、使用这些资产执行任意操作(如套利、抵押、清算),并在同一交易结束前归还借款(加上手续费)。若未能归还,交易回滚(对借出方无损)。

特点:无需抵押、原子性、被广泛用于套利或复杂 DeFi 操作,但也常被用于攻击链上脆弱协议。

16. 什么是 检查-效果-交互(check-effects-interactions)模式?

模式步骤:
检查(Checks):验证要求、权限、余额等 require。
效果(Effects):更新合约内部状态(修改余额、标记已提现等)。
交互(Interactions):与外部合约或地址交互(调用外部合约、转账等)。
目的:防止重入攻击(在与外部交互前先把内部状态更新为安全状态),是写安全合约的推荐模式。

17. 运行独立验证节点(成为以太坊验证者)所需的最小以太数量是多少?

最少需要 32 ETH(把 32 ETH 存入质押合约以激活一个独立 validator)。可以通过质押池(如 Rocket Pool 等)间接参与较少的金额,但独立激活需要 32 ETH。

18.fallback 和 receive 之间有什么区别?

receive() external payable:当合约收到 空数据 的 ETH 转账(例如 send / transfer / 直接转账)并且合约实现了 receive 时调用。
fallback() external [payable]:当没有匹配的函数签名被调用,或合约没有 receive 且收到了 ETH,fallback 会被触发。fallback 也可用于处理任意调用(包含数据)。

小结:receive 专用于接收简单 ETH 转账;fallback 用于处理非匹配的函数调用或作为通用入口。

19.什么是重入(reentrancy)?

重入攻击:攻击者在合约执行外部调用(通常发送 ETH 或调用外部合约)后,利用被调用合约回调该合约的函数,导致原函数的内部状态在预期更新前被重复执行(例如多次提现)。

防护:使用 check-effects-interactions、ReentrancyGuard(互斥锁)、把外部调用放在最后、以及使用 call 返回值处理而非 transfer(并要注意 gas)等方法。

20.如何使用 Solidity 进行类型转换?

基本语法:
整型转换(截断):uint256(x)、int8(y) 等(注意截断/符号)。
地址/uint 转换(低级):address(uint160(x)) 或 uint256(uint160(addr)) 等(需显式转换)。
bytes 与 string:bytes calldata、string(abi.encodePacked(…)) 等,字符串/字节间转换需注意编码与拷贝。

注意:强制类型转换可能截断数据或引入符号扩展,使用前务必确认范围与语义。

21.上海(Shanghai)升级后,每个区块的 gas 限制是多少?

关键点:协议本身并未把 block gas limit 固定为单一不变数;
这是一个由验证者/节点在共识范围内可调的参数(节点能在一定范围内调整 block gas target)。
在 Shanghai(2023)本身并没有直接把限制改成固定值;在不同时间点网络的常用/建议值会变化。

实际上,社区/网络在后续阶段与升级前后调整了网络的常见块 gas 上限:近几年主网常见上限曾在 ~30M 区间(历史报道),而在 2025 年晚些时候社区推动把上限提升到 ~60M 以增加吞吐量(相关报道)。因此要以链上当前状态为准(可通过区块浏览器或 gas limit 统计站点实时查看)。

22.tx.origin 和 msg.sender 有什么区别?

tx.origin:交易的原始发起者(最初发起该交易的 EOA)。
msg.sender:当前调用的直接调用者(可能是 EOA 或合约)。
安全建议:用于权限判断时不要用 tx.origin(见 Q11),应使用 msg.sender。

23.什么阻止无限循环永远运行?

Gas 限制:以太坊的每笔交易有 gasLimit(交易发起者设置)和每个区块有 block gas limit。执行每条 EVM 指令消耗 gas,当 gas 用尽时交易会 revert,阻止无限循环无限执行。

因此 EVM 的资源消耗用 gas 强制界限,防止无限循环耗尽网络资源。

24.如何向没有 payable 函数、receive 或 fallback 的合约发送以太?

不能:如果合约既没有 receive() 又没有 payable fallback,直接发送 ETH(value > 0)会失败(交易 revert)。

变通:可以把 ETH 发送到合约的创建者或中间合约(该合约实现 payable 并自行分发),或通过自毁(selfdestruct(target))把 ETH 强制转入目标合约(selfdestruct 会把余额发送到目标地址且不触发接收方代码)。但 selfdestruct 的使用应谨慎(安全/可维护性问题)。

25.view 和 pure 有什么区别?

view:表示函数不会修改区块链的状态(不会写 storage),但可以读取状态变量或区块信息(如 block.number)。调用时可作为 call 本地查询(不耗 gas,除非作为交易发送)。

pure:不仅不修改状态,也不读取合约状态(不能访问 state 变量 或 block 等),只能基于输入参数进行纯计算。

小结:pure 比 view 更严格(不能读 state)。

26.如何将 ERC-1155 的代币转换成 ERC-20 代币?

没有直接自动转换标准,常见做法:
使用一个 包装器(wrapper)合约:用户把 ERC-1155 的特定 token 转入包装合约,包装合约按某个比率 mint 对应的 ERC-20 代币;赎回时销毁 ERC-20 并退回 ERC-1155。

或在应用层实现兑换逻辑(桥接、兑换合约、市场合约)。
注意:需要考虑可替代性、编号(id)与分量(amount)的语义差异。

27.访问控制(Access Control)是什么?为什么重要?

访问控制:限制谁能调用合约中的敏感函数(如 mint、upgrade、withdraw)。
重要性:防止未授权操作、保护资金与合约逻辑、便于权限管理与审计。
常见实现:Ownable(单一拥有者)、角色管理(OpenZeppelin AccessControl)、多签(Gnosis Safe)、时间锁(Timelock)等。

28.修饰符(modifier)的作用是什么?

modifier:Solidity 提供的语法,用于在函数执行前后插入可复用逻辑(如权限检查、输入校验、重入锁等),写法简洁、可复用
常见示例:

1
2
3
4
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}

在函数中使用 onlyOwner 可自动插入检查逻辑。

29.uint256 可以存储的最大值是多少?

uint256 范围:0 到 2^256 - 1,即 115792089237316195423570985008687907853269984665640564039457584007913129639935(一个非常大的数,约 1.1579×10^77)。

30.什么是浮动利率(floating rate)和固定利率(fixed rate)?

固定利率(Fixed rate):在借贷/合约期内利率保持不变(借贷双方能预见利息成本/收益)。优点:可预测,适合长期锁定。

浮动利率(Floating / variable rate):利率随市场基准(如某个指数、借贷池的利用率或 oracle)变化而上下波动。优点:通常更灵活、可能在利率下降时期更便宜;缺点:不可预测且可能在利率高涨时成本急剧增加。

在 DeFi:常见如 Aave 的可变利率与稳定利率选项(stable rate 实际上是相对稳定的可变策略),以及在衍生品中对冲不同利率风险的使用。

  • Title: (八)web3面试第一弹
  • Author: viEcho
  • Created at : 2025-12-01 00:00:00
  • Updated at : 2025-12-09 18:01:40
  • Link: https://viecho.github.io/2025/1201/web3-interview1.html
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
(八)web3面试第一弹