xanoutas/solidity-security-findings huggingface dataset:
Missing Double  Initialisation  check in smart contract [ BaseV2Minter.sol]
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/hermes/minters/BaseV2Minter.sol#L78


# Vulnerability details

Hello Team,

## Summary :

I would like to share a missing Double initialisation check in smart contract  [BaseV2Minter.sol] due to  which there is a chance that intiialize() function will be called twice by mistake

## What is Initializer checks 

iNitialize is just a function that set the state and it is called after the contract is deployed. when we calling it we initialize the state inside the proxy contract, but we need to make sure that this function is called only once.

## Proof of Concept :

Step 1 : Open the Link = https://github.com/code-423n4/2023-05-maia/blob/main/src/hermes/minters/BaseV2Minter.sol#L78

Step 2 : At Line 78 you see an Initialize() function like this 

        function initialize(FlywheelGaugeRewards _flywheelGaugeRewards) external {
        if (initializer != msg.sender) revert NotInitializer();
        flywheelGaugeRewards = _flywheelGaugeRewards;
        initializer = address(0);
        activePeriod = (block.timestamp / week) * week;
    }

Step 3 : This Function Here you see there is missing check for double Initialization in function initialize()

inorder to avoid such issue I suggest developer to impose a double initlizer check form Openzepplin Library



        function initialize(FlywheelGaugeRewards _flywheelGaugeRewards) external  initializer {
        if (initializer != msg.sender) revert NotInitializer();
        flywheelGaugeRewards = _flywheelGaugeRewards;
        initializer = address(0);
        activePeriod = (block.timestamp / week) * week;
    }

## Recent Discovery Regarding this Missing Double Initialize Checks

Whitehat Ashiq Amien, security researcher at iosiro, disclosed a critical vulnerability in 88mph’s CRV:RENWBTC, CRV:STETH, and yaLink pools to Immunefi on June 7. The vulnerability was an unprotected init() function in the code of these specific pools that would have allowed a malicious user to steal $6.5m in tokens, mostly in crvRenWBTC. Following Immunefi’s disclosure of the bug, 88mph quickly paused the affected contracts, whitehacked all user funds, and returned those funds to their rightful owners after they had been saved. In recognition of the vulnerability’s potentially devastating impact, 88mph is awarding iosiro a bounty of $42,069 at the request of the whitehat.



Link = https://medium.com/immunefi/88mph-function-initialization-bug-fix-postmortem-c3a2282894d3




## Impact

1. If a variable is declared and initialized twice inside the same variable declaration statement, the second initialization immediately overwrites the first one. This is confusing at best, and probably indicates an underlying bug.


## Recommendation :

I will suggest developer to implement a double initialization check in initialize() function too



Thank You



## Assessed type

Invalid Validation
Multiple issues with decimal scaling will cause incorrect accounting of hTokens and underlying tokens
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L388-L390
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchBridgeAgent.sol#L1340-L1342
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/ArbitrumBranchPort.sol#L52-L54
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/ArbitrumBranchBridgeAgent.sol#L104
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchBridgeAgent.sol#L269
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchBridgeAgent.sol#L313
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchBridgeAgent.sol#L696
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchBridgeAgent.sol#L745


# Vulnerability details


`_normalizeDecimals()` and `_denormalizeDecimals()` are used to handle non-18 decimals tokens when bridging deposit, by scaling them to a normalized 18 decimals form for hToken accounting, and then denormalizing to the token's decimals when interacting with the underlying token.

However, there are 3 issues as follows,
1. implementations of `_normalizeDecimals()` and `_denormalizeDecimals()` are reversed. 
2. `_denormalizeDecimals()` is missing in `ArbitrumBranchPort.depositToPort()`.
3. `_normalizeDecimals()` is missing in functions within `BranchBridgeAgent`.

These issues will cause an incorrect accounting of hTokens and underlying tokens in the system. 


## Impact
Incorrect decimal scaling will lead to loss of fund as the amount deposited and withdrawn for bridging will be inaccurate, which can be abused by an attacker or result in users inccuring losses.

For example, an attacker can abuse the `ArbitrumBranchPort.depositToPort()` issue and steal from the system by first depositing a token that has more than 18 decimals, as the attacker will receive more hTokens than the deposited underlying token amount. The attacker can then make a profit by withdrawing from the port with the excess hTokens.

On the other hand, if the underlying token is less than 18 decimals, the depositor can inccur losses as the amount of underlying tokens deposited will be more than the amount of hTokens received.

## Detailed Explanation

### Issue 1 
`BranchBridgeAgent._normalizeDecimals()` and `BranchPort._denormalizeDecimals()` (shown vbelow) are incorrect as they are implemented in a reversed manner, such that `_denormalizeDecimals()` is normalizing to 18 decimals while `_normalizeDecimals()` is denormalizing to the underlying token decimals.

