diff --git a/foundry.toml b/foundry.toml index e8a82065..0f00da9e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -20,10 +20,12 @@ int_types = "long" quote_style = "double" number_underscore = "preserve" override_spacing = true +wrap_comments = true ignore = [ "src/EVault/EVault.sol", "src/EVault/shared/types/VaultStorage.sol", - "src/EVault/shared/types/VaultCache.sol" + "src/EVault/shared/types/VaultCache.sol", + "test/mocks/TestERC20.sol" ] diff --git a/src/EVault/Dispatch.sol b/src/EVault/Dispatch.sol index 08917a00..ec148e69 100644 --- a/src/EVault/Dispatch.sol +++ b/src/EVault/Dispatch.sol @@ -98,9 +98,9 @@ abstract contract Dispatch is } } - // External function which is only callable by the EVault itself. Its purpose is to be static called by `delegateToModuleView` - // which allows view functions to be implemented in modules, even though delegatecall cannot be directly used within - // view functions. + // External function which is only callable by the EVault itself. Its purpose is to be static called by + // `delegateToModuleView` which allows view functions to be implemented in modules, even though delegatecall cannot + // be directly used within view functions. function viewDelegate() external payable { if (msg.sender != address(this)) revert E_Unauthorized(); @@ -134,7 +134,8 @@ abstract contract Dispatch is mstore(0, 0x1fe8b95300000000000000000000000000000000000000000000000000000000) let strippedCalldataSize := sub(calldatasize(), PROXY_METADATA_LENGTH) // we do the mstore first offset by -12 so the 20 address bytes align right behind 36 + strippedCalldataSize - // note that it can write into the module address if the calldata is less than 12 bytes, therefore write before we write module + // note that it can write into the module address if the calldata is less than 12 bytes, therefore write + // before we write module mstore(add(24, strippedCalldataSize), caller()) mstore(4, module) calldatacopy(36, 0, strippedCalldataSize) diff --git a/src/EVault/IEVault.sol b/src/EVault/IEVault.sol index 9b53c530..9ed1f988 100644 --- a/src/EVault/IEVault.sol +++ b/src/EVault/IEVault.sol @@ -139,7 +139,8 @@ interface IERC4626 { /// @param amount Amount of assets to deposit (use max uint256 for full underlying token balance) /// @param receiver An account to receive the shares /// @return Amount of shares minted - /// @dev Deposit will round down the amount of assets that are converted to shares. To prevent losses consider using mint instead. + /// @dev Deposit will round down the amount of assets that are converted to shares. To prevent losses consider using + /// mint instead. function deposit(uint256 amount, address receiver) external returns (uint256); /// @notice Transfer underlying tokens from sender to the vault pool in return for requested amount of shares @@ -182,7 +183,8 @@ interface IVault is IERC4626 { /// @param amount Amount of assets to claim (use max uint256 to claim all available assets) /// @param receiver An account to receive the shares /// @return Amount of shares minted - /// @dev Could be used as an alternative deposit flow in certain scenarios. E.g. swap directly to the vault, call `skim` to claim deposit. + /// @dev Could be used as an alternative deposit flow in certain scenarios. E.g. swap directly to the vault, call + /// `skim` to claim deposit. function skim(uint256 amount, address receiver) external returns (uint256); } @@ -193,7 +195,8 @@ interface IBorrowing { /// @return The total borrows in asset units function totalBorrows() external view returns (uint256); - /// @notice Sum of all outstanding debts, in underlying units scaled up by shifting INTERNAL_DEBT_PRECISION_SHIFT bits + /// @notice Sum of all outstanding debts, in underlying units scaled up by shifting + /// INTERNAL_DEBT_PRECISION_SHIFT bits /// @return The total borrows in internal debt precision function totalBorrowsExact() external view returns (uint256); @@ -206,7 +209,8 @@ interface IBorrowing { /// @return The debt of the account in asset units function debtOf(address account) external view returns (uint256); - /// @notice Debt owed by a particular account, in underlying units scaled up by shifting INTERNAL_DEBT_PRECISION_SHIFT bits + /// @notice Debt owed by a particular account, in underlying units scaled up by shifting + /// INTERNAL_DEBT_PRECISION_SHIFT bits /// @param account Address to query /// @return The debt of the account in internal precision function debtOfExact(address account) external view returns (uint256); @@ -249,12 +253,15 @@ interface IBorrowing { /// @return Amount of debt pulled in asset units. function pullDebt(uint256 amount, address from) external returns (uint256); - /// @notice Request a flash-loan. A onFlashLoan() callback in msg.sender will be invoked, which must repay the loan to the main Euler address prior to returning. + /// @notice Request a flash-loan. A onFlashLoan() callback in msg.sender will be invoked, which must repay the loan + /// to the main Euler address prior to returning. /// @param amount In asset units - /// @param data Passed through to the onFlashLoan() callback, so contracts don't need to store transient data in storage + /// @param data Passed through to the onFlashLoan() callback, so contracts don't need to store transient data in + /// storage function flashLoan(uint256 amount, bytes calldata data) external; - /// @notice Updates interest accumulator and totalBorrows, credits reserves, re-targets interest rate, and logs vault status + /// @notice Updates interest accumulator and totalBorrows, credits reserves, re-targets interest rate, and logs + /// vault status function touch() external; } @@ -266,7 +273,8 @@ interface ILiquidation { /// @param violator Address that may be in collateral violation /// @param collateral Collateral which is to be seized /// @return maxRepay Max amount of debt that can be repaid, in asset units - /// @return maxYield Yield in collateral corresponding to max allowed amount of debt to be repaid, in collateral balance (shares for vaults) + /// @return maxYield Yield in collateral corresponding to max allowed amount of debt to be repaid, in collateral + /// balance (shares for vaults) function checkLiquidation(address liquidator, address violator, address collateral) external view @@ -275,8 +283,10 @@ interface ILiquidation { /// @notice Attempts to perform a liquidation /// @param violator Address that may be in collateral violation /// @param collateral Collateral which is to be seized - /// @param repayAssets The amount of underlying debt to be transferred from violator to sender, in asset units (use max uint256 to repay the maximum possible amount). - /// @param minYieldBalance The minimum acceptable amount of collateral to be transferred from violator to sender, in collateral balance units (shares for vaults) + /// @param repayAssets The amount of underlying debt to be transferred from violator to sender, in asset units (use + /// max uint256 to repay the maximum possible amount). + /// @param minYieldBalance The minimum acceptable amount of collateral to be transferred from violator to sender, in + /// collateral balance units (shares for vaults) function liquidate(address violator, address collateral, uint256 repayAssets, uint256 minYieldBalance) external; } @@ -285,7 +295,8 @@ interface ILiquidation { interface IRiskManager is IEVCVault { /// @notice Retrieve account's total liquidity /// @param account Account holding debt in this vault - /// @param liquidation Flag to indicate if the calculation should be performed in liquidation vs account status check mode, where different LTV values might apply. + /// @param liquidation Flag to indicate if the calculation should be performed in liquidation vs account status + /// check mode, where different LTV values might apply. /// @return collateralValue Total risk adjusted value of all collaterals in unit of account /// @return liabilityValue Value of debt in unit of account function accountLiquidity(address account, bool liquidation) @@ -295,9 +306,11 @@ interface IRiskManager is IEVCVault { /// @notice Retrieve account's liquidity per collateral /// @param account Account holding debt in this vault - /// @param liquidation Flag to indicate if the calculation should be performed in liquidation vs account status check mode, where different LTV values might apply. + /// @param liquidation Flag to indicate if the calculation should be performed in liquidation vs account status + /// check mode, where different LTV values might apply. /// @return collaterals Array of collaterals enabled - /// @return collateralValues Array of risk adjusted collateral values corresponding to items in collaterals array. In unit of account + /// @return collateralValues Array of risk adjusted collateral values corresponding to items in collaterals array. + /// In unit of account /// @return liabilityValue Value of debt in unit of account function accountLiquidityFull(address account, bool liquidation) external @@ -309,12 +322,14 @@ interface IRiskManager is IEVCVault { /// @notice Checks the status of an account and reverts if account is not healthy /// @param account The address of the account to be checked - /// @return magicValue Must return the bytes4 magic value 0xb168c58f (which is a selector of this function) when account status is valid, or revert otherwise. + /// @return magicValue Must return the bytes4 magic value 0xb168c58f (which is a selector of this function) when + /// account status is valid, or revert otherwise. /// @dev Only callable by EVC during status checks function checkAccountStatus(address account, address[] calldata collaterals) external returns (bytes4); /// @notice Checks the status of the vault and reverts if caps are exceeded - /// @return magicValue Must return the bytes4 magic value 0x4b3d1223 (which is a selector of this function) when account status is valid, or revert otherwise. + /// @return magicValue Must return the bytes4 magic value 0x4b3d1223 (which is a selector of this function) when + /// account status is valid, or revert otherwise. /// @dev Only callable by EVC during status checks function checkVaultStatus() external returns (bytes4); } @@ -378,12 +393,14 @@ interface IGovernance { /// @return borrowCap The borrow cap in AmountCap format function caps() external view returns (uint16 supplyCap, uint16 borrowCap); - /// @notice Retrieves the borrow LTV of the collateral, which is used to determine if the account is healthy during account status checks. + /// @notice Retrieves the borrow LTV of the collateral, which is used to determine if the account is healthy during + /// account status checks. /// @param collateral The address of the collateral to query /// @return Borrowing LTV in 1e4 scale function LTVBorrow(address collateral) external view returns (uint16); - /// @notice Retrieves the current liquidation LTV, which is used to determine if the account is eligible for liquidation + /// @notice Retrieves the current liquidation LTV, which is used to determine if the account is eligible for + /// liquidation /// @param collateral The address of the collateral to query /// @return Liquidation LTV in 1e4 scale function LTVLiquidation(address collateral) external view returns (uint16); @@ -394,7 +411,8 @@ interface IGovernance { /// @return liquidationLTV The value of fully converged liquidation LTV /// @return initialLiquidationLTV The initial value of the liquidation LTV, when the ramp began /// @return targetTimestamp The timestamp when the liquidation LTV is considered fully converged - /// @return rampDuration The time it takes for the liquidation LTV to converge from the initial value to the fully converged value + /// @return rampDuration The time it takes for the liquidation LTV to converge from the initial value to the fully + /// converged value function LTVFull(address collateral) external view @@ -415,7 +433,8 @@ interface IGovernance { /// @return The maximum liquidation discount in 1e4 scale function maxLiquidationDiscount() external view returns (uint16); - /// @notice Retrieves liquidation cool-off time, which must elapse after successful account status check before account can be liquidated + /// @notice Retrieves liquidation cool-off time, which must elapse after successful account status check before + /// account can be liquidated /// @return The liquidation cool off time in seconds function liquidationCoolOffTime() external view returns (uint16); @@ -444,7 +463,8 @@ interface IGovernance { /// @return The address of the Permit2 contract function permit2Address() external view returns (address); - /// @notice Splits accrued fees balance according to protocol fee share and transfers shares to the governor fee receiver and protocol fee receiver + /// @notice Splits accrued fees balance according to protocol fee share and transfers shares to the governor fee + /// receiver and protocol fee receiver function convertFees() external; /// @notice Set a new governor address @@ -463,25 +483,30 @@ interface IGovernance { /// @param rampDuration Ramp duration in seconds function setLTV(address collateral, uint16 borrowLTV, uint16 liquidationLTV, uint32 rampDuration) external; - /// @notice Completely clears LTV configuratrion, signalling the collateral is not considered safe to liquidate anymore + /// @notice Completely clears LTV configuratrion, signalling the collateral is not considered safe to liquidate + /// anymore /// @param collateral Address of the collateral function clearLTV(address collateral) external; /// @notice Set a new maximum liquidation discount /// @param newDiscount New maximum liquidation discount in 1e4 scale - /// @dev If the discount is zero (the default), the liquidators will not be incentivized to liquidate unhealthy accounts + /// @dev If the discount is zero (the default), the liquidators will not be incentivized to liquidate unhealthy + /// accounts function setMaxLiquidationDiscount(uint16 newDiscount) external; - /// @notice Set a new liquidation cool off time, which must elapse after successful account status check before account can be liquidated + /// @notice Set a new liquidation cool off time, which must elapse after successful account status check before + /// account can be liquidated /// @param newCoolOffTime The new liquidation cool off time in seconds - /// @dev Setting cool off time to zero allows liquidating the account in the same block as the last successful account status check + /// @dev Setting cool off time to zero allows liquidating the account in the same block as the last successful + /// account status check function setLiquidationCoolOffTime(uint16 newCoolOffTime) external; /// @notice Set a new interest rate model contract /// @param newModel The new IRM address function setInterestRateModel(address newModel) external; - /// @notice Set a new hook target and a new bitmap indicating which operations should call the hook target. Operations are defined in Constants.sol + /// @notice Set a new hook target and a new bitmap indicating which operations should call the hook target. + /// Operations are defined in Constants.sol /// @param newHookTarget The new hook target address /// @param newHookedOps Bitmask with the new hooked operations function setHookConfig(address newHookTarget, uint32 newHookedOps) external; diff --git a/src/EVault/modules/Governance.sol b/src/EVault/modules/Governance.sol index 0b5ade00..ed017351 100644 --- a/src/EVault/modules/Governance.sol +++ b/src/EVault/modules/Governance.sol @@ -41,11 +41,15 @@ abstract contract GovernanceModule is IGovernance, BalanceUtils, BorrowUtils, LT /// @notice Set new LTV configuration for a collateral /// @param collateral Address of the collateral - /// @param borrowLTV The new LTV for the collateral, used to determine health of the account during regular operations, in 1e4 scale - /// @param liquidationLTV The new LTV for the collateral, used to determine health of the account during liquidations, in 1e4 scale + /// @param borrowLTV The new LTV for the collateral, used to determine health of the account during regular + /// operations, in 1e4 scale + /// @param liquidationLTV The new LTV for the collateral, used to determine health of the account during + /// liquidations, in 1e4 scale /// @param initialLiquidationLTV The previous liquidation LTV at the moment a new configuration was set - /// @param targetTimestamp If the LTV is lowered, the timestamp when the ramped liquidation LTV will merge with the `targetLTV` - /// @param rampDuration If the LTV is lowered, duration in seconds, during which the liquidation LTV will be merging with `targetLTV` + /// @param targetTimestamp If the LTV is lowered, the timestamp when the ramped liquidation LTV will merge with the + /// `targetLTV` + /// @param rampDuration If the LTV is lowered, duration in seconds, during which the liquidation LTV will be merging + /// with `targetLTV` event GovSetLTV( address indexed collateral, uint16 borrowLTV, @@ -63,7 +67,8 @@ abstract contract GovernanceModule is IGovernance, BalanceUtils, BorrowUtils, LT /// @param newDiscount The new maximum liquidation discount in 1e4 scale event GovSetMaxLiquidationDiscount(uint16 newDiscount); - /// @notice Set a new liquidation cool off time, which must elapse after successful account status check, before account can be liquidated + /// @notice Set a new liquidation cool off time, which must elapse after successful account status check, before + /// account can be liquidated /// @param newCoolOffTime The new liquidation cool off time in seconds event GovSetLiquidationCoolOffTime(uint16 newCoolOffTime); @@ -230,7 +235,8 @@ abstract contract GovernanceModule is IGovernance, BalanceUtils, BorrowUtils, LT vaultStorage.accumulatedFees = vaultCache.accumulatedFees = Shares.wrap(0); - // For the Deposit events in increaseBalance the assets amount is zero - the shares are covered with the accrued interest + // For the Deposit events in increaseBalance the assets amount is zero - the shares are covered with the accrued + // interest if (!governorShares.isZero()) { increaseBalance(vaultCache, governorReceiver, address(0), governorShares, Assets.wrap(0)); } @@ -255,15 +261,16 @@ abstract contract GovernanceModule is IGovernance, BalanceUtils, BorrowUtils, LT } /// @inheritdoc IGovernance - /// @dev When the collateral asset is no longer deemed suitable to sustain debt (and not because of code issues, see `clearLTV`), - /// its LTV setting can be set to 0. Setting a zero liquidation LTV also enforces a zero borrowing LTV (`newBorrowLTV <= newLiquidationLTV`). - /// In such cases, the collateral becomes immediately ineffective for new borrows. However, for liquidation purposes, the LTV can be ramped down - /// over a period of time (`rampDuration`). This ramping helps users avoid hard liquidations with maximum discounts and gives them a chance to - /// close their positions in an orderly fashion. The choice of `rampDuration` depends on market conditions assessed by the governor. - /// They may decide to forgo the ramp entirely by setting the duration to zero, presumably in light of extreme market conditions, where ramping - /// would pose a threat to the vault's solvency. - /// In any case, when the liquidation LTV reaches its target of 0, this asset will no longer support the debt, but it will still be possible to - /// liquidate it at a discount and use the proceeds to repay an unhealthy loan. + /// @dev When the collateral asset is no longer deemed suitable to sustain debt (and not because of code issues, see + /// `clearLTV`), its LTV setting can be set to 0. Setting a zero liquidation LTV also enforces a zero borrowing LTV + /// (`newBorrowLTV <= newLiquidationLTV`). In such cases, the collateral becomes immediately ineffective for new + /// borrows. However, for liquidation purposes, the LTV can be ramped down over a period of time (`rampDuration`). + /// This ramping helps users avoid hard liquidations with maximum discounts and gives them a chance to close their + /// positions in an orderly fashion. The choice of `rampDuration` depends on market conditions assessed by the + /// governor. They may decide to forgo the ramp entirely by setting the duration to zero, presumably in light of + /// extreme market conditions, where ramping would pose a threat to the vault's solvency. In any case, when the + /// liquidation LTV reaches its target of 0, this asset will no longer support the debt, but it will still be + /// possible to liquidate it at a discount and use the proceeds to repay an unhealthy loan. function setLTV(address collateral, uint16 borrowLTV, uint16 liquidationLTV, uint32 rampDuration) public virtual diff --git a/src/EVault/modules/Initialize.sol b/src/EVault/modules/Initialize.sol index ee6c2e89..00c40255 100644 --- a/src/EVault/modules/Initialize.sol +++ b/src/EVault/modules/Initialize.sol @@ -71,8 +71,8 @@ abstract contract InitializeModule is IInitialize, BorrowUtils { initialized = true; } - /// @dev Calls the asset's symbol() method, taking care to handle MKR-like tokens that return bytes32 instead of string. - /// For tokens that do not implement symbol(), "UNDEFINED" will be returned. + /// @dev Calls the asset's symbol() method, taking care to handle MKR-like tokens that return bytes32 instead of + /// string. For tokens that do not implement symbol(), "UNDEFINED" will be returned. function getTokenSymbol(address asset) private view returns (string memory) { (bool success, bytes memory data) = address(asset).staticcall(abi.encodeCall(IERC20.symbol, ())); if (!success) return "UNDEFINED"; diff --git a/src/EVault/modules/Liquidation.sol b/src/EVault/modules/Liquidation.sol index d81a020a..07e5bb3e 100644 --- a/src/EVault/modules/Liquidation.sol +++ b/src/EVault/modules/Liquidation.sol @@ -87,7 +87,8 @@ abstract contract LiquidationModule is ILiquidation, BalanceUtils, LiquidityUtil // Violator's health check must not be deferred, meaning no prior operations on violator's account // would possibly be forgiven after the enforced collateral transfer to the liquidator if (isAccountStatusCheckDeferred(violator)) revert E_ViolatorLiquidityDeferred(); - // A cool off time must elapse since successful account status check in order to mitigate self-liquidaition attacks + // A cool off time must elapse since successful account status check in order to mitigate self-liquidaition + // attacks if (isInLiquidationCoolOff(violator)) revert E_LiquidationCoolOff(); // Violator has no liabilities, liquidation is a no-op @@ -125,7 +126,8 @@ abstract contract LiquidationModule is ILiquidation, BalanceUtils, LiquidityUtil // Compute discount - uint256 discountFactor = collateralAdjustedValue * 1e18 / liabilityValue; // discountFactor = health score = 1 - discount + // discountFactor = health score = 1 - discount + uint256 discountFactor = collateralAdjustedValue * 1e18 / liabilityValue; { uint256 minDiscountFactor; unchecked { @@ -142,11 +144,12 @@ abstract contract LiquidationModule is ILiquidation, BalanceUtils, LiquidityUtil vaultCache.oracle.getQuote(collateralBalance, liqCache.collateral, vaultCache.unitOfAccount); if (collateralValue == 0) { - // Worthless collateral can be claimed with no repay. The collateral can be actually worthless, or the amount of available - // collateral could be non-representable in the unit of account (rounded down to zero during conversion). In this case - // the liquidator is able to claim the collateral without repaying any debt. Note that it's not profitable as long as liquidation - // gas costs are larger than the value of a single wei of the reference asset. Care should be taken though when selecting - // unit of account and collateral assets. + // Worthless collateral can be claimed with no repay. The collateral can be actually worthless, or the + // amount of available collateral could be non-representable in the unit of account (rounded down to zero + // during conversion). In this case the liquidator is able to claim the collateral without repaying any + // debt. Note that it's not profitable as long as liquidation gas costs are larger than the value of a + // single wei of the reference asset. Care should be taken though when selecting unit of account and + // collateral assets. liqCache.yieldBalance = collateralBalance; return liqCache; } @@ -154,8 +157,8 @@ abstract contract LiquidationModule is ILiquidation, BalanceUtils, LiquidityUtil uint256 maxRepayValue = liabilityValue; uint256 maxYieldValue = maxRepayValue * 1e18 / discountFactor; - // Limit yield to borrower's available collateral, and reduce repay if necessary - // This can happen when borrower has multiple collaterals and seizing all of this one won't bring the violator back to solvency + // Limit yield to borrower's available collateral, and reduce repay if necessary. This can happen when borrower + // has multiple collaterals and seizing all of this one won't bring the violator back to solvency if (collateralValue < maxYieldValue) { maxRepayValue = collateralValue * discountFactor / 1e18; @@ -183,9 +186,9 @@ abstract contract LiquidationModule is ILiquidation, BalanceUtils, LiquidityUtil // Handle yield: liquidator receives violator's collateral - // Impersonate violator on the EVC to seize collateral. The yield transfer will trigger a health check on the violator's - // account, which should be forgiven, because the violator's account is not guaranteed to be healthy after liquidation. - // This operation is safe, because: + // Impersonate violator on the EVC to seize collateral. The yield transfer will trigger a health check on the + // violator's account, which should be forgiven, because the violator's account is not guaranteed to be healthy + // after liquidation. This operation is safe, because: // 1. `liquidate` function is enforcing that the violator is not in deferred checks state, // therefore there were no prior batch operations that could have registered a health check, // and if the check is present now, it must have been triggered by the enforced transfer. @@ -212,7 +215,8 @@ abstract contract LiquidationModule is ILiquidation, BalanceUtils, LiquidityUtil Assets owedRemaining = liqCache.liability.subUnchecked(liqCache.repay); decreaseBorrow(vaultCache, liqCache.violator, owedRemaining); - // decreaseBorrow emits Repay without any assets entering the vault. Emit Withdraw from and to zero address to cover the missing amount for offchain trackers. + // decreaseBorrow emits Repay without any assets entering the vault. Emit Withdraw from and to zero address + // to cover the missing amount for offchain trackers. emit Withdraw(liqCache.liquidator, address(0), address(0), owedRemaining.toUint(), 0); emit DebtSocialized(liqCache.violator, owedRemaining.toUint()); } diff --git a/src/EVault/modules/RiskManager.sol b/src/EVault/modules/RiskManager.sol index 03d16602..7abeda81 100644 --- a/src/EVault/modules/RiskManager.sol +++ b/src/EVault/modules/RiskManager.sol @@ -61,10 +61,11 @@ abstract contract RiskManagerModule is IRiskManager, LiquidityUtils { } /// @inheritdoc IRiskManager - /// @dev The function doesn't have a reentrancy lock, because onlyEVCChecks provides equivalent behaviour. It ensures that the caller - /// is the EVC, in 'checks in progress' state. In this state EVC will not accept any calls. Since all the functions which modify - /// vault state use callThroughEVC modifier, they are effectively blocked while the function executes. There are non-view functions without - /// callThroughEVC modifier (`flashLoan`, `disableCollateral`), but they don't change the vault's storage. + /// @dev The function doesn't have a reentrancy lock, because onlyEVCChecks provides equivalent behaviour. It + /// ensures that the caller is the EVC, in 'checks in progress' state. In this state EVC will not accept any calls. + /// Since all the functions which modify vault state use callThroughEVC modifier, they are effectively blocked while + /// the function executes. There are non-view functions without callThroughEVC modifier (`flashLoan`, + /// `disableCollateral`), but they don't change the vault's storage. function checkAccountStatus(address account, address[] calldata collaterals) public virtual @@ -81,15 +82,16 @@ abstract contract RiskManagerModule is IRiskManager, LiquidityUtils { /// @dev See comment about reentrancy for `checkAccountStatus` function checkVaultStatus() public virtual reentrantOK onlyEVCChecks returns (bytes4 magicValue) { // Use the updating variant to make sure interest is accrued in storage before the interest rate update. - // Because of interest rate retargetting during the vault status check, the vault status check must not be forgiven. + // Because of interest rate retargetting during the vault status check, the vault status check must not be + // forgiven. VaultCache memory vaultCache = updateVault(); uint256 newInterestRate = computeInterestRate(vaultCache); logVaultStatus(vaultCache, newInterestRate); - // We use the snapshot to check if the borrows or supply grew, and if so then we check the borrow and supply caps. - // If snapshot is initialized, then caps are configured. - // If caps are set in the middle of a batch, then snapshots represent the state of the vault at that time. + // We use the snapshot to check if the borrows or supply grew, and if so then we check the borrow and supply + // caps. If snapshot is initialized, then caps are configured. If caps are set in the middle of a batch, then + // snapshots represent the state of the vault at that time. if (vaultCache.snapshotInitialized) { vaultStorage.snapshotInitialized = vaultCache.snapshotInitialized = false; diff --git a/src/EVault/shared/AssetTransfers.sol b/src/EVault/shared/AssetTransfers.sol index 3348ded8..83a269d0 100644 --- a/src/EVault/shared/AssetTransfers.sol +++ b/src/EVault/shared/AssetTransfers.sol @@ -20,14 +20,15 @@ abstract contract AssetTransfers is Base { vaultStorage.cash = vaultCache.cash = vaultCache.cash + amount; } - /// @dev If the `CFG_EVC_COMPATIBLE_ASSET` flag is set, the function will protect users from mistakenly sending funds - /// to the EVC sub-accounts. Functions that push tokens out (`withdraw`, `redeem`, `borrow`) accept a `receiver` argument. - /// If the user sets one of their sub-accounts (not the owner) as the receiver, funds would be lost because a regular asset doesn't - /// support the EVC's sub-accounts. The private key to a sub-account (not the owner) is not known, so the user would not be able to move - /// the funds out. The function will make a best effort to prevent this by checking if the receiver of the token - /// is recognized by EVC as a non-owner sub-account. In other words, if there is an account registered in EVC as the owner for the - /// intended receiver, the transfer will be prevented. However, there is no guarantee that EVC will have the owner registered. - /// If the asset itself is compatible with EVC, it is safe to not set the flag and send the asset to a non-owner sub-account. + /// @dev If the `CFG_EVC_COMPATIBLE_ASSET` flag is set, the function will protect users from mistakenly sending + /// funds to the EVC sub-accounts. Functions that push tokens out (`withdraw`, `redeem`, `borrow`) accept a + /// `receiver` argument. If the user sets one of their sub-accounts (not the owner) as the receiver, funds would be + /// lost because a regular asset doesn't support the EVC's sub-accounts. The private key to a sub-account (not the + /// owner) is not known, so the user would not be able to move the funds out. The function will make a best effort + /// to prevent this by checking if the receiver of the token is recognized by EVC as a non-owner sub-account. In + /// other words, if there is an account registered in EVC as the owner for the intended receiver, the transfer will + /// be prevented. However, there is no guarantee that EVC will have the owner registered. If the asset itself is + /// compatible with EVC, it is safe to not set the flag and send the asset to a non-owner sub-account. function pushAssets(VaultCache memory vaultCache, address to, Assets amount) internal virtual { if ( to == address(0) diff --git a/src/EVault/shared/Base.sol b/src/EVault/shared/Base.sol index fb10ac1e..061d7b1e 100644 --- a/src/EVault/shared/Base.sol +++ b/src/EVault/shared/Base.sol @@ -89,9 +89,9 @@ abstract contract Base is EVCClient, Cache { callHook(vaultCache.hookedOps, operation, account); EVCRequireStatusChecks(accountToCheck == CHECKACCOUNT_CALLER ? account : accountToCheck); - // The snapshot is used only to verify that supply increased when checking the supply cap, and to verify that the borrows - // increased when checking the borrowing cap. Caps are not checked when the capped variables decrease (become safer). - // For this reason, the snapshot is disabled if both caps are disabled. + // The snapshot is used only to verify that supply increased when checking the supply cap, and to verify that + // the borrows increased when checking the borrowing cap. Caps are not checked when the capped variables + // decrease (become safer). For this reason, the snapshot is disabled if both caps are disabled. // The snapshot is cleared during the vault status check hence the vault status check must not be forgiven. if ( !vaultCache.snapshotInitialized diff --git a/src/EVault/shared/BorrowUtils.sol b/src/EVault/shared/BorrowUtils.sol index baea3820..69329ad0 100644 --- a/src/EVault/shared/BorrowUtils.sol +++ b/src/EVault/shared/BorrowUtils.sol @@ -150,7 +150,8 @@ abstract contract BorrowUtils is Base { } function calculateDTokenAddress() internal view virtual returns (address dToken) { - // inspired by https://github.com/Vectorized/solady/blob/229c18cfcdcd474f95c30ad31b0f7d428ee8a31a/src/utils/CREATE3.sol#L82-L90 + // inspired by: + // https://github.com/Vectorized/solady/blob/229c18cfcdcd474f95c30ad31b0f7d428ee8a31a/src/utils/CREATE3.sol#L82-L90 assembly ("memory-safe") { mstore(0x14, address()) // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ address(this) ++ 0x01). diff --git a/src/EVault/shared/Cache.sol b/src/EVault/shared/Cache.sol index 1eb345ab..fcbb9f6b 100644 --- a/src/EVault/shared/Cache.sol +++ b/src/EVault/shared/Cache.sol @@ -101,20 +101,20 @@ contract Cache is Storage, Errors { / (uint256(CONFIG_SCALE) << INTERNAL_DEBT_PRECISION_SHIFT); if (feeAssets != 0) { - // Fee shares are minted at a slightly worse price than user deposits (unless the exchange rate of shares to assets is < 1, - // which is only possible in an extreme case of large debt socialization). - // The discrepancy arises because, firstly, the virtual deposit is not accounted for, and secondly, all new - // interest is included in the total assets during the minting process, whereas during a regular user operation, the pre-money conversion - // is performed. - // This behavior is considered safe and reduces code complexity and gas costs, while its effect is positive for - // regular users (unless the exchange rate is abnormally < 1) + // Fee shares are minted at a slightly worse price than user deposits (unless the exchange rate of + // shares to assets is < 1, which is only possible in an extreme case of large debt socialization). The + // discrepancy arises because, firstly, the virtual deposit is not accounted for, and secondly, all new + // interest is included in the total assets during the minting process, whereas during a regular user + // operation, the pre-money conversion is performed. This behavior is considered safe and reduces code + // complexity and gas costs, while its effect is positive for regular users (unless the exchange rate is + // abnormally < 1) uint256 newTotalAssets = vaultCache.cash.toUint() + OwedLib.toAssetsUpUint(newTotalBorrows); newTotalShares = newTotalAssets * newTotalShares / (newTotalAssets - feeAssets); newAccumulatedFees += newTotalShares - vaultCache.totalShares.toUint(); } - // Store new values in vaultCache, only if no overflows will occur. Fees are not larger than total shares, since they are included in them. - + // Store new values in vaultCache, only if no overflows will occur. Fees are not larger than total shares, + // since they are included in them. if (newTotalBorrows <= MAX_SANE_DEBT_AMOUNT) { vaultCache.totalBorrows = newTotalBorrows.toOwed(); vaultCache.interestAccumulator = newInterestAccumulator; diff --git a/src/EVault/shared/Constants.sol b/src/EVault/shared/Constants.sol index dc350bbe..ac206290 100644 --- a/src/EVault/shared/Constants.sol +++ b/src/EVault/shared/Constants.sol @@ -7,9 +7,11 @@ pragma solidity ^0.8.0; uint256 constant INTERNAL_DEBT_PRECISION_SHIFT = 31; // max amount for Assets and Shares custom types based on a uint112. uint256 constant MAX_SANE_AMOUNT = type(uint112).max; -// max debt amount fits in uint144 (112 + 31 bits). Last 31 bits are zeros to ensure max debt rounded up equals max sane amount. +// max debt amount fits in uint144 (112 + 31 bits). +// Last 31 bits are zeros to ensure max debt rounded up equals max sane amount. uint256 constant MAX_SANE_DEBT_AMOUNT = uint256(MAX_SANE_AMOUNT) << INTERNAL_DEBT_PRECISION_SHIFT; -// proxy trailing calldata length in bytes. Three addresses, 20 bytes each: vault underlying asset, oracle and unit of account. +// proxy trailing calldata length in bytes. +// Three addresses, 20 bytes each: vault underlying asset, oracle and unit of account. uint256 constant PROXY_METADATA_LENGTH = 60; // gregorian calendar uint256 constant SECONDS_PER_YEAR = 365.2425 * 86400; diff --git a/src/EVault/shared/EVCClient.sol b/src/EVault/shared/EVCClient.sol index 065f668e..4ac870a8 100644 --- a/src/EVault/shared/EVCClient.sol +++ b/src/EVault/shared/EVCClient.sol @@ -36,7 +36,8 @@ abstract contract EVCClient is Storage, Events, Errors { evc.disableController(account); } - // Authenticate the account and the controller, making sure the call is made through EVC and the status checks are deferred + // Authenticate the account and the controller, making sure the call is made through EVC and the status checks are + // deferred function EVCAuthenticateDeferred(bool checkController) internal view virtual returns (address) { assert(msg.sender == address(evc)); // this ensures that callThroughEVC modifier was utilized @@ -58,7 +59,8 @@ abstract contract EVCClient is Storage, Events, Errors { return msg.sender; } - // Authenticate the governor, making sure neither a sub-account nor operator is used. Prohibit the use of control collateral + // Authenticate the governor, making sure neither a sub-account nor operator is used. Prohibit the use of control + // collateral function EVCAuthenticateGovernor() internal view virtual returns (address) { if (msg.sender == address(evc)) { (address onBehalfOfAccount,) = evc.getCurrentOnBehalfOfAccount(address(0)); diff --git a/src/EVault/shared/lib/ProxyUtils.sol b/src/EVault/shared/lib/ProxyUtils.sol index 66c8ce38..a7f1009f 100644 --- a/src/EVault/shared/lib/ProxyUtils.sol +++ b/src/EVault/shared/lib/ProxyUtils.sol @@ -20,7 +20,8 @@ library ProxyUtils { } } - // When `useView` modifier is used, the original caller's address is attached to the call data along with the metadata + // When `useView` modifier is used, the original caller's address is attached to the call data along with the + // metadata function useViewCaller() internal pure returns (address viewCaller) { assembly { viewCaller := shr(96, calldataload(sub(calldatasize(), add(PROXY_METADATA_LENGTH, 20)))) diff --git a/src/EVault/shared/lib/SafeERC20Lib.sol b/src/EVault/shared/lib/SafeERC20Lib.sol index 4a99621f..d1b99af6 100644 --- a/src/EVault/shared/lib/SafeERC20Lib.sol +++ b/src/EVault/shared/lib/SafeERC20Lib.sol @@ -14,7 +14,8 @@ library SafeERC20Lib { error E_TransferFromFailed(bytes errorTransferFrom, bytes errorPermit2); error E_Permit2AmountOverflow(); - // If no code exists under the token address, the function will succeed. EVault ensures this is not the case in `initialize`. + // If no code exists under the token address, the function will succeed. EVault ensures this is not the case in + // `initialize`. function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool, bytes memory) @@ -39,7 +40,8 @@ library SafeERC20Lib { if (!success) revert E_TransferFromFailed(tryData, fallbackData); } - // If no code exists under the token address, the function will succeed. EVault ensures this is not the case in `initialize`. + // If no code exists under the token address, the function will succeed. EVault ensures this is not the case in + // `initialize`. function safeTransfer(IERC20 token, address to, uint256 value) internal { (bool success, bytes memory data) = address(token).call(abi.encodeCall(IERC20.transfer, (to, value))); if (!isEmptyOrTrueReturn(success, data)) RevertBytes.revertBytes(data); diff --git a/src/EVault/shared/types/Assets.sol b/src/EVault/shared/types/Assets.sol index 7cede484..b8a7dcf7 100644 --- a/src/EVault/shared/types/Assets.sol +++ b/src/EVault/shared/types/Assets.sol @@ -34,7 +34,8 @@ library AssetsLib { function toSharesUp(Assets amount, VaultCache memory vaultCache) internal pure returns (Shares) { (uint256 totalAssets, uint256 totalShares) = ConversionHelpers.conversionTotals(vaultCache); unchecked { - return TypesLib.toShares((amount.toUint() * totalShares + (totalAssets - 1)) / totalAssets); // totalAssets >= VIRTUAL_DEPOSIT_AMOUNT > 1 + // totalAssets >= VIRTUAL_DEPOSIT_AMOUNT > 1 + return TypesLib.toShares((amount.toUint() * totalShares + (totalAssets - 1)) / totalAssets); } } diff --git a/src/EVault/shared/types/LTVConfig.sol b/src/EVault/shared/types/LTVConfig.sol index 80207a3d..a1a3c73d 100644 --- a/src/EVault/shared/types/LTVConfig.sol +++ b/src/EVault/shared/types/LTVConfig.sol @@ -32,7 +32,8 @@ library LTVConfigLib { return self.targetTimestamp != 0; } - // Get current LTV of the collateral. When liquidation LTV is lowered, it is ramped down to the target value over a period of time. + // Get current LTV of the collateral. When liquidation LTV is lowered, it is ramped down to the target value over a + // period of time. function getLTV(LTVConfig memory self, bool liquidation) internal view returns (ConfigAmount) { if (!liquidation) { return self.borrowLTV; diff --git a/src/EVault/shared/types/Owed.sol b/src/EVault/shared/types/Owed.sol index cf72a2c8..ff3c5de4 100644 --- a/src/EVault/shared/types/Owed.sol +++ b/src/EVault/shared/types/Owed.sol @@ -9,8 +9,8 @@ import "../Constants.sol"; /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Library for `Owed` custom type -/// @dev The owed type tracks borrowed funds in asset units scaled up by shifting left INTERNAL_DEBT_PRECISION_SHIFT bits. -/// @dev Increased precision allows for accurate interest accounting. +/// @dev The owed type tracks borrowed funds in asset units scaled up by shifting left INTERNAL_DEBT_PRECISION_SHIFT +/// bits. Increased precision allows for accurate interest accounting. library OwedLib { function toUint(Owed self) internal pure returns (uint256) { return Owed.unwrap(self); @@ -23,7 +23,8 @@ library OwedLib { } function isDust(Owed self) internal pure returns (bool) { - return Owed.unwrap(self) < (1 << INTERNAL_DEBT_PRECISION_SHIFT); // less than a minimum representable internal debt amount + // less than a minimum representable internal debt amount + return Owed.unwrap(self) < (1 << INTERNAL_DEBT_PRECISION_SHIFT); } function isZero(Owed self) internal pure returns (bool) { diff --git a/src/EVault/shared/types/Shares.sol b/src/EVault/shared/types/Shares.sol index 60c34f52..93731097 100644 --- a/src/EVault/shared/types/Shares.sol +++ b/src/EVault/shared/types/Shares.sol @@ -29,7 +29,8 @@ library SharesLib { function toAssetsUp(Shares amount, VaultCache memory vaultCache) internal pure returns (Assets) { (uint256 totalAssets, uint256 totalShares) = ConversionHelpers.conversionTotals(vaultCache); unchecked { - return TypesLib.toAssets((amount.toUint() * totalAssets + (totalShares - 1)) / totalShares); // totalShares >= VIRTUAL_DEPOSIT_AMOUNT > 1 + // totalShares >= VIRTUAL_DEPOSIT_AMOUNT > 1 + return TypesLib.toAssets((amount.toUint() * totalAssets + (totalShares - 1)) / totalShares); } } diff --git a/src/EVault/shared/types/Snapshot.sol b/src/EVault/shared/types/Snapshot.sol index 02628aba..04ee9bc4 100644 --- a/src/EVault/shared/types/Snapshot.sol +++ b/src/EVault/shared/types/Snapshot.sol @@ -5,7 +5,8 @@ pragma solidity ^0.8.0; import {Assets} from "./Types.sol"; /// @title Snapshot -/// @notice This struct is used to store a snapshot of the vault's cash and total borrows at the beginning of an operation (or a batch thereof) +/// @notice This struct is used to store a snapshot of the vault's cash and total borrows at the beginning of an +/// operation (or a batch thereof) struct Snapshot { // Packed slot: 14 + 14 + 4 = 32 // vault's cash holdings diff --git a/src/GenericFactory/GenericFactory.sol b/src/GenericFactory/GenericFactory.sol index 40e90233..43946010 100644 --- a/src/GenericFactory/GenericFactory.sol +++ b/src/GenericFactory/GenericFactory.sol @@ -16,7 +16,8 @@ interface IComponent { /// @title GenericFactory /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) -/// @notice The factory allows permissionless creation of upgradeable or non-upgradeable proxy contracts and serves as a beacon for the upgradeable ones +/// @notice The factory allows permissionless creation of upgradeable or non-upgradeable proxy contracts and serves as a +/// beacon for the upgradeable ones contract GenericFactory is MetaProxyDeployer { // Constants @@ -105,8 +106,10 @@ contract GenericFactory is MetaProxyDeployer { } /// @notice A permissionless funtion to deploy new proxies - /// @param desiredImplementation Address of the implementation contract expected to be registered in the factory during proxy creation - /// @param upgradeable If true, the proxy will be an instance of the BeaconProxy. If false, a minimal meta proxy will be deployed + /// @param desiredImplementation Address of the implementation contract expected to be registered in the factory + /// during proxy creation + /// @param upgradeable If true, the proxy will be an instance of the BeaconProxy. If false, a minimal meta proxy + /// will be deployed /// @param trailingData Metadata to be attached to every call passing through the new proxy /// @return The address of the new proxy /// @dev The desired implementation serves as a protection against (unintentional) front-running of upgrades diff --git a/src/GenericFactory/MetaProxyDeployer.sol b/src/GenericFactory/MetaProxyDeployer.sol index 19b10817..e172f27c 100644 --- a/src/GenericFactory/MetaProxyDeployer.sol +++ b/src/GenericFactory/MetaProxyDeployer.sol @@ -6,7 +6,8 @@ pragma solidity ^0.8.0; /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Contract for deploying minimal proxies with metadata, based on EIP-3448. -/// @dev The metadata of the proxies does not include the data length as defined by EIP-3448, saving gas at a cost of supporting variable size data. +/// @dev The metadata of the proxies does not include the data length as defined by EIP-3448, saving gas at a cost of +/// supporting variable size data. contract MetaProxyDeployer { error E_DeploymentFailed(); diff --git a/src/InterestRateModels/IRMLinearKink.sol b/src/InterestRateModels/IRMLinearKink.sol index 8dc56a36..3c5b2357 100644 --- a/src/InterestRateModels/IRMLinearKink.sol +++ b/src/InterestRateModels/IRMLinearKink.sol @@ -7,7 +7,8 @@ import "./IIRM.sol"; /// @title IRMLinearKink /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) -/// @notice Implementation of an interest rate model, where interest rate grows linearly with utilization, and spikes after reaching kink +/// @notice Implementation of an interest rate model, where interest rate grows linearly with utilization, and spikes +/// after reaching kink contract IRMLinearKink is IIRM { /// @notice Base interest rate applied when utilization is equal zero uint256 public immutable baseRate; diff --git a/src/ProtocolConfig/IProtocolConfig.sol b/src/ProtocolConfig/IProtocolConfig.sol index 80ac6060..71963e78 100644 --- a/src/ProtocolConfig/IProtocolConfig.sol +++ b/src/ProtocolConfig/IProtocolConfig.sol @@ -2,38 +2,32 @@ pragma solidity >=0.8.0; -/** - * @title IProtocolConfig - * @custom:security-contact security@euler.xyz - * @author Euler Labs (https://www.eulerlabs.com/) - * @notice Interface of the contract centralizing the protocol's (DAO's) configuration for all the EVault deployments - */ +/// @title IProtocolConfig +/// @custom:security-contact security@euler.xyz +/// @author Euler Labs (https://www.eulerlabs.com/) +/// @notice Interface of the contract centralizing the protocol's (DAO's) configuration for all the EVault deployments interface IProtocolConfig { - /** - * @notice check if a vault's interest fee is valid - * @param vault address of the vault - * @param interestFee an interest fee value to check - * @dev an interest fee is considered valid only when it is greater than or equal the min interest fee and less than or equal the max interest fee - * @dev if a vault has a specific interest fee ranges set by admin, it will be used, otherwise the generic ones will bech checked against - * @return bool true for valid, else false - */ + /// @notice check if a vault's interest fee is valid + /// @param vault address of the vault + /// @param interestFee an interest fee value to check + /// @dev an interest fee is considered valid only when it is greater than or equal the min interest fee and less + /// than or equal the max interest fee + /// @dev if a vault has a specific interest fee ranges set by admin, it will be used, otherwise the generic ones + /// will be checked against + /// @return bool true for valid, else false function isValidInterestFee(address vault, uint16 interestFee) external view returns (bool); - /** - * @notice get protocol fee config for a certain vault - * @param vault address of the vault - * @dev if vault == address(0), the generic config will be returned - * @return address protocol fee receiver - * @return uint16 protocol fee share - */ + /// @notice get protocol fee config for a certain vault + /// @param vault address of the vault + /// @dev if vault == address(0), the generic config will be returned + /// @return address protocol fee receiver + /// @return uint16 protocol fee share function protocolFeeConfig(address vault) external view returns (address, uint16); - /** - * @notice get interest fee ranges for a certain vault - * @param vault address of the vault - * @dev if vault == address(0), the generic ranges will be returned - * @return uint16 min interest fee - * @return uint16 max interest fee - */ + /// @notice get interest fee ranges for a certain vault + /// @param vault address of the vault + /// @dev if vault == address(0), the generic ranges will be returned + /// @return uint16 min interest fee + /// @return uint16 max interest fee function interestFeeRange(address vault) external view returns (uint16, uint16); } diff --git a/src/ProtocolConfig/ProtocolConfig.sol b/src/ProtocolConfig/ProtocolConfig.sol index 113676dc..c0f2ce72 100644 --- a/src/ProtocolConfig/ProtocolConfig.sol +++ b/src/ProtocolConfig/ProtocolConfig.sol @@ -43,55 +43,41 @@ contract ProtocolConfig is IProtocolConfig { /// @dev per-vault configuration of protocol fee config, takes priority over defaults mapping(address vault => ProtocolFeeConfig) internal _protocolFeeConfig; - /** - * @notice Set global default allowed interest fee limits - * @param newMinInterestFee lower limit of allowed interest fee - * @param newMaxInterestFee upper limit of allowed interest fee - */ + /// @notice Set global default allowed interest fee limits + /// @param newMinInterestFee lower limit of allowed interest fee + /// @param newMaxInterestFee upper limit of allowed interest fee event SetInterestFeeRange(uint16 newMinInterestFee, uint16 newMaxInterestFee); - /** - * @notice Set new fee receiver address - * @param newFeeReceiver new fee receiver address - */ + /// @notice Set new fee receiver address + /// @param newFeeReceiver new fee receiver address event SetFeeReceiver(address indexed newFeeReceiver); - /** - * @notice Set allowed interest fee limits override for a vault - * @param vault address of the vault - * @param exists if true a new setting was recorded, if false the override was disabled for the vault - * @param minInterestFee lower limit of allowed interest fee - * @param maxInterestFee upper limit of allowed interest fee - */ + /// @notice Set allowed interest fee limits override for a vault + /// @param vault address of the vault + /// @param exists if true a new setting was recorded, if false the override was disabled for the vault + /// @param minInterestFee lower limit of allowed interest fee + /// @param maxInterestFee upper limit of allowed interest fee event SetVaultInterestFeeRange(address indexed vault, bool exists, uint16 minInterestFee, uint16 maxInterestFee); - /** - * @notice Set interest fee configuration override for a vault - * @param vault address of the vault - * @param exists if true a new setting was recorded, if false the override was disabled for the vault - * @param feeReceiver address to receive protocol fees - * @param protocolFeeShare new protocol fee share - */ + /// @notice Set interest fee configuration override for a vault + /// @param vault address of the vault + /// @param exists if true a new setting was recorded, if false the override was disabled for the vault + /// @param feeReceiver address to receive protocol fees + /// @param protocolFeeShare new protocol fee share event SetFeeConfigSetting(address indexed vault, bool exists, address indexed feeReceiver, uint16 protocolFeeShare); - /** - * @notice Set a new global default protocol fee share - * @param protocolFeeShare previous default protocol fee share - * @param newProtocolFeeShare new default protocol fee share - */ + /// @notice Set a new global default protocol fee share + /// @param protocolFeeShare previous default protocol fee share + /// @param newProtocolFeeShare new default protocol fee share event SetProtocolFeeShare(uint16 protocolFeeShare, uint16 newProtocolFeeShare); - /** - * @notice Transfer admin rights to a new address - * @param newAdmin address of the new admin - */ + /// @notice Transfer admin rights to a new address + /// @param newAdmin address of the new admin event SetAdmin(address indexed newAdmin); - /** - * @dev constructor - * @param admin_ admin's address - * @param feeReceiver_ the address of the protocol fee receiver - */ + /// @dev constructor + /// @param admin_ admin's address + /// @param feeReceiver_ the address of the protocol fee receiver constructor(address admin_, address feeReceiver_) { if (admin_ == address(0)) revert E_InvalidAdmin(); if (feeReceiver_ == address(0)) revert E_InvalidReceiver(); @@ -146,10 +132,8 @@ contract ProtocolConfig is IProtocolConfig { _; } - /** - * @notice set admin address - * @param newAdmin admin's address - */ + /// @notice set admin address + /// @param newAdmin admin's address function setAdmin(address newAdmin) external onlyAdmin { if (newAdmin == address(0)) revert E_InvalidAdmin(); @@ -158,11 +142,9 @@ contract ProtocolConfig is IProtocolConfig { emit SetAdmin(newAdmin); } - /** - * @notice set protocol fee receiver - * @dev can only be called by admin - * @param newReceiver new receiver address - */ + /// @notice set protocol fee receiver + /// @dev can only be called by admin + /// @param newReceiver new receiver address function setFeeReceiver(address newReceiver) external onlyAdmin { if (newReceiver == address(0)) revert E_InvalidReceiver(); @@ -171,11 +153,9 @@ contract ProtocolConfig is IProtocolConfig { emit SetFeeReceiver(newReceiver); } - /** - * @notice set protocol fee share - * @dev can only be called by admin - * @param newProtocolFeeShare new protocol fee share - */ + /// @notice set protocol fee share + /// @dev can only be called by admin + /// @param newProtocolFeeShare new protocol fee share function setProtocolFeeShare(uint16 newProtocolFeeShare) external onlyAdmin { if (newProtocolFeeShare > CONFIG_SCALE) revert E_InvalidConfigValue(); @@ -184,12 +164,10 @@ contract ProtocolConfig is IProtocolConfig { protocolFeeShare = newProtocolFeeShare; } - /** - * @notice set generic min interest fee - * @dev can only be called by admin - * @param minInterestFee_ new min interest fee - * @param maxInterestFee_ new max interest fee - */ + /// @notice set generic min interest fee + /// @dev can only be called by admin + /// @param minInterestFee_ new min interest fee + /// @param maxInterestFee_ new max interest fee function setInterestFeeRange(uint16 minInterestFee_, uint16 maxInterestFee_) external onlyAdmin { if (maxInterestFee_ > CONFIG_SCALE || minInterestFee_ > maxInterestFee_) revert E_InvalidConfigValue(); @@ -199,14 +177,12 @@ contract ProtocolConfig is IProtocolConfig { emit SetInterestFeeRange(minInterestFee_, maxInterestFee_); } - /** - * @notice set interest fee range for specific vault - * @dev can only be called by admin - * @param vault vault's address - * @param exists_ a boolean to set or unset the ranges. When false, the generic ranges will be used for the vault - * @param minInterestFee_ min interest fee - * @param maxInterestFee_ max interest fee - */ + /// @notice set interest fee range for specific vault + /// @dev can only be called by admin + /// @param vault vault's address + /// @param exists_ a boolean to set or unset the ranges. When false, the generic ranges will be used for the vault + /// @param minInterestFee_ min interest fee + /// @param maxInterestFee_ max interest fee function setVaultInterestFeeRange(address vault, bool exists_, uint16 minInterestFee_, uint16 maxInterestFee_) external onlyAdmin @@ -220,14 +196,12 @@ contract ProtocolConfig is IProtocolConfig { emit SetVaultInterestFeeRange(vault, exists_, minInterestFee_, maxInterestFee_); } - /** - * @notice set protocol fee config for specific vault - * @dev can only be called by admin - * @param vault vault's address - * @param exists_ a boolean to set or unset the config. When false, the generic config will be used for the vault - * @param feeReceiver_ fee receiver address - * @param protocolFeeShare_ fee share - */ + /// @notice set protocol fee config for specific vault + /// @dev can only be called by admin + /// @param vault vault's address + /// @param exists_ a boolean to set or unset the config. When false, the generic config will be used for the vault + /// @param feeReceiver_ fee receiver address + /// @param protocolFeeShare_ fee share function setVaultFeeConfig(address vault, bool exists_, address feeReceiver_, uint16 protocolFeeShare_) external onlyAdmin diff --git a/src/SequenceRegistry/SequenceRegistry.sol b/src/SequenceRegistry/SequenceRegistry.sol index 1a9aab20..2a9cca1c 100644 --- a/src/SequenceRegistry/SequenceRegistry.sol +++ b/src/SequenceRegistry/SequenceRegistry.sol @@ -7,8 +7,10 @@ import {ISequenceRegistry} from "../interfaces/ISequenceRegistry.sol"; /// @title SequenceRegistry /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) -/// @notice This contract maintains sequence counters associated with opaque designator strings. Each counter starts at 1. -/// @dev Anybody can reserve a sequence ID. The only guarantee provided is that no two reservations for the same designator will get the same ID. +/// @notice This contract maintains sequence counters associated with opaque designator strings. +/// Each counter starts at 1. +/// @dev Anybody can reserve a sequence ID. The only guarantee provided is that no two reservations for the same +/// designator will get the same ID. contract SequenceRegistry is ISequenceRegistry { /// @dev Each designator maps to the previous sequence ID issued, or 0 if none were ever issued. mapping(string designator => uint256 lastSeqId) public counters; diff --git a/src/Synths/ESynth.sol b/src/Synths/ESynth.sol index 9d5d6d0b..5ab4e219 100644 --- a/src/Synths/ESynth.sol +++ b/src/Synths/ESynth.sol @@ -69,7 +69,8 @@ contract ESynth is ERC20Collateral, Ownable { _mint(account, amount); } - /// @notice Burns a certain amount of tokens from the accounts balance. Requires the account, except the owner to have an allowance for the sender. + /// @notice Burns a certain amount of tokens from the accounts balance. Requires the account, except the owner to + /// have an allowance for the sender. /// @param burnFrom The account to burn the tokens from. /// @param amount The amount of tokens to burn. function burn(address burnFrom, uint256 amount) external nonReentrant { @@ -80,14 +81,16 @@ contract ESynth is ERC20Collateral, Ownable { return; } - // The allowance check should be performed if the spender is not the account with the exception of the owner burning from this contract. + // The allowance check should be performed if the spender is not the account with the exception of the owner + // burning from this contract. if (burnFrom != sender && !(burnFrom == address(this) && sender == owner())) { _spendAllowance(burnFrom, sender, amount); } // If burning more than minted, reset minted to 0 unchecked { - minterCache.minted = minterCache.minted > amount ? minterCache.minted - uint128(amount) : 0; // down-casting is safe because amount < minted <= max uint128 + // down-casting is safe because amount < minted <= max uint128 + minterCache.minted = minterCache.minted > amount ? minterCache.minted - uint128(amount) : 0; } minters[sender] = minterCache; diff --git a/src/Synths/EulerSavingsRate.sol b/src/Synths/EulerSavingsRate.sol index 1e55d029..30cf87bf 100644 --- a/src/Synths/EulerSavingsRate.sol +++ b/src/Synths/EulerSavingsRate.sol @@ -12,10 +12,10 @@ import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol"; /// @title EulerSavingsRate /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) -/// @notice EulerSavingsRate is a ERC4626-compatible vault which allows users to deposit the underlying asset and receive -/// interest in the form of the same underlying asset. On withdraw, redeem and transfers, the account status checks must be -/// requested for the account which health might be negatively affected. Thanks to that, the shares of the EulerSavingsRate -/// vault might be used as collateral by other EVC-compatible vaults. +/// @notice EulerSavingsRate is a ERC4626-compatible vault which allows users to deposit the underlying asset and +/// receive interest in the form of the same underlying asset. On withdraw, redeem and transfers, the account status +/// checks must be requested for the account which health might be negatively affected. Thanks to that, the shares of +/// the EulerSavingsRate vault might be used as collateral by other EVC-compatible vaults. /// @dev Do NOT use with fee on transfer tokens /// @dev Do NOT use with rebasing tokens contract EulerSavingsRate is EVCUtil, ERC4626 { diff --git a/src/Synths/PegStabilityModule.sol b/src/Synths/PegStabilityModule.sol index 560c636a..adc64b18 100644 --- a/src/Synths/PegStabilityModule.sol +++ b/src/Synths/PegStabilityModule.sol @@ -12,10 +12,10 @@ import {ESynth} from "./ESynth.sol"; /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice The PegStabilityModule is granted minting rights on the ESynth and must allow slippage-free conversion from /// and to the underlying asset as per configured conversionPrice. On deployment, the fee for swaps to synthetic asset -/// and to underlying asset are defined. These fees must accrue to the PegStabilityModule contract and can not be withdrawn, -/// serving as a permanent reserve to support the peg. Swapping to the synthetic asset is possible up to the minting cap -/// granted for the PegStabilityModule in the ESynth. Swapping to the underlying asset is possible up to the amount of -/// the underlying asset held by the PegStabilityModule. +/// and to underlying asset are defined. These fees must accrue to the PegStabilityModule contract and can not be +/// withdrawn, serving as a permanent reserve to support the peg. Swapping to the synthetic asset is possible up to the +/// minting cap granted for the PegStabilityModule in the ESynth. Swapping to the underlying asset is possible up to the +/// amount of the underlying asset held by the PegStabilityModule. contract PegStabilityModule is EVCUtil { using SafeERC20 for IERC20; @@ -37,7 +37,8 @@ contract PegStabilityModule is EVCUtil { /// @param _underlying The address of the underlying asset. /// @param toUnderlyingFeeBPS The fee for swapping to the underlying asset in basis points. eg: 100 = 1% /// @param toSynthFeeBPS The fee for swapping to the synthetic asset in basis points. eg: 100 = 1% - /// @param _conversionPrice The conversion price between the synthetic and underlying asset. eg: 1e18 = 1 SYNTH == 1 UNDERLYING, 0.01e18 = 1 SYNTH == 0.01 UNDERLYING + /// @param _conversionPrice The conversion price between the synthetic and underlying asset. + /// eg: 1e18 = 1 SYNTH == 1 UNDERLYING, 0.01e18 = 1 SYNTH == 0.01 UNDERLYING constructor( address _evc, address _synth, diff --git a/src/interfaces/IPriceOracle.sol b/src/interfaces/IPriceOracle.sol index 986d6459..6f86edd2 100644 --- a/src/interfaces/IPriceOracle.sol +++ b/src/interfaces/IPriceOracle.sol @@ -11,7 +11,8 @@ interface IPriceOracle { /// @return The name of the oracle. function name() external view returns (string memory); - /// @notice One-sided price: How much quote token you would get for inAmount of base token, assuming no price spread. + /// @notice One-sided price: How much quote token you would get for inAmount of base token, assuming no price + /// spread. /// @param inAmount The amount of `base` to convert. /// @param base The token that is being priced. /// @param quote The token that is the unit of account. diff --git a/test/invariants/InvariantsSpec.t.sol b/test/invariants/InvariantsSpec.t.sol index 4f6febc6..cab7c58f 100644 --- a/test/invariants/InvariantsSpec.t.sol +++ b/test/invariants/InvariantsSpec.t.sol @@ -107,13 +107,15 @@ abstract contract InvariantsSpec { string constant BM_INVARIANT_B = "BM_INVARIANT_B: totalBorrowed = sum of all user debt"; - string constant BM_INVARIANT_C = "BM_INVARIANT_C: sum of all user debt == 0 <=> totalBorrowed == 0"; // TODO: Discarded + // TODO: Discarded + string constant BM_INVARIANT_C = "BM_INVARIANT_C: sum of all user debt == 0 <=> totalBorrowed == 0"; string constant BM_INVARIANT_D = "BM_INVARIANT_D: User liability should always decrease after repayment"; string constant BM_INVARIANT_E = "BM_INVARIANT_E: Unhealthy users can not borrow"; - string constant BM_INVARIANT_F = "BM_INVARIANT_F: If theres at least one borrow, the asset.balanceOf(vault) > 0"; //TODO: Discarded + // TODO: Discarded + string constant BM_INVARIANT_F = "BM_INVARIANT_F: If theres at least one borrow, the asset.balanceOf(vault) > 0"; string constant BM_INVARIANT_G = "BM_INVARIANT_G: a user should always be able to withdraw all if there is no outstanding debt"; diff --git a/test/invariants/handlers/modules/BorrowingModuleHandler.t.sol b/test/invariants/handlers/modules/BorrowingModuleHandler.t.sol index 2a2930af..8bed2e8c 100644 --- a/test/invariants/handlers/modules/BorrowingModuleHandler.t.sol +++ b/test/invariants/handlers/modules/BorrowingModuleHandler.t.sol @@ -174,7 +174,8 @@ contract BorrowingModuleHandler is BaseHandler { // if (success) { // (success, returnData) = - // actor.proxy(address(eTST), abi.encodeWithSelector(IBorrowing.borrow.selector, amount, address(actor))); + // actor.proxy(address(eTST), abi.encodeWithSelector(IBorrowing.borrow.selector, amount, + // address(actor))); // } // if (success) { diff --git a/test/invariants/invariants/VaultModuleInvariants.t.sol b/test/invariants/invariants/VaultModuleInvariants.t.sol index aab24f7f..75598edd 100644 --- a/test/invariants/invariants/VaultModuleInvariants.t.sol +++ b/test/invariants/invariants/VaultModuleInvariants.t.sol @@ -134,11 +134,12 @@ abstract contract VaultModuleInvariants is HandlerAggregator { // DISCARDED // /////////////////////////////////////////////////////////////////////////////////////////////// - /* function assert_VaultSimple_invariantA(address _vault) internal { - uint256 totalAssets = eTST.totalAssets(); - - assertEq(totalAssets, ghost_sumBalances[_vault], string.concat("VaultSimple_invariantA: ", vaultNames[_vault])); - } */ + //function assert_VaultSimple_invariantA(address _vault) internal { + // uint256 totalAssets = eTST.totalAssets(); + // + // assertEq(totalAssets, ghost_sumBalances[_vault], string.concat("VaultSimple_invariantA: ", + // vaultNames[_vault])); + //} /////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // diff --git a/test/unit/esr/ESR.Fuzz.t.sol b/test/unit/esr/ESR.Fuzz.t.sol index 62d57e7b..9b3e363f 100644 --- a/test/unit/esr/ESR.Fuzz.t.sol +++ b/test/unit/esr/ESR.Fuzz.t.sol @@ -38,7 +38,8 @@ contract ESRFuzzTest is ESRTest { // this tests shows that when you have a very small deposit and a very large interestAmount minted to the contract function testFuzz_gulp_under_uint168(uint256 interestAmount, uint256 depositAmount) public { depositAmount = bound(depositAmount, 0, type(uint112).max); - interestAmount = bound(interestAmount, 0, type(uint256).max - depositAmount); // this makes sure that the mint won't cause overflow + interestAmount = bound(interestAmount, 0, type(uint256).max - depositAmount); // this makes sure that the mint + // won't cause overflow asset.mint(address(esr), interestAmount); doDeposit(user, depositAmount); diff --git a/test/unit/evault/modules/Liquidation/full.t.sol b/test/unit/evault/modules/Liquidation/full.t.sol index 49be21af..66c1d209 100644 --- a/test/unit/evault/modules/Liquidation/full.t.sol +++ b/test/unit/evault/modules/Liquidation/full.t.sol @@ -340,7 +340,8 @@ contract VaultLiquidation_Test is EVaultTestBase { (uint256 maxRepay,) = eTST.checkLiquidation(lender, borrower, address(eTST2)); - // set the liquidator to be operator of the violator in order to be able act on violator's account and defer its liquidity check + // set the liquidator to be operator of the violator in order to be able act on violator's account and defer its + // liquidity check evc.setAccountOperator(borrower, lender, true); IEVC.BatchItem[] memory items = new IEVC.BatchItem[](2); diff --git a/test/unit/evault/modules/Vault/balancesWithInterest.t.sol b/test/unit/evault/modules/Vault/balancesWithInterest.t.sol index 3c292db9..4b0290c4 100644 --- a/test/unit/evault/modules/Vault/balancesWithInterest.t.sol +++ b/test/unit/evault/modules/Vault/balancesWithInterest.t.sol @@ -71,7 +71,8 @@ contract VaultTest_BalancesWithInterest is EVaultTestBase { assertEq(assetTST.balanceOf(user3), 1e18); assertEq(eTST.debtOf(user3), 1e18); - // Go ahead 1 year (+ 1 second because I did it this way by accident at first, don't want to bother redoing calculations below) + // Go ahead 1 year (+ 1 second because I did it this way by accident at first, don't want to bother redoing + // calculations below) skip(365 days + 1); startHoax(address(this)); eTST.setInterestRateModel(address(new IRMTestZero())); @@ -146,7 +147,8 @@ contract VaultTest_BalancesWithInterest is EVaultTestBase { // eVault balanceOf unchanged: assertEq(eTST.balanceOf(user1), 1e18); - // eVault maxWithdraw increases. 10% less than the amount owed, because of reserve fee. Matches "untouchedSupplyAPY" above: + // eVault maxWithdraw increases. 10% less than the amount owed, because of reserve fee. Matches + // "untouchedSupplyAPY" above: assertApproxEqAbs(eTST.convertToAssets(1e18), 1.094719911470713189e18, 0.00000001e18); // Conversion methods @@ -193,7 +195,8 @@ contract VaultTest_BalancesWithInterest is EVaultTestBase { // Same as in basic case: assertEq(eTST.debtOf(user3), 1.10524434607857021e18); - // eVault maxWithdraw increases. 10% less than the amount owed, because of reserve fee. Matches untouchedSupplyAPY above: + // eVault maxWithdraw increases. 10% less than the amount owed, because of reserve fee. Matches + // untouchedSupplyAPY above: assertEq(eTST.convertToAssets(eTST.balanceOf(user1)), 1.047359955735333033e18); assertEq(eTST.convertToAssets(eTST.balanceOf(user2)), 1.047359955735333033e18); diff --git a/test/unit/evault/modules/Vault/caps.t.sol b/test/unit/evault/modules/Vault/caps.t.sol index 81ab4d2d..da59ac89 100644 --- a/test/unit/evault/modules/Vault/caps.t.sol +++ b/test/unit/evault/modules/Vault/caps.t.sol @@ -528,7 +528,8 @@ contract VaultTest_Caps is EVaultTestBase { vm.expectRevert(Errors.E_SupplyCapExceeded.selector); eTST.deposit(2e18, user1); - // Lower supply cap. Withdrawal still works, even though it's not enough withdrawn to solve the policy violation: + // Lower supply cap. Withdrawal still works, even though it's not enough withdrawn to solve the policy + // violation: startHoax(address(this)); eTST.setCaps(32018, 0); assertEq(eTST.maxDeposit(user1), 0); @@ -564,7 +565,8 @@ contract VaultTest_Caps is EVaultTestBase { vm.expectRevert(Errors.E_SupplyCapExceeded.selector); eTST.mint(2e18, user1); - // Lower supply cap. Withdrawal still works, even though it's not enough withdrawn to solve the policy violation: + // Lower supply cap. Withdrawal still works, even though it's not enough withdrawn to solve the policy + // violation: startHoax(address(this)); eTST.setCaps(32018, 0); startHoax(user1);