暗号の世界では、誤ってクリックするだけで「デジタル災害」を引き起こす可能性があります。最も一般的な悪夢の一つは、資産を誤ったブロックチェーンに送ってしまうことです。例えば、EthereumのSepoliaテストネットのアドレスにETHを送るつもりだったのに、誤ってEthereumメインネットのアドレスに送ってしまった場合です。このような場合、Ethereumメインネットの誤送金された資金を取り戻すことはできるのでしょうか?資産を取り戻す鍵は、受取アドレスの種類にかかっています。本稿では、状況別に分析します。
EOA (Externally Owned Account)は、私たちが一般的に言う、秘密鍵またはニーモニックフレーズによって直接管理される普通のウォレットアドレスです。
資産回復の前提条件:
資産回復方法:
受取EOAアドレスの秘密鍵を持つ者が、直接対象チェーン上で資金を引き出す。
これは最も絶望的なシナリオの一つです。スマートコントラクトのアドレスは秘密鍵によって生成されないため、誰もコントラクトの秘密鍵を所有していなければ、そのコントラクトを制御できません。さらに、そのコントラクトに「誤入資産処理」の救済関数が事前に用意されていない場合、誤って送金された資金は永久にコントラクト内にロックされ、誰も取り出せなくなる可能性があります。
しかし、状況によっては希望の光もあります。次に、ETHをEthereumメインネットにロックしたシナリオを構築し、その資金を救出する方法を紹介します。
このシナリオは、ユーザーが本来はSepoliaテストネットのコントラクトを呼び出してETHを送金し、トークンを鋳造しようとしたのに、誤ってメインネットに接続してしまい、その結果ETHがメインネットのコントラクトにロックされたというものです。具体的な構築過程は以下の通りです。
1. EthereumのSepoliaテストネット上に、プロジェクト側(EOA)がコントラクトを展開し、主な機能はユーザーがETHを預けて対応するATokenを鋳造することです。コード例はmintTokens関数のようなものです。展開されたアドレスをAとします。注意点は、Aには直接ETHを引き出す関数が存在しないことです。
2. EthereumのSepoliaテストネット上に、プロジェクト側(EOA)が工場コントラクトを展開し、その機能は、提供された実装コントラクトのアドレスとsaltを基に、最小の代理コントラクト(Clones)方式で、実装コントラクトを指す代理コントラクト(例:deployProxyByImplementation関数)を展開することです。展開されたアドレスをBとします。ここで、deployProxyByImplementation関数を呼び出し、実装コントラクトのアドレスを_implementationとして渡し、Aのアドレスを指す代理コントラクト(C)を展開します。
3. ユーザーはSepoliaテストネット上でETHを送金してATokenを鋳造しようとし、代理コントラクトCに呼び出しを行いました。通常、代理コントラクトCは実装コントラクトAのmintTokens関数を呼び出し、ユーザーの操作を完了します。しかし、呼び出し時に誤ってメインネットに接続してしまい、ETHをメインネットのCアドレスに直接送ってしまいました。この時点で、EthereumメインネットのCアドレスにはコントラクトが展開されておらず、秘密鍵を持つ者もいません。そのため、ユーザーのお金は一時的にメインネットのCアドレスにロックされた状態です。
具体的な救援策を紹介する前に、必要な基本的な知識について説明します。
createとcreate2は、Solidityで一般的に使われるコントラクト展開の方式です。
2.2.2. 最小代理コントラクト(Clones)
https://docs.openzeppelin.com/contracts/4.x/api/proxy#clones
最小代理コントラクト、またはクローンコントラクト(Clones)は、非常に低コスト(Gas)で、指定された実装コントラクトを指す代理コントラクトを展開する手法です。Clonesコントラクト内では、createまたはcreate2を用いて代理コントラクトを展開できます。たとえば、cloneDeterministic関数を用いてcreate2方式で代理コントラクトを展開します。
cloneDeterministic関数で作成される代理コントラクトのバイトコードは非常に短く、フォーマットは:0x363d3d373d3d3d363d73<実装コントラクトのアドレス>5af43d82803e903d91602b57fd5bf3です。これは、実装コントラクトのアドレスをバイトコードにハードコーディングし、その代理コントラクトへの呼び出しをdelegatecallで実装コントラクトに委ねるものです。
このcloneDeterministic関数は、create2を用いて代理コントラクトを展開し、そのアドレスは、コントラクト作成者のアドレス、salt、実装コントラクトのアドレス、一定のバイトコードに依存します。実装コントラクトのバイトコード自体は関係しません。
次に、ユーザーのメインネットCアドレスにあるETHを救出する方法を紹介します。基本的な考え方は、EthereumメインネットのCアドレスにコントラクトコードを展開し、そのアドレスを乗っ取り、ETHを引き出すことです。具体的な操作手順は以下の通りです。
1. メインネットに、テストネットと同じアドレスBの工場コントラクトを展開します。工場コントラクトのアドレスを同じにする必要があるのは、後のcloneDeterministicによる代理コントラクト展開時に、代理コントラクトのアドレス計算が工場コントラクトのアドレスに依存しているためです。Sepoliaの工場コントラクトの展開取引を調査し、その中の展開者(プロジェクト側のアドレス)のnonceを取得します。その後、メインネットでは、展開者のアドレスのnonceを、テストネットの展開前のnonceに合わせて増やし、工場コントラクトを展開します。これにより、展開者のアドレスとnonceが同じであれば、メインネットでも工場コントラクトのアドレスはBになります。
2. 同じく、アドレスAの実装コントラクトを展開します。前述のとおり、ClonesのcloneDeterministic関数を使えば、実装コントラクトのアドレスは、saltと実装コントラクトのアドレスにより決まります。したがって、実装コントラクトの内容は関係なく、単にアドレスAにコントラクトを展開すれば良いのです。ETH引き出し機能を持つコントラクトをアドレスAに展開します。テストネットでは、実装コントラクトAはプロジェクト側(EOA)が展開したもので、そのアドレスはトランザクションの発行者とnonceに依存します。したがって、テストネットで実装コントラクトAを展開した取引のnonceを調査し、そのnonceを用いてメインネットで展開すれば、アドレスAに展開されます。
![]#最小代理合约(Clones)#https://img-cdn.gateio.im/webp-social/moments-75bfcbe9932d6fff8befe5ac282e022d.webp(
3. メインネットに、テストネットと同じアドレスCの代理コントラクトを展開します。テストネットでの代理コントラクトCの展開取引を調査し、そこからsaltを取得します。そのsaltと、実装コントラクトAのアドレスを用いて、factoryコントラクトBのdeployProxyByImplementation関数を呼び出し、代理コントラクトを展開します。
4. メインネットの代理コントラクトCに対して資金引き出し呼び出しを行うことで、ロックされていたETHを引き出し、ユーザーに返還します。
)# 2.4. まとめ
上述の救援策からわかるのは、資金を救出できる条件には多くの要素が関わっているということです。例えば、コントラクトの展開者のnonceが未使用であること、資金をロックしているコントラクトに引き出し関数やアップグレード可能な仕組み、Clonesのような代理コントラクトを用いた仕組みが整っていることなどです。
したがって、取引の際は十分に注意し、送信前に内容をよく確認してください。また、ZANが提供するAI SCANなどの脆弱性スキャンツールを活用し、コントラクトの安全性を検査することをお勧めします。もし資金がロックされてしまった場合でも、慌てずにZANのコントラクトセキュリティ監査チームに相談し、資金救援を依頼してください。
本記事はZANTeam(Xアカウント @zan_team)及びAntChain OpenLabs(Xアカウント @AntChainOpenLab)のCara(Xアカウント@Cara6289)が執筆しました。
44.57K 人気度
104.85K 人気度
65.98K 人気度
184.66K 人気度
6.49K 人気度
Web3安全シリーズ:誤って他のチェーンに送った資金は、まだ取り戻せるのか?
暗号の世界では、誤ってクリックするだけで「デジタル災害」を引き起こす可能性があります。最も一般的な悪夢の一つは、資産を誤ったブロックチェーンに送ってしまうことです。例えば、EthereumのSepoliaテストネットのアドレスにETHを送るつもりだったのに、誤ってEthereumメインネットのアドレスに送ってしまった場合です。このような場合、Ethereumメインネットの誤送金された資金を取り戻すことはできるのでしょうか?資産を取り戻す鍵は、受取アドレスの種類にかかっています。本稿では、状況別に分析します。
1. シナリオ1:受取アドレスがEOA
EOA (Externally Owned Account)は、私たちが一般的に言う、秘密鍵またはニーモニックフレーズによって直接管理される普通のウォレットアドレスです。
資産回復の前提条件:
資産回復方法:
受取EOAアドレスの秘密鍵を持つ者が、直接対象チェーン上で資金を引き出す。
2. シナリオ2:受取アドレスがコントラクト
これは最も絶望的なシナリオの一つです。スマートコントラクトのアドレスは秘密鍵によって生成されないため、誰もコントラクトの秘密鍵を所有していなければ、そのコントラクトを制御できません。さらに、そのコントラクトに「誤入資産処理」の救済関数が事前に用意されていない場合、誤って送金された資金は永久にコントラクト内にロックされ、誰も取り出せなくなる可能性があります。
しかし、状況によっては希望の光もあります。次に、ETHをEthereumメインネットにロックしたシナリオを構築し、その資金を救出する方法を紹介します。
2.1. シナリオの概要
このシナリオは、ユーザーが本来はSepoliaテストネットのコントラクトを呼び出してETHを送金し、トークンを鋳造しようとしたのに、誤ってメインネットに接続してしまい、その結果ETHがメインネットのコントラクトにロックされたというものです。具体的な構築過程は以下の通りです。
1. EthereumのSepoliaテストネット上に、プロジェクト側(EOA)がコントラクトを展開し、主な機能はユーザーがETHを預けて対応するATokenを鋳造することです。コード例はmintTokens関数のようなものです。展開されたアドレスをAとします。注意点は、Aには直接ETHを引き出す関数が存在しないことです。
2. EthereumのSepoliaテストネット上に、プロジェクト側(EOA)が工場コントラクトを展開し、その機能は、提供された実装コントラクトのアドレスとsaltを基に、最小の代理コントラクト(Clones)方式で、実装コントラクトを指す代理コントラクト(例:deployProxyByImplementation関数)を展開することです。展開されたアドレスをBとします。ここで、deployProxyByImplementation関数を呼び出し、実装コントラクトのアドレスを_implementationとして渡し、Aのアドレスを指す代理コントラクト(C)を展開します。
3. ユーザーはSepoliaテストネット上でETHを送金してATokenを鋳造しようとし、代理コントラクトCに呼び出しを行いました。通常、代理コントラクトCは実装コントラクトAのmintTokens関数を呼び出し、ユーザーの操作を完了します。しかし、呼び出し時に誤ってメインネットに接続してしまい、ETHをメインネットのCアドレスに直接送ってしまいました。この時点で、EthereumメインネットのCアドレスにはコントラクトが展開されておらず、秘密鍵を持つ者もいません。そのため、ユーザーのお金は一時的にメインネットのCアドレスにロックされた状態です。
2.2. 重要な知識点
具体的な救援策を紹介する前に、必要な基本的な知識について説明します。
2.2.1. create & create2
createとcreate2は、Solidityで一般的に使われるコントラクト展開の方式です。
2.2.2. 最小代理コントラクト(Clones)
最小代理コントラクト、またはクローンコントラクト(Clones)は、非常に低コスト(Gas)で、指定された実装コントラクトを指す代理コントラクトを展開する手法です。Clonesコントラクト内では、createまたはcreate2を用いて代理コントラクトを展開できます。たとえば、cloneDeterministic関数を用いてcreate2方式で代理コントラクトを展開します。
cloneDeterministic関数で作成される代理コントラクトのバイトコードは非常に短く、フォーマットは:0x363d3d373d3d3d363d73<実装コントラクトのアドレス>5af43d82803e903d91602b57fd5bf3です。これは、実装コントラクトのアドレスをバイトコードにハードコーディングし、その代理コントラクトへの呼び出しをdelegatecallで実装コントラクトに委ねるものです。
このcloneDeterministic関数は、create2を用いて代理コントラクトを展開し、そのアドレスは、コントラクト作成者のアドレス、salt、実装コントラクトのアドレス、一定のバイトコードに依存します。実装コントラクトのバイトコード自体は関係しません。
2.3. 救援策
次に、ユーザーのメインネットCアドレスにあるETHを救出する方法を紹介します。基本的な考え方は、EthereumメインネットのCアドレスにコントラクトコードを展開し、そのアドレスを乗っ取り、ETHを引き出すことです。具体的な操作手順は以下の通りです。
1. メインネットに、テストネットと同じアドレスBの工場コントラクトを展開します。工場コントラクトのアドレスを同じにする必要があるのは、後のcloneDeterministicによる代理コントラクト展開時に、代理コントラクトのアドレス計算が工場コントラクトのアドレスに依存しているためです。Sepoliaの工場コントラクトの展開取引を調査し、その中の展開者(プロジェクト側のアドレス)のnonceを取得します。その後、メインネットでは、展開者のアドレスのnonceを、テストネットの展開前のnonceに合わせて増やし、工場コントラクトを展開します。これにより、展開者のアドレスとnonceが同じであれば、メインネットでも工場コントラクトのアドレスはBになります。
2. 同じく、アドレスAの実装コントラクトを展開します。前述のとおり、ClonesのcloneDeterministic関数を使えば、実装コントラクトのアドレスは、saltと実装コントラクトのアドレスにより決まります。したがって、実装コントラクトの内容は関係なく、単にアドレスAにコントラクトを展開すれば良いのです。ETH引き出し機能を持つコントラクトをアドレスAに展開します。テストネットでは、実装コントラクトAはプロジェクト側(EOA)が展開したもので、そのアドレスはトランザクションの発行者とnonceに依存します。したがって、テストネットで実装コントラクトAを展開した取引のnonceを調査し、そのnonceを用いてメインネットで展開すれば、アドレスAに展開されます。
![]#最小代理合约(Clones)#https://img-cdn.gateio.im/webp-social/moments-75bfcbe9932d6fff8befe5ac282e022d.webp(
3. メインネットに、テストネットと同じアドレスCの代理コントラクトを展開します。テストネットでの代理コントラクトCの展開取引を調査し、そこからsaltを取得します。そのsaltと、実装コントラクトAのアドレスを用いて、factoryコントラクトBのdeployProxyByImplementation関数を呼び出し、代理コントラクトを展開します。
4. メインネットの代理コントラクトCに対して資金引き出し呼び出しを行うことで、ロックされていたETHを引き出し、ユーザーに返還します。
)# 2.4. まとめ
上述の救援策からわかるのは、資金を救出できる条件には多くの要素が関わっているということです。例えば、コントラクトの展開者のnonceが未使用であること、資金をロックしているコントラクトに引き出し関数やアップグレード可能な仕組み、Clonesのような代理コントラクトを用いた仕組みが整っていることなどです。
したがって、取引の際は十分に注意し、送信前に内容をよく確認してください。また、ZANが提供するAI SCANなどの脆弱性スキャンツールを活用し、コントラクトの安全性を検査することをお勧めします。もし資金がロックされてしまった場合でも、慌てずにZANのコントラクトセキュリティ監査チームに相談し、資金救援を依頼してください。
本記事はZANTeam(Xアカウント @zan_team)及びAntChain OpenLabs(Xアカウント @AntChainOpenLab)のCara(Xアカウント@Cara6289)が執筆しました。