xanoutas/solidity-security-findings huggingface dataset:
Pool Creation Failure Due to WETH Transfer Compatibility Issue on Some Chains
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

In the `Router.sol` file of the `mimswap`, there's a method to create a pool for native tokens by wrapping them to their "wrapped" counterpart before sending them to the newly created pool.

```solidity
src/mimswap/periphery/Router.sol:
  73:     function createPoolETH(
  74:         address token,
  75:         bool useTokenAsQuote,
  76:         uint256 lpFeeRate,
  77:         uint256 i,
  78:         uint256 k,
  79:         address to,
  80:         uint256 tokenInAmount
  81:     ) external payable returns (address clone, uint256 shares) {
  82:         if (useTokenAsQuote) {
  83:             _validateDecimals(18, IERC20Metadata(token).decimals());
  84:         } else {
  85:             _validateDecimals(IERC20Metadata(token).decimals(), 18);
  86:         }
  87: 
  88:         clone = IFactory(factory).create(useTokenAsQuote ? address(weth) : token, useTokenAsQuote ? token : address(weth), lpFeeRate, i, k);
  89: 
  90:         weth.deposit{value: msg.value}();
  91:         token.safeTransferFrom(msg.sender, clone, tokenInAmount);
  92:         address(weth).safeTransferFrom(address(this), clone, msg.value);
  93:         (shares, , ) = IMagicLP(clone).buyShares(to);
  94:     }
```

However, the transfer done using `address(weth).safeTransferFrom` (see line 92). This works fine on most chains (Ethereum, Optimism, Polygon, BSC) which uses the standard `WETH9` contract that handles the case  when `src == msg.sender`:

```solidity
WETH9.sol
        if (src != msg.sender && allowance[src][msg.sender] != uint(- 1)) {
            require(allowance[src][msg.sender] >= wad);
            allowance[src][msg.sender] -= wad;
        }
```

