xanoutas/solidity-security-findings huggingface dataset:
`MagicLp.flashLoan` function can be exploited to empty the contract base and quote tokens balances
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/mimswap/MagicLP.sol#L290-L353


# Vulnerability details

## Impact

- `MagicLp.flashLoan` function enabes users to utilize the contract quote and base balances, but due to a missing balance check; the contracts balances can be drained, let's see the following scenario:

  ```javascript
  function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {

      // 1. @audit-issue : first the baseAmount & quoteAmount are transferred to the attacker contract, and these amounts are equal to the balance - 1wei , where 1 wei will be left in the contract for each token
          _transferBaseOut(assetTo, baseAmount);
          _transferQuoteOut(assetTo, quoteAmount);

      // 2. @audit-issue : the attacker contract calls `MagicLP.sync()` to update the reserves of the contract to the current balance; which is the 1 wei left
          if (data.length > 0) {
              ICallee(assetTo).FlashLoanCall(msg.sender, baseAmount, quoteAmount, data);
          }


      // 3. @audit-issue : now the baseBalance = 1 wei, and quoteBalance = 1 wei
          uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
          uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));

      // 4. @audit-issue : the baseBalance ==_BASE_RESERVE_ == 1 wei, and quoteBalance == _QUOTE_RESERVE_ == 1 wei, so the txn will not revert

          // no input -> pure loss
          if (baseBalance < _BASE_RESERVE_ && quoteBalance < _QUOTE_RESERVE_) {
              revert ErrFlashLoanFailed();
          }

     // 5. @audit-issue : this block will not be entered since baseBalance ==_BASE_RESERVE_ == 1 wei

          // sell quote case
          // quote input + base output
          if (baseBalance < _BASE_RESERVE_) {
              uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
              (uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) = querySellQuote(
                  tx.origin,
                  quoteInput
              );

              if (uint256(_BASE_RESERVE_) - baseBalance > receiveBaseAmount) {
                  revert ErrFlashLoanFailed();
              }

              _transferBaseOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
              if (_RState_ != uint32(newRState)) {
                  _QUOTE_TARGET_ = newQuoteTarget.toUint112();
                  _RState_ = uint32(newRState);
                  emit RChange(newRState);
              }
              emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);
          }



     // 6. @audit-issue : this block will not be entered since quoteBalance == _QUOTE_RESERVE_ == 1 wei

          // sell base case
          // base input + quote output
          if (quoteBalance < _QUOTE_RESERVE_) {
              uint256 ba
BLAST_POINTS_OPERATOR might be wrong
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/blast/libraries/BlastPoints.sol#L7


# Vulnerability details

## Impact
The following address for BLAST_POINTS_OPERATOR in the library is hardcoded constant, however when checking it on the blockexplorer of Blast, I couldn't associate it with anything. If it is wrong, then it would lead to the contracts calling this library to fail their calls.

## Proof of Concept
Missing data about this address on Blast explorer

## Tools Used
Manual review

## Recommended Mitigation Steps
Consider double checking if the address is valid


## Assessed type

Library
The lock() function does not check if the amount being locked is greater than or equal to the minLockAmount. 
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/staking/LockingMultiRewards.sol#L155-L167


# Vulnerability details

## Impact
it violates the intended behavior of the contract, which is to enforce a minimum lock amount for all locked positions. If a user is able to lock an amount below the `minLockAmount`, it could potentially lead to the following consequences:
1. Inconsistent state: The contract's state will be inconsistent, as there will be locked positions with amounts below the minimum threshold.
2. Potential security issues: Allowing users to create locks with small amounts could potentially lead to security issues or exploits, especially if there are calculations or operations that assume a certain minimum lock amount.
3. Difficult to manage: Having locked positions with amounts below the minimum threshold can make it more difficult to manage and reason about the contract's state and behavior.


## Proof of Concept
The lock() function does not check if the amount being locked is greater than or equal to the minLockAmount, while the _createLock() function does perform this check.
Here's the relevant code section:

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

           _updateRewardsForUser(msg.sender);

           _balances[msg.sender].unlocked -= amount;
           unlockedSupply -= amount;

           _createLock(msg.sender, amount);
       }
In the `lock()` function, the only check performed is that the amount is not zero. It then updates the user's rewards, reduces the unlocked balance and the unlockedSupply, and calls the `_createLock()` function to create a new lock for the user.
The `_createLock()` function performs the check for the `minLockAmount`:

       function _createLock(address user, uint256 amount) internal {
           // ... (omitted code)

           if (lockCount == maxLocks) {
               revert ErrMaxUserLocksExceeded();
           }

           if (amount < minLockAmount) {
               revert ErrLockAmountTooSmall();
           }

           // ... (omitted code)
       }


## Tools Used
Manual
## Recommended Mitigation Steps
The `lock()` function should also include a check for the `minLockAmount` before calling the `_createLock()` function. Here's how the code can be modified:

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

           if (amount < minLockAmount) {
               revert ErrLockAmountTooSmall();
           }

           _updateRewardsForUser(msg.sender);

           _balances[msg.sender].unlocked -= amount;
           unlockedSupply -= amount;

           _createLock(msg.sender, amount);
       }

By adding the check if (amount < minLockAmount) { revert ErrLockAmountTooSmall(); } in the lock() function, the contract ensures that
The Rescue function won't revert if the Token wasn't from the supported tokens
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

the rescue function is supposed to be used to transfer tokens to an address with a specific amount and it is guarded with an 'onlyOwner' modifier which gives access to admin only, the function should revert if the token is not a supported token.

since the '!' not character was forgotten, the function won't revert if the token isn't supported, allowing any token to be sent using the rescue function


## Proof of Concept

```
function rescue(address token, address to, uint256 amount) external onlyOwner {
        if (supportedTokens[token]) {
            revert ErrNotAllowed();
        }

        token.safeTransfer(to, amount);
        emit LogTokenRescue(token, to, amount);
    }
