ValidationLogicは、ユーザーのアクションが契約のルールに準拠していることを確認する役割を担い、ユーザーが預金、借入、返済、清算、融資のフラッシュ、債務モードの切り替えなどを行う際に、担保と債務について厳格なチェックを行います。Debt Tokens: Debtトークンは、ユーザーの借入負債を追跡するために使用され、貸し出された資金量と1:1です。 Debtトークンは、固定および変動金利オプション(DebtDAI Stable、DebtDAI Variableなど)で利用可能で、Debtトークンは譲渡できません。
金利モデルには2つのタイプ(安定型と変動型)がありますが、モデルの計算は変曲点モデルに似ています。最適利用率の変曲点における勾配1と最適利用率以上の勾配2が分割して計算され、この条件下で固定金利モデルと変動金利モデルにも分けられる。
ユーザーはレンディングプールの入金関数を呼び出すことで入金を行います。この関数は、4つのパラメータ(資産アドレス、入金額、受取人のアドレス、紹介コード)を受け取ります。まず、コントラクトがアクティブでないことを確認し、次にValidationLogic.validateDepositによって入金額が0より大きいことを確認し、リザーブがアクティブで凍結されていないことを確認します。その後、システムはreserve.updateState()を呼び出して準備金の状態を更新し、流動性蓄積指数と変動借入指数を更新し、期間中に発生した利息を計算します。
計算式は以下の通りです。"text-align: left;">流動性利率、安定借入利率、および変動借入利率(すべてDefaultReserveInterestRateStrategy.calculateInterestRates関数によって計算される)。資産の移転では、システムはユーザーの原資産を aToken 契約に移転し、ユーザーが提出した onBehalfOf アドレスに同量の aToken を鋳造します。aTokenは、利息の蓄積を処理するために、スケーリングされた残高メカニズムを使用します。ユーザーの最初の入金の場合、システムは自動的に資産をユーザーの担保としてマークします。
ユーザーは出金関数を呼び出すことでお金を引き出すことができます。まず、対応するaTokenアドレスを含む指定されたアセットのリザーブデータを受け取り、aToken内のユーザーの残高をチェックします。次に、ValidationLogic.validateWithdraw関数が呼び出され、出金額が有効かどうか、ユーザーの残高が十分かどうか、リザーブが有効かどうかなど、出金リクエストを検証します。このうち、利用者の健康状態や、出金が担保に影響するかどうかは、GenericLogic.balanceDecreaseAllowedによってチェックされる。balanceDecreaseAllowed関数では、calculateUserAccountData関数とcalculateHealthFactorFromBalances関数が資金引き出し後の流動性閾値を計算し、ユーザーの総担保、総借入額、ユーザーの現在のヘルスファクターをチェックして、ユーザーのヘルスファクターが流動性閾値にあるかどうかを判断します。利用者の健康状態が流動性閾値に達しているかどうか。
その後、準備金のステータスが更新され、金利が更新され、引き出し額が関数に渡されます。ユーザーが現在の残高と同じ引き出し額を要求した場合、ユーザー設定が更新され、リザーブが担保として使用されなくなったとマークされます。最後に、ユーザーのaTokenは破棄され、引き出された資産は指定されたアドレスに転送されます。
コンパウンドと比較すると、AAVE V2の引き出しプロセスには次のような主な特徴があります:
プロトコルへのユーザーの預金を表すaTokenを使用して、引き出しは実際にaTokenを破棄します。
柔軟性を高めるために、ユーザーが指定したアドレスに(toパラメータを使って)出金できるようにします。
部分引き出しと全額引き出しのオプションを提供します。
出金検証において、AAVEはより洗練されたbalanceDecreaseAllowed関数を使用し、ユーザーの全体的な担保ポジションに対する出金の影響をチェックします。
AAVEの出金処理では、CompoundのようにaccrueInterest関数ではなく、直接金利を更新します。
借入
ユーザーは借入機能を通じて借入を行い、借入は価格予測マシンから資産の現在価格を取得することで実行されます。ETHに換算される。借入はその後、ValidationLogic.validateBorrowとGenericLogic.calculateUserAccountDataによって正当性がチェックされ、onBehalfOfアドレスを含む総担保資産、総負債、現在のLTV(Loan-to-Value)比率、清算しきい値、および市場の安定性(同様のもの)を計算します。(CompoundのgetHypotheticalAccountLiquidityInternalに類似)、借入に十分な担保資産があるかどうか。reserve.updateStateは、金利や借入インデックスなどのリザーブの状態を更新する(このステップはCompoundのaccrueInterestに類似)。'sのaccrueInterest)に似ており、金利の計算と更新に使用されます。
その後、ユーザーが選択したinterestRateMode(stableまたはfloating)に基づいて負債が生成され、異なる金利モデルを持つトークンコントラクトが選択されてトークンが鋳造されます。また、トークンの鋳造がチェックされ、onBehalfOfアドレスが呼び出し元でない場合、呼び出し元のユーザーから借り入れる権限がトークン契約から差し引かれます。DebtTokenがユーザーに鋳造された後、プロトコルはupdateInterestRatesを介して借入レートを更新し、新しいレートと借入以降の準備プールの変更を反映する。ユーザーが借入れの原資産のリリースを要求した場合、プロトコルはその資産をユーザーに直接転送します。
コンパウンドと比較して、AAVE V2の借入・貸出プロセスには以下の主な特徴があります:
安定金利と変動金利の2つの金利モデルをサポートします。
借入と融資は、別の検証ロジック契約を使って検証されます。
ユーザーの借入を表すためにDebtTokensを使用します。
クレジットの委任をサポートし、ユーザーが他の住所に代わって借りることを可能にします。
返済
ユーザーはrepay関数を使って返済を行います。stableDebtとvariableDebtを含む)を取得する。ユーザーによって選択された金利モード(安定または変動)に応じて、ValidationLogic.validateRepayは、ユーザーの債務残高が返済を行うのに十分かどうかなど、ユーザーの返済操作の正当性を検証します。返済される債務の具体的なタイプは、ユーザーが選択した金利モード(安定金利または変動金利)に基づいて決定される。利用者の返済額が現在の借入金残高に満たない場合は、利用者から提示された返済額で一部返済を行い、それ以外の場合は全額返済を行う。準備金の状態を更新する updateState は、契約書の金利、借入量、借入指数を計算し、更新するために使用される。その後、対応する安定負債トークンが焼却され、updateInterestRates によって借入金利が更新される。この時点で、返済後にユーザーの負債(安定負債と変動負債の両方)がすべてゼロになった場合、ユーザーの借入ステータスは偽とマークされ、ユーザーはもう借入していないことが示される。最後に、ユーザーは返済額を口座から契約書のaToken契約アドレスに振り込む。
コンパウンドと比較して、AAVE V2の返済プロセスには次のような主な特徴があります:
安定金利モードと変動金利モードの両方での返済をサポートします。
DebtTokenを使用して負債を表し、管理し、返済時に対応する負債トークンを燃やします。
部分返済と全額返済をサポートし、安定債務と変動債務を別々に処理します。
クレジット委任により、他のアドレスへの返済をサポートします。
清算
ユーザーはlendingpoolのliquidationCall関数を通じて清算を行います。この関数は、LendingPoolCollateralManagerのliquidationCall関数を呼び出し、関数が正常に実行されるようにします。まず、GenericLogic.calculateUserAccountDataは担保資産と負債資産の準備データとユーザーの設定情報を取得し、ユーザーの健全性係数を計算し、getUserCurrentDebtを介してユーザーの現在の安定負債と変動負債を取得します。
ValidationLogic.validateLiquidationCall関数は、ユーザーの健全性係数、債務ステータス、および担保設定のチェックを含む、清算コールの正当性を検証します。ヘルスファクターがしきい値より小さく、担保として使用されており、どちらの負債もゼロでない場合、検証はパスします。次に、ユーザーの最大清算可能債務が計算され、実際に清算が必要な債務の数が決定される。清算された負債が利用者の利用可能な担保を超える場合、清算額が調整される。
清算人が清算人が担保に入れた原資産を受け取ることを選択した場合、担保準備金に十分な流動性があることを確認する必要があります。負債準備金のステータスを更新し、清算人がトークンを受け取るかどうかに応じて、適切な数の変動負債トークンと安定負債トークンを燃やす。清算後の市場の状況を反映するために負債の金利を更新します。清算人は、清算人がaTokenを受け取ることを選択した場合、適切な数のaTokenで清算人に報酬を与え、aTokenを受け取らない場合、担保の状態と担保の金利を更新し、ユーザーのアカウントから対応する数のaTokenを燃やし、原資産を清算人に転送します。最後に、清算に必要な負債資産が清算人から対応する準備aトークンに転送され、清算プロセスが完了します。
AAVE V2の清算プロセスには、コンパウンドと比較して以下の主な特徴があります:
担保と負債資産の複数の組み合わせの清算をサポートします。
清算人がトークンまたは原資産のいずれかを受け取ることを選択できます。
清算プロセスはよりモジュール化されており、検証ロジックや計算ロジックなどを別々の機能に分離しています。
安定金利と変動金利の両方の債務タイプの清算をサポートします。
フラッシュローン
ユーザーはlendingpoolのフラッシュローン機能を使ってフラッシュローンを行います。貸出契約としてのフラッシュローンは、渡されたモードパラメータに応じて、現在のフラッシュローンをすぐに返済するか、後で返済する負債として返済することができます。
この関数はまず、ValidationLogic.validateFlashloanを介して入力パラメータが一致するかをチェックし、フラッシュクレジットに必要な保険料を計算し、必要なaTokenを受信者のアドレスに直接転送します。受信者の executeOperation が呼び出され、事前に定義されたフラッシュ・ローンが実装される。フラッシュ・ローン操作の AAVE 実装には、交換、交換清算、交換返済の操作がすでに含まれている。executeOperationがこれらの操作を完了すると、返済されるライトニングクレジットの金額と対応する手数料が記録されます。ユーザーが非債務モードで資金を返却することを選択した場合、システムはリザーブステータスを更新し、リザーブ流動性を蓄積し、流動性インデックスを更新します。最後に、資金と手数料は依頼者からリザーブプールへ送金される。ユーザーが負債モードで処理することを選択した場合、_executeBorrowが呼び出され、対応する負債ポジションがオープンされます。
借入金モードの切り替え
AAVEV2では、swapBorrowRateMode関数により、安定金利モードと変動金利モードを切り替えることができます。を切り替えることができます。まず、getUserCurrentDebt 関数を使用して、ユーザーの現在の安定金利債務と対象資産の変動金利債務を取得し、ユーザーの債務ステータスを判断します。次に、ValidationLogic.validateSwapRateMode 関数が呼び出され、切り替え操作が合法であることを検証します。これは、ユーザーがモード切り替えをサポートするのに十分な安定負債または変動負債を持っているかどうかをチェックし、スイッチターゲットモードが資産の構成とユーザーの負債プロファイルに一致していることを確認します。reserve.updateStateを呼び出して資産リザーブの状態を更新し、リザーブデータが最新であることを確認します。これに続いて、安定負債トークンを燃やして浮動負債トークンを鋳造するか、浮動負債トークンを燃やして安定負債トークンを鋳造することで、2つの負債トークンを相互に変換します。変換が完了すると、reserve.updateInterestRatesはターゲット資産レートを更新し、市場の現状とユーザーの負債の変化を反映するようにします。
セキュリティ脆弱性チェックリスト
空っぽの市場によるラウンドの脆弱性
AAVEとCompoundの両方で、短い市場での精度の損失による丸めの脆弱性の問題があります。空の市場 (つまり、市場で貸し出しを行っているユーザーがいない) の場合、cumulateToLiquidityIndex 関数の liquidityIndex の値は、コントラクトに対応する原資産トークンの数に依存するため、攻撃者は大量の原資産トークンをコントラクトにフラッシュ レンディングすることで、a トークンの価格を操作することができます。
複合フォークプロジェクトHundred Financeの最初のハッキングと同様に、HopeLendイベントでは、攻撃者はまずliquidityIndexを操作して、hETHWBTC対WBTCの値を1:1に制御しました。攻撃者はまず、liquidityIndexを操作して、WBTCに対するhETHWBTCの価値を1:1にコントロールし、その後、原資産を交換して資金を借りることでliquidityIndexを増加させた。その後、_handleFlashLoanRepayment関数がフラッシュローンのサイクルを通じて何度も何度も呼び出される。


