Skip to content

Using round down in flashFee() could potentially lead to flash loan failures. #31

@c4-bot-2

Description

@c4-bot-2

Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/59b1f70cbf170871f9604e73e7fe70b70981ab43/contracts/core/flashloan/BalancerFlashLender.sol#L63

Vulnerability details

Vulnerability details

in BalancerFlashLender.flashFee()
Calculated using round down

    function flashFee(address, uint256 amount) external view override returns (uint256) {
        uint256 perc = _balancerVault.getProtocolFeesCollector().getFlashLoanFeePercentage();
        if (perc == 0 || amount == 0) {
            return 0;
        }

@>      return (amount * perc) / _BALANCER_MAX_FEE_PERCENTAGE;
    }

But balancer is used `round up'.

https://github.com/balancer/balancer-v2-monorepo/blob/ac63d64018c6331248c7d77b9f317a06cced0243/pkg/vault/contracts/Fees.sol#L60

     * @dev Returns the protocol fee amount to charge for a flash loan of `amount`.
     */
    function _calculateFlashLoanFeeAmount(uint256 amount) internal view returns (uint256) {
        // Fixed point multiplication introduces error: we round up, which means in certain scenarios the charged
        // percentage can be slightly higher than intended.
        uint256 percentage = getProtocolFeesCollector().getFlashLoanFeePercentage();
@>      return FixedPoint.mulUp(amount, percentage);
    }

This way when we set the allowance in StrategyLeverage, it may be 1 wei smaller than it actually needs to be.
Take deploy() for example

    function deploy() external payable onlyOwner nonReentrant returns (uint256 deployedAmount) {
        if (msg.value == 0) revert InvalidDeployAmount();
        // 1. Wrap Ethereum
        address(wETHA()).functionCallWithValue(abi.encodeWithSignature("deposit()"), msg.value);
        // 2. Initiate a WETH Flash Loan
        uint256 leverage = calculateLeverageRatio(
            msg.value,
            getLoanToValue(),
            getNrLoops()
        );
        uint256 loanAmount = leverage - msg.value;
@>      uint256 fee = flashLender().flashFee(wETHA(), loanAmount);
        //§uint256 allowance = wETH().allowance(address(this), flashLenderA());
@>      if(!wETH().approve(flashLenderA(), loanAmount + fee)) revert FailedToApproveAllowance();
        if (
            !flashLender().flashLoan(
                IERC3156FlashBorrowerUpgradeable(this),
                wETHA(),
                loanAmount,
                abi.encode(msg.value, msg.sender, FlashLoanAction.SUPPLY_BOORROW)
            )
        ) {
            revert FailedToRunFlashLoan();
        }
...

Finally receiveFlashLoan()check allowances or balancer will fail at the end of the flash loan when getting the fee due to insufficient allowances leading to revert, which will cause all methods relying on the flash loan to fail

Impact

A flash loan will cause several core functions of the protocol to fail, such as valult's deposit/withdraw/rebalance.

Recommended Mitigation

Use round up for flashFee() calculations

Assessed type

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    2 (Med Risk)Assets not at direct risk, but function/availability of the protocol could be impacted or leak value🤖_11_groupAI based duplicate group recommendationbugSomething isn't workingdowngraded by judgeJudge downgraded the risk level of this issueduplicate-11satisfactorysatisfies C4 submission criteria; eligible for awards

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions