Platform: Code4rena
Start Date: 04/03/2024
Pot Size: $88,500 USDC
Total HM: 31
Participants: 105
Period: 11 days
Judge: ronnyx2017
Total Solo HM: 7
Id: 342
League: ETH
Rank: 47/105
Findings: 2
Award: $193.61
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: t4sk
Also found by: Bauchibred, hunter_w3b, lanrebayode77
110.5616 USDC - $110.56
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Oracle.sol#L272-L326
priceX96
and verifyPriceX96
variables are used wrong V3Oracle::_getReferenceTokenPriceX96
The issue occurs when both usesChainlink
and usesTWAP
are true. In this case, the priceX96
variable is set to the value of chainlinkPriceX96
, and verifyPriceX96
is set to the value of twapPriceX96
or vice versa, depending on the oracle mode. However, when the function checks the difference between priceX96
and verifyPriceX96
, it does not take into account which variable was set to the chainlink price and which was set to the TWAP price.
This can result in a significant price difference between the two oracle sources, which can cause the function to revert or return incorrect price data.
For example, if the chainlink price is much higher than the TWAP price, and priceX96
is set to the chainlink price and verifyPriceX96
is set to the TWAP price, the function will revert because the difference between the two prices is too high. However, if the opposite is true and priceX96
is set to the TWAP price and verifyPriceX96
is set to the chainlink price, the function will not revert and will return incorrect price data.
function _getReferenceTokenPriceX96(address token, uint256 cachedChainlinkReferencePriceX96) internal view returns (uint256 priceX96, uint256 chainlinkReferencePriceX96) { if (token == referenceToken) { return (Q96, chainlinkReferencePriceX96); } TokenConfig memory feedConfig = feedConfigs[token]; if (feedConfig.mode == Mode.NOT_SET) { revert NotConfigured(); } uint256 verifyPriceX96; bool usesChainlink = ( feedConfig.mode == Mode.CHAINLINK_TWAP_VERIFY || feedConfig.mode == Mode.TWAP_CHAINLINK_VERIFY || feedConfig.mode == Mode.CHAINLINK ); bool usesTWAP = ( feedConfig.mode == Mode.CHAINLINK_TWAP_VERIFY || feedConfig.mode == Mode.TWAP_CHAINLINK_VERIFY || feedConfig.mode == Mode.TWAP ); if (usesChainlink) { uint256 chainlinkPriceX96 = _getChainlinkPriceX96(token); chainlinkReferencePriceX96 = cachedChainlinkReferencePriceX96 == 0 ? _getChainlinkPriceX96(referenceToken) : cachedChainlinkReferencePriceX96; chainlinkPriceX96 = (10 ** referenceTokenDecimals) * chainlinkPriceX96 * Q96 / chainlinkReferencePriceX96 / (10 ** feedConfig.tokenDecimals); if (feedConfig.mode == Mode.TWAP_CHAINLINK_VERIFY) { verifyPriceX96 = chainlinkPriceX96; } else { priceX96 = chainlinkPriceX96; } } if (usesTWAP) { uint256 twapPriceX96 = _getTWAPPriceX96(feedConfig); if (feedConfig.mode == Mode.CHAINLINK_TWAP_VERIFY) { verifyPriceX96 = twapPriceX96; } else { priceX96 = twapPriceX96; } } if (feedConfig.mode == Mode.CHAINLINK_TWAP_VERIFY || feedConfig.mode == Mode.TWAP_CHAINLINK_VERIFY) { _requireMaxDifference(priceX96, verifyPriceX96, feedConfig.maxDifference); } }
Manual Review
The function should check which oracle source is being used for priceX96
and verifyPriceX96
and adjust the difference calculation accordingly. For example:
if (usesChainlink && usesTWAP) { if (feedConfig.mode == Mode.CHAINLINK_TWAP_VERIFY) { priceX96 = chainlinkPriceX96; verifyPriceX96 = twapPriceX96; } else if (feedConfig.mode == Mode.TWAP_CHAINLINK_VERIFY) { priceX96 = twapPriceX96; verifyPriceX96 = chainlinkPriceX96; } uint256 priceDifferenceX96 = priceX96 > verifyPriceX96 ? priceX96 - verifyPriceX96 : verifyPriceX96 - priceX96; _requireMaxDifference(priceDifferenceX96, feedConfig.maxDifference); }
This ensures that the difference calculation is always correct, regardless of which oracle source is being used for priceX96
and verifyPriceX96
.
Math
#0 - c4-pre-sort
2024-03-22T08:41:25Z
0xEVom marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-03-23T16:29:24Z
0xEVom marked the issue as duplicate of #10
#2 - 0xEVom
2024-03-23T16:29:39Z
Fails to articulate the root cause.
#3 - c4-judge
2024-04-01T15:12:05Z
jhsagd76 marked the issue as partial-50
#4 - c4-judge
2024-04-01T15:42:20Z
jhsagd76 changed the severity to 2 (Med Risk)
๐ Selected for report: 14si2o_Flint
Also found by: Bauchibred, K42, Sathish9098, hunter_w3b, invitedtea, popeye, yongskiws
83.0533 USDC - $83.05
Revert Lend
ContestOverview:
Revert Lend is a decentralized lending protocol that allows liquidity providers on Uniswap v3 to collateralize their liquidity provider (LP) positions and borrow loans.
Key Features:
How it Works:
Supplying Assets:
Borrowing Assets:
Collateralized Positions:
Use Cases:
src
V3Vault.sol: This contract is a Revert Lend Vault for token lending and borrowing using Uniswap V3 LP positions as collateral. It is an implementation of the ERC4626
Vault Standard and is itself an ERC20 token representing shares of the total lending pool. The V3Vault
contract manages one ERC20 asset for lending and borrowing, but collateral positions can be composed of any two tokens configured with a collateralFactor
greater than 0.
key features:
Lending
: Users can deposit assets into the vault and earn interest. The interest rate is determined by the InterestRateModel
contract and can vary based on the utilization of the asset.Borrowing
: Users can borrow assets from the vault by providing collateral. The amount they can borrow is determined by the collateralFactor
and the value of their collateral.Liquidation
: If the value of a user's collateral falls below a certain threshold, their position can be liquidated
.Fees
: The vault charges fees for borrowing
and liquidation
. These fees are used to cover the costs of the protocol and to incentivize liquidators.Collateral Management
: The contract uses Uniswap V3 LP
positions as collateral
. These positions can be added, removed, and transformed using the NonfungiblePositionManager contract.Core Logic: The core logic of this contract revolves around the creation
, management
, and liquidation of loans
that are collateralized
with Uniswap V3 LP tokens. The contract allows users to deposit Uniswap V3 LP tokens as collateral and borrow a single ERC20 token (referred to as the "asset") against that collateral. The loans are represented as ERC721 tokens, with each token representing a unique loan position.
V3Oracle.sol: The V3Oracle
contract serve as an oracle for valuing Uniswap v3 LP
positions in a specified token. It utilizes both Chainlink
and Uniswap v3 TWAP
to derive accurate price estimates.
Key Features:
Implementation Details:
setTokenConfig
function allows the owner to set or update these configurations.getValue
function calculates the value of a Uniswap v3 LP position in a specified token._getReferenceTokenPriceX96
function to obtain the price of the reference token (typically USDC or another stablecoin) using both Chainlink and TWAP._getReferenceTokenPriceX96
function uses both Chainlink and TWAP to derive the reference token price.CHAINLINK_TWAP_VERIFY
or TWAP_CHAINLINK_VERIFY
, it verifies the price against the secondary oracle.emergencyAdmin
address can be set by the owner.Additional Features:
Max Pool Price Difference: Allows the owner to set a maximum allowable difference between the pool price and the derived oracle pool price.
Breakdown of Position: Provides a method to obtain a breakdown of a Uniswap v3 position, including tokens, fee tier, liquidity, and current amounts.
InterestRateModel.sol: The InterestRateModel
contract calculate interest rates
for both borrowing
and supplying assets
in a lending protocol.
Key Features:
borrow
and supply interest rates
based on the current cash and debt levels in the protocol.Functionality:
getRatesPerSecondX96
function calculates the borrow and supply interest rates per second, multiplied by Q96
.getUtilizationRateX96
function.getUtilizationRateX96
function calculates the utilization rate by dividing the current debt by the sum of current cash and debt.automators
AutoExit.sol: The AutoExit.sol
contract is automate the execution of limit orders or stop-loss
orders for Uniswap v3 positions. It allows a designated operator to execute these orders when certain conditions are met, such as reaching a specific tick value.
Key Features:
Functionality:
Order Configuration:
configToken
function.Order Execution:
execute
function is used to execute orders.Reward System:
Additional Features:
Automator.sol: The Automator
contract is a base contract for automating
the execution of orders for Uniswap v3 positions
. It provides a framework for managing operators, vaults, and withdrawal addresses, as well as configuring TWAP
parameters for order validation.
Key Features:
Functionality:
setOperator
and setVault
functions allow the owner to control which addresses can execute orders and withdraw funds.withdrawBalances
function allows the withdrawer to withdraw token balances, and the withdrawETH
function allows the withdrawer to withdraw ETH balances._validateSwap
function checks if a swap can be executed based on the current pool price and oracle parameters. It calculates the minimum amount out and reverts if the price difference exceeds the maximum allowed._hasMaxTWAPTickDifference
function checks if the current tick is within a specified range of the TWAP tick, which is calculated from the pool's history._getTWAPTick
function attempts to retrieve the TWAP tick from the pool's history.Additional Features:
_decreaseFullLiquidityAndCollect
, to remove full liquidity from a position and collect any accrued fees._transferToken
function to transfer tokens or unwrap WETH and send ETH.transformers
AutoCompound.sol: The AutoCompound
contract allows operators to execute auto-compounding
operations, which involve collecting fees, swapping tokens, and reinvesting the proceeds back into the position.
Key Features:
Implementation Details:
Auto-Compounding:
execute
function is the entry point for auto-compounding. It collects fees, performs a swap if necessary, and reinvests the proceeds back into the position.ExecuteParams
struct defines the parameters for auto-compounding, including the token ID, swap direction, and swap amount.ExecuteState
struct is used to track the state of the auto-compounding process.Swap Execution:
_poolSwap
function is used to execute swaps based on TWAP oracle parameters._validateSwap
function from the Automator
contract is used to ensure that the swap can be executed within the specified price range.Reward Management:
totalRewardX64
variable represents the total reward percentage for auto-compounding.setReward
function allows the owner to adjust the reward percentage.Additional Features:
AutoRange.sol: Allows operators to execute range adjustments, which involve creating a new position with a different range while maintaining the same liquidity
and fees
.
Key Features:
Functionality:
execute
function is the entry point for range adjustment. It collects fees, performs a swap if necessary, and creates a new position with the adjusted range.ExecuteParams
struct defines the parameters for range adjustment, including the token ID, swap direction, swap amount, and reward percentage.ExecuteState
struct is used to track the state of the range adjustment process._routerSwap
function is used to execute swaps based on TWAP oracle parameters._validateSwap
function from the Automator
contract is used to ensure that the swap can be executed within the specified price range.PositionConfig
struct defines the configuration parameters for positions.configToken
function allows operators to configure positions with specific range parameters and reward settings.LeverageTransformer.sol: This contract to add functionality for leveraging
and deleveraging Uniswap v3 positions
directly within a single transaction. It allows operators to execute leverage operations, which involve borrowing tokens to increase position size, and deleverage operations, which involve reducing position size and repaying borrowed tokens.
Key Features:
Functionality:
leverageUp
function is the entry point for leverage up operations. It borrows tokens from the vault, swaps them to the desired tokens, and adds liquidity to the position.LeverageUpParams
struct defines the parameters for leverage up operations, including the token ID, borrow amount, swap parameters, and recipient for leftover tokens.leverageDown
function is the entry point for leverage down operations. It removes liquidity from the position, collects fees, swaps tokens to the desired token, and repays the borrowed tokens to the vault.LeverageDownParams
struct defines the parameters for leverage down operations, including the token ID, liquidity to remove, fee collection parameters, swap parameters, and recipient for leftover tokens._routerSwap
function from the Swapper
contract is used to execute swaps based on TWAP oracle parameters._validateSwap
function from the Swapper
contract is used to ensure that the swap can be executed within the specified price range.V3Utils.sol: This contract V3Utils
is a utility contract for Uniswap V3 positions. It's a stateless and ownerless contract, designed to facilitate various operations on Uniswap V3 positions, such as swapping, adding or removing liquidity, and collecting fees. It does not hold any ERC20 or NFTs.
Key Features:
Swap and Mint: This feature allows users to swap tokens and mint new NFTs representing Uniswap V3 positions. It supports both ERC20 and Ether swaps, and can handle multiple swaps in one transaction.
Increase Liquidity: Users can increase liquidity in their existing Uniswap V3 positions. The contract supports both ERC20 and Ether deposits, and can handle multiple deposits in one transaction.
Collect Fees: Users can collect fees accrued from their Uniswap V3 positions. The contract ensures that the collected fees match the expected amount.
Permit2 Integration: The contract uses the Permit2 contract for handling permissions. This allows users to grant permissions to the contract to perform actions on their behalf.
Core Logic: The contract revolves around the execute
function and the various swap
functions. The execute
function performs a set of instructions on a provided NFT, while the swap
functions handle swapping tokens and adding liquidity. The contract also includes various helper functions for handling permissions, preparing token transfers, and checking token balances.
Additional Features:
The contract includes various additional features, such as support for Ether swaps and deposits, handling of fee-on-transfer tokens, and unwrapping of WETH. The contract also emits various events, such as CompoundFees
, ChangeRange
, WithdrawAndCollectAndSwap
, and SwapAndMint
, to log its actions.
utils
FlashloanLiquidator.sol: This contract, FlashloanLiquidator
, is a helper contract that allows atomic liquidation and needed swaps by using Uniswap V3 Flashloan.
Key Features:
Flashloan Liquidation: This feature allows users to liquidate loans using Uniswap V3 Flashloans. The contract supports both ERC20 and Ether loans, and can handle multiple swaps in one transaction.
Swapper Integration: The contract uses the Swapper contract for handling token swaps. This allows users to swap tokens as part of the liquidation process.
Core Logic: The contract core logic is liquidate
function and the uniswapV3FlashCallback
function. The liquidate
function initiates the liquidation process by requesting a flashloan from Uniswap V3. The uniswapV3FlashCallback
function is then called by Uniswap V3 to perform the actual liquidation and swaps. The contract also includes various helper functions for handling permissions, preparing token transfers, and checking token balances.
Swapper.sol: This abstract contract that provides base functionality to do swaps with different routing protocols.
Key Features:
Swap Functionality: This feature allows users to swap tokens using different routing protocols. The contract supports both ERC20 and Ether swaps, and can handle multiple swaps in one transaction.
Router Integration: The contract uses different routing protocols, such as 0x Exchange Proxy and Uniswap Universal Router, for handling token swaps. This allows users to swap tokens using the most efficient route.
Additional Features: The contract includes various additional features, such as support for Ether swaps, handling of fee-on-transfer tokens, and unwrapping of WETH. The contract also emits various events, such as Swap
, to log its actions.
V3Vault.sol
Collateral factors for tokens must be less than or equal to MAX_COLLATERAL_FACTOR_X32
.
Reserve factor must be less than Q32.
Liquidation penalty must be between MIN_LIQUIDATION_PENALTY_X32
and MAX_LIQUIDATION_PENALTY_X32
.
Reserve protection factor must be greater than or equal to MIN_RESERVE_PROTECTION_FACTOR_X32
.
Daily increase limits must be less than the respective MAX_DAILY_LIMIT
constants.
Interest rates returned from the rate model must be non-negative.
Total debt shares can never exceed the theoretical backing from collateral.
Collateral value used for any single token cannot exceed its configured limit factor.
Withdrawals cannot exceed a user's balance, pending approvals, or total availability.
Repays
and borrows
cannot cause a loan's debt to become negative.
Liquidation penalties must leave sufficient value for the liquidator.
Reserve amounts withdrawn respect the protection factor threshold.
Owned token counts are consistent with the mapping indexes.
V3Oracle.sol
feedConfigs
mapping should only contain valid configurations:
TokenConfig.feed
should be a valid Chainlink aggregator address.TokenConfig.maxFeedAge
should be a reasonable value to ensure the Chainlink price is not stale.TokenConfig.pool
should be a valid Uniswap V3 pool address with the correct token pair.TokenConfig.twapSeconds
should be a reasonable value for the TWAP calculation.TokenConfig.mode
should be a valid mode (not NOT_SET
).TokenConfig.maxDifference
should be greater than or equal to MIN_PRICE_DIFFERENCE
.The maxPoolPriceDifference
should always be greater than or equal to MIN_PRICE_DIFFERENCE
.
The emergencyAdmin
address should be a valid Ethereum address when set.
The referenceToken
and chainlinkReferenceToken
should be valid ERC20 token addresses.
For any token configured with Mode.CHAINLINK_TWAP_VERIFY
or Mode.TWAP_CHAINLINK_VERIFY
, the price difference between the Chainlink oracle and the TWAP oracle should not exceed TokenConfig.maxDifference
.
The getValue
function should revert if:
feedConfigs
mapping.maxPoolPriceDifference
.The getPositionBreakdown
function should return the correct token0, token1, fee tier, liquidity, current amounts, and uncollected fees for a valid position tokenId.
The _getReferenceTokenPriceX96
function should return the correct price for the given token based on the configured mode, and the Chainlink and TWAP oracles should be used as specified by the mode.
The _getTWAPPriceX96
function should correctly calculate the TWAP price for the token based on the configured pool and TWAP period.
The _getChainlinkPriceX96
function should correctly retrieve the latest Chainlink price for the token and revert if the price is stale or invalid.
Automator.sol
operators[address]
and vaults[address]
should only be true
for valid addresses controlled by the owner.TWAPSeconds
should always be greater than or equal to MIN_TWAP_SECONDS
(60).maxTWAPTickDifference
should always be less than or equal to MAX_TWAP_TICK_DIFFERENCE
(200).tokenId
passed to _validateOwner
should be owned by either msg.sender
or the vault
address.amountOutMin
returned by _validateSwap
should be less than or equal to the actual amount out calculated by the swap.currentTick
returned by _validateSwap
should match the current tick of the Uniswap V3 pool.sqrtPriceX96
and priceX96
returned by _validateSwap
should match the current price in the Uniswap V3 pool.twapTick
returned by _getTWAPTick
should be within the maxTWAPTickDifference
range of the currentTick
.feeAmount0
and feeAmount1
returned by _decreaseFullLiquidityAndCollect
should be less than or equal to the sum of amount0
and amount1
.amount0
and amount1
returned by _decreaseFullLiquidityAndCollect
should match the amounts actually collected from the Uniswap V3 pool.ETH
balance of the contract should only change as a result of weth.withdraw
or to.call
in _transferToken
.AutoCompound.sol
Invariant 1: The total reward is always less than or equal to 2%.
totalRewardX64
variable is initialized to MAX_REWARD_X64
, which is 2% of Q64.setReward
function can only decrease the totalRewardX64
variable, not increase it.Invariant 2: The amount of fees charged for each compound is always less than or equal to the amount of reward added to the protocol balance.
rewardX64 * compounded0 / Q64
and rewardX64 * compounded1 / Q64
, where rewardX64
is the total reward, compounded0
is the amount of token 0 added to the position, and compounded1
is the amount of token 1 added to the position.rewardX64 * maxAddAmount0 / Q64
and rewardX64 * maxAddAmount1 / Q64
, where maxAddAmount0
is the maximum amount of token 0 that can be added to the position, and maxAddAmount1
is the maximum amount of token 1 that can be added to the position.compounded0
and compounded1
are always less than or equal to maxAddAmount0
and maxAddAmount1
, respectively, the amount of fees charged is always less than or equal to the amount of reward added to the protocol balance.Revert Lend
ProtocolAccordingly, I analyzed and audited the subject in the following steps;
Core Protocol Contract Overview:
I focused on thoroughly understanding the codebase and providing recommendations to improve its functionality.
The main goal was to take a close look at the important contracts and how they work together in the Revert Lend
.
I start with the following contracts, which play crucial roles in the Revert Lend
:
V3Vault.sol V3Oracle.sol AutoExit.sol Automator.sol AutoCompound.sol AutoRange.sol LeverageTransformer.sol
I started my analysis by examining the intricate structure and functionalities of the Revert Lend
protocol. The V3Vault
contract, the core component of the protocol, manages the lending and borrowing of assets using Uniswap V3 LP
positions as collateral. It implements the ERC4626
Vault Standard and is itself an ERC20 token representing shares of the total lending pool. The V3Oracle
contract serves as an oracle for valuing Uniswap v3 LP positions in a specified token, utilizing both Chainlink
and Uniswap v3 TWAP
to derive accurate price estimates. The InterestRateModel
contract calculates interest rates for both borrowing and supplying assets in the protocol.
Documentation Review:
Then went to Review these documentations for a more detailed and technical explanation of the Revert Lend
.
Compiling code and running provided tests:
Manuel Code Review In this phase, I initially conducted a line-by-line analysis, following that, I engaged in a comparison mode.
Line by Line Analysis: Pay close attention to the contract's intended functionality and compare it with its actual behavior on a line-by-line basis.
Comparison Mode: Compare the implementation of each function with established standards or existing implementations, focusing on the function names to identify any deviations.
Overall, I consider the quality of the Revert Lend
protocol codebase to be Good. The code appears to be mature and well-developed. We have noticed the implementation of various standards adhere to appropriately. Details are explained below:
Codebase Quality Categories | Comments |
---|---|
Architecture & Design | The protocol features a modular design, segregating functionality into distinct contracts (e.g., V3Vault , V3Oracle , Automator , LeverageTransformer , V3Utils ) for clarity and ease of maintenance. |
Error Handling & Input Validation | Functions check for conditions and validate inputs to prevent invalid operations, though the depth of validation (e.g., for edge cases transactions) would benefit from closer examination. |
Code Maintainability and Reliability | The contracts are written with emphasis on sustainability and simplicity. The functions are single-purpose with little branching and low cyclomatic complexity. The protocol includes a novel mechanism for collecting fees that offers an incentive for this work to be done by outside actors,thereby removing the associated complexity. |
Code Comments | The contracts are accompanied by comprehensive comments, facilitating an understanding of the functional logic and critical operations within the code. Functions are described purposefully, and complex sections are elucidated with comments to guide readers through the logic. Despite this, certain areas, particularly those involving intricate mechanics or tokenomics, could benefit from even more detailed commentary to ensure clarity and ease of understanding for developers new to the project or those auditing the code. |
Testing | The contracts exhibit a commendable level of test coverage 80% but with aim to 100% for indicative of a robust testing regime. This coverage ensures that a wide array of functionalities and edge cases are tested, contributing to the reliability and security of the code. However, to further enhance the testing framework, the incorporation of fuzz testing and invariant testing is recommended. These testing methodologies can uncover deeper, systemic issues by simulating extreme conditions and verifying the invariants of the contract logic, thereby fortifying the codebase against unforeseen vulnerabilities. |
Code Structure and Formatting | The codebase benefits from a consistent structure and formatting, adhering to the stylistic conventions and best practices of Solidity programming. Logical grouping of functions and adherence to naming conventions contribute significantly to the readability and navigability of the code. While the current structure supports clarity, further modularization and separation of concerns could be achieved by breaking down complex contracts into smaller, more focused components. This approach would not only simplify individual contract logic but also facilitate easier updates and maintenance. |
Strengths | The key strengths of the Revert Lend protocol are that it provides a robust, configurable and risk-managed framework for collateralized lending and borrowing using Uniswap positions, through features like dynamic interest rates, reserve management, configurable tokens, transformations, and daily/emergency controls. |
Documentation | The NatSpec is mostly complete for all external functions,and there are helpful inline comments throughout. However, there currently is no external documentation for users or integrators.The external documentation for users or integrators should be created to provide a comprehensive understanding of the contract's functionality, purpose, and interaction methods. This documentation should include detailed explanations of the contract's features, how to use and integrate its functions, as well as any relevant use cases and examples. Providing clear and thorough external documentation will ensure that users and integrators have a complete understanding of the contract and can effectively utilize its capabilities. |
The V3Vault contract manages token lending/borrowing using Uniswap V3 LP positions as collateral. It implements the IERC4626 Vault standard and itself is an ERC20 token representing shares of the total lending pool.
Users can deposit tokens into the vault in exchange for vault shares, which represents their portion of the total lending pool. This increases the total assets available for lending.
Borrowers can use LP positions (Uniswap V3 NFTs) as collateral to borrow the vault's underlying asset token. The amount that can be borrowed is determined by the collateral value of the position.
Interest accrues on borrowed assets and lent assets separately. The interest rate model contract calculates utilization-based interest rates taking into account the amount of assets supplied vs borrowed.
LP position collateral values and borrower debt values fluctuate with market prices via the oracle. If a position's collateral drops below a threshold, it becomes liquidatable.
In the event of liquidation, the liquidator can pay off the debt and receive part of the position's collateral value. Any shortfall is socialized across vault share holders.
Users can withdraw lent assets or redeem vault shares in exchange for the underlying asset tokens at the current exchange rates.
The Vault holds underlying asset tokens and manages total supply, debt levels, collateral configurations etc. Other helper contracts like the oracle, interest model and liquidator help facilitate the core functionality.
File Name | Core Functionality | Technical Characteristics | Importance and Management |
---|---|---|---|
V3Vault.sol | The core functionality for V3Vault contract includes creating new collateralized positions, borrowing assets, repaying borrowed tokens, liquidating positions, and managing reserves and interest rates | The technical characteristics involve using various libraries and interfaces such as Uniswap V3 , OpenZeppelin , and ERC721 . | The V3Vault's importance lies in providing a lending and borrowing solution while utilizing Uniswap V3 LP positions as collateral. The management of the contract is done through various admin functions, including setting limits, configuring transformer contracts, and updating token configurations. |
V3Oracle.sol | Provides oracle prices for Uniswap v3 LP positions in a specified token. | Uses both Chainlink and Uniswap v3 TWAP for price calculation and offers emergency fallback mode for price verification. | Critical for accurately calculating the value of Uniswap v3 LP positions in V3Vault .Configurable token configurations to specify feed sources and verification modes, customizable max pool price difference to prevent price manipulation attacks, emergency admin can take special actions without timelock. |
InterestRateModel.sol | Calculates borrow and supply interest rates based on the utilization rate. | Interest rates are multiplied by Q96 for precision.Utilization rate is calculated as debt divided by the sum of cash and debt.Interest rates are adjusted based on a kink point in the utilization rate. | Crucial for determining the interest paid by borrowers and earned by suppliers in the Vault.Interest rate values (base rate , multiplier , jump multiplier , kink ) can be updated by the owner. |
AutoExit.sol | Automates the execution of limit orders or stop-loss orders for Uniswap V3 positions. | Uses a revert-controlled bot (operator) to execute optimized swaps using external swap routers.Positions are configured with trigger ticks, swap parameters, and reward percentages. | Allows users to set automated exit strategies for their Uniswap V3 positions, reducing the risk of losses or missed opportunities. Positions can be configured and executed by the operator account.Position configurations include trigger ticks , swap parameters, and reward percentages . |
Automator.sol | Automates the execution of token withdrawals, NFT liquidity decreases, and token collections for Uniswap V3 positions. | Uses a revert-controlled bot (operator) to execute optimized swaps using external swap routers.Positions are configured with trigger ticks, swap parameters, and reward percentages. Validates swap prices using a TWAP and maximum tick difference. | Allows users to automate the management of their Uniswap V3 positions, reducing the risk of losses or missed opportunities.Operators can be activated/deactivated by the owner. Vaults can be activated/deactivated by the owner. The withdrawer address can be set by the owner. TWAP configuration can be set by the owner. |
AutoCompound.sol | Automates the compounding of Uniswap V3 positions by adjusting token balances and swapping tokens to maintain a target reward percentage. | Positions are configured with trigger ticks, swap parameters, and reward percentages. Validates swap prices using a TWAP and maximum tick difference. Allows for the withdrawal of leftover token balances and accumulated protocol fees. | Allows users to optimize their Uniswap V3 positions by automatically adjusting token balances and swapping tokens to maintain a target reward percentage. Reduces the risk of losses or missed opportunities by automating the compounding process. Operators can be activated/deactivated by the owner. Vaults can be activated/deactivated by the owner. The withdrawer address can be set by the owner. The total reward percentage can be lowered by the owner. |
AutoRange.sol | Automates the adjustment of Uniswap V3 positions by changing the range of the position (lower and upper ticks ). | Positions are configured with trigger ticks, swap parameters, and reward percentages.Validates swap prices using a TWAP and maximum tick difference. Allows for the configuration of positions with a minimum reward percentage. Supports both direct and vault-mediated execution. | Allows users to optimize their Uniswap V3 positions by automatically adjusting the range to capture price movements.Reduces the risk of losses or missed opportunities by automating the adjustment process. |
LeverageTransformer.sol | Facilitates leverage and deleverage operations on Uniswap V3 positions directly within a single transaction. | Supports both borrowing and repaying of assets. Allows for swapping of borrowed assets to increase liquidity . Provides flexibility in setting swap parameters and minimum amounts. Integrates with Vaults for seamless execution. | Only the Vault contract can call the leverageUp and leverageDown functions. The Vault contract is responsible for managing the underlying assets and positions. The owner of the Vault can set parameters such as the leverage ratio and swap settings. |
V3Utils.sol | Its core functionality includes executing instructions on provided NFTs (like changing range , withdrawing and swapping , or compounding fees), managing permissions, and swapping tokens | The technical characteristics involve using SafeCast for uint256, EIP712 permits for secure execution of instructions, and the IERC721Receiver interface for handling NFT transfers. | The importance of this contract lies in its ability to manage and automate complex processes related to Uniswap V3 positions, making it easier for users and developers to interact with the protocol |
FlashloanLiquidator.sol | Its core functionality is to liquidate loans using flash loans , execute swaps , and transfer the lent amount and fees back to the pool, while returning any leftover tokens to the liquidator. | The technical characteristics include using custom structs (FlashCallbackData , LiquidateParams ) for data management and the IUniswapV3FlashCallback interface for flash loan callbacks. | The importance of this contract is to facilitate seamless liquidations and swaps within the Uniswap V3 ecosystem, and its management is handled through the liquidate and uniswapV3FlashCallback functions, which ensure proper execution of liquidations and swaps while prioritizing security and efficiency. |
Swapper.sol | Its core functionality includes executing swaps , managing token approvals , and handling swap callbacks . | The contract's technical characteristics involve using SafeERC20 for secure token transfers, custom structs (ZeroxRouterData , UniversalRouterData , RouterSwapParams , PoolSwapParams ) for data management, and the IUniswapV3SwapCallback interface for swap callbacks. | The importance of this Swapper contract lies in facilitating swaps within the Uniswap V3 ecosystem, and its management is handled through functions like _routerSwap , _poolSwap , and uniswapV3SwapCallback , which ensure proper execution of swaps and callbacks while prioritizing security and efficiency. |
V3Vault.sol
Centralization Risks: The V3Vault.sol
contract has an owner who has the power to set various parameters, including the emergency admin
, interest rate model
, and oracle
. If the owner's private key is compromised, it could lead to unauthorized changes in the contract, potentially leading to a loss of funds. Additionally, the contract has a function setTransformer
which allows the owner to set a transformer contract. If a malicious contract is set as the transformer, it could potentially steal funds.
Technical Risks:
Complex logic for interest calculation, collateral checks etc. increase risk of bugs.
Values like collateral
factors are configured through the contract. Misconfiguration could lead to issues.
Integration Risks:
Adding/removing
tokens/strategies
also introduces risks from changing integrations.
Interfacing with external contracts like Uniswap
, permit
increases third party risks. Bugs in linked contracts impact this one.
V3Oracle.sol
Systemic Risks
setTokenConfig
function. This function accepts various parameters, including a reference pool for TWAP
calculations. However, there is no inherent check to prevent circular dependencies between token configurations. For example, Token A's reference pool could depend on the price of Token B, while Token B's reference pool could depend on the price of Token A. This circular dependency could lead to unpredictable behavior or inaccurate price calculations when using the _getReferenceTokenPriceX96
function.To mitigate this risk, it's essential to ensure that token configurations are set up in a way that avoids circular dependencies between reference pools. This can be achieved by carefully designing the token configuration process, either by implementing additional checks within the contract or through off-chain validation before submitting token configurations.
liquidity pools
to provide liquidity for trading. If these pools are not sufficiently liquid, it could be difficult to execute trades or obtain fair prices.Centralization Risks
Owner control: The contract has an owner
who has the power to set various parameters, including the emergency admin
, the max pool
price difference, and the token configurations. If the owner's private key is compromised, it could lead to unauthorized changes in the contract, potentially leading to a loss of funds.
Emergency admin control: The contract has an emergency admin
who has the power to update the oracle mode for a given token.
Integration Risks
Third-party risk: The contract integrates with several third-party services, such as oracles
and liquidity pools
. If these services are compromised or malfunction, it could impact the functionality of the contract.
Configuration errors: The contract has several configurable parameters, such as the max feed age
, the TWAP period
, and the max price
difference. If these parameters are not set correctly, it could lead to incorrect pricing of LP positions. Additionally, the contract allows for updating the oracle mode for a given token. If the oracle mode is not set correctly, it could lead to incorrect pricing of LP positions.
InterestRateModel.sol
Systemic Risks
getRatesPerSecondX96
function calculates both the borrow and supply rates based on the utilization rate, which is determined by the ratio of debt to available cash. If the contract's calculation of utilization rate is flawed or manipulated, it could result in incorrect interest rates being set. This could have significant consequences for the wider system, as it could lead to either borrowers
paying too much interest or lenders receiving too little interest. If this contract is used as a reference for interest rates in other parts of the protocol, the impact of a flawed utilization rate calculation could be amplified
.Centralization Risks:
owned
by a single entity, which has the power to update the interest rate values
. This centralization of control could be exploited by a malicious actor who gains control of the owner's account, or by the owner themselves if they act in their own interests rather than the interests of the system as a whole.Technical Risks:
setValues
function does not include any checks to ensure that the new interest rate values are within acceptable bounds. This could allow a malicious actor to set the interest rates to arbitrarily
high or low values, which could have significant consequences for users and other contracts that rely on accurate interest rate calculations.AutoExit.sol
Systemic Risks:
Cascading failures: The contract is designed to automatically remove or swap positions when they reach a certain tick, which could lead to cascading liquidations in the event of a sudden market crash. This might amplify market volatility and negatively impact the overall stability of the DeFi ecosystem.
Interdependencies: The contract relies on external contracts, such as the INonfungiblePositionManager
, swap routers
, and token contracts. If any of these contracts have vulnerabilities or experience issues, the AutoExit
contract may also be affected, leading to potential unintended consequences.
Centralization Risks:
Operator dependency: The contract relies on a revert-controlled
bot (operator) for executing optimized swaps using an external swap router. This introduces centralization risks, as the operator could potentially manipulate or censor transactions, or become a single point of failure if it goes offline.
Withdrawer dependency: The contract has a withdrawer
address that can withdraw accumulated fees. If this address is controlled by a centralized entity, it could lead to centralization risks and potential censorship.
Technical Risks:
token0TriggerTick
and a token1TriggerTick
. If these values are not set correctly, the position might not be executed as intended. For example, if token0TriggerTick
is greater than or equal to token1TriggerTick
, the contract will revert with an "InvalidConfig
" error when attempting to configure the position. This could lead to user confusion or unintended position behavior. To mitigate this risk, ensure that users understand how to correctly configure tick triggers and implement appropriate validation checks when setting these values.Integration Risks:
AutoExit
contract uses external swap routers (ZeroXRouter
and UniversalRouter
) for executing swaps. If these routers experience downtime, have vulnerabilities, or introduce incompatible changes, the AutoExit
contract's functionality could be affected. To mitigate this risk, ensure that the swap routers are thoroughly audited, tested, and monitored for any changes or issues. Additionally, consider implementing a fallback mechanism to use alternative swap routers in case of integration issues.Automator.sol
Systemic Risks
Automator
contract is designed to manage liquidity positions and perform swaps. In the event of a sudden market crash, the contract might initiate a large number of liquidations or swaps, which could amplify market volatility and negatively impact the overall stability of the DeFi.Technical Risks
Integration Risks
unwrapping
WETH to ETH using the _transferToken
function. If there are any issues with the WETH contract or its compatibility with the Automator contract, it could lead to unintended behavior or loss of funds.INonfungiblePositionManager
contract for managing liquidity positions. If there are any compatibility issues, changes in the INonfungiblePositionManager
contract, or vulnerabilities, it could impact the functionality of the Automator contract.ZeroXRouter
and UniversalRouter
) for executing swaps. If these routers experience downtime, have vulnerabilities, or introduce incompatible changes, the Automator contract's functionality could be affected. Additionally, if the swap routers do not support specific tokens or have issues with certain token contracts, it could lead to integration issues.AutoCompound.sol
Centralization Risks:
setReward
).operators
) that can execute compounding operations (execute
and executeWithVault
).withdrawer
) that can withdraw accumulated protocol fees (withdrawBalances
).swap0To1
) and swap amount (amountIn
). If these calculations are incorrect or manipulated, it could lead to undesirable outcomes.Integration Risks:
approve
or setApprovalForAll
). If the approvals are not set correctly, the contract may not be able to execute the desired operations.AutoRange.sol
Systemic Risks
revert-controlled
bot (operator) to execute position adjustments and swaps. If the bot experiences issues, is compromised, or becomes unavailable, it could lead to a cascade of unadjusted positions, potentially impacting the overall stability of the DeFi ecosystem, particularly if these positions are significant in size or number.Integration Risks
AutoRange
contract interacts with Vault contracts for managing positions. If there are any compatibility issues, changes in the Vault contracts, or vulnerabilities, it could impact the functionality of the AutoRange contract.LeverageTransformer.sol
Systemic Risks
liquidations: The LeverageTransformer
contract enables users to adjust their leverage positions in a single transaction. In the event of a sudden market crash or significant price movement, multiple users might simultaneously adjust their positions, leading to a cascade of liquidations. This could amplify market volatility and negatively impact the overall stability of the protocol.
Leverage-induced market manipulation: The contract allows users to easily leverage or deleverage their positions, which could encourage market manipulation by malicious actors. By rapidly adjusting their positions, these actors could artificially inflate or deflate asset prices, causing market instability and negatively affecting other market participants.
Centralization Risks
Vault dependency: The contract relies on Vault contracts for borrowing, repaying, and managing positions. If the Vault contracts are controlled by a centralized entity or have centralization risks, this could lead to potential censorship or single points of failure.
Swap router dependency: The contract uses external swap routers
for executing swaps. If these routers are controlled by centralized entities or have centralization risks, it could impact the functionality of the LeverageTransformer contract.
Technical Risks
leverage parameters
, such as borrow amount, swap amounts, and liquidity adjustments. If these parameters are set incorrectly, it could lead to unintended behavior, such as insufficient liquidity, failed swaps, or loss of funds.Integration Risks
Vault compatibility: The LeverageTransformer
contract interacts with Vault contracts for managing positions. If there are any compatibility issues, changes in the Vault contracts, or vulnerabilities, it could impact the functionality of the LeverageTransformer contract.
Swap router compatibility: The contract uses external swap routers for executing swaps. If these routers experience downtime, have vulnerabilities, or introduce incompatible changes, the LeverageTransformer
contract's functionality could be affected. Additionally, if the swap routers do not support specific tokens or have issues with certain token contracts, it could lead to integration issues.
FlashloanLiquidator.sol
Systemic Risks:
FlashloanLiquidator
contract does not properly repay the flash loan, the borrower could lose their collateral.FlashloanLiquidator
contract is designed to liquidate loans that are in default. However, there is always the risk that the loan will not be fully liquidated, and the borrower will still owe money to the lender.Technical Risks:
FlashloanLiquidator
contract uses swaps to convert the borrowed assets into the desired asset. If the market price of the assets changes significantly during the swaps, the FlashloanLiquidator
contract may not be able to repay the flash loan.Integration Risks:
UniswapV3Pool
, NonfungiblePositionManager
, and Vault contracts. If these contracts are updated or changed, the FlashloanLiquidator
contract may not be able to function properly.FlashloanLiquidator
contract requires careful configuration to ensure that it functions properly. If the contract is not configured correctly, it could result in losses for the borrower or the lender.Ref:https://xin-xia.github.io/publication/icse194.pdf
Dynamic Collateral Factors: The protocol could implement dynamic collateral factors that adjust based on market conditions. This could help mitigate risk and ensure that collateral values are always sufficient.
Liquidation Incentives: The protocol could offer incentives for liquidators to encourage them to liquidate under-collateralized positions quickly and efficiently. This could help maintain the health of the system and prevent losses.
Collateral Swaps: The protocol could allow users to swap their collateral for other assets without closing their positions. This could provide more flexibility and allow users to adjust their collateral based on market conditions.
Flash Loan Integration: The protocol could be integrated with flash loan providers to allow users to take out short-term loans for arbitrage opportunities or other purposes.
Automated Rebalancing: The protocol could implement automated rebalancing to help maintain the target collateralization ratio. This could be particularly useful for positions that use Uniswap V3 LP tokens as collateral, as their value can change rapidly.
Fee Discounts for Long-Term Stakers: The protocol could offer fee discounts to users who stake their tokens for a certain period of time. This could incentivize long-term participation and help stabilize the system.
Lending Pools for Different Risk Profiles: The protocol could create separate lending pools for different risk profiles. For example, there could be a low-risk pool with lower interest rates and more conservative collateral requirements, and a high-risk pool with higher interest rates and more aggressive collateral requirements.
Integration with Oracles for Price Feeds: The protocol could be integrated with oracles to provide more accurate and reliable price feeds. This could help prevent liquidations due to inaccurate price data.
Improved Liquidation Mechanism: The protocol could implement an improved liquidation mechanism that allows liquidators to purchase collateral at a discount, rather than selling it at a discount. This could help ensure that liquidations are always profitable for liquidators and reduce the risk of liquidations being ignored.
Collateral Diversification: The protocol could allow users to diversify their collateral by splitting it across multiple assets. This could help reduce risk and increase stability.
Automated Strategy Execution: The protocol could implement automated strategy execution, allowing users to set up complex trading strategies that are executed automatically based on market conditions.
20 hours
#0 - c4-pre-sort
2024-03-24T07:54:01Z
0xEVom marked the issue as sufficient quality report
#1 - c4-judge
2024-04-01T14:45:25Z
jhsagd76 marked the issue as grade-c
#2 - jhsagd76
2024-04-01T14:46:22Z
The "What could they have done better?" section is completely irrelevant.
#3 - albahaca0000
2024-04-03T20:30:19Z
Hey, sorry @jhsagd76 , could you please reread this analysis again? Regarding the analysis itself, I believe it aligns well with the code4rena judging criteria. It provides a thorough examination of each contract's risks, including systemic risks, technical risks, integration risks, and centralization risks or admin abuse risks.
Areas of interest include: Full representation of the projectโs risk model: Admin abuse risks Systemic risks Technical risks Integration risks
In this report, it mentions excellent security analysis of every contract, covering systemic risk, integration risk, technical risk, and centralization risk or admin abuse risk. Based on the data analysis, there are no other analyses of grade A or grade B that can identify risks as effectively as this report does. It thoroughly analyzes every contract and suggests numerous risks.
Here are some key insights from the analysis:
V3Vault.sol
Centralization Risks: The V3Vault.sol
contract has an owner who has the power to set various parameters, including the emergency admin
, interest rate model
, and oracle
. If the owner's private key is compromised, it could lead to unauthorized changes in the contract, potentially leading to a loss of funds. Additionally, the contract has a function setTransformer
which allows the owner to set a transformer contract. If a malicious contract is set as the transformer, it could potentially steal funds.
Technical Risks:
Complex logic for interest calculation, collateral checks etc. increase risk of bugs.
Values like collateral
factors are configured through the contract. Misconfiguration could lead to issues.
Integration Risks:
Adding/removing
tokens/strategies
also introduces risks from changing integrations.
Interfacing with external contracts like Uniswap
, permit
increases third party risks. Bugs in linked contracts impact this one.
V3Oracle.sol
Systemic Risks
#4 - jhsagd76
2024-04-04T01:54:26Z
Alright, I just saw too much generic and irrelevant content in this analysis, particularly in the "What could they have done better?" section. But as you said, we also can't ignore the specific parts of the report.
#5 - c4-judge
2024-04-04T01:54:33Z
jhsagd76 marked the issue as grade-b