Skip to content
On this page

L1-5: Token Wrapper(WETH)

1. 課題

ETHのERC20互換ラッパートークンを作成します。ユーザーは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 関数名とイベント名の競合

Solidityではdeposit関数名とDepositイベント名が曖昧さを引き起こす可能性があります。receive()内でdeposit()を呼び出すと、コンパイラがエラーを報告する場合があります。

4.3 残高不足の未チェック

withdrawで残高をチェックしないと、burnが失敗します。

5. 落とし穴の原因

5.1

receive()のないコントラクトはETHの受け取り時にリバートします。deposit()の呼び出しを忘れて直接ETHを送金したユーザーのトランザクションは失敗します。

5.2

receive関数内でdeposit()を呼び出すと、Solidityコンパイラはdepositを関数ではなくDepositイベントへの参照として解決する場合があります。this.deposit()を使用するか、ロジックをインライン化します。

5.3

OZ ERC20の_burnは残高をチェックしますが、withdrawの入口で先にチェックしないと、失敗するburnにgasを無駄にする可能性があります。

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 fallbackreceive()は純粋なETHを処理、fallback()はデータ付きを処理
ETHカストディのセキュリティコントラクトは総供給量と等しいETHを保持

Built with AiAda