```
## Tools Used

Manual Review 

## Recommended Mitigation Steps

add the '!' character to make the function revert if the token is not a supported token

```
function rescue(address token, address to, uint256 amount) external onlyOwner {
       -- if (supportedTokens[token])
       ++ if (!supportedTokens[token])
 {
            revert ErrNotAllowed();
        }
       token.safeTransfer(to, amount);
        emit LogTokenRescue(token, to, amount);
    }
```


## Assessed type

Token-Transfer
If deposited token is different from MIM and USDB users won't be able to withdraw it
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

https://github.com/Abracadabra-money/abracadabra-money-contracts/blob/main/src/blast/BlastOnboardingBoot.sol#L162


# Vulnerability details

## Impact
If a user locks a token other MIM or USDB they won’t be able to withdraw it.

## Description
Only MIM and USDB are allowed for staking however users can deposit any token in the  whitelist. If they lock them they won't get any rewards and lose access to them because locked tokens cannot be withdrawn.

```
    function _claimable(address user) internal view returns (uint256 shares) {
        uint256 totalLocked = totals[MIM].locked + totals[USDB].locked;

        if (totalLocked == 0) {
            return 0;
        }

        uint256 userLocked = balances[user][MIM].locked + balances[user][USDB].locked;
        return (userLocked * totalPoolShares) / totalLocked;
    }
```

## Recommended Mitigation Steps
Remove the whitelist. Users should be able to deposit only MIM and USDB.


## Assessed type

Invalid Validation
Users can not get reward when they stake in `LockingMultiRewards` contract
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
When any user calls the `LockingMultiRewards::stake` function, `_updateRewardsForUser` is calculated according to the old `_balace`.
## Proof of Concept
Imagine a newly registered user calls `LockingMultiRewards::stake` function: 

1. It will redirect to `_stakeFor` function.
```solidity
    function stake(uint256 amount, bool lock_) public whenNotPaused {
        _stakeFor(msg.sender, amount, lock_);
    }
```

2. First it'll transfer tokens from user with safeTransferFrom at [line 464](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L464). There is no `_balances` update right now and for this user `_balances.locked` and `_balances.unlocked` is 0. Then it'll redirect it to `_updateRewardsForUser` function.
```solidity
    function _stakeFor(address account, uint256 amount, bool lock_) internal {
        if (amount == 0) {
            revert ErrZeroAmount();
        }

        // This staking contract isn't using balanceOf, so it's safe to transfer immediately
        stakingToken.safeTransferFrom(msg.sender, address(this), amount);
        stakingTokenBalance += amount;

        _updateRewardsForUser(account);

        if (lock_) {
            _createLock(account, amount);
        } else {
            _balances[account].unlocked += amount;
            unlockedSupply += amount;

            emit LogStaked(account, amount);
        }
    }