The result is that for tokens with > 18 decimals, `_normalizeDecimals()` will overscale the decimals, while for tokens with < 18 decimals, `_normalizeDecimals()` will underscale the decimals.

https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchBridgeAgent.sol#L1340-L1342
```Solidity
    function _normalizeDecimals(uint256 _amount, uin
gaugeFactory owner can make unfair reward(hermes tokens)/ gaugeFactory will get unfair hermes tokens  
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/hermes/minters/BaseV2Minter.sol#L113


# Vulnerability details


## Proof of Concept
Uniswap V3 can have multiple pools for the same pairs of ERC20 tokens with different fee params. A fews has the most liquidity, while other pools have extremely little TVL or even not created yet. Attackers can abuse it, create low TVL pool where liquidity in this pool mostly (or fully) belong to attacker’s position. Gaugefactory owner can create lot of low TVL pool where liquidity in those pool mostly belong to gaugefactory owner’s positions. So gaugefactory owner will create only gauges of this low TVL pool(which belongs to him).
Gaugefactory owner will provide weights to all gauges. 
So protocol will emits lot of hermes tokens for this gaugefactory which all goes to the gaugefactory owner.

There is also a problem. Some gaugefactory will have more gauges and some will have less gauges.      Some gaugefactory will have more total weights and some will have less total weights. But every gaugefactory  will emit the same hermes tokens.      


## Impact
This is an unfair  reward mechanism. This can create inflation of hermes tokens. governence participants  needs education about this because they will add gaugefactory in the protocol.
Governence participants  can also manipulate this by creating   their own gauge factory so that they can emits more hermes tokens  .     





## Assessed type

Context
`UlyssesToken.removeAsset()` updates the wrong `assetId`.
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/d0ebf632cf8caddb21e39d49ae15b18245319cd2/src/ulysses-amm/UlyssesToken.sol#L72


# Vulnerability details

## Impact
The asset removal logic would be broken as it tracks the wrong `assetId` after a removal.

## Proof of Concept
The owner can remove an asset using `removeAsset()` and it updates the `assetId` mapping inside the function.

```solidity
    function removeAsset(address asset) external nonReentrant onlyOwner {
        // No need to check if index is 0, it will underflow and revert if it is 0
        uint256 assetIndex = assetId[asset] - 1;

        uint256 newAssetsLength = assets.length - 1;

        if (newAssetsLength == 0) revert CannotRemoveLastAsset();

        totalWeights -= weights[assetIndex];

        address lastAsset = assets[newAssetsLength];

        assetId[lastAsset] = assetIndex; //@audit should be assetIndex + 1
        assets[assetIndex] = lastAsset;
        weights[assetIndex] = weights[newAssetsLength];

        assets.pop();
        weights.pop();
        assetId[asset] = 0;

        emit AssetRemoved(asset);

        updateAssetBalances();

        asset.safeTransfer(msg.sender, asset.balanceOf(address(this)));
    }
```

In this protocol, valid asset ids are started from 1 but it sets the raw `assetIndex` after the removal. After that, the asset removals wouldn't work properly.

1. Let's suppose `assets = [A, B, C], assetId[A] = 1, assetId[B] = 2, assetId[C] = 3`.
2. The admin calls `removeAsset(A)`.
3. Then `assets = [B, C], assetId[B] = 2, assetId[C] = 0` because `assetIndex == 0` inside the function.
4. After that, the `C` asset can't be removed because `assetId[C] = 0` and it will be considered as an unadded asset.

## Tools Used
Manual Review

## Recommended Mitigation Steps
[removeAsset()](https://github.com/code-423n4/2023-05-maia/blob/d0ebf632cf8caddb21e39d49ae15b18245319cd2/src/ulysses-amm/UlyssesToken.sol#L72) should be modified like this.

```solidity
assetId[lastAsset] = assetIndex + 1;
```


## Assessed type

Other
removeAsset()  wrong setting assetIndex
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L72


# Vulnerability details

## Impact
in `removeAsset()`, Incorrectly set the assetIndex, resulting in subsequent remove fail,Even remove the wrong asset

## Proof of Concept

`removeAsset()` The code is as follows:

```solidity
    function removeAsset(address asset) external nonReentrant onlyOwner {
        // No need to check if index is 0, it will underflow and revert if it is 0
@>      uint256 assetIndex = assetId[asset] - 1;

        uint256 newAssetsLength = assets.length - 1;

        if (newAssetsLength == 0) revert CannotRemoveLastAsset();

        totalWeights -= weights[assetIndex];

        address lastAsset = assets[newAssetsLength];
@>      assetId[lastAsset] = assetIndex;
        assets[assetIndex] = lastAsset;
        weights[assetIndex] = weights[newAssetsLength];

        assets.pop();
        weights.pop();
        assetId[asset] = 0;

        emit AssetRemoved(asset);

        updateAssetBalances();

        asset.safeTransfer(msg.sender, asset.balanceOf(address(this)));
    }
