msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Malicious actor cause rebase to an old inflation multiplier
Severity: high

Description:
The protocol has a rebasing mechanism that allows to sync the inflation multiplier between both L1 and L2 chains. The call to rebase is permissionless (anyone can trigger it). Insufficant checks allow a malicious actor to rebase to an old value.\\n```\\n    function rebase(uint32 _l2Gas) external {\\n        inflationMultiplier = IECO(l1Eco).getPastLinearInflation(\\n            block.number\\n        );\\n\\n        bytes memory message = abi.encodeWithSelector(\\n            IL2ECOBridge.rebase.selector,\\n            inflationMultiplier\\n        );\\n\\n        sendCrossDomainMessage(l2TokenBridge, _l2Gas, message);\\n    }\\n```\\n\\nA malicious actor can call this function a large amount of times to queue messages on `L2CrossDomainMessenger`. Since it is expensive to execute so much messages from `L2CrossDomainMessenger` (especially if the malicious actor sets `_l2Gas` to a high value) there will be a rebase message that will not be relayed through `L2CrossDomainMessenger` (or in failedMessages array).\\nSome time passes and other legitimate rebase transactions get executed.\\nOne day the malicious actor can execute one of his old rebase messages and set the value to the old value. The attacker will debalance the scales between L1 and L2 and can profit from it.

Impact:
debalance the scales between L1 and L2 ECO token

Recommended mitigation:
When sending a rebase from L1, include in the message the L1 block number. In L2 rebase, validate that the new rebase block number is above previous block number
Vulnerability: `USSDRebalancer.sol#SellUSSDBuyCollateral` the check of whether collateral is DAI is wrong
Severity: high

Description:
The `SellUSSDBuyCollateral` function use `||` instand of `&&` to check whether the collateral is DAI. It is wrong and may cause `SellUSSDBuyCollateral` function revert.\\n```\\n196       for (uint256 i = 0; i < collateral.length; i++) {\\n197         uint256 collateralval = IERC20Upgradeable(collateral[i].token).balanceOf(USSD) * 1e18 / (10**IERC20MetadataUpgradeable(collateral[i].token).decimals()) * collateral[i].oracle.getPriceUSD() / 1e18;\\n198         if (collateralval * 1e18 / ownval < collateral[i].ratios[flutter]) {\\n199         if (collateral[i].token != uniPool.token0() || collateral[i].token != uniPool.token1()) {\\n200             // don't touch DAI if it's needed to be bought (it's already bought)\\n201             IUSSD(USSD).UniV3SwapInput(collateral[i].pathbuy, daibought/portions);\\n202           }\\n203         }\\n204       }\\n```\\n\\nLine 199 should use `&&` instand of `||` to ensure that the token is not DAI. If the token is DAI, the `UniV3SwapInput` function will revert because that DAI's `pathbuy` is empty.

Impact:
The `SellUSSDBuyCollateral` will revert and USSD will become unstable.

Recommended mitigation:
```\\n      for (uint256 i = 0; i < collateral.length; i// Add the line below\\n// Add the line below\\n) {\\n        uint256 collateralval = IERC20Upgradeable(collateral[i].token).balanceOf(USSD) * 1e18 / (10**IERC20MetadataUpgradeable(collateral[i].token).decimals()) * collateral[i].oracle.getPriceUSD() / 1e18;\\n        if (collateralval * 1e18 / ownval < collateral[i].ratios[flutter]) {\\n// Remove the line below\\n         if (collateral[i].token != uniPool.token0() || collateral[i].token != uniPool.token1()) {\\n// Add the line below\\n         if (collateral[i].token != uniPool.token0() && collateral[i].token != uniPool.token1()) {\\n            // don't touch DAI if it's needed to be bought (it's already bought)\\n            IUSSD(USSD).UniV3SwapInput(collateral[i].pathbuy, daibought/portions);\\n          }\\n        }\\n      }\\n```\\n
Vulnerability: The getOwnValuation() function contains errors in the price calculation
Severity: high