The problem is that the `WETH` implementation on `Blast` uses [a different contract](https://blastscan.io/address/0x4300000000000000000000000000000000000004#code), and does not have this `src == msg.sender` handling.

Also, the issue is presented in [Wrapped Arbitrum](https://arbiscan.io/address/0x8b194beae1d3e0788a1a35173978001acdfba668#code) and  [Wrapped Fantom](https://ftmscan.com/token/0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83#code).

## Impact
The failure to approve the `Router` contract to spend `WETH` tokens will prevent the protocol from creating native tokens pools on multiple chains like Blast.

## Proof of Concept
Run test using this command

```bash
forge test --match-test testPoC_TransferFromRevert --fork-url https://rpc.blast.io
```

```solidity
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "forge-std/console.sol";
import {IERC20} from "forge-std/interfaces/IERC20.sol";

contract PairTest is Test {
    address alice = address(0xf683Ce59521AA464066783d78e40CD9412f33D21);
    address bob = address(0x2);
    // WETH address on Blast network
    IERC20 public constant WETH = IERC20(0x43000000000000000000
Tokens yeild can not be set to claimable.
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The enabling of yeild on rebasing tokens can be permanently DOS-ed

## Proof of Concept
Abracadabra uses this signature for the configure function for the rebasing function of WETH and USDB

solidity
    function configure(YieldMode) external returns (YieldMode);


But the actual signature for this is 

solidity
   function configure(YieldMode yieldMode) external returns (uint256) 

And it returns the balance of caller

https://github.com/blast-io/blast/blob/c39cdf1fa7ef9e0d4eaf64a7a5cf7b3c46c739fd/blast-optimism/packages/contracts-bedrock/src/L2/ERC20Rebasing.sol#L220C2-L226

so if the balance of contract is above 2, this function becomes unusable and can't configure the yeild to be claimable. 

and the following function always fail

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

This can be exploited by attacker by donating a very little amount of eth to the magicLP, blastOnBoarding and now the blastOnBoarding can't make yeild claimable when the following function is invoked. 

https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/src/blast/BlastOnboarding.sol#L175C14-L183



## Tools Used
Manual Review

## Recommended Mitigation Steps
Use the correct signature used in blast codebase.


## Assessed type

DoS
Miscalculation in addLiquidity of Router results in unauthorized spending of tokens
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description

Users are expected to add liquidity through `addLiquidity()` of the Router. It receives base and quote amounts which are adjusted through `_adjustAddLiquidity()`:

```
function addLiquidity(
    address lp,
    address to,
    uint256 baseInAmount,
    uint256 quoteInAmount,
    uint256 minimumShares,
    uint256 deadline
) external ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
    (baseAdjustedInAmount, quoteAdjustedInAmount) = _adjustAddLiquidity(lp, baseInAmount, quoteInAmount);
    IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, baseAdjustedInAmount);
    IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, quoteAdjustedInAmount);
    shares = _addLiquidity(lp, to, minimumShares);
}
```

The adjustment function has DODOv2 code which determines the effective base:quote ratio and adjusts the higher of the two input values so that no input is wasted. Additionally Abrakadbra inserted the following logic at the start of the function:
```
(uint256 baseReserve, uint256 quoteReserve) = IMagicLP(lp).getReserves();
uint256 baseBalance = IMagicLP(lp)._BASE_TOKEN_().balanceOf(address(lp)) + baseInAmount;
uint256 quoteBalance = IMagicLP(lp)._QUOTE_TOKEN_().balanceOf(address(lp)) + quoteInAmount;
baseInAmount = baseBalance - baseReserve;
quoteInAmount = quoteBalance - quoteReserve;
```

The intention is that if the current balance of token X is higher than reserve, the input should be reduced so that only the delta needs to be sent. However the logic is miswritten.
The balanceOf() and getReserve() calls have been reversed. The correct logic should be:
`requested amount = reserves + requested amount - balance`
Instead it is:
`requested amount = balance + requested amount - reserves`. 
The higher the current `balanceOf()`, the higher the final requested amount becomes. This is a critical issue because tokens can be donated to the MagicLP by an attacker, making the victim send more tokens than expected. The POC gives an example of such a sequence.

Essentially this makes addLiquidity(amountX, amountY, minShares) become: (amountX2, amountY2, minShares) where
amountX2 > amountX, amountY2 > amountY. By definition this is unauthorized use of the victim's funds.

## Impact

Unauthorized spending of victim's tokens, leading to monetary losses.

## Proof of Concept

1. MagicLP has (500 USDC, 500 DAI) reserves and balances, 100 shares
2. Victim calls addLiquidity for (1000 USDC, 1000 DAI) , min 200 shares
3. Attacker frontrans and donates (1000 USDC, 500 DAI) to the LP
4. addLiquidity() executes:
- baseInAmount = 1500 + 1000 - 500 = 2000 USDC
- quoteInAmount = 1000 + 1000 - 500 = 1500 DAI
- baseIncreaseRatio = 2000 / 500 = 4
- quoteIncreaseRatio = 1500 / 500 = 3
- quoteAdjustedInAmou
Loss of assumed functionality of the Onboarding contract in a highly-sensitive area
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description

The BlastOnboardingBoot contract contains the bootstraping funds. After launch it will start a reward staking pool and allow users to claim tokens into it. This is in the launch sequence:
```
staking = new LockingMultiRewards(pool, 30_000, 7 days, 13 weeks, address(this));
staking.setOperator(address(this), true);
staking.transferOwnership(owner);
// Approve staking contract
pool.safeApprove(address(staking), totalPoolShares);
```

Note that the code approves the staking pool towithdraw from the created LP. 
The code also provisions setting of the staking contract to another one. 
```
// Just in case we need to change the staking contract after
// the automatic bootstrapping process
function setStaking(LockingMultiRewards _staking) external onlyOwner {
    if (ready) {
        revert ErrCannotChangeOnceReady();
    }
    staking = _staking;
    emit LogStakingChanged(address(_staking));
}
```

The issue is that this function lacks two actions:
- Resetting the previous staking pool's approval to zero
- More importantly, approving the new staking pool all the bootstrap pool shares

As a result, owner which assumes changing to a new staking pool is perfectly safe, will in fact cause reverts when users try claiming in the new pool. It is still possible to revert back to the old staking pool, but in fact it will never be possible to migrate to a new pool as no other approve() call exists in the contract.

## Impact

Loss of assumed functionality of the Onboarding contract in a highly-sensitive area.

## Tools Used

Manual audit

## Recommended Mitigation Steps

- Approve the new contract
- Revoke approval from the previous contract for good sanitization


## Assessed type

ERC20
A user's tokens could be locked for an extended duration beyond their intention and without their control
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description

The LockingMultiRewards allows anyone to stake MagicLP tokens in return for rewards.
LP tokens are sent with either functions below:
```
function stake(uint256 amount, bool lock_) public whenNotPaused {
    _stakeFor(msg.sender, amount, lock_);
}
/// @notice Locks an existing unlocked balance.
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);
}
```

This will lead to `_createLock()`, which calculates the `nextUnlockTime()`:

```
function _createLock(address user, uint256 amount) internal {
    Balances storage bal = _balances[user];
    uint256 _nextUnlockTime = nextUnlockTime();
```
```
function nextUnlockTime() public view returns (uint256) {
    return nextEpoch() + lockDuration;
}
```

Note that `nextEpoch()` would always return the next 1-week (or reward duration) slot.
```
function epoch() public view returns (uint256) {
    return (block.timestamp / rewardsDuration) * rewardsDuration;
}
function nextEpoch() public view returns (uint256) {
    return epoch() + rewardsDuration;
}
```

An issue arises because the user cannot specify the latest desired unlock time. This opens the path for the tokens to be locked for longer than expected, which may have significant impact for users if they need the funds. Consider the following case, where `x` is week number:
- It is day 7x + 6 (one day from nextEpoch)
- Assumed unlock time is `7x + 7 + lockDuration`
- The TX does not execute in next 1 day for any reason (gas price went up, validator does not include TX, etc)
- It is now day 7x+7, another epoch starts. Now nextEpoch is `7x + 14`
- Executed unlock time is `7x + 14 + lockDuration`

This means user's funds are locked for an additional 7 days more than expected.

Note that delayed execution of a user's TX has always been considered in scope, certainly for Med severity impacts:
	- https://solodit.xyz/issues/m-13-interactions-with-amms-do-not-use-deadlines-for-operations-code4rena-paraspace-paraspace-contest-git
	- https://solodit.xyz/issues/m-04-lack-of-deadline-for-uniswap-amm-code4rena-asymmetry-finance-asymmetry-contest-git
	- https://solodit.xyz/issues/m-03-missing-deadline-param-in-swapexactamountout-allowing-outdated-slippage-and-allow-pending-transaction-to-be-executed-unexpectedly-code4rena-pooltogether-pooltogether-git

Essentially the locking function lacks a `deadline` parameter similar to swapping functions, and the impact is temporary freeze of funds.

## Impact

A user's tokens could be locked for an extended duration beyond their intention and without their control.

## Tools Used

Manual audit

## Recommended Mitigation St
MagicLpAggregator always returns lower than correct answer, leading to arbitrage loss
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


# Vulnerability details

## Description

MagicLpAggregator is used to price LP tokens for "closely-tied" underlying tokens. It calculates the price below:
```
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());
}
```

The code takes the minimal answer between the underlying oracles and considers all reserves to be worth that amount:
`return int256(minAnswer * (baseReserve + quoteReserve) / pair.totalSupply());`

The issue is that any difference in price between the assets represents an easy arbitrage opportunity. Suppose we have tokens (A,B), where real oracle shows:
- A = $0.99
- B = $1

The Pool has 1000000 LP tokens and contains:
- 1000000 A
- 1000000 B

The LP value would calculate as:
`0.99 * 2000000 / 1000000 = $1.98`
The actual value is:
`(0.99 * 1000000 + 1 * 1000000) / 1000000 = $1.99`

Suppose a platform trades LPs using the aggregator pricing. An attacker could:
- Buy 100,000 LP tokens at $198000
- Withdraw from the pool the underlying shares
- Sell 100,000 A, 100,000 B at $199000
- Profit $1000 from the exchange, when the difference is just $0.01 (this is very common fluctuation even with pegged assets).

The delta comes at the expense of LP holders whose position gets minimized.

## Impact

Loss of value due to arbitrage of any platform using MagicLpAggregator pricing.

## Tools Used

Manual audit

## Recommended Mitigation Steps

Always calculate the value based on the real underlying token value multiplied by amount.

Consider creating two separate oracles for lower-bound and upper-bound results. Then a lending protocol could indeed use the lower-bound for determining collateral value.


## Assessed type

MEV
Permanent loss of yield for stakers in reward pools due to precision loss.
Audit: 2024-03-abracadabra-money-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description

The LockingMultiRewards contract facilitates distribution of rewards across the remaining epoch duration. The calculation for reward rate is provided below:
`reward.rewardRate = amount / _remainingRewardTime;`

The `rate` is later multiplied by the  duration to get the total rewards for elapsed time, demonstrated below:
```
function rewardsForDuration(address rewardToken) external view returns (uint256) {
    return _rewardData[rewardToken].rewardRate * rewardsDuration;
}
```

An issue occurs because there is no sufficient wrapping of the amount before dividing by `_remainingRewardTime`. The number is divided and later multiplied by elapsed time, causing a loss of precision of the amount modulo remaining time. For the provided period of `1 week` by the sponsor, the maximum amount lost can be calculated:
`7 * 24 * 3600 - 1 = 604799`. 
Note that the average case loss is half of the worst case assuming even distribution across time. However since rewards are usually not sent at the low end of remaining time (see the `minRemainingTime` variable, the actual average would be higher).

The effect of this size of loss depends on the decimals and value of the reward token. For USDC, this would be $0.6. For WBTC,  it would be $423 (at time of writing). The loss is shared between all stakers relative to their stake amount. The loss occurs for every notification, so it is clear losses will be severe. 

Sponsor remarked in the channel that reward tokens would be real, popular tokens. We believe USDC / WBTC on ARB are extremely popular tokens and therefore remain fully in scope as reward tokens. 

## Severity Rationalization
Impact - high
Likelihood - medium
-> Severity - high

## Impact

Permanent loss of yield for stakers in reward pools due to precision loss.

## Proof of Concept

- Reward pool offers WBTC rewards
- Epoch has just started, 

## Tools Used

Manual audit

## Recommended Mitigation Steps

Store the `rewardRate` scaled by 1e18, so loss of precision will be lower by magnitude of `1e18`.


## Assessed type

Math
Use of deprecated Chainlink function `latestAnswer`
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#L39


# Vulnerability details

## Impact
According to Chainlink's documentation, the `latestAnswer` function is deprecated. This function does not error if no answer has been reached but returns 0, causing an incorrect price fed to the MagicLpAggregator::latestAnswer.


## Proof of Concept
```solidity
        uint256 baseAnswerNomalized = uint256(baseOracle.latestAnswer()) * (10 ** (WAD - baseOracle.decimals()));
        uint256 quoteAnswerNormalized = uint256(quoteOracle.latestAnswer()) * (10 ** (WAD - quoteOracle.decimals()));