cumulateToLiquidityIndex関数では、rayDivの精度の損失が再びリザーブを増幅します。liquidityIndexの値を増幅し、最終的に交換可能なWBTCの量を増幅します(攻撃取引: https://etherscan.io/tx/0x1a7ee0a7efc70ed7429edef069a1dd001fbff378748d91f17ab1876dc6d10392)。
監査ポイント:監査時には、為替レートの計算が操作しやすいかどうか、四捨五入が適切かどうかに注意を払う必要があります。また、マーケットが空になって操作されるのを防ぐため、新しいマーケットが作られたらすぐにプロジェクトチームがaTokensを鋳造するよう勧めることができます。
ERC677/ERC777トークンによる再エントリーの脆弱性
複合フォークプロジェクトであるHundred Financeの2度目のハッキングと似ています。Agave攻撃の場合、攻撃者はliquidateCall関数を呼び出し、負債なしで自分自身を清算しました。清算されたトークンはGnosis Chainで使用されているERC-677標準トークンで、送金時に外部から受信アドレスの関数を呼び出すため、清算契約は攻撃契約を呼び出し、攻撃契約はフラッシュローンで入手した2,728WETHを預け、2,728aWETHを鋳造し、これを担保にAgaveプロジェクトで利用可能なすべての資産を貸した。プロジェクトで利用可能なすべての資産。外部呼び出しが終わると、liquidationCall関数は攻撃者が以前に預けた2728aWETHを直接清算し、清算人に転送します。

(参照元:https://x.com/danielvf/status/1503756428212936710; アタック トランザクション: https://gnosis.blockscout.com/tx/0xa262141abcf7c127b88b4042aee8bf601f4f3372c9471dbd75cb54e76524f18e)
監査のポイント:監査では、デビットおよびクレジット機能に関連するコードがCEI(Checks-Effects-Interactions)仕様に準拠しているか、または再入力防止ロックが存在するかどうかを調べ、コールバック機能を持つトークンの影響を考慮する必要があります。
不適切な予測メカニズムによる価格操作のリスク
ブリズファイナンスプロジェクトのハッキングの場合、LUNAの価格が急落し続けたため、LUNAの価格を操作するために使用されたプロトコルは十分に強固ではありませんでした。価格が急落し続けると、プロトコルで使用されるチェーンリンクの価格情報が不正確になり、高価なLUNAを担保に資金を借り入れる可能性が出てきた。同時に、プロジェクトには既存のフェイルセーフ・メカニズムがなく、事前に警告を受けていたようだが、損失を防ぐための予防措置が間に合わなかった。価格がその水準を下回ると、誰でも(0.10ドルを大きく下回る)大量のLUNAを市場価格で購入し、それを担保(0.10ドルの価値)としてプラットフォームから資金を借りることが可能になった。
(参照元:https://x.com/BlizzFinance/status/1524911400992243761)
監査のポイント。プロジェクトを監査する際には、担保価値を算出する際に使用される予後予測の給餌メカニズムが外部からの操作の影響を受けやすいかどうかに注意を払う必要があり、プロジェクト・オーナーは、単一の価格ソースによるリスクを回避するために、複数の価格ソースを使用して包括的な鑑定を実施するよう助言される場合がある。また、そのような不測の事態を防ぐために、プロジェクトに合理的な停止メカニズムがあるかどうかも重要である。
周辺契約への外部呼び出しによってもたらされる意図しない問題
AAVEプロトコルとPara Swapプロトコルの相互作用では、AaveCollateral Repay Adapter V3契約の_buyOnParaSwap関数には複数のセキュリティ脆弱性があります。この関数は safeApprove メソッドを呼び出すことで、tokenTransferProxy の assetToSwapFrom の上限を maxAmountToSwap に設定しますが、償還が行われなかったり部分的に行われたりした場合を考慮しておらず、その結果、上限が完全に使用されなかった場合、残額が変更されずに残ります。さらに、この関数は償還を実行するために外部のコントラクト呼び出し(augustus.call(buyCalldata))に依存しており、paraswapData パスの検証や制限が適切に行われていないため、攻撃者は悪意を持って構築された paraswapData を通して、デコードされた buyCalldata と augustus コントラクト・アドレスを操作することができます。これにより、攻撃者は意図した償還ロジックを回避したり、償還を完全に回避したりすることができます。この関数は交換後に assetToSwapFrom のトークン上限を減らしたりチェックしたりしないため、交換に失敗したり迂回されたりしても、攻撃者は変更されていない上限を使用して契約からトークンを引き出すことができ、不正な資金移動が可能になります。入力データと交換結果の検証の欠如や、トークンの上限を効果的に管理することの失敗により、コントラクトが攻撃者に悪用される状況が発生します。

(トランザクションを攻撃する: https://)etherscan.io/tx/0xc27c3ec61c61309c9af35af062a834e0d6914f9352113617400577c0f2b0e9de)
AUDIT POINT: 監査の際には、外部サードパーティと相互作用するコードに特に注意してください。外部サードパーティプロトコルのインタラクションコードに特に注意を払うこと。外部契約の入力と出力が厳密に制限されているかどうか、相互作用ロジックがプロトコルのコアモデルや資金の安全性に潜在的な影響を与えるかどうか、悪意のあるデータがセキュリティ問題を引き起こすのを防ぐために入力データがクリーニングされ検証されているかどうかを重点的に評価する。外部とのやりとりのコードロジックとデータ検証メカニズムを厳密に見直すことで、そのような脆弱性のリスクを効果的に減らすことができます。
トークンと金利戦略の互換性の問題
ポリゴンチェーンへのAAVEの展開中に、以下のような問題が発生しました。InterestRateStrategy 設定の互換性のない問題により、機能例外が発生し、WETH に対して互換性のないレート戦略が誤って設定されました。
誤って設定されたInterestRateStrategy契約のインターフェイスは次のとおりです。

また、AAVE V2のAAVE V2のLendingPool実装のコードは以下の通りです:
(参照元:https://x.com/mookim_eth/status/1659589328727859205)
インターフェイスの非互換性により、新しいInterestRateStrategyをLendingPoolから正しく呼び出すことができず、AAVE V2のWETHプール機能の中断に直結し、ユーザーはETHを入出金することができませんでした。
監査ポイント監査の際には、コード(またはフォーク)の主要コンポーネントのインターフェイスが完全に互換性があることを確認してください。また、上記の問題はマルチ・チェイニング機能によって引き起こされるものではありませんが、監査中に意図しない結果を引き起こす可能性のあるさまざまなチェイニング機能に注意する必要があります。
ダスト・トークン問題
AAVEへの入出金はsetUsingAsCollateral関数で設定します。usingAsCollateralを使用すると、担保戦略を柔軟に管理できます。外部プロトコルやコントラクトがAAVEの貸出機能を通じて初めて資金を借りたとき、貸出機能はusingAsCollateralをtrueに設定し、外部プロトコルやコントラクトがAAVEから完全に資金を引き出したとき、AAVEのプロトコルハンドラのusingAsCollateralの状態はfalseに設定される。しかし実際には、AAVEが出金のために燃やすaTokenの数を計算する際、算術精度の誤差により、この時点でプロトコルハンドラに残っているaTokenが非常に少ない場合がある。その結果、次にプロトコルハンドラがAAVEに入金を行う際、usingAsCollateralは変更されずにtrueに設定されたままとなり、プロトコルハンドラのコントラクトにsetUserUseReserveAsCollateral関数を呼び出すインターフェイスがないために、プロトコルハンドラが借用操作を実行できなくなる可能性があります。
監査ポイント:監査するときは、トークンの互換性、呼び出し実装ロジックの互換性など、相互作用している外部プロトコルとの互換性の問題があるかどうかを判断するために、呼び出しているプロトコルに完全に精通し、その特性を完全に理解する必要があります。
おわりに
この小冊子では、AAVE V2プロトコルの中核となる設計、主要な機能、および監査の側面について詳しく説明しています。このマニュアルが、開発者とセキュリティ研究者が潜在的なリスクを特定し、プロトコルの安全な運用を確保するための、より良い助けとなることを願っています。スペースの都合上、この記事では一部のコードと画像を省略しているので、読者はこの記事の最後にあるオリジナルの記事をクリックしてGitHubにジャンプし、完全版(https://github.com/slowmist/AAVE-V2-Security-Audit-Checklist)を読むことができる。