Description:
The getOwnValuation() function in the provided code has incorrect price calculation logic when token0() or token1() is equal to USSD. The error leads to inaccurate price calculations.\\nThe `USSDRebalancer.getOwnValuation()` function calculates the price based on the sqrtPriceX96 value obtained from the uniPool.slot0() function. The calculation depends on whether token0() is equal to USSD or not. If token0() is equal to USSD, the price calculation is performed as follows:\\n```\\n  price = uint(sqrtPriceX96)*(uint(sqrtPriceX96))/(1e6)  (96 * 2);\\n```\\n\\nHowever,there is an error in the price calculation logic. The calculation should be:\\n```\\nprice = uint(sqrtPriceX96) * uint(sqrtPriceX96) * 1e6  (96 * 2);\\n```\\n\\nIf token0() is not equal to USSD, the price calculation is slightly different:\\n```\\n price = uint(sqrtPriceX96)*(uint(sqrtPriceX96))*(1e18 /* 1e12 + 1e6 decimal representation */)  (96 * 2);\\n        // flip the fraction\\n        price = (1e24 / price) / 1e12;\\n```\\n\\nThe calculation should be:\\n```\\n price = uint(sqrtPriceX96)*(uint(sqrtPriceX96))*(1e6 /* 1e12 + 1e6 decimal representation */)  (96 * 2);\\n        // flip the fraction\\n        price = (1e24 / price) / 1e12;\\n```\\n

Impact:
The incorrect price calculation in the getOwnValuation() function can lead to significant impact on the valuation of assets in the UniSwap V3 pool. The inaccurate prices can result in incorrect asset valuations, which may affect trading decisions, liquidity provision, and overall financial calculations based on the UniSwap V3 pool.

Recommended mitigation:
When token0() is USSD, the correct calculation should be uint(sqrtPriceX96) * uint(sqrtPriceX96) * 1e6 >> (96 * 2). When token1() is USSD, the correct calculation should be\\n```\\nprice = uint(sqrtPriceX96)*(uint(sqrtPriceX96))*(1e6 /* 1e12 + 1e6 decimal representation */)  (96 * 2);\\n        // flip the fraction\\n        price = (1e24 / price) / 1e12;\\n```\\n
Vulnerability: The price from `StableOracleDAI` is returned with the incorrect number of decimals
Severity: high

Description:
The price returned from the `getPriceUSD` function of the `StableOracleDAI` is scaled up by `1e10`, which results in 28 decimals instead of the intended 18.\\nIn `StableOracleDAI` the `getPriceUSD` function is defined as follows...\\n```\\n    function getPriceUSD() external view override returns (uint256) {\\n        address[] memory pools = new address[](1);\\n        pools[0] = 0x60594a405d53811d3BC4766596EFD80fd545A270;\\n        uint256 DAIWethPrice = DAIEthOracle.quoteSpecificPoolsWithTimePeriod(\\n            1000000000000000000, // 1 Eth\\n            0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // WETH (base token)\\n            0x6B175474E89094C44Da98b954EedeAC495271d0F, // DAI (quote token)\\n            pools, // DAI/WETH pool uni v3\\n            600 // period\\n        );\\n\\n        uint256 wethPriceUSD = ethOracle.getPriceUSD();\\n\\n        // chainlink price data is 8 decimals for WETH/USD, so multiply by 10 decimals to get 18 decimal fractional\\n        //(uint80 roundID, int256 price, uint256 startedAt, uint256 timeStamp, uint80 answeredInRound) = priceFeedDAIETH.latestRoundData();\\n        (, int256 price,,,) = priceFeedDAIETH.latestRoundData();\\n\\n        return (wethPriceUSD * 1e18) / ((DAIWethPrice + uint256(price) * 1e10) / 2);\\n    }\\n```\\n\\nThe assumption is made that the `DAIWethPrice` is 8 decimals, and is therefore multiplied by `1e10` in the return statement to scale it up to 18 decimals.\\nThe other price feeds used in the protocol are indeed received with decimals, however, the Chainlink DAI/ETH price feed returns a value with 18 decimals as can be seen on their site.

Impact:
This means that the price returned from the `getPriceUSD` function is scaled up by `1e10`, which results in 28 decimals instead of the intended 18, drastically overvaluing the DAI/USD price.\\nThis will result in the USSD token price being a tiny fraction of what it is intended to be. Instead of being pegged to $1, it will be pegged to $0.0000000001, completely defeating the purpose of the protocol.\\nFor example, if a user calls `USSD.mintForToken`, supplying DAI, they'll be able to mint `1e10` times more USSD than intended.