```

When setting `assetId[lastAsset]` use `assetId[lastAsset] = assetIndex`

However, the current protocol need use `index + 1` for assetId
The correct value should be `assetId[lastAsset] = assetIndex + 1`.

For example, suppose Current:
assets = [A,B]
assetId[A] = 1
assetId[B] = 2

1. After executing `removeAsset(A)` it becomes

assets = [B]
assetId[B] = 0      (error , correct is 1)

2. If execute addAsset(C), it will become
assets = [B,C]
assetId[B] = 0
assetId[C] = 2


3. then execute remove(B), it will `underflow`

```solidity
    function removeAsset(address asset) external nonReentrant onlyOwner {
        // No need to check if index is 0, it will underflow and revert if it is 0
@>      uint256 assetIndex = assetId[asset] - 1;
```
The above example demonstrates the failure to remove
Due to the wrong assetIndex is also possible to cause the removal the wrong asset


## Tools Used

## Recommended Mitigation Steps

```solidity
    function removeAsset(address asset) external nonReentrant onlyOwner {
        // No need to check if index is 0, it will underflow and revert if it is 0
       uint256 assetIndex = assetId[asset] - 1;

        uint256 newAssetsLength = assets.length - 1;

        if (newAssetsLength == 0) revert CannotRemoveLastAsset();

        totalWeights -= weights[assetIndex];

        address lastAsset = assets[newAssetsLength];
-       assetId[lastAsset] = assetIndex;
+       assetId[lastAsset] = assetIndex + 1;


....
    
```


## Assessed type

Decimal
UlyssesToken.removeAsset's implementations is incorrectly
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L61-L85


# Vulnerability details

## Impact
the weights would be wrong after removing an asset

## Proof of Concept
to make the test easier, I made some change in the source code:
1) comment out [ERC4626MultiToken.sol#L51](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/erc-4626/ERC4626MultiToken.sol#L51)
```diff
--- a/src/erc-4626/ERC4626MultiToken.sol
+++ b/src/erc-4626/ERC4626MultiToken.sol
@@ -48,7 +48,7 @@ abstract contract ERC4626MultiToken is ERC20, ReentrancyGuard, IERC4626MultiToke
         if (length != _assets.length || length == 0) revert InvalidLength();

         for (uint256 i = 0; i < length;) {
-            require(ERC20(_assets[i]).decimals() == 18);
+            //require(ERC20(_assets[i]).decimals() == 18);
             require(_weights[i] > 0);

             _totalWeights += _weights[i];
```
2) save the following code in `UlyssesTokenMock.sol` file, and put the file under `ulysses-amm`, **the difference between UlyssesTokenMock.sol and UlyssesToken.sol is that I remove unnecessary token transfer and updateAssetBalances**
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Ownable} from "solady/auth/Ownable.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";

import {ERC20} from "solmate/tokens/ERC20.sol";

import {ERC4626MultiToken, IERC4626MultiToken} from "@ERC4626/ERC4626MultiToken.sol";

import {IUlyssesToken} from "./interfaces/IUlyssesToken.sol";

/// @title Ulysses Token - tokenized Vault multi asset implementation for Ulysses pools
contract UlyssesTokenMock is ERC4626MultiToken, Ownable, IUlyssesToken {
    using SafeTransferLib for address;
    using FixedPointMathLib for uint256;

    uint256 public immutable id;

    constructor(
        uint256 _id,
        address[] memory _assets,
        uint256[] memory _weights,
        string memory _name,
        string memory _symbol,
        address _owner
    ) ERC4626MultiToken(_assets, _weights, _name, _symbol) {
        _initializeOwner(_owner);
        require(_id != 0);
        id = _id;
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC4626MultiToken
    function totalAssets() public view override returns (uint256 _totalAssets) {
        return totalSupply;
    }

    ///@inheritdoc IUlyssesToken
    function addAsset(address asset, uint256 _weight) external nonReentrant onlyOwner {
        if (assetId[asset] != 0) revert AssetAlreadyAdded();
        //require(ERC20(asset).decimals() == 18);
        require(_weight > 0);

        assetId[asset] = assets.length + 1;
        assets.push(asset);
        weights.
rerange/rebalance occupied protocolFees
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/talos/base/TalosBaseStrategy.sol#L298


# Vulnerability details

## Impact
`rerange/rebalance` occupied the `protocolFees` that are temporarily stored in the contract and may cause permanent loss

## Proof of Concept
When `rerange()` is executed, there are two important steps as follows
1. Execute `_withdrawAll(_tokenId)` to retrieve all the `tokens` from the LP and put them into the contract
```solidity
    function _withdrawAll(uint256 _tokenId) internal {
        uint128 _liquidity = liquidity; // Saves an extra SLOAD if totalSupply is non-zero.
        if (_liquidity == 0) return;
        INonfungiblePositionManager _nonfungiblePositionManager = nonfungiblePositionManager;
@>      _nonfungiblePositionManager.decreaseLiquidity(
            INonfungiblePositionManager.DecreaseLiquidityParams({
                tokenId: _tokenId,
                liquidity: _liquidity,
                amount0Min: 0,
                amount1Min: 0,
                deadline: block.timestamp
            })
        );
        liquidity = 0;
        _nonfungiblePositionManager.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: _tokenId,
@>              recipient: address(this),
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );
    }
```
2.Execute `rerange()` to use all the balance in the contract to deposit to LP again

`TalosStrategySimple.doRerange()` -> `PoolActions.rerange()` -> `getThisPositionTicks`:

```solidity

    function rerange(
        INonfungiblePositionManager nonfungiblePositionManager,
        ActionParams memory actionParams,
        uint24 poolFee
    )
        internal
        returns (int24 tickLower, int24 tickUpper, uint256 amount0, uint256 amount1, uint256 tokenId, uint128 liquidity)
    {
        int24 baseThreshold = actionParams.tickSpacing * actionParams.optimizer.tickRangeMultiplier();

        uint256 balance0;
        uint256 balance1;
@>      (balance0, balance1, tickLower, tickUpper) = getThisPositionTicks(
            actionParams.pool, actionParams.token0, actionParams.token1, baseThreshold, actionParams.tickSpacing
        );
        emit Snapshot(balance0, balance1);

        (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
            INonfungiblePositionManager.MintParams({
                token0: address(actionParams.token0),
                token1: address(actionParams.token1),
                fee: poolFee,
                tickLower: tickLower,
                tickUpper: tickUpper,
                amount0Desired: balance0,
                amount1Desired: balance1,
                amount0Min: 0,
                amount1Min: 0,
                recipient: address(this),
                deadline: block.timestamp
            })
        );
    }


    function getThisPositionTicks(
        IUnisw
withdrawProtocolFees() Possible malicious or accidental withdrawal of all rewards
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/talos/boost-aggregator/BoostAggregator.sol#L159-L161


# Vulnerability details

## Impact
`claimReward()` will take all rewards if the `amountRequested` passed in is 0, which may result in user's rewards lost

## Proof of Concept
In `BoostAggregator.withdrawProtocolFees()`, the owner can take the protocol rewards `protocolRewards`
The code is as follows:
```solidity
    function withdrawProtocolFees(address to) external onlyOwner {
        uniswapV3Staker.claimReward(to, protocolRewards);
@>      delete protocolRewards;
    }
```

From the above code we can see that `uniswapV3Staker` is called to fetch and then clear `protocolRewards`

Let's look at the implementation of `uniswapV3Staker.claimReward()`.

```solidity
contract UniswapV3Staker is IUniswapV3Staker, Multicallable {
....
    function claimReward(address to, uint256 amountRequested) external returns (uint256 reward) {
        reward = rewards[msg.sender];
@>      if (amountRequested != 0 && amountRequested < reward) {
            reward = amountRequested;
            rewards[msg.sender] -= reward;
        } else {
            rewards[msg.sender] = 0;
        }

        if (reward > 0) hermes.safeTransfer(to, reward);

        emit RewardClaimed(to, reward);
    }
```

The current implementation is that if the `amountRequested==0` passed in means that all `rewards[msg.sender]` of this `msg.sender` are taken



This leads to problems.
1. If a malicious `owner` calls `withdrawProtocolFees()` twice in a row, it will definitely take all the `rewards` of the `BoostAggregator`.
2. probably didn't realize that `withdrawProtocolFees()` was called when `protocolRewards==0`

As a result, the rewards that belong to users in `BoostAggregator` are lost



## Tools Used

## Recommended Mitigation Steps

Modify `claimReward()` to remove `amountRequested != 0`

```solidity
contract UniswapV3Staker is IUniswapV3Staker, Multicallable {
....
    function claimReward(address to, uint256 amountRequested) external returns (uint256 reward) {
        reward = rewards[msg.sender];
-       if (amountRequested != 0 && amountRequested < reward) {        
+       if (amountRequested < reward) {
            reward = amountRequested;
            rewards[msg.sender] -= reward;
        } else {
            rewards[msg.sender] = 0;
        }

        if (reward > 0) hermes.safeTransfer(to, reward);

        emit RewardClaimed(to, reward);
    }
```


## Assessed type

Context