xanoutas/solidity-security-findings huggingface dataset:
If a STRATEGY TOKEN is "Toggled off" STRATEGIES will still be able to withdraw but returning of tokens with replenishReserves will be disabled.
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L158-L169
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L172-L186


# Vulnerability details

## Impact
`BranchPort.manage` allows a registered Strategy to withdraw certain amounts of enabled strategy tokens. It validates access rights ie. if called by a strategy registered for the requested token. 
It however doesn't check if the token itself is currently enabled. 

Conversely `BranchPort.replenishTokens` allows to force withdraw managed tokens from a strategy. It however performs a check if the token is currently an active strategy token. 

Strategy token may be disabled by  `toggleStrategyToken()` even if there are active strategies managing it active. In such case these strategies will still be able to withdraw the tokens with calls to `manage()` while `replenishTokens` will not be callable on them and thus tokens won't be force returnable. 


## Tools Used
Pen and Paper.
## Recommended Mitigation Steps
1. Add check on enabled strategy token in `manage()`
2. Validate   `getPortStrategyTokenDebt[_strategy][_token] > 0` instead of `!isStrategyToken[_token]` in `replenishReserves()`


## Assessed type

Access Control
BranchBridgeAgent invokes anyCall with  PAY ON SOURCE but doesn't send value with it. All calls will fail.
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol#L1006-L1011
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/lib/AnycallFlags.sol#L11


# Vulnerability details

## Impact
`IAnycallProxy().anyCall()` operates under one of two modes of taking fees, namely fees are taken either on source or on the destination chain. Fee mode is decided by the caller with an appropriate value of the fourth parameter, ie. `uint256 _flag` . Values 0,4 denote the SOURCE chain. Such invocation require `ether` to be send along (`IAnycallProxy().anyCall()` is payable) and in case of insufficient value being sent will fail.
```
    function anyCall(
        address _to,
        bytes calldata _data,
        uint256 _toChainID,
        uint256 _flags,
        bytes calldata _extdata
    ) external payable;
```
`BranchBridgeAgent._performCall()` invokes `anyCall()` with `AnycallFlags.FLAG_ALLOW_FALLBACK` which is  defined as `0x1 << 2` ie `4`

source: `BranchBridgeAgent.sol`
```
    function _performCall(bytes memory _calldata) internal virtual {
        //Sends message to AnycallProxy
        IAnycallProxy(localAnyCallAddress).anyCall(
            rootBridgeAgentAddress, _calldata, rootChainId, AnycallFlags.FLAG_ALLOW_FALLBACK, ""
        );
    }
```
source: `AnycallFlags.sol`
```
    uint256 public constant FLAG_ALLOW_FALLBACK = 0x1 << 2;
```

Consequently all cross bridge requests will fail.