Recommended mitigation:
Remove the `* 1e10` from the return statement.\\n```\\n// Remove the line below\\n   return (wethPriceUSD * 1e18) / ((DAIWethPrice // Add the line below\\n uint256(price) * 1e10) / 2);\\n// Add the line below\\n   return (wethPriceUSD * 1e18) / (DAIWethPrice // Add the line below\\n uint256(price) / 2);\\n```\\n
Vulnerability: Wrong computation of the amountToSellUnit variable
Severity: high

Description:
The variable `amountToSellUnits` is computed wrongly in the code which will lead to an incorrect amount of collateral to be sold.\\nThe `BuyUSSDSellCollateral()` function is used to sell collateral during a peg-down recovery event. The computation of the amount to sell is computed using the following formula:\\n```\\n// @audit-issue Wrong computation\\nuint256 amountToSellUnits = IERC20Upgradeable(collateral[i].token).balanceOf(USSD) * ((amountToBuyLeftUSD * 1e18 / collateralval) / 1e18) / 1e18;\\n```\\n\\nThe idea is to sell an amount which is equivalent (in USD) to the ratio of `amountToBuyLeftUSD / collateralval`. Flattening the equation it ends up as:\\n```\\nuint256 amountToSellUnits = (collateralBalance * amountToBuyLeftUSD * 1e18) / (collateralval * 1e18 * 1e18);\\n\\n// Reducing the equation\\nuint256 amountToSellUnits = (collateralBalance * amountToBuyLeftUSD) / (collateralval * 1e18);\\n```\\n\\n`amountToBuyLeftUSD` and `collateralval` already have 18 decimals so their decimals get cancelled together which will lead the last 1e18 factor as not necessary.

Impact:
The contract will sell an incorrect amount of collateral during a peg-down recovery event.

Recommended mitigation:
Issue Wrong computation of the amountToSellUnit variable\\nDelete the last 1e18 factor
Vulnerability: Calls to Oracles don't check for stale prices
Severity: medium

Description:
Calls to Oracles don't check for stale prices.\\nNone of the oracle calls check for stale prices, for example StableOracleDAI.getPriceUSD():\\n```\\n(, int256 price, , , ) = priceFeedDAIETH.latestRoundData();\\n\\nreturn\\n    (wethPriceUSD * 1e18) /\\n    ((DAIWethPrice + uint256(price) * 1e10) / 2);\\n```\\n

Impact:
Oracle price feeds can become stale due to a variety of reasons. Using a stale price will result in incorrect calculations in most of the key functionality of USSD & USSDRebalancer contracts.

Recommended mitigation:
Read the `updatedAt` parameter from the calls to `latestRoundData()` and verify that it isn't older than a set amount, eg:\\n```\\nif (updatedAt < block.timestamp - 60 * 60 /* 1 hour */) {\\n   revert("stale price feed");\\n}\\n```\\n
Vulnerability: Inaccurate collateral factor calculation due to missing collateral asset
Severity: medium

Description:
The function `collateralFactor()` in the smart contract calculates the collateral factor for the protocol but fails to account for the removal of certain collateral assets. As a result, the total value of the removed collateral assets is not included in the calculation, leading to an inaccurate collateral factor.\\nThe `collateralFactor()` function calculates the current collateral factor for the protocol. It iterates through each collateral asset in the system and calculates the total value of all collateral assets in USD.\\nFor each collateral asset, the function retrieves its balance and converts it to a USD value by multiplying it with the asset's price in USD obtained from the corresponding oracle. The balance is adjusted for the decimal precision of the asset. These USD values are accumulated to calculate the totalAssetsUSD.\\n```\\n   function collateralFactor() public view override returns (uint256) {\\n        uint256 totalAssetsUSD = 0;\\n        for (uint256 i = 0; i < collateral.length; i++) {\\n            totalAssetsUSD +=\\n                (((IERC20Upgradeable(collateral[i].token).balanceOf(\\n                    address(this)\\n                ) * 1e18) /\\n                    (10 **\\n                        IERC20MetadataUpgradeable(collateral[i].token)\\n                            .decimals())) *\\n                    collateral[i].oracle.getPriceUSD()) /\\n                1e18;\\n        }\\n\\n        return (totalAssetsUSD * 1e6) / totalSupply();\\n    }\\n```\\n\\nHowever, when a collateral asset is removed from the collateral list, the `collateralFactor` function fails to account for its absence. This results in an inaccurate calculation of the collateral factor. Specifically, the totalAssetsUSD variable does not include the value of the removed collateral asset, leading to an underestimation of the total collateral value. The function `SellUSSDBuyCollateral()` in the smart contract is used for rebalancing. However, it relies on the `collateralFactor` calculation, which has been found to be inaccurate. The `collateralFactor` calculation does not accurately assess the portions of collateral assets to be bought or sold during rebalancing. This discrepancy can lead to incorrect rebalancing decisions and potentially impact the stability and performance of the protocol.\\n```\\n    function removeCollateral(uint256 _index) public onlyControl {\\n        collateral[_index] = collateral[collateral.length - 1];\\n        collateral.pop();\\n    }\\n```\\n

Impact:
As a consequence, the reported collateral factor may be lower than it should be, potentially affecting the risk assessment and stability of the protocol.

Recommended mitigation:
Ensure accurate calculations and maintain the integrity of the collateral factor metric in the protocol's risk management system.
Vulnerability: Inconsistency handling of DAI as collateral in the BuyUSSDSellCollateral function
Severity: medium

Description:
DAI is the base asset of the `USSD.sol` contract, when a rebalacing needs to occur during a peg-down recovery, collateral is sold for DAI, which then is used to buy USSD in the DAI / USSD uniswap pool. Hence, when DAI is the collateral, this is not sold because there no existe a path to sell DAI for DAI.\\nThe above behavior is handled when collateral is about to be sold for DAI, see the comment `no need to swap DAI` (link to the code):\\n```\\nif (collateralval > amountToBuyLeftUSD) {\\n   // sell a portion of collateral and exit\\n   if (collateral[i].pathsell.length > 0) {\\n       uint256 amountBefore = IERC20Upgradeable(baseAsset).balanceOf(USSD);\\n       uint256 amountToSellUnits = IERC20Upgradeable(collateral[i].token).balanceOf(USSD) * ((amountToBuyLeftUSD * 1e18 / collateralval) / 1e18) / 1e18;\\n       IUSSD(USSD).UniV3SwapInput(collateral[i].pathsell, amountToSellUnits);\\n       amountToBuyLeftUSD -= (IERC20Upgradeable(baseAsset).balanceOf(USSD) - amountBefore);\\n       DAItosell += (IERC20Upgradeable(baseAsset).balanceOf(USSD) - amountBefore);\\n   } \\n   else {\\n       // no need to swap DAI\\n       DAItosell = IERC20Upgradeable(collateral[i].token).balanceOf(USSD) * amountToBuyLeftUSD / collateralval;\\n   }\\n}\\n\\nelse {\\n   // @audit-issue Not handling the case where this is DAI as is done above.\\n   // sell all or skip (if collateral is too little, 5% treshold)\\n   if (collateralval >= amountToBuyLeftUSD / 20) {\\n      uint256 amountBefore = IERC20Upgradeable(baseAsset).balanceOf(USSD);\\n      // sell all collateral and move to next one\\n      IUSSD(USSD).UniV3SwapInput(collateral[i].pathsell, IERC20Upgradeable(collateral[i].token).balanceOf(USSD));\\n      amountToBuyLeftUSD -= (IERC20Upgradeable(baseAsset).balanceOf(USSD) - amountBefore);\\n      DAItosell += (IERC20Upgradeable(baseAsset).balanceOf(USSD) - amountBefore);\\n   }\\n}\\n```\\n\\nThe problem is in the `else branch` of the first if statement `collateralval > amountToBuyLeftUSD`, which lacks the check `if (collateral[i].pathsell.length > 0)`

Impact:
A re-balancing on a peg-down recovery will fail if the `collateralval` of DAI is less than `amountToBuyLeftUSD` but greater than `amountToBuyLeftUSD / 20` since the DAI collateral does not have a sell path.

Recommended mitigation:
Handle the case as is the done on the if branch of collateralval > amountToBuyLeftUSD:\\n```\\nif (collateral[i].pathsell.length > 0) {\\n  // Sell collateral for DAI\\n}\\nelse {\\n // No need to swap DAI\\n}\\n```\\n
Vulnerability: Risk of Incorrect Asset Pricing by StableOracle in Case of Underlying Aggregator Reaching minAnswer
Severity: medium