```

3. Since `balanceOf(user)` is calculated from `_balances` variable, it'll return 0 and `balance` variable in this function will be 0.  
```solidity
    function _updateRewardsForUser(address user) internal {
        uint256 balance = balanceOf(user);
        uint256 totalSupply_ = totalSupply();

        for (uint256 i; i < rewardTokens.length; ) {
            address token = rewardTokens[i];
            _udpateUserRewards(user, balance, token, _updateRewardsGlobal(token, totalSupply_));

            unchecked {
                ++i;
            }
        }
    }
```

Then with [line 563](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L563) it'll go `_udpateUserRewards` function, and then it'll go `_earned` function with balance=0.  Therefor reward will be 0 for this user. 

For other users, this reward calculation will be calculated with old `_balaces`, even though user transfers tokens in `_stakeFor` function.
## Tools Used
Manual Review
## Recommended Mitigation Steps
Balance should be updated before `_updateRewardsForUser(account);` in function `_stakeFor`. 


## Assessed type

Context
`MagicLpAggregator` is easily manipulatable through donation attacks to the MagicLP pool
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

Apart from not making sense at all, the price returned by `latestAnswer` in `MagicLpAggregator` is easily manipulatable

[MagicLpAggregator.sol#L37-L46](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;

        (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 following function, it first:
- obtains the price returned by the `baseOracle` (likely MIM/USD).
- obtains the price returned by the `quoteOracle` (likely USDB/USD).
- 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.

However, this is easily manipulatable. Let us first suppose that the answer returned by `MagicLpAggregator` is correct, that is it returns the correct price.

The MagicLP allows one to deposit tokens into the pool, and not redeem any shares via the `sync()` function

[MagicLP.sol#L134-L136](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L134)
```solidity
    function sync() external nonReentrant {
        _sync();
    }
```
which calls

[MagicLP.sol#L567C1-L579C6](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L567-L579)
```solidity
    function _sync() internal {
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));

        if (baseBalance != _BASE_RESERVE_) {
            _BASE_RESERVE_ = baseBalance.toUint112();
        }
        if (quoteBalance != _QUOTE_RESERVE_) {
            _QUOTE_RESERVE_ = quoteBalance.toUint112();
        }

        _twapUpdate();
    }
```

A malicious attacker can first transfer some amount of base tokens and quote tokens to the pool and call `sync()` to increase the reserves without increasing the total shares in the pool. Going back to our formula
TWAP manipulation
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/mimswap/MagicLP.sol#L560-L565


# Vulnerability details

## Impact
An attacker can perform a large swap in the MagicLP pool to change the cumulative price high, followed by swapping back the traded amount to reset the original price in the same txn. Since for the succeeding swap `timeElapsed==0`, the twap price does not update, leaving the manipulative price behind.

## Proof of Concept

This twap design is inherited from the DODO, which is slight different from the UniswapV3 TWAP. In uniswap, the price update is based on the price at the end of a block. So, even if attacker try to manipulate, they have to wait few blocks to reflect the manipulated price which induce the risk of arbitrage. 

However, that's not the case here. After making a large swap via sellBase/sellQuote, the `_setReserve` internally calls the `_twapUpdate` afterwards.
```solidity
    function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
        _BASE_RESERVE_ = baseReserve.toUint112();
        _QUOTE_RESERVE_ = quoteReserve.toUint112();

        _twapUpdate();
    }
```

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/mimswap/MagicLP.sol#L262
https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/mimswap/MagicLP.sol#L285

## Tools Used
Manual review
## Recommended Mitigation Steps
Only update the twap price for the past block, before changing quote/baseReserve. 


## Assessed type

Invalid Validation
A faulty inheritance relationship of BlastOnboardingBoot leading to DoS
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
BlastOnboardingBoot inherits from BlastOnboardingBootDataV1, which inherits from BlastOnboardingData, and in the constructor, the governor is address(this) and there are no functions such as claimGasYields and claimTokenYields, which leads to a DoS, making it impossible to fetch these blast-specific money. And other functions like deposit and withdraw are not inherited, either

## Proof of Concept
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L34
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L23

## Tools Used
Mannual Audit

## Recommended Mitigation Steps
Make BlastOnboardingBoot inherit from BlastOnboarding.





## Assessed type

DoS
Flash Loan Exploitation Allows Drainage of Pool Without Repayment
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L290
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L134
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L567


# Vulnerability details

## Impact
Attacker can drain entire pool with `flashLoan()` by manipulating `_BASE_RESERVE_` and `_QUOTE_RESERVE_` with custom logic in callback function.

## Proof of Concept
The `flashLoan()` function in the contract enables users to borrow base and quote tokens, provided they repay the loan within the same transaction. Additionally, the recipient of the loan must implement the `ICallee.FlashLoanCall()` callback function which they can use to execute custom logic.

After the callback function, there are 3 scenarios:

Insufficient Repayment Check: it proceeds to check `baseBalance` against `_BASE_RESERVE_` and `quoteBalance` against `_QUOTE_RESERVE_`. If both the base and quote token balances are less than their respective reserves (`_BASE_RESERVE_` and `_QUOTE_RESERVE_`), the function reverts, indicating that the caller failed to repay the flash loan.

```solidity
        // no input -> pure loss
        if (baseBalance < _BASE_RESERVE_ && quoteBalance < _QUOTE_RESERVE_) {
            revert ErrFlashLoanFailed();
        }