## Tools Used
Manual review, Multichain documentation: (Link to `anyCall` flags definition)[https://docs.multichain.org/developer-guide/anycall-v7/quickstart-cross-chain-text-example]
## Recommended Mitigation Steps
Judging from the `RootBridgeAgent.anyExecute()` implementation the intended mode is to pay fees on the destination chain (with fallback enabled). Under this assumption `_flag = 6` should be passed ie.
`AnycallFlags.FLAG_ALLOW_FALLBACK_DST`


## Assessed type

Other
The code uses arithmetic operations without explicitly checking for possible overflows or underflows
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/erc-20/ERC20MultiVotes.sol#L43


# Vulnerability details

## Impact
The impact of the Integer Overflow/Underflow vulnerability can be summarized as follows:

- **Data Inaccuracy**: The vulnerability can lead to incorrect calculations and inaccurate data, potentially compromising the integrity of voting processes and other critical operations.

- **Loss of Assets**: An underflow or overflow can result in the loss of funds or tokens if balances or transfers are affected. Attackers may exploit this to drain user funds or disrupt the financial state of the contract.

- **System Instability**: The vulnerability can cause unexpected behavior or even crashes, disrupting the contract's normal operation and potentially leading to denial of service or loss of user funds.

- **Security Exploitation**: Malicious actors can exploit the vulnerability to manipulate the contract, tamper with voting results, gain unauthorized access, or perform other malicious activities.

- **Reputation Damage**: The presence of the vulnerability can erode user trust, damage the project's reputation, and deter potential users or investors from engaging with the contract.

## Proof of Concept
The potential Integer Overflow/Underflow vulnerability can be found in the following line of code:  [LINK](https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/erc-20/ERC20MultiVotes.sol#L43)

    return balanceOf[account] - userDelegatedVotes[account];

In this line, the subtraction operation (-) is performed between **balanceOf[account]** and **userDelegatedVotes[account]**. If **balanceOf[account]** is smaller than **userDelegatedVotes[account]**, an underflow can occur, resulting in unexpected behavior and potential vulnerabilities.

## Tools Used
Manual Review

## Recommended Mitigation Steps

To fix the Integer Overflow/Underflow vulnerability in the code, we can add a check to ensure that **balanceOf[account]** is greater than or equal to **userDelegatedVotes[account]** before performing the subtraction operation. Here's an example of how we can modify the code to address this issue:

    function freeVotes(address account) public view virtual returns (uint256) {
        uint256 accountBalance = balanceOf[account];
        uint256 delegatedVotes = userDelegatedVotes[account];
        
            if (accountBalance < delegatedVotes) {
                // Handle the error condition (e.g., revert, return a default value, etc.)
                revert("Insufficient account balance");
            }
            
            return accountBalance - delegatedVotes;
        }

By adding this check, the code ensures that the **freeVotes** function will only return a value if **accountBalance** is greater than or equal to **delegatedVotes**. If **accountBalance** is less than **delegatedVotes**, it will revert the transaction with an appropriate err
Use of slot0 to get sqrtPriceLimitX96 can lead to price manipulation.
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/RootBridgeAgent.sol#L674
https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/RootBridgeAgent.sol#L717


# Vulnerability details

## Impact
In the `RootBrigdeAgent.sol` the function's `_gasSwapOut` and `_gasSwapIn` uses `UniswapV3.slot0` to get the value of `sqrtPriceX96` which it use to perform the swap, however the `sqrtPriceX96` gotten from `Uniswap.slot0` is the most recent data point and can be manipulated easily via `MEV` bots & `Flashloans`⚡️ with sandwich attacks can cause lose of funds when interact with the `Uniswap.swap` function.

## Proof of Concept
You can see the `_gasSwapIn` function in the `RootBrigdeAgent.sol` [Link here](https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/RootBridgeAgent.sol#L674C1-L689C75)

```solidity

     //Get sqrtPriceX96
        (uint160 sqrtPriceX96,,,,,,) = IUniswapV3Pool(poolAddress).slot0();

        // Calculate Price limit depending on pre-set price impact
        uint160 exactSqrtPriceImpact = (sqrtPriceX96 * (priceImpactPercentage / 2)) / GLOBAL_DIVISIONER;

        //Get limit
        uint160 sqrtPriceLimitX96 =
            zeroForOneOnInflow ? sqrtPriceX96 - exactSqrtPriceImpact : sqrtPriceX96 + exactSqrtPriceImpact;

        //Swap imbalanced token as long as we haven't used the entire amountSpecified and haven't reached the price limit
        try IUniswapV3Pool(poolAddress).swap(
            address(this),
            zeroForOneOnInflow,
            int256(_amount),
            sqrtPriceLimitX96,
            abi.encode(SwapCallbackData({tokenIn: gasTokenGlobalAddress}))
```
and You can see the `_gasSwapOut` function in the `RootBrigdeAgent.sol` [Link here](https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/RootBridgeAgent.sol#L717C1-L734C11)

```solidity
   (uint160 sqrtPriceX96,,,,,,) = IUniswapV3Pool(poolAddress).slot0();

            // Calculate Price limit depending on pre-set price impact
            uint160 exactSqrtPriceImpact = (sqrtPriceX96 * (priceImpactPercentage / 2)) / GLOBAL_DIVISIONER;

            //Get limit
            sqrtPriceLimitX96 =
                zeroForOneOnInflow ? sqrtPriceX96 + exactSqrtPriceImpact : sqrtPriceX96 - exactSqrtPriceImpact;
        }

        //Swap imbalanced token as long as we haven't used the entire amountSpecified and haven't reached the price limit
        (int256 amount0, int256 amount1) = IUniswapV3Pool(poolAddress).swap(
            address(this),
            !zeroForOneOnInflow,
            int256(_amount),
            sqrtPriceLimitX96,
            abi.encode(SwapCallbackData({tokenIn: address(wrappedNativeToken)}))
        );
```

The both use the the `sqrtPriceX96` gotten from `Uniswap.slot0` 
An Attacker can Simply manipulate the `sqrt
`doRebalance` in Talos is vulnerable to Flash loan Attacks resulting loss of funds
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/strategies/TalosStrategySimple.sol#L42
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/libraries/PoolActions.sol#L50


# Vulnerability details

## Impact

Loss of funds due to MEV Sandwich attacks.

## Proof of Concept

Rebalancing is done using `doRebalance` method in `TalosStrategySimple`. 

```solidity
File: TalosStrategySimple.sol

  function doRebalance() internal override returns (uint256 amount0, uint256 amount1) {
      int24 baseThreshold = tickSpacing * optimizer.tickRangeMultiplier();

      PoolActions.ActionParams memory actionParams =
          PoolActions.ActionParams(pool, optimizer, token0, token1, tickSpacing);

@->   PoolActions.swapToEqualAmounts(actionParams, baseThreshold);

      (tickLower, tickUpper, amount0, amount1, tokenId, liquidity) =
          nonfungiblePositionManager.rerange(actionParams, poolFee);
  }

```
[Link to code](https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/strategies/TalosStrategySimple.sol#L42)

Line in focus here is:

```solidity
    PoolActions.swapToEqualAmounts(actionParams, baseThreshold);
```

`swapToEqualAmounts` method in `PoolActions.sol` library:

```solidity
File: PoolActions.sol

  function swapToEqualAmounts(ActionParams memory actionParams, int24 baseThreshold) internal {
@->   (bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96) = actionParams
          .pool
          .getSwapToEqualAmountsParams(
          actionParams.optimizer, actionParams.tickSpacing, baseThreshold, actionParams.token0, actionParams.token1
      );

      //Swap imbalanced token as long as we haven't used the entire amountSpecified and haven't reached the price limit
      actionParams.pool.swap(
          address(this),
          zeroForOne,
          amountSpecified,
 @->      sqrtPriceLimitX96,
          abi.encode(SwapCallbackData({zeroForOne: zeroForOne}))
      );
  }

```
[Link to Code](https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/libraries/PoolActions.sol#L50)

Here, the value of `sqrtPriceLimitX96` is calculated through `getSwapToEqualAmountsParams` method in `PoolVariables.sol` library. It basically represents the square root of the lowest or highest price that you are willing to perform the trade at. So basically it is a slippage control parameter.

```solidity
File: PoolVariables.sol

232:    (uint160 sqrtPriceX96, int24 currentTick,,,,,) = _pool.slot0();

251:    uint160 exactSqrtPriceImpact = (sqrtPriceX96 * (_strategy.priceImpactPercentage() / 2)) / GLOBAL_DIVISIONER;
252:    sqrtPriceLimitX96 = zeroForOne ? sqrtPriceX96 - exactSqrtPriceImpact : sqrtPriceX96 + exactSqrtPriceImpact;

```

`getSwapToEqualAmountsParams` method uses the `_pool.slot0` to determine The current price of the pool as a sqrt. [slot0](https://docs.uniswap.org/contracts/v3/reference/core/interfaces/pool/IUniswapV3PoolState#slot0) is the most recent data point and can easily be manipul
## Significant roundoff error in depositToPort function (ArbitrumBranchBridgeAgent.sol )
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/ArbitrumBranchBridgeAgent.sol#L102


# Vulnerability details

## Significant roundoff error in depositToPort function (ArbitrumBranchBridgeAgent.sol )

This issue is caused with different decimals than 18. As an Eg USDC, WBTC. Let's consider the USDC as the case scenario. If User
deposit USDC into the depositToPort function, He needs to enter the amount as at least 1Million USDC (underlying) in order to mint
1 wei of localtoken. But here actually user sends the 1 wei of USDC (10^-6). So user is confused what's going with there since he
needs to enter the values as a minimum of 1Million USDC, but the actual sending amount is 1 wei of USDC.

## Proof of Concept

```solidity
    function depositToPort(address underlyingAddress, uint256 amount) external payable lock {
        IArbPort(localPortAddress).depositToPort(
            msg.sender, msg.sender, underlyingAddress, _normalizeDecimals(amount, ERC20(underlyingAddress).decimals())
        );
    }
```

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/ArbitrumBranchBridgeAgent.sol#L102C5-L106C6

```solidity
    function depositToPort(address _depositor, address _recipient, address _underlyingAddress, uint256 _deposit)
        external
        requiresBridgeAgent
    {
        address globalToken = IRootPort(rootPortAddress).getLocalTokenFromUnder(_underlyingAddress, localChainId);
        if (globalToken == address(0)) revert UnknownUnderlyingToken();

        _underlyingAddress.safeTransferFrom(_depositor, address(this), _deposit);

        IRootPort(rootPortAddress).mintToLocalBranch(_recipient, globalToken, _deposit);
    }
```

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/ArbitrumBranchPort.sol#L45C1-L55C6

User need to enter the amount as more than 1Million USDC in to depositToPort function.
But the actual amount sent is only 10^-6 of USDC.

He will be minted 1wei of local token. Here root cause of this issue is, not using the correct equation for normalize &
denormalize functions as well as needed to use the \_normalizeDecimals function between the asset amount & minting token amount.
But here initially amount is normalized then transfer the undelying token & minting the same amount of local token. Borth reasons
caused an error.

## Tools Used

Manual Auditing

## Recommended Mitigation Steps

There are 2 sets of mitigation steps here.

Mitigation 1:

Remove the normalize & denormalize between the assets to minting amount. Then consider the apply same decimals to local token.

Mitigation 2:

Correct normalize/ denormalize functions as well as depositToPort & withdrawFromPort functions given below.

```solidity
    function depositToPort(address underlyingAddress, uint256 amount) external payable lock {
        IArbPort(localPortAddress).depo
Id not saved when adding a vault with `addVault` or partner with `addPartner`
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/factories/PartnerManagerFactory.sol#L58-L64
https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/factories/PartnerManagerFactory.sol#L67-L73


# Vulnerability details

## Impact

In the `PartnerManagerFactory` contract when adding a new vault with the `addVault` or adding a new partner with `addPartner` function, both functions does not save the id of the added vault (or partner), this will cause function like `migratePartnerVault` to revert when they are not supposed to.

## Proof of Concept

The issue occurs in both `addVault` and `addPartner` functions below :

**File: PartnerManagerFactory.sol** [Line 58-64](https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/factories/PartnerManagerFactory.sol#L58-L64)
```solidity
function addPartner(PartnerManager newPartnerManager) external onlyOwner {
    uint256 id = partners.length;
    partners.push(newPartnerManager);
    // @audit not saving the new partner id
    partnerIds[newPartnerManager] == id;

    emit AddedPartner(newPartnerManager, id);
}
```

**File: PartnerManagerFactory.sol** [Line 67-73](https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/factories/PartnerManagerFactory.sol#L67-L73)
```solidity
function addVault(IBaseVault newVault) external onlyOwner {
    uint256 id = vaults.length;
    vaults.push(newVault);
    // @audit not saving the new vault id
    vaultIds[newVault] == id;

    emit AddedVault(newVault, id);
}
```

As you can see when trying to save the id of the new vault or partner, the functions use the comparison operator `==` instead of the assignement operator `=`.

So both functions will just compare `partnerIds[newPartnerManager]` or `vaultIds[newVault]` with the id value, and regardless of the result (true or false) the functions call will succeed (will not revert) and thus any added vault or partner will have the dafault id of 0.

This will cause problems for the protocol functions who rely on those ids, for example the function `migratePartnerVault` in `ERC4626PartnerManager` contains the following check statement :

**File: ERC4626PartnerManager.sol** [Line 189](https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/tokens/ERC4626PartnerManager.sol#L189)
```solidity
if (factory.vaultIds(IBaseVault(newPartnerVault)) == 0) revert UnrecognizedVault();
```

Because of the metioned error, all the added vaults will have an id equal to 0 and so the function `migratePartnerVault` will always revert making it impossible to migrate assets to new Partner Vault.


## Tools Used

Manual review

## Recommended Mitigation Steps

Correct the error in both `addVault` and `addPartner` functions by using `=` instead of `==` :

```solidity
function addPartner(PartnerManager newPartnerManager) external onlyOwner {
    uint256 id = partners.length;
    partners.push(newPartnerManager);
    // @audit save the new partner id
    partnerIds[newPartnerManager] = id;

    emit Ad
_createDepositSingle() call bridgeOut missing normalizeDecimals
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/BranchBridgeAgent.sol#L866


# Vulnerability details

## Impact
Wrong decimal place conversion, resulting in wrong quantity

## Proof of Concept
in `_createDepositSingle()` will call `IPort(localPortAddress).bridgeOut()`
The parameter `_deposit` is not converted to 18 decimal


_createDepositSingle()
```solidity
    function _createDepositSingle(
        address _user,
        address _hToken,
        address _token,
        uint256 _amount,
        uint256 _deposit,
        uint128 _gasToBridgeOut
    ) internal {
        //Deposit / Lock Tokens into Port
@>      IPort(localPortAddress).bridgeOut(_user, _hToken, _token, _amount, _deposit);

        //Deposit Gas to Port
        _depositGas(_gasToBridgeOut);
...

```

but in `IPort(localPortAddress).bridgeOut()` method ，will do `_denormalizeDecimals` on `deposit`
from 18 decimal to `token.decimals`

```solidity
    function bridgeOut(
        address _depositor,
        address _localAddress,
        address _underlyingAddress,
        uint256 _amount,
        uint256 _deposit
    ) external virtual requiresBridgeAgent {
        if (_amount - _deposit > 0) {
            _localAddress.safeTransferFrom(_depositor, address(this), _amount - _deposit);
            ERC20hTokenBranch(_localAddress).burn(_amount - _deposit);
        }
        if (_deposit > 0) {
            _underlyingAddress.safeTransferFrom(
@>              _depositor, address(this), _denormalizeDecimals(_deposit, ERC20(_underlyingAddress).decimals())
            );
        }
    }
```

So we need to convert `deposit` to 18 decimal before calling `bridgeOut`


Note: `_createDepositMultiple` have similar questions

## Tools Used

## Recommended Mitigation Steps

```solidity
    function _createDepositSingle(
        address _user,
        address _hToken,
        address _token,
        uint256 _amount,
        uint256 _deposit,
        uint128 _gasToBridgeOut
    ) internal {
        //Deposit / Lock Tokens into Port
-      IPort(localPortAddress).bridgeOut(_user, _hToken, _token, _amount, _deposit);        
+      IPort(localPortAddress).bridgeOut(_user, _hToken, _token, _amount, _normalizeDecimals(_deposit));

        //Deposit Gas to Port
        _depositGas(_gasToBridgeOut);
...

```


## Assessed type

Context
_normalizeDecimals() Wrong calculation formula
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol#L1341


# Vulnerability details

## Impact
Wrong decimal place conversion, resulting in wrong quantity

## Proof of Concept
in `callOutSignedAndBridge()` The number of tokens will be converted to 18 decimal when `packedData` is performed.
```solidity
    function callOutSignedAndBridge(bytes calldata _params, DepositInput memory _dParams, uint128 _remoteExecutionGas)
        external
        payable
        lock
        requiresFallbackGas
    {
        //Encode Data for cross-chain call.
        bytes memory packedData = abi.encodePacked(
            bytes1(0x05),
            msg.sender,
            depositNonce,
            _dParams.hToken,
            _dParams.token,
            _dParams.amount,
@>          _normalizeDecimals(_dParams.deposit, ERC20(_dParams.token).decimals()),
            _dParams.toChain,
            _params,
            msg.value.toUint128(),
            _remoteExecutionGas
        );
```

But the method of converting to 18 decimal is wrong
```solidity
    /**
     * @notice Internal function that normalizes an input to 18 decimal places.
     * @param _amount amount of tokens
     * @param _decimals number of decimal places
     */
    function _normalizeDecimals(uint256 _amount, uint8 _decimals) internal pure returns (uint256) {
@>      return _decimals == 18 ? _amount : _amount * (10 ** _decimals) / 1 ether;
    }
```

The correct should be `_amount * 1 ether / (10 ** _decimals) `


Many places have problems using this method

Note: `BranchPort._denormalizeDecimals()` Have similar questions


## Tools Used

## Recommended Mitigation Steps

```solidity
    /**
     * @notice Internal function that normalizes an input to 18 decimal places.
     * @param _amount amount of tokens
     * @param _decimals number of decimal places
     */
    function _normalizeDecimals(uint256 _amount, uint8 _decimals) internal pure returns (uint256) {
-       return _decimals == 18 ? _amount : _amount * (10 ** _decimals) / 1 ether;
+       return _decimals == 18 ? _amount : _amount * 1 ether / (10 ** _decimals);
    }
```


## Assessed type

Context