xanoutas/solidity-security-findings huggingface dataset:
min and maxAnswer never checked for oracle price feed
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/oracles/EthOracle.sol#L31


# Vulnerability details

## Description
Chainlink aggregators have a built in circuit breaker if the price of an asset goes outside of a predetermined price band. The result is that if an asset experiences a huge drop in value (i.e. LUNA crash) the price of the oracle will continue to return the minPrice instead of the actual price of the asset. This would allow user to continue borrowing with the asset but at the wrong price. This is exactly what happened to [Venus on BSC when LUNA imploded](https://rekt.news/venus-blizz-rekt/). However, the protocol misses to implement such a check. 

[Link to code](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/oracles/EthOracle.sol#L31):
```js
    function getLatestPrice() public view override returns (IOracle.Price memory price) {
@-->    (, int256 answer, uint256 startedAt, uint256 updatedAt,) = _ethPriceFeed.latestRoundData();
        if ( answer<= 0 ) revert InvalidPriceFromOracle();        
        if ( startedAt ==0 || updatedAt == 0 ) revert InvalidPriceUpdatedAt();    

        price.price = uint256(answer);
        price.lastUpdate = updatedAt;
    }
```

## Similar past issues
- https://github.com/sherlock-audit/2023-05-USSD-judging/issues/598
- https://github.com/sherlock-audit/2023-02-blueberry-judging/issues/18

## Tools Used
Manual review

## Recommended Mitigation Steps
Add logic along the lines of:
```js
    require(answer >= minPrice && answer <= maxPrice, "invalid price");
```
min and max prices can be gathered using [one of these ways](https://medium.com/cyfrin/chainlink-oracle-defi-attacks-93b6cb6541bf#99af:~:text=Developers%20%26%20Auditors%20can%20find%20Chainlink%E2%80%99s%20oracle%20feed).



## Assessed type

Oracle
Missing checks for whether the L2 Sequencer is active
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/oracles/EthOracle.sol#L31


# Vulnerability details

## Description
Chainlink recommends that users using price oracles, check whether the Arbitrum Sequencer is [active](https://docs.chain.link/data-feeds/l2-sequencer-feeds#arbitrum). If the sequencer goes down, the Chainlink oracles will have stale prices from before the downtime, until a new L2 OCR transaction goes through. Users who submit their transactions via the [L1 Delayed Inbox](https://developer.arbitrum.io/tx-lifecycle#1b--or-from-l1-via-the-delayed-inbox) will be able to take advantage of these stale prices. Use the [Chainlink oracle recommended checks](https://blog.chain.link/how-to-use-chainlink-price-feeds-on-arbitrum/#almost_done!_meet_the_l2_sequencer_health_flag:~:text=as%20%E2%80%9C_priceFeedAddress%E2%80%9D%20parameter.-,Almost%20Done!%20Meet%20the%20L2%20Sequencer%20Health%20Flag,-Transactions%20in%20Arbitrum) to determine whether the sequencer is offline or not, and don't allow operations to take place while the sequencer is offline.

[Link to code](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/oracles/EthOracle.sol#L31):
```js
    function getLatestPrice() public view override returns (IOracle.Price memory price) {
@-->    (, int256 answer, uint256 startedAt, uint256 updatedAt,) = _ethPriceFeed.latestRoundData();
        if ( answer<= 0 ) revert InvalidPriceFromOracle();        
        if ( startedAt ==0 || updatedAt == 0 ) revert InvalidPriceUpdatedAt();    

        price.price = uint256(answer);
        price.lastUpdate = updatedAt;
    }
```

## Tools Used
Manual review

## Recommended Mitigation Steps
Use the [Chainlink oracle recommended checks](https://blog.chain.link/how-to-use-chainlink-price-feeds-on-arbitrum/#almost_done!_meet_the_l2_sequencer_health_flag:~:text=as%20%E2%80%9C_priceFeedAddress%E2%80%9D%20parameter.-,Almost%20Done!%20Meet%20the%20L2%20Sequencer%20Health%20Flag,-Transactions%20in%20Arbitrum) to determine whether the sequencer is offline or not, and don't allow operations to take place while the sequencer is offline.


## Assessed type

Other
Unused user funds not refunded after `exactInputSingle()` swap
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/hooks/UseSwapper.sol#L67


# Vulnerability details

## Description
The [exactInputSingle() call](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/hooks/UseSwapper.sol#L67) assumes that all of the user funds or `params.amountIn` will be consumed. This is not necessarily true. A swap can be interrupted earlier than expected when there's not enough liquidity in a pool. Hence it's recommended that the protocol adds the logic to handle any unspent funds after `exactInputSingle()`.

## Proof of Concept
- See a similar past issue: https://solodit.xyz/issues/swaprouter-doesnt-refund-unspent-eth-after-swapping-spearbit-none-velodrome-finance-pdf

Please refer the second point "_A swap can be interrupted earlier when there's not enough liquidity in a pool._".  Also refer its recommendation section which suggests returning unspent ETH at the end of all swap styles, **even for** `SwapRouter.exactInputSingle()`.
<br>

Also refer the exact fix made by the protocol inside `exactInputSingle()` [here](https://github.com/velodrome-finance/slipstream/commit/0c5da40e5258d3c8de7d03c0a85b89209111365a#diff-b9120db8cf76d7ed7f03380f9e411df80c8bc8b9cad19ff84339046ac419b515R121) (_click "Expand all" there to see the full code and the function name_).

## Tools Used
Manual review

## Recommended Mitigation Steps
Just like the protocol has implemented a transfer after `exactOutputSingle` [here](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/hooks/UseSwapper.sol#L96), add the logic to transfer any unspent funds after `exactInputSingle()` too.


## Assessed type

Other
Missing deadline in `UseSwapper::_swap()`
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/hooks/UseSwapper.sol#L57


# Vulnerability details

## Description
The [_swap()](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/hooks/UseSwapper.sol#L57) function does not provide a `deadline` param either for `ExactInputSingleParams` or for `ExactOutputSingleParams`. Thus the swap could go through way later than the user was ready to accept, when the market conditions may have changed considerably. In the current implementation, there is no way for the user to specify it.

## Tools Used
Manual review

## Recommended Mitigation Steps
Add a param `deadline: params.deadline` inside both `ExactInputSingleParams` and `ExactOutputSingleParams` calls.


## Assessed type

Other
Rounding-down of `flashFee` can result in calls to flash loan to revert
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/flashloan/BalancerFlashLender.sol#L63
https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/strategies/StrategyLeverage.sol#L247-L249


# Vulnerability details

## Description
The [BalancerFlashLender::flashFee()](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/flashloan/BalancerFlashLender.sol#L63) function returns a rounded-down fee. This `fee` is then [later used](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/strategies/StrategyLeverage.sol#L247-L249) across the protocol while providing spend approval to the flash loan provider. This can result in an approval less than that expected by the provider and hence cause the call to flash loan to revert. This is because [flash loan providers calculate their fee by rounding up in their favour](https://docs.uniswap.org/contracts/v2/guides/smart-contract-integration/using-flash-swaps#single-token), instead of rounding down:
```js
File: contracts/core/flashloan/BalancerFlashLender.sol

    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;
    }
```

and 
```js
File: contracts/core/strategies/StrategyLeverage.sol

    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();
        }

        deployedAmount = _pendingAmount;
        _deployedAmount = _deployedAmount + deployedAmount;
        emit StrategyAmountUpdate(_deployedAmount);
        // Pending amount is not cleared to save gas
        // _pendingAmount = 0;
    }
```

## Impact
Flash loan call reverts for many amount and fee percentage combinations.

## Tools Used
Manual review

## Recommended Mitigation Steps
Round up in favour of the protocol. A
`GovernableOwnable.sol` would have no `_governer` role after an individual upgrade
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/GovernableOwnable.sol#L27-L30


# Vulnerability details

## Description
The [_initializeGovernableOwnable() function](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/GovernableOwnable.sol#L27-L30) of the `GovernableOwnable` contract is an internal initializer which at deployment time gets called from the `StrategyAAVEv3.sol` contract. It seems however that [even though `GovernableOwnable.sol` is upgradeable](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/GovernableOwnable.sol#L18), it can't be safely individually upgraded without causing loss of `_governer` role and one has to always depend on `StrategyAAVEv3.sol` for an upgrade. 
```js
@--->   contract GovernableOwnable is OwnableUpgradeable {
            
            address private _governor;
            
            error CallerNotTheGovernor();
            error InvalidGovernorAddress();

            event GovernshipTransferred(address indexed previousGovernor, address indexed newGovernor);

@--->       function _initializeGovernableOwnable(address initialOwner, address initialGovernor) internal initializer {
                _transferOwnership(initialOwner);
                _transferGovernorship(initialGovernor); 
            }
```

and

```js
@--->   function transferGovernorship(address _newGovernor) public virtual onlyGovernor {
            if(_newGovernor == address(0)) revert InvalidGovernorAddress();
            _transferGovernorship(_newGovernor);
        }
```

`transferGovernorship()` too can only be called by `onlyGovernor` rendering it unusable after an individual upgrade.

## Impact
Governor role lost after individual upgrade.

## Tools Used
Manual review

## Recommended Mitigation Steps
Add a function `setGoverner()` protected by the `onlyOwner` modifier which can be called after an individual upgrade of this contract.


## Assessed type

Upgradable
deposit can be DoS
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
DoS attacks make it impossible for others to deposit

## Proof of Concept

`maxDeposit` is set in the `deposit` function:

```solidity
    if (maxDeposit > 0) {
        uint256 afterDeposit = msg.value + ((balanceOf(msg.sender) * _tokenPerETH(maxPriceAge)) / 1e18);
        if (afterDeposit > maxDeposit) revert MaxDepositReached();
    }
```
_tokenPerETH = (totalSupply() * 1 ether) / _totalAssets;

Because _totalAssets = totalCollateralInEth - totalDebtInEth
So the value of _totalAssets might be small.

Suppose _totalAssets = 1wei,
afterDeposit = msg.value + balanceOf(msg.sender) * totalSupply()

Since `totalSupply` is constantly increasing, Therefore, when the value of `totalSupply` is accumulated (many deposits in a short time) and `_totalAssets` in the pool is small, a user with `balanceOf(msg.sender)` greater than 0 May not be able to deposit.

The attacker can make `totalSupply` increase temporarily, or the attacker can donate the balance to the specified account so that his balance is greater than 0.

## Tools Used
vscode, manual

## Recommended Mitigation Steps
Change the calculation method of `maxDeposit`.



## Assessed type

DoS
`deltaCollateralInETH` needs to be rounded up inside `calcDeltaPosition()`
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/hooks/UseLeverage.sol#L58


# Vulnerability details

## Description
The [calcDeltaPosition() function](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/hooks/UseLeverage.sol#L58) rounds-down `deltaDebtInETH` as well as `deltaCollateralInETH`. These values are [later deducted from the total debt & collateral](https://github.com/code-423n4/2024-05-bakerfi/blob/main/contracts/core/strategies/StrategyLeverage.sol#L495-L512) respectively. This effectively means that both the remaining debt & the remaining collateral backing this debt get rounded-up. This is incorrect. While it's alright to round-up the debt as it is in the favour of the protocol, the remaining collateral backing the debt should always be rounded-down. Hence, `deltaCollateralInETH` should have been rounded-up.
```js
    function calcDeltaPosition(
        uint256 percentageToBurn,
        uint256 totalCollateralBaseInEth,
        uint256 totalDebtBaseInEth
    ) public pure returns (uint256 deltaCollateralInETH, uint256 deltaDebtInETH) {
        if (percentageToBurn == 0 || percentageToBurn > PERCENTAGE_PRECISION) {
            revert InvalidPercentageValue();
        }
        // Reduce Collateral based on the percentage to Burn
        deltaDebtInETH = (totalDebtBaseInEth * percentageToBurn) / PERCENTAGE_PRECISION; 
        // Reduce Debt based on the percentage to Burn
@--->   deltaCollateralInETH = (totalCollateralBaseInEth * percentageToBurn) / PERCENTAGE_PRECISION;  // @audit : Should have been rounded-up.
    }
```

## Impact
The collateral ratio can be skewed and can dip below acceptable ratio. 

## Tools Used
Manual review

## Recommended Mitigation Steps
Round up `deltaCollateralInETH`. A library like solmate can be used which has `mulDivUp`:
```diff
-   deltaCollateralInETH = (totalCollateralBaseInEth * percentageToBurn) / PERCENTAGE_PRECISION;
+   deltaCollateralInETH = totalCollateralBaseInEth.mulDivUp(percentageToBurn, PERCENTAGE_PRECISION);
```


## Assessed type

Math
BalancerFlashLender#receiveFlashLoan does not validate the originalCallData
Audit: 2024-05-bakerfi-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details


## Impact
receiveFlashLoan does not validate the originalCallData,
The attacker can pass any parameters into the receiveFlashLoan function and execute any Strategy instruction :`_supplyBorrow` `_repayAndWithdraw` `_payDeb`.

## Proof of Concept

The `StrategyLeverage#receiveFlashLoan` function only validates whether the msg.sender is `_balancerVault`, but does not validate the `originalCallData`:

```solidity
   function receiveFlashLoan(address[] memory tokens,
        uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData
    ) external {
 @>     if (msg.sender != address(_balancerVault)) revert InvalidFlashLoadLender();
        if (tokens.length != 1) revert InvalidTokenList();
        if (amounts.length != 1) revert InvalidAmountList();
        if (feeAmounts.length != 1) revert InvalidFeesAmount();

        //@audit originalCallData is not verified
        (address borrower, bytes memory originalCallData) = abi.decode(userData, (address, bytes));
        address asset = tokens[0];
        uint256 amount = amounts[0];
        uint256 fee = feeAmounts[0];
        // Transfer the loan received to borrower
        IERC20(asset).safeTransfer(borrower, amount);

@>      if (IERC3156FlashBorrowerUpgradeable(borrower).onFlashLoan(borrower,
                tokens[0], amounts[0], feeAmounts[0], originalCallData
            ) != CALLBACK_SUCCESS
        ) {
            revert BorrowerCallbackFailed();
        }
        ....
    }
```

_balancerVault.flashLoan can specify the recipient:

```solidity 
    _balancerVault.flashLoan(address(this), tokens, amounts, abi.encode(borrower, data));
```

An attacker can initiate flashLoan from another contract and specify the recipient as `BalancerFlashLender`. `_balancerVault` will call the `balancerFlashlender#receiveFlashLoan` function, 
Since the caller of the receiveFlashLoan function is `_balancerVault`, this can be verified against `msg.sender`.

The `StrategyLeverage#onFlashLoan` function parses the instructions to be executed from `originalCallData(FlashLoanData)` and executes them.

```solidity
    function onFlashLoan(address initiator,address token,uint256 amount, uint256 fee, bytes memory callData
    ) external returns (bytes32) {
        if (msg.sender != flashLenderA()) revert InvalidFlashLoanSender();
        if (initiator != address(this)) revert InvalidLoanInitiator();
        // Only Allow WETH Flash Loans
        if (token != wETHA()) revert InvalidFlashLoanAsset();
        //loanAmount = leverage - msg.value;
@>      FlashLoanData memory data = abi.decode(callData, (FlashLoanData));
        if (data.action == FlashLoanAction.SUPPLY_BOORROW) {
            _supplyBorrow(data.originalAmount, amount, fee);
            // Use the Borrowed to pay ETH and deleverage
        } else if (data.
Unrestricted access to `sgReceive()` allows arbitrary messages to be injected
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/bridge_adapters/StargateBridgeAdapter.sol#L183


# Vulnerability details

## Impact
The [`sgReceive()`](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/bridge_adapters/StargateBridgeAdapter.sol#L183) function in the `StargateBridgeAdapter` contract is a critical part of the integration with Stargate protocol's cross-chain functionality. It is designed to receive and process transactions coming from other chains. However, the current implementation assumes the messages it receives are from within the protocol and doesn't check any of its parameters other than the payload.

According to Stargate's [documentation](https://stargateprotocol.gitbook.io/stargate/~/revisions/eN5XxylzOXAFSV7dcIwI/developers/contract-addresses/caveats-sgreceive#this-means-all-sgreceive-implementations-could-called-by-any-remote-contract), any contract from any chain can call `sgReceive()`. Also from the documentation:
> This is fine though, so long as the sgReceive() implementation makes no assumptions about the sender and simply takes the ERC20 token passed to it, performs its proprietary logic, and is stateless.

However, the implementation simply ignores the `_token` and `amountLD` variables:
```solidity
    function sgReceive(
        uint16, // _srcChainid
        bytes memory, // _srcAddress
        uint256, // _nonce
        address, // _token
        uint256, // amountLD
        bytes memory payload
    ) external override onlyExecutor {
```

This lack of input validation allows an attacker to abuse the function to create approvals of any size for any token and to inject an arbitrary payload into the UTB:
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/bridge_adapters/StargateBridgeAdapter.sol#L207-L215
```solidity
	IERC20(swapParams.tokenIn).approve(utb, swapParams.amountIn);

	IUTB(utb).receiveFromBridge(
		postBridge,
		target,
		paymentOperator,
		utbPayload,
		refund
	);
```

Furthermore, the token extracted from the payload in `swapParams.tokenIn` may be a different address than that of the token received as deployment addresses of equivalent tokens differ across chains. This will lead to swaps failing in certain cases, which will lead to loss of funds as messages are considered delivered by Stargate even if they fail.

## Proof of Concept
An attacker could follow these steps to exploit the issue:

1. The attacker deploys a malicious contract on a different chain.
2. The malicious contract submits a cross-chain transaction via a Stargate endpoint to call `sgReceive()` on the `StargateBridgeAdapter` contract with crafted parameters.
3. The `StargateBridgeAdapter` contract processes the call as if it were a legitimate transaction from within the protocol.

## Tools Used
Manual review

## Recommended Mitigation Steps
Ensure the received `_token` and `amountLD`
ecrecover used but there is no 0(invalid ) check for response of erecover nor expiration of the signed data
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/07ef78215e3d246d47a410651906287c6acec3ef/src/UTBFeeCollector.sol#L44-L62


# Vulnerability details

## Impact
 ecrecover used but there is no 0(invalid ) check for response of  ecrecover 

in the function `collectFees` the contract `UTBFeeCollector` the  response of  ecrecover is not checked whether its 0 or not 

The solidity function ecrecover is used, however the error result of 0 is not checked for. See documentation: https://docs.soliditylang.org/en/v0.8.9/units-and-global-variables.html?highlight=ecrecover#mathematical-and-cryptographic-functions "recover the address associated with the public key from elliptic curve signature or return zero on error. "

Now you can supply invalid input parameters to the Sig.recover function, which will then result 0. If you also set o.maker to be 0 then this will match and an invalid signature is not detected.

So you can do all kinds of illegal & unexpected transactions.


## Proof of Concept
```solidity 
function collectFees(
        FeeStructure calldata fees,
        bytes memory packedInfo,
        bytes memory signature
    ) public payable onlyUtb {
        bytes32 constructedHash = keccak256(
            abi.encodePacked(BANNER, keccak256(packedInfo))
        );
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
        address recovered = ecrecover(constructedHash, v, r, s);
        require(recovered == signer, "Wrong signature");
        if (fees.feeToken != address(0)) {
            IERC20(fees.feeToken).transferFrom(
                utb,
                address(this),
                fees.feeAmount
            );
        }
    }
```
## Tools Used
vs code 
## Recommended Mitigation Steps
Verify that the result from ecrecover isn't 0




## Assessed type

Other