Variable opening fee will always be wrongly computed if collateral is not a stablecoin
Borrowing fees will be computed wrongly because of a combination of hardcoded values and a wrongly implemented setter function.\\nTapioca applies a linearly scaling creation fee to open a new CDP in Big Bang markets. This is done via the internal `_computeVariableOpeningFee()` function every time a new borrow is performed.\\nIn order to compute the variable fee, the exchange rate will be queried. This rate is important in order to understand the current price of USDO related to the collateral asset.\\nIf `_exchangeRate >= minMintFeeStart`, then `minMintFee` will be applied.\\nIf `_exchangeRate <= maxMintFeeStart`, then `maxMintFee` will be applied\\nOtherwise, a proportional percentage will be applied to compue the fee\\nAs per the comment in the code snippet shows below, Tapioca wrongly assumes that the exchange rate will always be `USDO <> USDC`, when in reality the actual collateral will dictate the exchange rate returned.\\nIt is also important to note the fact that contrary to what one would assume, `maxMintFeeStart` is assumed to be smaller than `minMintFeeStart` in order to perform the calculations:\\n```\\n// BBLendingCommon.sol\\n\\nfunction _computeVariableOpeningFee(uint256 amount) internal returns (uint256) {\\n        if (amount == 0) return 0; \\n \\n        //get asset <> USDC price ( USDO <> USDC ) \\n        (bool updated, uint256 _exchangeRate) = assetOracle.get(oracleData); \\n        if (!updated) revert OracleCallFailed();\\n   \\n        if (_exchangeRate >= minMintFeeStart) { \\n            return (amount * minMintFee) / FEE_PRECISION;\\n        }\\n        if (_exchangeRate <= maxMintFeeStart) { \\n            return (amount * maxMintFee) / FEE_PRECISION;\\n        }\\n     \\n        uint256 fee = maxMintFee\\n            - (((_exchangeRate - maxMintFeeStart) * (maxMintFee - minMintFee)) / (minMintFeeStart - maxMintFeeStart)); \\n \\n        if (fee > maxMintFee) return (amount * maxMintFee) / FEE_PRECISION;\\n        if (fee < minMintFee) return (amount * minMintFee) / FEE_PRECISION;\\n\\n        if (fee > 0) {\\n            return (amount * fee) / FEE_PRECISION;\\n        }\\n        return 0;\\n    }\\n```\\n\\nIt is also important to note that `minMintFeeStart` and `maxMintFeeStart` are hardcoded when being initialized inside `BigBang.sol` (as mentioned, `maxMintFeeStart` is smaller than minMintFeeStart):\\n```\\n// BigBang.sol\\n\\nfunction _initCoreStorage(\\n        IPenrose _penrose,\\n        IERC20 _collateral,\\n        uint256 _collateralId,\\n        ITapiocaOracle _oracle,\\n        uint256 _exchangeRatePrecision,\\n        uint256 _collateralizationRate,\\n        uint256 _liquidationCollateralizationRate,\\n        ILeverageExecutor _leverageExecutor\\n    ) private {\\n        // rest of code\\n        \\n        maxMintFeeStart = 975000000000000000; // 0.975 *1e18\\n        minMintFeeStart = 1000000000000000000; // 1*1e18\\n\\n        // rest of code\\n    } \\n```\\n\\nWhile the values hardcoded initially to values that are coherent for a USDO <> stablecoin exchange rate, these values won't make sense if we find ourselves fetching an exchcange rate of an asset not stable.\\nLet's say the collateral asset is ETH. If ETH is at 4000$, then the exchange rate will return a value of 0,00025. This will make the computation inside `_computeVariableOpeningFee()` always apply the maximum fee when borrowing because `_exchangeRate` is always smaller than `maxMintFeeStart` by default.\\nAlthough this has an easy fix (changing the values stored in `maxMintFeeStart` and minMintFeeStart), this can't be properly done because the `setMinAndMaxMintRange()` function wrongly assumes that `minMintFeeStart` must be smaller than `maxMintFeeStart` (against what the actual calculations dictate in the _computeVariableOpeningFee()):\\n```\\n// BigBang.sol\\n\\nfunction setMinAndMaxMintRange(uint256 _min, uint256 _max) external onlyOwner {\\n        emit UpdateMinMaxMintRange(minMintFeeStart, _min, maxMintFeeStart, _max);\\n\\n        if (_min >= _max) revert NotValid(); \\n\\n        minMintFeeStart = _min;\\n        maxMintFeeStart = _max;\\n    } \\n```\\n\\nThis will make it impossible to properly update the `maxMintFeeStart` and `minMintFeeStart` to have proper values because if it is enforced that `maxMintFeeStart` > than `minMintFeeStart`, then `_computeVariableOpeningFee()` will always enter the first `if (_exchangeRate >= minMintFeeStart)` and wrongly return the minimum fee.
The mitigation for this is straightforward. Change the `setMinAndMaxMintRange()` function so that `_max` is enforced to be smaller than _min:\\n```\\n// BigBang.sol\\n\\nfunction setMinAndMaxMintRange(uint256 _min, uint256 _max) external onlyOwner {\\n        emit UpdateMinMaxMintRange(minMintFeeStart, _min, maxMintFeeStart, _max);\\n\\n// Remove the line below\\n        if (_min >= _max) revert NotValid(); \\n// Add the line below\\n        if (_max >= _min) revert NotValid(); \\n\\n        minMintFeeStart = _min;\\n        maxMintFeeStart = _max;\\n    } \\n```\\n\\nAlso, I would recommend not to hardcode the values of `maxMintFeeStart` and `minMintFeeStart` and pass them as parameter instead, inside `_initCoreStorage()` , as they should always be different considering the collateral configured for that market.
Medium. Although this looks like a bug that doesn't have a big impact in the protocol, it actually does. The fees will always be wrongly applied for collaterals different from stablecoins, and applying these kind of fees when borrowing is one of the core mechanisms to keep USDO peg, as described in Tapioca's documentation. If this mechanisms doesn't work properly, users won't be properly incentivized to borrow/repay considering the different market conditions that might take place and affect USDO's peg to $1.
```\\n// BBLendingCommon.sol\\n\\nfunction _computeVariableOpeningFee(uint256 amount) internal returns (uint256) {\\n        if (amount == 0) return 0; \\n \\n        //get asset <> USDC price ( USDO <> USDC ) \\n        (bool updated, uint256 _exchangeRate) = assetOracle.get(oracleData); \\n        if (!updated) revert OracleCallFailed();\\n   \\n        if (_exchangeRate >= minMintFeeStart) { \\n            return (amount * minMintFee) / FEE_PRECISION;\\n        }\\n        if (_exchangeRate <= maxMintFeeStart) { \\n            return (amount * maxMintFee) / FEE_PRECISION;\\n        }\\n     \\n        uint256 fee = maxMintFee\\n            - (((_exchangeRate - maxMintFeeStart) * (maxMintFee - minMintFee)) / (minMintFeeStart - maxMintFeeStart)); \\n \\n        if (fee > maxMintFee) return (amount * maxMintFee) / FEE_PRECISION;\\n        if (fee < minMintFee) return (amount * minMintFee) / FEE_PRECISION;\\n\\n        if (fee > 0) {\\n            return (amount * fee) / FEE_PRECISION;\\n        }\\n        return 0;\\n    }\\n```\\n