xanoutas/solidity-security-findings huggingface dataset:
Griefing attack: small stakes reward griefing due to rounding down to 0
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L528-#L534


# Vulnerability details

## Vulnerability details
Whenever any operation with the given user as a beneficiary is performed, this user's rewards are checkpointed via function `_updateRewardsGlobal()`, which calculates the reward checkpoint by a call to function `_rewardPerToken`

    function _rewardPerToken(address rewardToken, uint256 lastTimeRewardApplicable_, uint256 totalSupply_) public view returns (uint256) {
        if (totalSupply_ == 0) {
            return _rewardData[rewardToken].rewardPerTokenStored;
        }

        uint256 timeElapsed = lastTimeRewardApplicable_ - _rewardData[rewardToken].lastUpdateTime;
        uint256 pendingRewardsPerToken = (timeElapsed * _rewardData[rewardToken].rewardRate * 1e18) / totalSupply_;

        return _rewardData[rewardToken].rewardPerTokenStored + pendingRewardsPerToken;
    }

`rewardRate` is set in `notifyRewardAmount` function as below:

        reward.rewardRate = amount / _remainingRewardTime;

It is clearly that `pendingRewardsPerToken` can be equal to 0. Consider this mock scenario:

- `timeElapsed` = 12 seconds (equal to the time of a block)
- `_rewardData[rewardToken].rewardRate` = 2
- `totalSupply_` = 1e22
- `_rewardData[rewardToken].rewardPerTokenStored` = x

So the result of the function is 
`x + 12 * 2 * 1e18 / 1e22`
Which is equal to
` x + 24e18 / 1e22` = `x` due to rounding down

Which is incorrect as the calculation does not account for the rewards accumulated since the last checkpoint.