Description:
Chainlink aggregators have a built-in circuit breaker to prevent the price of an asset from deviating outside a predefined price range. This circuit breaker may cause the oracle to persistently return the minPrice instead of the actual asset price in the event of a significant price drop, as witnessed during the LUNA crash.\\nStableOracleDAI.sol, StableOracleWBTC.sol, and StableOracleWETH.sol utilize the ChainlinkFeedRegistry to fetch the price of the requested tokens.\\n```\\nfunction latestRoundData(\\n  address base,\\n  address quote\\n)\\n  external\\n  view\\n  override\\n  checkPairAccess()\\n  returns (\\n    uint80 roundId,\\n    int256 answer,\\n    uint256 startedAt,\\n    uint256 updatedAt,\\n    uint80 answeredInRound\\n  )\\n{\\n  uint16 currentPhaseId = s_currentPhaseId[base][quote];\\n  AggregatorV2V3Interface aggregator = _getFeed(base, quote);\\n  require(address(aggregator) != address(0), "Feed not found");\\n  (\\n    roundId,\\n    answer,\\n    startedAt,\\n    updatedAt,\\n    answeredInRound\\n  ) = aggregator.latestRoundData();\\n  return _addPhaseIds(roundId, answer, startedAt, updatedAt, answeredInRound, currentPhaseId);\\n}\\n```\\n\\nChainlinkFeedRegistry#latestRoundData extracts the linked aggregator and requests round data from it. If an asset's price falls below the minPrice, the protocol continues to value the token at the minPrice rather than its real value. This discrepancy could have the protocol end up minting drastically larger amount of stableCoinAmount as well as returning a much bigger collateral factor.\\nFor instance, if TokenA's minPrice is $1 and its price falls to $0.10, the aggregator continues to report $1, rendering the related function calls to entail a value that is ten times the actual value.\\nIt's important to note that while Chainlink oracles form part of the OracleAggregator system and the use of a combination of oracles could potentially prevent such a situation, there's still a risk. Secondary oracles, such as Band, could potentially be exploited by a malicious user who can DDOS relayers to prevent price updates. Once the price becomes stale, the Chainlink oracle's price would be the sole reference, posing a significant risk.

Impact:
In the event of an asset crash (like LUNA), the protocol can be manipulated to handle calls at an inflated price.

Recommended mitigation:
StableOracle should cross-check the returned answer against the minPrice/maxPrice and revert if the answer is outside of these bounds:\\n```\\n    (, int256 price, , uint256 updatedAt, ) = registry.latestRoundData(\\n        token,\\n        USD\\n    );\\n    \\n    if (price >= maxPrice or price <= minPrice) revert();\\n```\\n\\nThis ensures that a false price will not be returned if the underlying asset's value hits the minPrice.
Vulnerability: `BuyUSSDSellCollateral()` always sells 0 amount if need to sell part of collateral
Severity: medium

