[H-01] Particularly high value of MINIMUM_BORROWED_AMOUNT can make the protocol unusable.
Summary
The high value of MINIMUM_BORROWED_AMOUNT in Constants.sol can make the protocol unusable for most pools where SaleToken is a 'lower' valued token than HoldToken.
Vulnerability Detail
There is a check in LiquidityManager.sol that checks if the borrowedAmount is greater than the constant MINIMUM_BORROWED_AMOUNT = 100000. Else it reverts. This is particularly tricky when the SaleToken is a 'lower' valued token than HoldToken.
Impact
Lets take an example of a standard pool of WBTC/WETH, when WETH is the saleToken and WBTC is the holdToken, the user has to provide absurd amounts of liquidity to interact with the protocol.
Here is an end-end coded PoC that shows how 'large' amount of Liquidity we have to provide.
Create a Makefile and get a recent block number and MAINNET_RPC in your .env file.
include .env
test_func:
@forge test --fork-url ${MAINNET_RPC} --fork-block-number ${MAINNET_BLOCK} -vvvv --ffi --mt ${P}
Now paste this into your directory and run make test_func P=test_Borrow
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import { LiquidityBorrowingManager } from "contracts/LiquidityBorrowingManager.sol";
import { AggregatorMock } from "contracts/mock/AggregatorMock.sol";
import { HelperContract } from "../testsHelpers/HelperContract.sol";
import { INonfungiblePositionManager } from "contracts/interfaces/INonfungiblePositionManager.sol";
import {ApproveSwapAndPay} from "contracts/abstract/ApproveSwapAndPay.sol";
import {LiquidityManager} from "contracts/abstract/LiquidityManager.sol";
import {TickMath} from "../../contracts/vendor0.8/uniswap/TickMath.sol";
Feel free to adjust the liquidity in `LoanInfo` and see that lower liquidity will revert.
As you can see from the outputs, the amount of `WBTC` spent is quite a chunk and you can see how much WBTC is held by the largest holders in Ethereum -> [here](https://www.coincarp.com/currencies/wrapped-bitcoin/richlist/) this is the case between WBTC and WETH, this value can go even higher when using pools like WBTC/USDC, WETH/USDC and since the protocol is supposed to use any Uniswap V3 pool, there are a lot more other [pools](https://info.uniswap.org/#/pools) with higher price differences which will make the `Borrow` function revert everytime a user tries to use the protocol.
## Code Snippet
https://github.com/sherlock-audit/2023-10-real-wagmi/blob/b33752757fd6a9f404b8577c1eae6c5774b3a0db/wagmi-leverage/contracts/abstract/LiquidityManager.sol#L135C6-L140C6
```solidity
if (borrowedAmount > Constants.MINIMUM_BORROWED_AMOUNT) {
++borrowedAmount;
} else {
revert TooLittleBorrowedLiquidity(liquidity);
}
This condition is harder to satisfy when the saleToken is significantly 'lower' valued than the holdToken
Tool used
Manual Review
Recommendation
Set an appropriate value for MINIMUM_BORROWED_AMOUNT .
Kral01
high
[H-01] Particularly high value of MINIMUM_BORROWED_AMOUNT can make the protocol unusable.
Summary
The high value of
MINIMUM_BORROWED_AMOUNT
in Constants.sol can make the protocol unusable for most pools whereSaleToken
is a 'lower' valued token thanHoldToken
.Vulnerability Detail
There is a check in LiquidityManager.sol that checks if the
borrowedAmount
is greater than the constantMINIMUM_BORROWED_AMOUNT = 100000
. Else it reverts. This is particularly tricky when theSaleToken
is a 'lower' valued token thanHoldToken
.Impact
Lets take an example of a standard pool of WBTC/WETH, when WETH is the saleToken and WBTC is the holdToken, the user has to provide absurd amounts of liquidity to interact with the protocol.
Here is an end-end coded PoC that shows how 'large' amount of Liquidity we have to provide.
import {ApproveSwapAndPay} from "contracts/abstract/ApproveSwapAndPay.sol";
import {LiquidityManager} from "contracts/abstract/LiquidityManager.sol";
import {TickMath} from "../../contracts/vendor0.8/uniswap/TickMath.sol";
import {console} from "forge-std/console.sol";
contract ContractTest is Test, HelperContract { IERC20 WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); IERC20 USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IUniswapV3Pool WBTC_WETH_500_POOL = IUniswapV3Pool(0x4585FE77225b41b697C938B018E2Ac67Ac5a20c0); IUniswapV3Pool WETH_USDT_500_POOL = IUniswapV3Pool(0x11b815efB8f581194ae79006d24E0d814B7697F6); address constant NONFUNGIBLE_POSITION_MANAGER_ADDRESS = 0xC36442b4a4522E871399CD717aBDD847Ab11FE88; /// Mainnet, Goerli, Arbitrum, Optimism, Polygon address constant UNISWAP_V3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984; /// Mainnet, Goerli, Arbitrum, Optimism, Polygon address constant UNISWAP_V3_QUOTER_V2 = 0x61fFE014bA17989E743c5F6cB21bF9697530B21e; bytes32 constant UNISWAP_V3_POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; /// Mainnet, Goerli, Arbitrum, Optimism, Polygon address constant alice = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; address constant bob = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC; AggregatorMock aggregatorMock; LiquidityBorrowingManager borrowingManager;
}
This condition is harder to satisfy when the
saleToken
is significantly 'lower' valued than theholdToken
Tool used
Manual Review
Recommendation
Set an appropriate value for
MINIMUM_BORROWED_AMOUNT
.Duplicate of #181