フォールバック関数とは?
フォールバック関数とは、スマートコントラクトで定義された他の関数に一致しない外部呼び出しを処理する、Solidityの特殊な関数です。Solidityが送信されたトランザクションデータから通常の関数を特定できない場合に、コントラクトの最後の呼び出し振り分け先として機能します。呼び出しにデータが含まれず、コントラクトにreceive関数が別途定義されていない場合にも、フォールバック関数が実行されることがあります。アップグレード可能なスマートコントラクト、プロキシシステム、モジュラーウォレット、決済コントラクト、カスタム呼び出しルーターなど、暗号資産アプリケーションで広く使用されています。Solidityの公式ドキュメントによると、1つのコントラクトに定義できるフォールバック関数は1つまでです。
フォールバック関数は、コントラクト外部から届く呼び出しに応答するため、externalの可視性を指定する必要があります。一致しない呼び出しを通じてネイティブ暗号資産を受け取る必要がある場合、payableにできます。高度な形式の関数を使用すると、トランザクションデータ全体を確認し、生の応答データを返すこともできます。フォールバック関数は、すべてのスマートコントラクト操作の失敗を処理する汎用エラーハンドラーではありません。SolidityとEthereum Virtual Machineが定める特定の呼び出し振り分け条件を満たす場合にのみ実行されます。フォールバック関数と呼ばれる理由
「フォールバック」は、優先される動作や想定された動作が利用できない場合に使う代替動作を意味します。ユーザーが通常のスマートコントラクト関数を呼び出すと、Solidityはまず、送信されたトランザクションデータがコントラクトで宣言された関数のいずれかに一致するかを確認します。一致する関数がない場合、コントラクトはフォールバック関数に処理を振り分けます。この動作により、ウォレット、スマートコントラクト、アプリケーションから未対応の呼び出しが届いた際の処理を開発者が定義できます。フォールバック関数は、呼び出しを拒否する、支払いを受け入れる、イベントを記録する、データを返す、別のコントラクトに転送するといった処理を行えます。適切なフォールバック関数が定義されていない場合、未対応の呼び出しは通常リバートします。フォールバック関数が呼び出される条件
外部呼び出しがスマートコントラクトに到達し、通常の関数が送信された関数セレクターに一致しない場合、フォールバック関数が呼び出されます。呼び出しのcalldataが空で、コントラクトにreceive関数が定義されていない場合にも呼び出されます。データを含む呼び出しは、ネイティブ暗号資産の送金を伴うかどうかにかかわらず、フォールバック関数に到達することがあります。呼び出しに0以外の暗号資産額が含まれる場合、フォールバック関数はpayableでなければなりません。payableでないフォールバック関数は、価値の移転を伴う一致しない呼び出しを拒否します。コントラクトにreceive関数がある場合、calldataが空の呼び出しは代わりにreceive関数へ到達します。したがって、receive関数が併設されているかどうかによって、フォールバック関数の役割は異なります。スマートコントラクトの関数振り分けの仕組み
外部のスマートコントラクト呼び出しには通常、calldataと呼ばれるエンコード済みのトランザクションデータが含まれます。標準的な関数呼び出しでは、先頭の4バイトが関数セレクターです。Solidity ABI仕様では、セレクターは関数の正規署名のKeccak-256ハッシュの先頭4バイトと定義されています。
正規署名は、関数名とパラメーターの型を組み合わせたものです。Solidityは、受信したセレクターをコントラクトのpublic関数およびexternal関数のセレクターと照合します。一致するセレクターがあれば、対応する関数が呼び出しを処理します。一致するセレクターがなければ、フォールバック関数が利用可能な場合、Solidityは呼び出しをそこへ振り分けます。4バイト未満のcalldataでは通常のSolidity関数を特定できないため、フォールバック関数に到達することがあります。calldataが空の場合は別の振り分けルールが適用され、receive関数が優先されます。フォールバック関数とreceive関数の違い
フォールバック関数とreceive関数は、それぞれ異なる目的を持つ別個のSolidityエントリーポイントです。receive関数は、calldataが空の呼び出しを処理するために設計されています。フォールバック関数は主に、宣言済みの関数に一致しないデータを含む呼び出しを処理します。receive関数の主な目的はネイティブ暗号資産の受け取りであるため、常にpayableです。フォールバック関数をpayableにするには、開発者が明示的に指定する必要があります。両方の関数が定義されている場合、データのない送金はreceive関数に到達します。データを含む一致しない呼び出しは、フォールバック関数に到達します。receive関数がなく、フォールバック関数がpayableであれば、データのない送金をフォールバック関数で処理できます。暗号資産の受け取りに関する最新のSolidityガイダンスでは、payableのフォールバック関数がある場合、receive関数も定義することが推奨されています。
この分離により、意図した支払いと、誤ってエンコードされた関数呼び出しを区別しやすくなります。フォールバック関数と通常の関数の違い
通常のSolidity関数には宣言された名前があり、通常は専用の関数セレクターを持ちます。既知のコントラクトインターフェースを通じて、通常の関数を呼び出すことができます。フォールバック関数には、通常の呼び出しに使う名前がありません。ユーザーが直接呼び出すための専用セレクターもありません。代わりに、コントラクトの通常のインターフェースに一致しない呼び出しを処理します。コントラクトには通常の関数を複数定義できますが、フォールバック関数は1つしか定義できません。分散型アプリケーションの主要なビジネスロジックは、通常の関数に記述するのが一般的です。プロキシルーターとして意図的に使用する場合を除き、フォールバック関数の目的は限定し、明確に文書化する必要があります。フォールバック関数とエラー処理の違い
他のスマートコントラクト関数が失敗するたびに、フォールバック関数が実行されるわけではありません。calldataが通常の関数に一致し、その関数の実行中にリバートしても、フォールバック関数は呼び出されません。一致した関数がpayableでないために支払いを拒否しても、Solidityが支払いをフォールバック関数に振り分けることはありません。関数セレクターが正しくても引数の形式が不正な場合、照合された呼び出しは通常、デコード中にリバートします。require条件、算術チェック、アクセス制御ルール、外部呼び出し、カスタムエラーの失敗によっても、フォールバック関数は実行されません。開発者は通常のエラー処理、戻り値の確認、カスタムエラー、該当するtry/catchロジックを使って、これらの失敗を処理する必要があります。フォールバック関数が扱うのは一致しない呼び出しであり、一致した呼び出しの内部で発生した失敗ではありません。payableのフォールバック関数とは?
payableのフォールバック関数は、一致しない外部呼び出しに添付されたネイティブ暗号資産を受け取ることができます。呼び出しとともに送信された額は、トランザクションのvalueフィールドから取得できます。送信者のアドレスもフォールバック関数から参照できます。十分なガスがあれば、payableのフォールバック関数は状態を更新する、イベントを発行する、呼び出しを転送する、その他許可された処理を実行できます。フォールバック関数をpayableにすると、コントラクトに資金管理上の責任が生じます。コントラクトには、確実な残高計上、管理された出金、緊急時の手順、リエントランシー対策が必要になる場合があります。暗号資産を保有する意図がないコントラクトは、通常、予期しない支払いを拒否するべきです。payableの指定は、明確なプロトコル上の目的がある場合に限って行ってください。payableではないフォールバック関数とは?
payableではないフォールバック関数は、ネイティブ暗号資産の移転を伴わない一致しない呼び出しを処理できます。フォールバック関数がpayableでない場合、0以外の価値を伴う一致しない呼び出しはリバートします。カスタムの振り分けやログ記録が必要でも、資金は受け取りたくない場合に有用な設計です。payableでないフォールバック関数を使い、未対応の関数セレクターを明示的に拒否することもできます。未知の呼び出しを拒否すると、ユーザーやアプリケーションが誤ったインターフェースを使っていることを検出しやすくなります。フォールバック関数が参照できる情報
フォールバック関数は、現在の呼び出しを開始したアドレスを参照できます。payableであれば、呼び出しに添付されたネイティブ暗号資産の額も参照できます。呼び出し元が送信したcalldata全体を確認できます。calldata全体には、関数セレクターと、その後に続くエンコード済みの値が含まれる場合があります。パラメーター付きの形式では、このデータをバイト列として受け取れます。呼び出し元へ生のバイト列を直接返すこともできます。Solidityは、フォールバック関数が返す生データに通常のABIエンコードを自動的に適用しません。この動作は、実装コントラクトから返されたデータをそのまま返す必要があるプロキシコントラクトで特に有用です。フォールバック関数でcalldataをデコードすべきか?
フォールバック関数でcalldataを手動で確認し、デコードできます。先頭4バイトを想定するセレクターと比較できます。残りのバイトを想定されるパラメーター型にデコードできます。ただし、Solidityのドキュメントでは、手動でのフォールバックデコードは最後の手段としてのみ行うことが推奨されています。名前付きの通常関数を使えば、インターフェースが明確になり、自動デコードの安全性が高まり、連携も容易になります。手動デコードでは、calldataの長さ、オフセット、型、対応セレクターを検証しなければなりません。機密性の高い状態変更を行う前に、不正なデータを拒否する必要があります。デコードを誤ると、予期しない動作、トランザクションの失敗、セキュリティ脆弱性につながる可能性があります。アップグレード可能なプロキシコントラクトのフォールバック関数
アップグレード可能なプロキシコントラクトは、暗号資産アプリケーションにおけるフォールバック関数の最も重要な用途の1つです。プロキシは、コントラクトアドレスと保存状態を、アプリケーションのビジネスロジックを含むコードから分離します。ユーザーはプロキシアドレスにトランザクションを送信します。プロキシは、現在のロジックを含む実装コントラクトのアドレスを保存します。プロキシ自身が直接定義する関数に一致しない呼び出しは、そのフォールバック関数に到達します。フォールバック関数は、delegatecallを使って実装コントラクトに呼び出しを転送します。プロキシのアップグレードに関するEthereumのドキュメントでは、ユーザーが同じプロキシアドレスを使い続けながら、ビジネスロジックを変更できる仕組みが説明されています。
実装を変更した後も、プロキシは残高やアプリケーションのストレージを保持します。この方法を使うと、すべてのユーザーを新しいコントラクトアドレスへ移行させずに、バグ修正や新機能の導入を行える場合があります。delegatecallとは?
delegatecallは、呼び出し元のコンテキストで別のコントラクトのコードを実行するEthereum Virtual Machineの命令です。プロキシシステムでは、実装コントラクトがコードを提供します。プロキシは、アドレス、暗号資産残高、永続ストレージを提供します。委任実行中も、元の送信者とトランザクションの価値を参照できます。そのため、実装コントラクトは、あたかもそのロジックがプロキシの一部であるかのように動作します。EIP-7は、呼び出し元の送信者および価値のコンテキストを維持しながら外部コードを実行する低水準の命令としてdelegatecallを導入しました。
実装コントラクトはプロキシのストレージを読み書きできるため、delegatecallは強力な機能です。同じ理由で、危険性もあります。悪意のある、または互換性のない実装によって、残高、権限、所有権データ、その他の重要な状態が破損する可能性があります。プロキシのフォールバック関数が呼び出しを振り分ける方法
まず、プロキシはユーザーから送信されたcalldata全体を受け取ります。フォールバック関数は現在の実装アドレスを特定します。プロキシはdelegatecallを使い、元のcalldataをその実装に転送します。実装はプロキシのストレージを使ってロジックを実行します。プロキシは実装からの応答データを取得します。委任呼び出しが成功すると、プロキシは同じ応答を元の呼び出し元へ返します。委任呼び出しが失敗した場合、プロキシは元の失敗情報を呼び出し元へ返す必要があります。転送を誤ると、エラーが隠れる、戻りデータが変更される、連携先が結果を誤って解釈するといった問題が起きる可能性があります。プロキシのストレージスロット
プロキシは、実装アドレスを永続的なコントラクトストレージのどこかに保存する必要があります。その保存場所は、実装コントラクトで使われる変数と衝突してはいけません。ERC-1967は、プロキシの実装、ビーコン、管理者情報の標準化されたストレージスロットを定義しています。
これらの場所は、Solidityコンパイラーが割り当てる通常のストレージスロットとの衝突を避けるよう選ばれています。標準化されたスロットを使うと、ブロックエクスプローラーや開発ツールがプロキシとの関係を特定しやすくなります。標準スロットを使うと偶発的な衝突を減らせますが、アップグレードに伴うすべてのリスクがなくなるわけではありません。実装の各バージョンで、ストレージレイアウトの互換性を維持する必要があります。ストレージレイアウトのリスク
実装コントラクトをアップグレードしても、プロキシは既存のデータを保持します。新しい実装は、以前の実装と同じ方法でそのデータを解釈しなければなりません。既存の状態変数の順序を変更すると、ある値が別の値として読み取られる可能性があります。変数の型を変更すると、その意味が破損したり、値にアクセスできなくなったりする場合があります。継承関係の変更も、想定されるストレージレイアウトに影響する可能性があります。新しい状態変数は、選択したプロキシアーキテクチャのストレージ規則に従って追加する必要があります。トランザクションとしてのアップグレードが正常に完了しても、コントラクトのストレージが気付かれないまま損傷することがあります。そのため、実装を更新するたびにストレージ互換性を確認する必要があります。関数セレクターの衝突
関数セレクターの長さは4バイトです。異なる関数シグネチャが理論上、同じセレクターを生成することがあります。これはセレクター衝突と呼ばれます。プロキシと実装の両方が呼び出し可能な関数を定義している場合、セレクター衝突は特に重要です。ユーザーが実装に到達すると期待したセレクターを、プロキシが横取りする場合があります。ERC-1967は、セレクターの衝突が予期しない応答を引き起こし、悪意あるプロキシの動作を隠す可能性さえあると説明しています。開発者は、プロキシ、実装、管理機能、計画中のアップグレードに含まれるすべてのセレクターをテストする必要があります。関数名だけでは、複数のコントラクト間でセレクターが重複しないことを確認できません。アップグレード権限のリスク
アップグレード可能なプロキシでは通常、実装アドレスを変更できるアカウントまたはガバナンス機構が設定されています。その権限を管理する者は、フォールバック関数を通じて実行されるコードを変更できます。アップグレードキーが侵害されると、資産を移転したりアカウント残高を変更したりする実装が導入される可能性があります。悪意ある管理者が正当なロジックを有害なロジックに置き換える場合もあります。管理者アドレス、署名者の必要数、ガバナンスプロセス、実行遅延、緊急権限、アップグレード履歴を確認してください。プロキシのフォールバック関数のソースコードが検証済みであっても、将来のすべての実装が安全であるとは限りません。アップグレード可能な設計には、継続的なガバナンスおよび運用上の信頼が必要です。ダイヤモンドコントラクトのフォールバック関数
ダイヤモンドコントラクトは、異なる関数セレクターを、ファセットと呼ばれる複数の実装コントラクトに振り分けます。フォールバック関数は、受信したセレクターを確認し、その関数を担当するファセットを特定します。その後、delegatecallを使って選択したファセットに呼び出しを転送します。ERC-2535は、モジュラー型スマートコントラクトシステム向けの複数ファセット・プロキシアーキテクチャを定義しています。
この設計は、1つの実装コントラクトに収めるには大きすぎるアプリケーションのロジックを支えられる場合があります。個々の関数群を追加、置換、削除することもできます。柔軟性が高まる一方で、セレクター、ストレージ、初期化、権限、アップグレードの複雑さも増します。1つのファセットだけを確認しても、ダイヤモンドアドレスから利用できるすべての動作は分かりません。モジュラー型スマートアカウントのフォールバック関数
スマートコントラクトアカウントは、モジュール機能を追加するためにフォールバックハンドラーを利用できます。ハンドラーは、署名、トークンのコールバック、アカウント復旧、セッション権限、アプリケーション固有のインターフェースなどに対応する場合があります。ERC-7579は、スマートアカウントの機能を拡張できるモジュールとしてフォールバックハンドラーを説明しています。
アカウント側で、どのハンドラーを導入、置換、削除できるか制御する必要があります。権限のないハンドラーによって、アカウントが予期しない動作をする経路が生まれる可能性があります。フォールバックモジュールの変更によって、外部呼び出しへのアカウントの応答方法が変わることをウォレットユーザーは理解しておく必要があります。ガスとフォールバック関数
呼び出し元から十分なガスが提供されれば、フォールバック関数で複雑な処理を実行できます。すべての呼び出し元が同じ量のガスを提供するとは限りません。従来の暗号資産送金方式の中には、受信側のコントラクトに少量のガスしか転送しないものがあります。Solidityの最新ドキュメントでは、sendとtransferは非推奨であり、削除が予定されていると警告しています。少量のガスでは、ストレージの更新、追加送金、コントラクトの作成、コストの高い外部呼び出しに足りない場合があります。ネットワークのアップグレードによって、ガスコストが変わる場合もあります。そのため、支払い処理を複雑なフォールバックロジックに依存させるべきではありません。少ないガス上限だけを、悪意あるコールバックへの唯一の防御策とみなしてはいけません。フォールバック関数のリエントランシーリスク
十分なガスがあれば、フォールバック関数は別のコントラクトを呼び出すことがあります。暗号資産を送信した、またはやり取りを開始したコントラクトを再び呼び出す場合もあります。送信側コントラクトが外部呼び出しの前に会計処理を完了していないと、リエントランシーが起こる可能性があります。リエントランシーに関するSolidityのセキュリティガイダンスでは、外部コントラクトの呼び出しによって、元の処理が完了する前に制御が戻る場合があると説明されています。
外部呼び出しを行う前に、コントラクトは条件を検証する必要があります。信頼できないコントラクトへ制御を移す前に、重要な内部状態を更新してください。適切なリエントランシー対策を追加の防御層として利用できます。すべての外部呼び出しは、信頼できないコードが実行される可能性があるものとして扱ってください。サービス拒否のリスク
フォールバック関数は、意図的または偶発的にリバートすることがあります。大きな処理を完了する前に別のコントラクトが支払い成功を必須としていると、受取人がその処理を妨げる場合があります。フォールバック関数が過剰なガスを消費したり、高コストの外部呼び出しを行ったりすることもあります。支払いに失敗すると分配ループ全体がリバートするような設計では、特に危険です。暗号資産の送金に関するSolidityのガイダンスでは、多くの決済システムで引き出し方式の設計が推奨されています。
引き出し方式なら、受取人はそれぞれ独立して資金を請求できます。その場合、1人の受取人のフォールバック動作によって、他のユーザー向けの支払いが妨げられる可能性を抑えられます。低水準呼び出しが予期せず成功するリスク
低水準呼び出しは、対象の実行が成功したか、リバートしたかを返します。実行結果が成功でも、想定した名前付き関数が存在していたとは限りません。未知のセレクターが許容的なフォールバック関数に到達し、正常に完了することがあります。成功ステータスだけを確認する呼び出し元のコントラクトは、要求した処理が実行されたと誤認する可能性があります。低水準呼び出しを使う場合、開発者は戻りデータと想定される状態変更を検証する必要があります。対象の関数があらかじめ分かっている場合は、型付きインターフェースの方が一般に安全です。フォールバック関数とネイティブ暗号資産
payableのフォールバック関数は、一致しない呼び出しを通じてネイティブ暗号資産を受け取れます。receive関数がない場合、calldataが空の通常の送金も処理できます。payableのreceive関数またはフォールバック関数がないコントラクトは、通常、直接の通常送金を拒否します。ただし、コントラクトの残高を常に0に保つ保証はできません。EVMには、receive関数やフォールバック関数を実行せずにコントラクト残高を増加させる例外的な方法があります。コントラクト内部の会計処理では、実際の残高が支払い処理関数による記録額と常に一致するとは限らない点を考慮してください。フォールバック関数とERC-20トークン
通常のERC-20トークン移転では、受取側コントラクトのフォールバック関数は呼び出されません。トークンコントラクトのストレージ内で残高が変更されます。受取側コントラクトでは、コードが実行されないままトークン残高が増えることがあります。ERC-20は、受取側のコールバックを必須とせず、トークンコントラクトを通じて移転を行う仕様を定義しています。
すべてのトークン入金を検出するために、フォールバック関数が実行されることを前提にしてはいけません。アプリケーションでは通常、専用の入金関数または個別の残高計上ルールを使用します。フォールバック関数とNFT
安全なNFT移転方式の中には、受取先コントラクトの特定のレシーバー関数を呼び出すものがあります。このレシーバー関数は汎用のフォールバック関数とは別のものです。必要なレシーバー関数がない場合、そのセレクターに一致する関数がないため、呼び出しがフォールバック関数に到達する可能性があります。コントラクトが必要な動作を意図的に実装していない限り、許容的なフォールバック関数でNFTレシーバーに対応しているように見せるべきではありません。不適切な応答によって、移転がリバートしたり、資産を回収できなくなったりすることがあります。フォールバック関数でイベントを発行できますか?
支払いまたは未対応の呼び出しを受けたとき、フォールバック関数はイベントを発行できます。送信者、移転額、セレクター、その他の有用な値を記録できます。この情報は、開発者が連携状況を監視し、予期しないアクティビティを調査するのに役立ちます。イベントの発行にはガスが必要であり、呼び出し元のガス上限が非常に少ないと失敗する場合があります。オンチェーンイベントは公開情報であり、機密記録として扱うべきではありません。トランザクションデータ自体がすでに公開されているため、calldata全体を記録すると不要なコストが生じる場合もあります。フォールバック関数でストレージを変更できますか?
十分なガスがあれば、フォールバック関数でコントラクトストレージを変更できます。カウンター、支払い記録、権限、振り分け情報、その他の状態を更新できます。ストレージの変更はガス使用量を増やし、関数が与えるセキュリティ上の影響範囲も広げます。重要な状態を変更するフォールバック関数には、明確なアクセス制御を設け、条件を十分にテストする必要があります。呼び出し元が限られたガスしか提供しない場合、支払いを受け取る処理をストレージ書き込みに依存させるべきではありません。フォールバック関数を継承できますか?
フォールバック関数はSolidityの継承に参加できます。virtualとして宣言し、派生コントラクトで置き換えられます。修飾子を使用することもできます。複数のコントラクトが関連するロジックを提供していると、継承によって最終的な呼び出し動作が分かりにくくなる場合があります。監査担当者は1つのソースファイルだけでなく、コンパイル後の継承構造全体を確認してください。フォールバック関数のテスト方法
開発者は、未知の4バイトセレクターを含む呼び出しをテストする必要があります。4バイト未満のcalldataもテストしてください。暗号資産を添付しない空のcalldataをテストしてください。暗号資産を添付した空のcalldataもテストしてください。データを含む一致しないcalldataについて、valueを添付する場合としない場合の両方をテストしてください。receive関数が存在する場合に、呼び出しの振り分けがどう異なるか確認してください。プロキシでは、戻りデータとリバートデータが正しく転送されることをテストしてください。アップグレードテストでは、既存のストレージがすべて有効なままであることを確認してください。セキュリティテストでは、リエントランシー、セレクター衝突、不正なcalldata、権限のないアップグレード、ガス不足を検証してください。フォールバック関数の監査方法
監査ではまず、どのような呼び出しがフォールバック関数に到達するかをすべて特定します。関数がpayableかどうかを確認します。コントラクトにreceive関数が別途定義されているかを確認します。ストレージの更新、外部呼び出し、修飾子、アセンブリ操作、権限チェックをすべて調査します。プロキシの監査では、実装アドレスの選択方法を検証する必要があります。delegatecallの対象を権限のないユーザーが変更できないようにしてください。戻りデータとリバートデータを正確に転送する必要があります。実装のアップグレードでは、ストレージ互換性を維持する必要があります。管理者、ガバナンスプロセス、アップグレードの遅延、緊急時の制御も確認してください。フォールバック関数のベストプラクティス
通常のアプリケーション動作には、可能な限り名前付きの通常関数を使用してください。意図的にネイティブ暗号資産の通常送金を受け付ける場合は、receive関数を別途定義してください。カスタムの振り分けが不要であれば、未対応のセレクターを拒否してください。支払いに関係するフォールバック関数の動作は、シンプルに保ってください。すべてのcalldataと呼び出し元を信頼できないものとして扱ってください。手動でデコードする前に、セレクターとデータ長を検証してください。未検証の転送ロジックを自作せず、監査済みのプロキシ標準を使用してください。実装、管理者、ビーコンのストレージ位置を保護してください。アップグレード後もストレージレイアウトを維持してください。すべてのアップグレード関数に明確なアクセス制御を適用してください。呼び出しを転送する際は、成功データと失敗データを正確に返してください。適切な状態更新の順序とリエントランシー対策を採用してください。通常のウォレットインターフェースでは生成されない可能性のある特殊な呼び出しもテストしてください。安定版の最新Solidityコンパイラーを使用し、既知のコンパイラーバグに関する公式一覧を確認してください。
フォールバック関数でよくあるミス
よくあるミスの1つは、一致した関数がリバートした後にフォールバック関数が実行されると思い込むことです。安全な出金方法を設けずに関数をpayableにすることも、よくあるミスです。一部のコントラクトでは、明確なreceive関数を定義せず、フォールバック関数で通常の支払いを受け付けています。手動でcalldataをデコードする際に、データ長を確認せず処理を進めることがあります。低水準呼び出しの呼び出し元が、フォールバック関数の実行成功を要求した関数の実行成功と誤認する場合があります。プロキシが実装アドレスを、アプリケーションの状態と衝突する場所に保存することがあります。アップグレードで変数の順序を変更し、残高や権限を破損させる場合があります。制限のないdelegatecallの実行先を設定すると、プロキシのコンテキストで任意のコードが実行される可能性があります。複雑なフォールバック関数は、想定より少ないガスしか受け取れないと失敗する場合があります。ERC-20移転によってフォールバック関数が実行されると、誤って想定する開発者もいます。フォールバック関数に関するよくある誤解
フォールバック関数は、あらゆるスマートコントラクトのエラーを処理する汎用機能ではありません。トランザクションが失敗するたびに呼び出されるわけではありません。自動的にpayableになるわけではありません。通常の暗号資産の受け取りに常に適した関数というわけではありません。コントラクトに定義できるフォールバック関数は1つだけです。フォールバック関数には通常の関数セレクターがありません。暗号資産の移転がなくても、一致しないcalldataを処理できます。フォールバック関数が正常に実行されても、呼び出し元が正しいコントラクトインターフェースを使用したとは限りません。プロキシのフォールバック処理を通じて、ユーザーはプロキシアドレスに保存されていないコードを実行することがあります。プロキシのソースコードを検証しても、現在の実装が安全だとは証明できません。よくある質問
Solidityのフォールバック関数とは何ですか?
フォールバック関数は、スマートコントラクトの他の関数に一致しない呼び出しを処理する特殊なexternal関数です。フォールバック関数はいつ実行されますか?
宣言された関数に送信されたセレクターが一致しない場合、またはreceive関数がなくcalldataが空の場合に実行されます。1つのコントラクトに複数のフォールバック関数を定義できますか?
いいえ。Solidityのコントラクトに定義できるフォールバック関数は1つまでです。フォールバック関数には名前がありますか?
いいえ。通常の関数名ではなく、fallbackキーワードを使って宣言します。フォールバック関数はexternalでなければなりませんか?
はい。フォールバック関数にはexternalの可視性を指定する必要があります。フォールバック関数は自動的にpayableになりますか?
いいえ。payableを明示的に指定した場合に限り、ネイティブ暗号資産を受け取れます。payableでないフォールバック関数に価値を送るとどうなりますか?
0以外の額を移転しようとする一致しない呼び出しはリバートします。フォールバック関数とreceive関数の違いは何ですか?
receive関数は空のcalldataを処理し、フォールバック関数は主に、宣言された関数に一致しない呼び出しを処理します。通常の暗号資産送金はどの関数が処理しますか?
receive関数があれば同関数が処理します。receive関数がない場合は、payableのフォールバック関数が処理します。別の関数が失敗した後にフォールバック関数は実行されますか?
いいえ。一致した関数の内部で失敗しても、実行がフォールバック関数に振り分けられることはありません。フォールバック関数はトランザクションデータを確認できますか?
はい。呼び出し元から送信されたcalldata全体にアクセスできます。フォールバック関数はデータを返せますか?
はい。高度な形式では、生のバイト列を呼び出し元へ直接返せます。プロキシコントラクトがフォールバック関数を使用するのはなぜですか?
delegatecallを使ってユーザーの呼び出しを実装コントラクトに転送するためです。delegatecallで維持されるものは何ですか?
元の送信者とvalueを維持したまま、プロキシのストレージと残高を使って実装コードを実行します。delegatecallは実装コントラクトのストレージを変更しますか?
いいえ。委任された実装コードは通常、呼び出し元であるプロキシのストレージを読み書きします。実装コントラクトとは何ですか?
アップグレード可能なプロキシを通じて実行されるビジネスロジックを含むコントラクトです。セレクター衝突とは何ですか?
異なる関数シグネチャが同じ4バイトのセレクターを生成することです。ストレージレイアウトが重要なのはなぜですか?
互換性のないストレージレイアウトにより、アップグレード後の実装が既存のプロキシデータを誤って読み取ったり、上書きしたりする可能性があります。フォールバック関数によってリエントランシーが発生することはありますか?
あります。十分なガスがあれば、別のコントラクトから再度呼び出される場合があります。フォールバック関数は支払いを拒否できますか?
はい。payableでないフォールバック関数、または明示的にリバートするフォールバック関数は、通常のメッセージ呼び出しによる支払いを拒否できます。コントラクト残高が増加するあらゆる方法を防げますか?
いいえ。一部のEVMの仕組みでは、receive関数やフォールバック関数を実行せずにコントラクト残高を増やすことができます。ERC-20の移転でフォールバック関数は実行されますか?
通常は実行されません。トークンコントラクトが受取側を呼び出さずに残高を更新するためです。フォールバック関数はイベントを発行できますか?
はい。イベントの処理に十分なガスが呼び出しで提供されている必要があります。フォールバック関数はストレージを更新できますか?
はい。ただし、ストレージの更新はコストが増えるため、ガスが限られている場合は失敗する可能性があります。フォールバック関数で修飾子を使用できますか?
はい。Solidityの修飾子を使用できます。フォールバック関数を継承できますか?
はい。virtualとして宣言し、派生コントラクトでオーバーライドできます。低水準呼び出しの成功は、要求した関数の存在を証明しますか?
いいえ。未知のセレクターがフォールバック関数によって受け入れられた可能性があります。すべてのスマートコントラクトにフォールバック関数が必要ですか?
いいえ。一致しない呼び出しに対するカスタム処理が必要な場合にのみ定義してください。アップグレード可能なコントラクトのフォールバック関数は安全ですか?
転送ロジック、ストレージレイアウト、実装、アップグレード権限が適切に設計され、監査されていれば、安全に利用できる場合があります。フォールバック関数の最大のリスクは何ですか?
用途によって異なりますが、プロキシによる委任、制限のない外部呼び出し、安全でないアップグレード、予期しない支払い動作はいずれも重大なリスクになり得ます。まとめ
フォールバック関数は、スマートコントラクトで宣言された他の関数に一致しない外部呼び出しを処理する、Solidityの特殊なエントリーポイントです。receive関数が定義されていない場合は、calldataが空の呼び出しも処理できます。payableのフォールバック関数はネイティブ暗号資産を受け取れますが、payableでないフォールバック関数は、価値を伴う一致しない呼び出しを拒否します。暗号資産の通常送金を意図的に受け取る場合は、一般にreceive関数の方が明確です。フォールバック関数はdelegatecallを通じて実装コントラクトへ呼び出しを転送できるため、アップグレード可能なプロキシに不可欠です。この仕組みでは、実装コードをプロキシのストレージに対して実行しながら、元の送信者とvalueを維持します。プロキシによるフォールバック処理には、ストレージレイアウト、セレクター衝突、アップグレード権限、delegatecallに関するリスクが伴います。フォールバック関数は、リエントランシー、サービス拒否、ガス不足、デコード、予期しない呼び出し成功などの問題を引き起こす場合もあります。開発者はフォールバック関数の役割を限定し、必要に応じて未対応の呼び出しを拒否し、通常のアプリケーションロジックには名前付きの通常関数を使うべきです。この名前のないエントリーポイントは、支払い、プロキシの振り分け、モジュラーアカウントの動作、分散型アプリケーション全体の実行を制御する場合があるため、フォールバック関数の理解は暗号資産のスマートコントラクトを評価するうえで不可欠です。関連コンテンツ
ZK-Rollup
ZK-Rollup-as-a-Service
zk-SNARK
ホット
現在、市場で大きな注目を集めているトレンドの暗号資産
トップ取引高
取引高が最も多い暗号資産