Attacker can griefing attack by continously calling `stake` or `lock` function with dust amount, since they only revert when amount = 0:

    function _stakeFor(address account, uint256 amount, bool lock_) internal {
        if (amount == 0) {
            revert ErrZeroAmount();  // <---
        }

    function lock(uint256 amount) public whenNotPaused {
        if (amount == 0) {
            revert ErrZeroAmount();  // <--
        }

## Impact
Incorrect checkpoint value being stored for the beneficiary, which will affect future reward calculations for this beneficiary.

## Tools Used
Manual review

## Recommended Mitigation Steps
Use validation checks to ensure that the required calculation doesn't round down to zero:

       require(pendingRewardsPerToken >= totalSupply_, "rounding down to 0")


## Assessed type

Other
`MagicLPAggregator` will return wrong price for base tokens less than 18 decimals.
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L37-L46


# Vulnerability details

## Impact
`MagicLPAggregator` will return wrong price for tokens less than 18 decimals.

The `MagicLPAggregator` intends to return price of a LP in terms of USD in 18 decimals.

[MagicLpAggregator.sol#L29-L31](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L29-L31)
```solidity
 function decimals() external pure override returns (uint8) {
        return 18;
}
```

But let's take a look at the following function.
[MagicLpAggregator.sol#L37-L46](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L37-L46)
```solidity
    function latestAnswer() public view override returns (int256) {
        uint256 baseAnswerNomalized = uint256(baseOracle.latestAnswer()) * (10 ** (WAD - baseOracle.decimals()));
        uint256 quoteAnswerNormalized = uint256(quoteOracle.latestAnswer()) * (10 ** (WAD - quoteOracle.decimals()));
        uint256 minAnswer = baseAnswerNomalized < quoteAnswerNormalized ? baseAnswerNomalized : quoteAnswerNormalized;

        (uint256 baseReserve, uint256 quoteReserve) = _getReserves();
        baseReserve = baseReserve * (10 ** (WAD - baseDecimals));
        quoteReserve = quoteReserve * (10 ** (WAD - quoteDecimals));
        return int256(minAnswer * (baseReserve + quoteReserve) / pair.totalSupply());
    }
```

Let us break down the above function, it first: 
- obtains the price returned by the `baseOracle`
- obtains the price returned by the `quoteOracle` 
- finds the minimum price returned by the two and store in `minAnswer` 
- obtains the total `baseReserve + quoteReserve` of the MagicLP 
- takes `minAnswer * (baseReserve + quoteReserve) / pair.totalSupply()` 
- note: pair.totalSupply() is the total number of MagicLP shares in the pool.

But if you look at the above closely, you will notice that although the function seems correct it outputs the price in the wrong decimals (not 18):

- We know that `minAnswer` will always be the normalized oracle price to 18 decimals

- `(baseReserve + quoteReserve)` will always be the normalized token prices to 18 decimals

- However, `pair.totalSupply()` is not normalized, and depends on the decimals of the base token!

[MagicLP.sol#L163-L165](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L163-L165)
```solidity
    function decimals() public view override returns (uint8) {
        return IERC20Metadata(_BASE_TOKEN_).decimals();
    }
```

Therefore if `IERC20Metadata(_BASE_TOKEN_).decimals()` is not 18 decimals, let us say 6 decimals (ie. USDT token)

Than the decimals for the final output isn't correct as 18 + 18 - 6 = 30 decimals which is 10^12 times the intended price we wanted to return.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Normalize `pair.
In `MagicLpAggregator.latestAnswer()` and `MagicLpAggregator.latestRoundData()`, there is missing check for active Arbitrum Sequencer
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L37-L50


# Vulnerability details

## Impact
The smart contracts is expected to be deployed on Blast blockchain which is L2 EVM-compatible Optimistic Rollup supporting native yield. The issue here is, if the Blast [Sequencer](https://docs.blast.io/building/transaction-finality#finalized) goes down, oracle data will not be kept up to date, and thus could become stale. However, users are able to continue to interact with the protocol directly through the L1 optimistic rollup contract. You can review [Chainlink docs](https://docs.chain.link/data-feeds/l2-sequencer-feeds#optimism-base-metis-and-scroll) on L2 Sequencer Uptime Feeds for more details on this.

> Optimistic rollup protocols move all execution off the layer 1 (L1) Ethereum chain, complete execution on a layer 2 (L2) chain, and return the results of the L2 execution back to the L1. These protocols have a sequencer that executes and rolls up the L2 transactions by batching multiple transactions into a single transaction.

> If a sequencer becomes unavailable, it is impossible to access read/write APIs that consumers are using and applications on the L2 network will be down for most users without interacting directly through the L1 optimistic rollup contracts. The L2 has not stopped, but it would be unfair to continue providing service on your applications when only a few users can use them.

However, this L2 Sequencer is missing in current implemetation which can be seen [here](https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L37-L50),

As a result, users may be able to use the protocol while oracle feeds are stale. This could cause many problems.

For example:

1)A user has an account with 100 tokens, valued at 1 ETH each, and no borrows

2)The Arbitrum sequencer goes down temporarily

3)While it’s down, the price of the token falls to 0.5 ETH each

4)The current value of the user’s account is 50 ETH, so they should be able to borrow a maximum of 200 ETH to keep account healthy ((200 + 50) / 200 = 1.2) Because of the stale price, the protocol lets them borrow 400 ETH ((400 + 100) / 400 = 1.2)

If the Blast sequencer goes down, the protocol will allow users to continue to operate at the previous (stale) rates.

## Proof of Concept
https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L37-L50


## Tools Used
Manual review 

## Recommended Mitigation Steps

Use sequencer oracle to determine whether the sequencer is offline or not, and don't allow orders to be executed while the sequencer is offline.

Check the Chainlink Documentation for a full example: check [here](https://docs.chain.link/data-feeds/l2-sequencer-feeds#example-code)

Also ch
No Withdraw Function
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L13


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

There's no function to withdraw ETH sent to the contract, which could lead to funds being locked.
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

contract BlastGovernor is OperatableV2 {
    event LogFeeToChanged(address indexed feeTo);
    error ErrZeroAddress();

    address public feeTo;

 @>   receive() external payable {}

## Tools Used

## Recommended Mitigation Steps

There should be a withdrawal function so that there is no eth struck in the contract.

++ function withdrawMoney() external onlyOwner {
++   (bool success, ) = msg.sender.call{ value: address(this).balance } ("");
 ++  require(success, "Transfer failed.");
}




## Assessed type

Context
_adjustAddLiquidity and buyShares have different calculation method
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L523


# Vulnerability details

## Impact
In the calculation of the LP's share, a different calculation is used, making it possible to have more share

## Proof of Concept
https://github.com/DODOEX/contractV2/blob/main/contracts/SmartRoute/proxies/DODODspProxy.sol#L162
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L523
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L381

## Tools Used
Mannual Audit

## Recommended Mitigation Steps
using the same calculation method


## Assessed type

Invalid Validation
Missing Return Statement in `_getReserves` Function in `MagicLpAggregator` Contract
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L34


# Vulnerability details

The `MagicLpAggregator` contract contains a flaw within the `_getReserves` function where it fails to return the reserve values fetched from the `pair` contract. This oversight results in the `latestAnswer` function always returning zero, which can have severe implications for any systems that depend on this contract for accurate liquidity pool pricing data.

## Impact
The missing return statement in the `_getReserves` function leads to the `latestAnswer` function always returning zero. This affects any dependent systems or contracts that rely on accurate price data from the `MagicLpAggregator` contract, as they will receive incorrect information, potentially leading to financial loss or system failure.

## Proof of Concept
The `_getReserves` function in the provided code snippet does not return the fetched reserves:

```solidity
33:    function _getReserves() internal view virtual returns (uint256, uint256) {
34:        (uint256 baseReserve, uint256 quoteReserve) = pair.getReserves();
35:    }
```
https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L33C5-L35C6

Due to the missing return statement, the latestAnswer function uses uninitialized variables for `baseReserve` and `quoteReserve`, which default to zero:

```solidity
function latestAnswer() public view override returns (int256) {
    // ...
    (uint256 baseReserve, uint256 quoteReserve) = _getReserves();
    baseReserve = baseReserve * (10 ** (WAD - baseDecimals)); // baseReserve defaults to 0
    quoteReserve = quoteReserve * (10 ** (WAD - quoteDecimals)); // quoteReserve defaults to 0
    // ...
}
```
https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L42C1-L44C69

## Tools Used
Manual Review

## Recommended Mitigation Steps
Update `_getReserves()`

```diff
-    function _getReserves() internal view virtual returns (uint256, uint256) {
+    function _getReserves() internal view virtual returns (uint256 baseReserve, uint256 quoteReserve) {
-        (uint256 baseReserve, uint256 quoteReserve) = pair.getReserves();
+        (baseReserve, quoteReserve) = pair.getReserves();
     }
```


## Assessed type

Other
claimTokenYields function can lead to DoS; _updateRewardsForUsers nested loops risk gas exhaustion.
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/blast/BlastOnboarding.sol#L164-L173
https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/staking/LockingMultiRewards.sol#L572-L593


# Vulnerability details

1. BlastOnboarding - `claimTokenYields` function:

`claimTokenYields` function in the BlastOnboarding contract accepts an array of token addresses as input and iterates over them to claim token yields. The loop length is determined by the size of the input array, which could potentially be large. This can lead to excessive gas consumption and potential DoS (Denial of Service) attacks.

## Impact
An attacker can pass a large array of token addresses to the `claimTokenYields` function, causing the loop to iterate over a significant number of tokens. This can result in the transaction exceeding the gas limit and failing, effectively preventing other users from interacting with the contract and claiming their token yields.

Let's consider a scenario where an attacker creates an array of 1000 token addresses and calls the `claimTokenYields` function with this array. The function will iterate over all 1000 tokens, claiming yields for each one. This can lead to a significant increase in gas consumption, potentially exceeding the gas limit and causing the transaction to fail. As a result, other users may be unable to claim their token yields until the gas limit is increased or the attack subsides.

Code: [claimTokenYields](https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/blast/BlastOnboarding.sol#L164-L173)
```solidity
function claimTokenYields(address[] memory tokens) external onlyOwner {
    for (uint256 i = 0; i < tokens.length; i++) {
        if (!supportedTokens[tokens[i]]) {
            revert ErrUnsupportedToken();
        }
        if (registry.nativeYieldTokens(tokens[i])) {
            BlastYields.claimAllTokenYields(tokens[i], feeTo);
        }
    }
}
```

## Recommendation
Consider implementing a reasonable limit on the number of token addresses that can be passed to the `claimTokenYields` function. This can be done by adding a check on the length of the input array and reverting if it exceeds a predefined threshold.

2. LockingMultiRewards - `_updateRewardsForUsers` function:

`_updateRewardsForUsers` function in the LockingMultiRewards contract has nested loops: an outer loop over reward tokens and an inner loop over user addresses. The loop lengths are determined by the number of reward tokens and the size of the input array of user addresses, respectively. This can lead to excessive gas consumption and potential DoS attacks.

## Impact
An attacker can manipulate the input array of user addresses passed to the `_updateRewardsForUsers` function, providing a large number of user addresses. This can cause the function to iterate over a significant number of
Manipulation of oracles and flashloan attacks risk inaccurate pricing, exploitation.
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L33-L35
https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L37-L46


# Vulnerability details

## Vulnerability Details
1. Manipulation of the underlying token oracles:
   - **Impact:** If the `baseOracle` or `quoteOracle` is manipulated or provides incorrect pricing, it can lead to a distorted price calculation by the `MagicLpAggregator`. This can result in incorrect pricing information being used by other contracts or users relying on this oracle, potentially leading to financial losses or exploits.

   - **Root Cause:** The `MagicLpAggregator` contract relies on external oracles (`baseOracle` and `quoteOracle`) for the prices of MIM and USDB tokens. If these oracles are not secure or can be manipulated, it introduces a point of failure in the price calculation.

   - **Scenario:** An attacker manipulates the price feed of the `baseOracle` to report a significantly lower price for the MIM token. When the `latestAnswer` function is called on the `MagicLpAggregator`, it retrieves the manipulated price from the `baseOracle` and uses it in the price calculation. As a result, the `MagicLpAggregator` reports an incorrect price for the MIM/USDB pair, which can be exploited by the attacker for financial gain.

   - Code: [latestAnswer](https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/oracles/aggregators/MagicLpAggregator.sol#L37-L46)
     ```solidity
     function latestAnswer() public view override returns (int256) {
         uint256 baseAnswerNomalized = uint256(baseOracle.latestAnswer()) * (10 ** (WAD - baseOracle.decimals()));
         uint256 quoteAnswerNormalized = uint256(quoteOracle.latestAnswer()) * (10 ** (WAD - quoteOracle.decimals()));
         uint256 minAnswer = baseAnswerNomalized < quoteAnswerNormalized ? baseAnswerNomalized : quoteAnswerNormalized;
         // ...
     }
     ```

2. Flashloan attacks on the liquidity pool:
   - **Impact:** If an attacker can manipulate the reserves of the MIM/USDB liquidity pool using flashloans, it can distort the price calculation of the `MagicLpAggregator`. This can lead to incorrect pricing information and potential exploitation of contracts or users relying on this oracle.

   - **Root Cause:** The `MagicLpAggregator` calculates the price based on the current reserves of the MIM/USDB liquidity pool. If the pool is vulnerable to flashloan attacks, an attacker can temporarily inflate or deflate the reserves, influencing the price calculation.

   - **Scenario:** An attacker takes out a large flashloan of MIM tokens and temporarily inflates the MIM reserves in the liquidity pool. When the `latestAnswer` function is called on the `MagicLpAggregator`, it retrieves the inflated reserves and
The oracle price fetch in `OUSGInstantManager::getOUSGPrice()` does not have sanity check if the price is unexpectedly high
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L472-L485


# Vulnerability details

## Impact
While the function has sanity check if the price is unexpectedly low, it lacked the sanity check if the price is unexpectedly high. 

- [`getOUSGPrice()`](https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L472-L485)
    ```solidity
    /**
    * @notice Returns the current price of OUSG in USDC
    *
    * @dev Sanity check: this function will revert if the price is unexpectedly low
    *
    * @return price The current price of OUSG in USDC
    */
    function getOUSGPrice() public view returns (uint256 price) {
        (price, ) = oracle.getPriceData();
        require(
        price > MINIMUM_OUSG_PRICE, // @audit has covered the unexpected low side but not the high side
        "OUSGInstantManager::getOUSGPrice: Price unexpectedly low"
        );
    }
    ``` 

This will threaten the solvency of the protocol because if the price of OUSG is unexpectedly too high (doesn't match the actual price), then when the users redeem, they will receive more USDC than what is rightfully should. The protocol will absorb these losses.

## Proof of Concept
## Tools Used
Manual Review

## Recommended Mitigation Steps
Record the previous price and compare it to the current fetched price. Should the disparity between the two price is too large in percentage, then revert the transaction. As to how much price disparity is "too large", it's up to the protocol team to decide.

This fix will cover both the unexepectedly too high and too low price.


## Assessed type

Oracle