Description:
Due to rounding error there is misbehaviour in `BuyUSSDSellCollateral()` function. It results in selling 0 amount of collateral.\\nSuppose the only collateral in protocol is 1 WBTC; 1 WBTC costs 30_000 USD; UniV3Pool DAI/ USSD has following liquidity: (3000e6 USSD, 2000e18 DAI) And also USSD is underpriced so call rebalance:\\n```\\n    function rebalance() override public {\\n      uint256 ownval = getOwnValuation(); // it low enough to dive into if statement (see line below) \\n      (uint256 USSDamount, uint256 DAIamount) = getSupplyProportion(); // (3000e6 USSD, 2000e18 DAI)\\n      if (ownval < 1e6 - threshold) {\\n        // peg-down recovery\\n        BuyUSSDSellCollateral((USSDamount - DAIamount / 1e12)/2); //  500 * 1e6     = (3000e6 - 2000e18 / 1e12) / 2\\n```\\n\\nTake a look into BuyUSSDSellCollateral (follow comments):\\n```\\n    function BuyUSSDSellCollateral(uint256 amountToBuy) internal { // 500e6\\n      CollateralInfo[] memory collateral = IUSSD(USSD).collateralList();\\n      //uint amountToBuyLeftUSD = amountToBuy * 1e12 * 1e6 / getOwnValuation();\\n      uint amountToBuyLeftUSD = amountToBuy * 1e12; // 500e18\\n      uint DAItosell = 0;\\n      // Sell collateral in order of collateral array\\n      for (uint256 i = 0; i < collateral.length; i++) {\\n        // 30_000e18 = 1e8 * 1e18 / 10**8 * 30_000e18 / 1e18\\n        uint256 collateralval = IERC20Upgradeable(collateral[i].token).balanceOf(USSD) * 1e18 / (10**IERC20MetadataUpgradeable(collateral[i].token).decimals()) * collateral[i].oracle.getPriceUSD() / 1e18;\\n        if (collateralval > amountToBuyLeftUSD) {\\n          // sell a portion of collateral and exit\\n          if (collateral[i].pathsell.length > 0) {\\n            uint256 amountBefore = IERC20Upgradeable(baseAsset).balanceOf(USSD); // 0\\n            // amountToSellUnits = 1e8 * ((500e18 * 1e18 / 30_000e18) / 1e18) / 1e18 = 1e8 * (0) / 1e18 = 0\\n            uint256 amountToSellUnits = IERC20Upgradeable(collateral[i].token).balanceOf(USSD) * ((amountToBuyLeftUSD * 1e18 / collateralval) / 1e18) / 1e18;\\n            // and finally executes trade of 0 WBTC\\n            IUSSD(USSD).UniV3SwapInput(collateral[i].pathsell, amountToSellUnits);\\n            amountToBuyLeftUSD -= (IERC20Upgradeable(baseAsset).balanceOf(USSD) - amountBefore); // 0 = 0 - 0\\n            DAItosell += (IERC20Upgradeable(baseAsset).balanceOf(USSD) - amountBefore); // 0 += 0\\n            // rest of code\\n```\\n\\nSo protocol will not buy DAI and will not sell DAI for USSD in UniswapV3Pool to support peg of USSD to DAI

Impact:
Protocol is not able of partial selling of collateral for token. It block algorithmic pegging of USSD to DAI

Recommended mitigation:
Refactor formula of amountToSellUnits\\n```\\n// uint256 amountToSellUnits = (decimals of collateral) * (DAI amount to get for sell) / (price of 1 token of collateral)\\nuint256 amountToSellUnits = collateral[i].token).decimals() * amountToBuyLeftUSD / collateral[i].oracle.getPriceUSD()\\n```\\n
Vulnerability: If collateral factor is high enough, flutter ends up being out of bounds
Severity: medium

Description:
In `USSDRebalancer` contract, function `SellUSSDBuyCollateral` will revert everytime a rebalance calls it, provided the collateral factor is greater than all the elements of the `flutterRatios` array.\\nFunction `SellUSSDBuyCollateral` calculates `flutter` as the lowest index of the `flutterRatios` array for which the collateral factor is smaller than the `flutter` ratio.\\n```\\nuint256 cf = IUSSD(USSD).collateralFactor();\\nuint256 flutter = 0;\\nfor (flutter = 0; flutter < flutterRatios.length; flutter++) {\\n if (cf < flutterRatios[flutter]) {\\n   break;\\n }\\n}\\n```\\n\\nThe problem arises when, if collateral factor is greater than all flutter values, after the loop `flutter = flutterRatios.length`.\\nThis `flutter` value is used afterwards here:\\n```\\n// rest of code\\nif (collateralval * 1e18 / ownval < collateral[i].ratios[flutter]) {\\n  portions++;\\n}\\n// rest of code\\n```\\n\\nAnd here:\\n```\\n// rest of code\\nif (collateralval * 1e18 / ownval < collateral[i].ratios[flutter]) {\\n if (collateral[i].token != uniPool.token0() || collateral[i].token != uniPool.token1()) {\\n   // don't touch DAI if it's needed to be bought (it's already bought)\\n   IUSSD(USSD).UniV3SwapInput(collateral[i].pathbuy, daibought/portions);\\n }\\n}\\n// rest of code\\n```\\n\\nAs we can see in the tests of the project, the flutterRatios array and the collateral `ratios` array are set to be of the same length, so if flutter = flutterRatios.length, any call to that index in the `ratios` array will revert with an index out of bounds.

Impact:
High, when the collateral factor reaches certain level, a rebalance that calls `SellUSSDBuyCollateral` will always revert.

Recommended mitigation:
When checking `collateral[i].ratios[flutter]` always check first that flutter is `< flutterRatios.length`.