```
Referenced documentation: [Chainlink_Deprecated](https://docs.chain.link/docs/deprecated-aggregatorinterface-api-reference/),[Chainlink - Migration](https://docs.chain.link/docs/migrating-to-flux-aggregator/#3-use-the-new-functions)

## Recommended Mitigation Steps

Use the `latestRoundData` function to get the price instead. Add checks on the return data with proper revert messages if the price is stale or the round is uncomplete.




## Assessed type

Oracle
Missing return statement
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#L33


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
Missing return statement in _getReserves: Function doesn't return the fetched reserves.
We are not returning anything in _getReserves.It should return 2 unit246 integer  but we are not returning anything.

we are calling this function in latestAnswer() for getting baseReserve and quoteReserve but we are not getting any output from _getReserves().





## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.


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


 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();
## Tools Used

## Recommended Mitigation Steps

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


## Assessed type

Context
Contract `MagicLpAggregator.sol` uses deprecated latestAnswer() function
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#L38-L39


# Vulnerability details

## Impact

The function `MagicLpAggregator::latestAnswer()` contains the following lines of code

```solidity
uint256 baseAnswerNomalized = uint256(baseOracle.latestAnswer()) * (10 ** (WAD - baseOracle.decimals()));
uint256 quoteAnswerNormalized = uint256(quoteOracle.latestAnswer()) * (10 ** (WAD - quoteOracle.decimals()));
```

From the [Chainlink docs](https://docs.chain.link/data-feeds/api-reference#latestanswer) we can verify that the use of `latestAnswer()` is deprecated and should not be used.

The impact is that the `latestAnswer` can return 0 and cause the variables `baseAnswerNomalized` and `quoteAnswerNormalized` to be set as 0.

## Proof of Concept

The NatSpec of `latestAnswer` is the following

> @dev #[deprecated] Use latestRoundData instead. This does not error if no answer has been reached, it will simply return 0. Either wait to point to an already answered Aggregator or use the recommended latestRoundData instead which includes better verification information.
 
## Tools Used

Manual Review, Solodit

## Recommended Mitigation Steps

Use the latestRoundData function to get the price instead.


## Assessed type

Oracle