Appearance
L1-5: Token Wrapper (WETH)
1. 问题
创建 ERC20 兼容的 ETH 包装代币。用户存入 ETH 获得 WETH,销毁 WETH 取回 ETH,汇率 1:1。
2. 原因
原生 ETH 不符合 ERC20 标准,无法直接与 DeFi 协议交互。WETH 作为 ETH 的 ERC20 等价物:
- 所有 DeFi 协议使用相同接口处理代币
- ETH 持有者可以参与 AMM、借贷等协议
- 不需要协议特殊处理原生 ETH
3. 方案
继承 OpenZeppelin ERC20
solidity
contract WrappedETH is ERC20("WrappedEth", "WETH")
核心函数
deposit()payable: ETH → WETH (mint)withdraw(uint256): WETH → ETH (burn + transfer)receive(): 自动 deposit
4. 遇到的陷阱
4.1 直接 ETH 转账被忽略
如果未实现 receive(),用户直接发送 ETH 到合约会被拒绝或静默丢失。
4.2 函数名与事件名冲突
deposit 函数名和 Deposit 事件名在 Solidity 中可能产生歧义。在 receive() 中调用 deposit() 时编译器报错。
4.3 余额不足未检查
withdraw 中不检查余额就会导致 burn 失败。
5. 陷阱的原因
5.1
无 receive() 的合约收到 ETH 时回滚。用户如果忘记调用 deposit() 而直接转账会导致交易失败。
5.2
Solidity 编译器在 receive 函数中调用 deposit() 时,deposit 被解析为 Deposit 事件的引用而非函数。需要使用 this.deposit() 或内联逻辑。
5.3
OZ ERC20 的 _burn 会检查余额,但如果未在 withdraw 入口先检查,可能浪费 gas 在失败的 burn 上。
6. 如何解决陷阱
- 实现
receive() external payable,内联 deposit 逻辑 - 在
withdraw中先require(balanceOf(msg.sender) >= amount) - 使用显式
this.deposit()或在 receive 中直接编写逻辑
7. 技术要点
| 要点 | 说明 |
|---|---|
| WETH 标准 | 1 ETH = 1 WETH,完全可互换 |
| ERC20 继承 | OZ ERC20 提供完整实现 |
| receive vs fallback | receive() 处理纯 ETH,fallback() 处理带数据 |
| ETH 托管安全 | 合约持有与总供应量相等的 ETH |