```

Quote Token Balance Adjustment: If `baseBalance` is below `_BASE_RESERVE_`, the function simulates a `sellQuote()` operation by calculating the additional quote token needed to cover the shortfall. If `receiveBaseAmount` is less than the calculated difference, the function reverts, indicating insufficient repayment.

```solidity
        // sell quote case
        // quote input + base output
        if (baseBalance < _BASE_RESERVE_) {
            uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
            (uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) = querySellQuote(
                tx.origin,
                quoteInput
            );

            if (uint256(_BASE_RESERVE_) - baseBalance > receiveBaseAmount) {
                revert ErrFlashLoanFailed();
            }

            _transferBaseOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
            if (_RState_ != uint32(newRState)) {
                _QUOTE_TARGET_ = newQuoteTarget.toUint112();
                _RState_ = uint32(newRState);
                emit RChange(newRState);
            }
            emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);
        }
```

Base Token Balance Adjustment: If `quoteBalance` is below `_QUOTE_RESERVE_`, the function simulates a `sellBase()` operation by calculating the additional base token needed to cover the shortfall. If `receiveQuoteAmount` is less than the calculated difference, the function reverts, indicating insufficient repayment. 

```solidity
        // sell base case
DegenBox#flashLoan can be re-entrancy
Audit: 2024-03-abracadabra-money-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/DegenBox.sol#L340


# Vulnerability details

## Impact

The borrower/attacker calls `deposit` increment `_tokenBalanceOf(token)` in `onFlashLoan`, skips the verification of return token in `flashLoan`,
Steal the token.

## Proof of Concept

```solidity
    function flashLoan(IFlashBorrower borrower, address receiver, IERC20 token, uint256 amount, bytes calldata data) public {
        uint256 fee = amount.mul(FLASH_LOAN_FEE) / FLASH_LOAN_FEE_PRECISION;
        token.safeTransfer(receiver, amount);

        borrower.onFlashLoan(msg.sender, token, amount, fee, data);

@>      require(_tokenBalanceOf(token) >= totals[token].addElastic(fee.to128()), "BentoBox: Wrong amount");
        emit LogFlashLoan(address(borrower), token, amount, fee, receiver);
    }
    
    function _tokenBalanceOf(IERC20 token) internal view returns (uint256 amount) {
        amount = token.balanceOf(address(this)).add(strategyData[token].balance);
    }
```

As you can see from the above function,`flashLoan` verifies the balance of the token after calling the `borrower.onFlashLoan` to ensure that the borrower has returned the borrowed token.

The problem is that the borrower/attacker can call the `deposit` function in the contract in `onFlashLoan`, increasing the balance of the token.

The attacker then flashLoan his own token, the attacker can steal all the tokens in the contract.

Let's look at the deposit function, which adds the balance of the token:

```solidity
    function deposit(
        IERC20 token_,
        address from,
        address to,
        uint256 amount,
        uint256 share
    ) public payable allowed(from) returns (uint256 amountOut, uint256 shareOut) {
        .....
        if (token_ == USE_ETHEREUM) {
            IWETH(address(wethToken)).deposit{value: amount}();
        } else if (from != address(this)) {
@>          token.safeTransferFrom(from, address(this), amount);
        }
        .....
    }
```

## Tools Used
vscode, manual

## Recommended Mitigation Steps
Add ReentrancyGuard



## Assessed type

Reentrancy