💲
Compoundの内部実装について調べてみた Part3
とあるossのDeFiレンディングプロトコルにコントリビュートさせていただける機会があったので、レンディングプロトコルの先駆けであるCompoundの内部実装を調べてみることにしました。
ほぼメモかつ、途中のところが多々あるので、読み物としては非常に読みにくいかもですが、備忘録として容赦してもらえると、、🙏
誤り等あればぜひご指摘もお願いします!
Part1
Part2
Part3はInterfaceに関してです。
interface
コアプロトコル
-
CometMainInterface.sol (abstract contract)
-
実際の実装
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.15; import "./CometCore.sol"; /** * @title Compound's Comet Main Interface (without Ext) * @notice An efficient monolithic money market protocol * @author Compound */ abstract contract CometMainInterface is CometCore { error Absurd(); error AlreadyInitialized(); error BadAsset(); error BadDecimals(); error BadDiscount(); error BadMinimum(); error BadPrice(); error BorrowTooSmall(); error BorrowCFTooLarge(); error InsufficientReserves(); error LiquidateCFTooLarge(); error NoSelfTransfer(); error NotCollateralized(); error NotForSale(); error NotLiquidatable(); error Paused(); error ReentrantCallBlocked(); error SupplyCapExceeded(); error TimestampTooLarge(); error TooManyAssets(); error TooMuchSlippage(); error TransferInFailed(); error TransferOutFailed(); error Unauthorized(); event Supply(address indexed from, address indexed dst, uint amount); event Transfer(address indexed from, address indexed to, uint amount); event Withdraw(address indexed src, address indexed to, uint amount); event SupplyCollateral(address indexed from, address indexed dst, address indexed asset, uint amount); event TransferCollateral(address indexed from, address indexed to, address indexed asset, uint amount); event WithdrawCollateral(address indexed src, address indexed to, address indexed asset, uint amount); /// @notice Event emitted when a borrow position is absorbed by the protocol event AbsorbDebt(address indexed absorber, address indexed borrower, uint basePaidOut, uint usdValue); /// @notice Event emitted when a user's collateral is absorbed by the protocol event AbsorbCollateral(address indexed absorber, address indexed borrower, address indexed asset, uint collateralAbsorbed, uint usdValue); /// @notice Event emitted when a collateral asset is purchased from the protocol event BuyCollateral(address indexed buyer, address indexed asset, uint baseAmount, uint collateralAmount); /// @notice Event emitted when an action is paused/unpaused event PauseAction(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused); /// @notice Event emitted when reserves are withdrawn by the governor event WithdrawReserves(address indexed to, uint amount); function supply(address asset, uint amount) virtual external; function supplyTo(address dst, address asset, uint amount) virtual external; function supplyFrom(address from, address dst, address asset, uint amount) virtual external; function transfer(address dst, uint amount) virtual external returns (bool); function transferFrom(address src, address dst, uint amount) virtual external returns (bool); function transferAsset(address dst, address asset, uint amount) virtual external; function transferAssetFrom(address src, address dst, address asset, uint amount) virtual external; function withdraw(address asset, uint amount) virtual external; function withdrawTo(address to, address asset, uint amount) virtual external; function withdrawFrom(address src, address to, address asset, uint amount) virtual external; function approveThis(address manager, address asset, uint amount) virtual external; function withdrawReserves(address to, uint amount) virtual external; function absorb(address absorber, address[] calldata accounts) virtual external; function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) virtual external; function quoteCollateral(address asset, uint baseAmount) virtual public view returns (uint); function getAssetInfo(uint8 i) virtual public view returns (AssetInfo memory); function getAssetInfoByAddress(address asset) virtual public view returns (AssetInfo memory); function getCollateralReserves(address asset) virtual public view returns (uint); function getReserves() virtual public view returns (int); function getPrice(address priceFeed) virtual public view returns (uint); function isBorrowCollateralized(address account) virtual public view returns (bool); function isLiquidatable(address account) virtual public view returns (bool); function totalSupply() virtual external view returns (uint256); function totalBorrow() virtual external view returns (uint256); function balanceOf(address owner) virtual public view returns (uint256); function borrowBalanceOf(address account) virtual public view returns (uint256); function pause(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused) virtual external; function isSupplyPaused() virtual public view returns (bool); function isTransferPaused() virtual public view returns (bool); function isWithdrawPaused() virtual public view returns (bool); function isAbsorbPaused() virtual public view returns (bool); function isBuyPaused() virtual public view returns (bool); function accrueAccount(address account) virtual external; function getSupplyRate(uint utilization) virtual public view returns (uint64); function getBorrowRate(uint utilization) virtual public view returns (uint64); function getUtilization() virtual public view returns (uint); function governor() virtual external view returns (address); function pauseGuardian() virtual external view returns (address); function baseToken() virtual external view returns (address); function baseTokenPriceFeed() virtual external view returns (address); function extensionDelegate() virtual external view returns (address); /// @dev uint64 function supplyKink() virtual external view returns (uint); /// @dev uint64 function supplyPerSecondInterestRateSlopeLow() virtual external view returns (uint); /// @dev uint64 function supplyPerSecondInterestRateSlopeHigh() virtual external view returns (uint); /// @dev uint64 function supplyPerSecondInterestRateBase() virtual external view returns (uint); /// @dev uint64 function borrowKink() virtual external view returns (uint); /// @dev uint64 function borrowPerSecondInterestRateSlopeLow() virtual external view returns (uint); /// @dev uint64 function borrowPerSecondInterestRateSlopeHigh() virtual external view returns (uint); /// @dev uint64 function borrowPerSecondInterestRateBase() virtual external view returns (uint); /// @dev uint64 function storeFrontPriceFactor() virtual external view returns (uint); /// @dev uint64 function baseScale() virtual external view returns (uint); /// @dev uint64 function trackingIndexScale() virtual external view returns (uint); /// @dev uint64 function baseTrackingSupplySpeed() virtual external view returns (uint); /// @dev uint64 function baseTrackingBorrowSpeed() virtual external view returns (uint); /// @dev uint104 function baseMinForRewards() virtual external view returns (uint); /// @dev uint104 function baseBorrowMin() virtual external view returns (uint); /// @dev uint104 function targetReserves() virtual external view returns (uint); function numAssets() virtual external view returns (uint8); function decimals() virtual external view returns (uint8); function initializeStorage() virtual external; } -
役割
Cometプロトコルの主要な機能を定義
-
実装内容
-
基本的な操作
- supply(): 資産の供給
- withdraw(): 資産の引き出し
- transfer(): 資産の転送
-
担保管理
- supplyCollateral(): 担保の供給
- withdrawCollateral(): 担保の引き出し
- transferCollateral(): 担保の転送
-
清算機能
- absorb(): 清算ポジションの吸収
- buyCollateral(): 担保資産の購入
- isLiquidatable(): 清算可能かどうかの判定
-
金利管理
- getSupplyRate(): 供給金利の取得
- getBorrowRate(): 借入金利の取得
- getUtilization(): 利用率の取得
-
プロトコル管理
- pause(): 各種機能の一時停止
- withdrawReserves(): 準備金の引き出し
- 各種パラメータの設定と取得
-
エラー処理
- 多数のカスタムエラーを定義(BadAsset, NotCollateralized, Pausedなど)
-
イベント
- 各種操作に関するイベントを定義(Supply, Transfer, Withdrawなど)
-
基本的な操作
-
-
CometExtInterface.sol (abstract contract)
-
実際の実装
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.15; import "./CometCore.sol"; /** * @title Compound's Comet Ext Interface * @notice An efficient monolithic money market protocol * @author Compound */ abstract contract CometExtInterface is CometCore { error BadAmount(); error BadNonce(); error BadSignatory(); error InvalidValueS(); error InvalidValueV(); error SignatureExpired(); function allow(address manager, bool isAllowed) virtual external; function allowBySig(address owner, address manager, bool isAllowed, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) virtual external; function collateralBalanceOf(address account, address asset) virtual external view returns (uint128); function baseTrackingAccrued(address account) virtual external view returns (uint64); function baseAccrualScale() virtual external view returns (uint64); function baseIndexScale() virtual external view returns (uint64); function factorScale() virtual external view returns (uint64); function priceScale() virtual external view returns (uint64); function maxAssets() virtual external view returns (uint8); function totalsBasic() virtual external view returns (TotalsBasic memory); function version() virtual external view returns (string memory); /** * ===== ERC20 interfaces ===== * Does not include the following functions/events, which are defined in `CometMainInterface` instead: * - function decimals() virtual external view returns (uint8) * - function totalSupply() virtual external view returns (uint256) * - function transfer(address dst, uint amount) virtual external returns (bool) * - function transferFrom(address src, address dst, uint amount) virtual external returns (bool) * - function balanceOf(address owner) virtual external view returns (uint256) * - event Transfer(address indexed from, address indexed to, uint256 amount) */ function name() virtual external view returns (string memory); function symbol() virtual external view returns (string memory); /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) virtual external returns (bool); /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) virtual external view returns (uint256); event Approval(address indexed owner, address indexed spender, uint256 amount); } -
役割
Cometプロトコルの拡張機能を定義
-
実装内容
-
権限管理機能
- allow(): マネージャーへの権限付与/取り消し
- allowBySig(): 署名を使用した権限付与/取り消し
-
情報取得機能
- collateralBalanceOf(): アカウントの担保残高を取得
- baseTrackingAccrued(): アカウントの基本追跡利息を取得
- 各種スケール値の取得(baseAccrualScale(), baseIndexScale()など)
-
ERC20互換機能
- name(), symbol(): トークンの基本情報
- approve(), allowance(): トークンの承認機能
-
権限管理機能
-
abstract contractのわけ
-
interfaceとabstract contractの違い
-
interface:
- 関数の宣言のみを含む
- 状態変数(state variables)を持たん
- 関数の実装を含むことができない
- 他のインターフェースを継承できるが、コントラクトは継承できない
-
abstract contract:
- 関数の宣言と実装の両方を含む
- 状態変数を持つことができる
- 一部の関数を実装し、一部を抽象関数として残すことができる
- 他のコントラクトやインターフェースを継承できる
-
interface:
- 選択理由
-
状態変数の必要性:
- Cometプロトコルは複雑な状態管理を必要とします
- abstract contractは状態変数を定義できるため、より柔軟な設計が可能です
-
共通の実装の共有:
- 一部の関数は共通の実装を持つことができます
- abstract contractを使用することで、これらの共通実装を共有できます
-
継承の柔軟性:
- abstract contractは他のコントラクトやインターフェースを継承できます
- この場合、CometCoreを継承しているため、その機能を利用できます
-
エラー定義の共有:
- カスタムエラーはinterfaceでは定義できませんが、abstract contractでは定義できます
- エラー定義を共有することで、一貫性のあるエラーハンドリングが可能です
-
実装の柔軟性:
- 一部の関数は抽象関数として残し、具体的な実装は継承先で行うことができます
- これにより、異なる実装を持つ複数のコントラクトを作成できます
-
状態変数の必要性:
Discussion