xanoutas/solidity-security-findings huggingface dataset:
redeem() beforeRedeem using the wrong owner parameter
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#L257


# Vulnerability details

## Impact
Wrong owner parameter, causing users to lose rewards

## Proof of Concept
In `TalosStrategyStaked.sol`
If the user's `shares` have changed, we need to do `flywheel.accrue()` first, which will accrue `rewards` and update the corresponding `userIndex`.
This way we can ensure the accuracy of `rewards`.
So we will call `flywheel.accrue()` beforeDeposit/beforeRedeem/transfer etc.

Take `redeem()` as an example, the code is as follows:

```solidity
contract TalosStrategyStaked is TalosStrategySimple, ITalosStrategyStaked {
...

    function beforeRedeem(uint256 _tokenId, address _owner) internal override {
        _earnFees(_tokenId);
@>      flywheel.accrue(_owner);
    }
```


But when `beforeRedeem()` is called with the wrong owner passed in, the `redeem()` code is as follows:


```solidity
    function redeem(uint256 shares, uint256 amount0Min, uint256 amount1Min, address receiver, address _owner)
        public
        virtual
        override
        nonReentrant
        checkDeviation
        returns (uint256 amount0, uint256 amount1)
    {
...
        if (msg.sender != _owner) {
            uint256 allowed = allowance[_owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[_owner][msg.sender] = allowed - shares;
        }

        if (shares == 0) revert RedeemingZeroShares();
        if (receiver == address(0)) revert ReceiverIsZeroAddress();

        uint256 _tokenId = tokenId;
@>      beforeRedeem(_tokenId, receiver);

        INonfungiblePositionManager _nonfungiblePositionManager = nonfungiblePositionManager; // Saves an extra SLOAD
        {
            uint128 liquidityToDecrease = uint128((liquidity * shares) / totalSupply);

            (amount0, amount1) = _nonfungiblePositionManager.decreaseLiquidity(
                INonfungiblePositionManager.DecreaseLiquidityParams({
                    tokenId: _tokenId,
                    liquidity: liquidityToDecrease,
                    amount0Min: amount0Min,
                    amount1Min: amount1Min,
                    deadline: block.timestamp
                })
            );

            if (amount0 == 0 && amount1 == 0) revert AmountsAreZero();

@>          _burn(_owner, shares);

            liquidity -= liquidityToDecrease;
        }    
```


From the above code, we see that the parameter is `receiver`, but the person whose shares are burn is `_owner`.

We need to accrue `_owner`, not `receiver`

This leads to a direct reduction of the user's shares without `accrue`, and the user loses the corresponding rewards


## Tools Used

## Recommended Mitigation Steps

```solidity
    function redeem(uint256 shares, uint256 amount0Min, uint256 amount1Min, address receiver, address _owner)
        public
        virtual
        override
        nonReen
`` amount < minOutput``  Should be checked for all swaps instead of last swap. This will break the ``swap `` functionality and attacker can swap ``amount`` even less than ``minOutput``
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
If the comparison is not checked after each swap, it allows for ``unintended behavior`` where the ``amount`` can fall below the specified ``minOutput`` during the intermediate swaps. This means that an attacker could successfully execute swaps with outputs lower than the desired minimum, potentially resulting in unfavorable outcomes for users.

When users interact with the contract, they rely on the minOutput parameter to determine the acceptable level of output from the swap. If the comparison is not checked for each swap, it deviates from user expectations and can lead to confusion or disappointment if the resulting output is lower than the specified minOutput

## Proof of Concept

```solidity
FILE: Breadcrumbs2023-05-maia/src/ulysses-amm/UlyssesRouter.sol

 function swap(uint256 amount, uint256 minOutput, Route[] calldata routes) external returns (uint256) {
        address(getUlyssesLP(routes[0].from).asset()).safeTransferFrom(msg.sender, address(this), amount);

        uint256 length = routes.length;

        for (uint256 i = 0; i < length;) {
            amount = getUlyssesLP(routes[i].from).swapIn(amount, routes[i].to);

            unchecked {
                ++i;
            }
        }

        if (amount < minOutput) revert OutputTooLow();

        unchecked {
            --length;
        }

        address(getUlyssesLP(routes[length].to).asset()).safeTransfer(msg.sender, amount);

        return amount;
    }

```
STEPS:

- Let's assume the initial value of amount is greater than minOutput, and the loop iterates over multiple swaps.

- During the loop iterations, an attacker can exploit the situation by performing a sequence of swaps where each swap decreases the amount significantly.

- If the comparison amount < minOutput is checked only at the end of the loop, the attacker can successfully complete all the swaps, even if the amount falls below the desired minOutput during intermediate steps.

- As a result, the final value of amount after all the swaps may be below the specified minOutput, violating the intended condition set by the user.

- Despite the minOutput condition not being satisfied, the transaction continues executing, and the attacker may receive a transfer of a smaller output than expected.

- This attack scenario allows the attacker to exploit the contract and potentially receive a reduced output without triggering a revert during the loop, leading to undesired outcomes for the users.


## Tools Used
Manual audit

## Recommended Mitigation Steps

 ``amount < minOutput`` check should be added ``inside the loop``. For each swap we need to check  ``amount < minOutput`` to ensure the swap amount is greater than ``minOutput``



## Assessed type

Uniswap
updateBribeDepot and updatePoolMinimumWidth functions not having access control
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L543-L561


# Vulnerability details

## Impact
A malicious user could call the functions to update the bribe depot or minimum width of a pool to a malicious value, which could have negative consequences for the pool.

## Proof of Concept
The `updatePoolMinimumWidth` function allows users to update the minimum width of a pool. This could be exploited by malicious users to reduce the minimum width to a very low value, making it easier for them to take control of the pool.

The `setPoolManager` function allows users to set the pool manager for a pool. This could be exploited by malicious users to set themselves as the pool manager, giving them full control over the pool.
## Tools Used
Manual Review
## Recommended Mitigation Steps
I recommend that you add Access Control to the `updateBribeDepot` and `updatePoolMinimumWidth` functions.This would prevent malicious users from calling the functions unless they have been granted special permission.
I also recommend that the functions be logged so that it can be monitored for suspicious activity. This would help to identify any malicious users who are trying to exploit the vulnerability.


## Assessed type

Uniswap
Users can’t deposit(stake) nft .
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L483


# Vulnerability details



## Proof of Concept
 In UniswapV3Staker contract when one call function onERC721Received , this calls a private function _stakeToken , in   function _stakeToken  IncentiveKey memory key will be always new because it takes block.timestamp. So bytes32 incentiveId is also new. And  incentives[incentiveId].totalRewardUnclaimed default value is 0. So the function will not be executed for [if (incentives[incentiveId].totalRewardUnclaimed == 0) revert NonExistentIncentiveError(); ]. 
  
## Impact
Protocol will not work as expected. 


## Assessed type

Error
Decimals are scaled incorrectly for tokens which do not have 18 decimals
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#L1340
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L388


# Vulnerability details

## Impact  
*Although this appears to be two seperate bugs, this is clearly one error where the developers accidentally swapped the implementation logic for `_denormalizeDecimals` and `_normalizeDecimals`. And since the fix simply requires swapping the logic for these two functions back around, it is treated as one issue here.* 

In the supplied repo the functionality of `_normalizeDecimals` and `_denormalizeDecimals` is swapped. This breaks core functionality related to `withdrawFromPort` and `depositToPort` for tokens which do not have 18 decimals.   

This is because **withdrawals** for non-18 decimal tokens will be hugely inflated (meaning that a bridge can be inadvertently drained by a user requesting a small amount), while **deposits** for non-18 decimal tokens into the protocol are vastly deflated, which means much less is deposited into the protocol for these tokens than intended by users.

The impact is high as it will render the protocol unable to safely accept deposits or withdrawals for non-18 decimal tokens. Loss of funds is possible, but is unlikely as the bug works both ways, meaning that depositing enough tokens into the protocol to exploit it via this issue is economically difficult. However, the impact remains high because this breaks a core feature of the protocol, the intended compatibility for non-18 decimal tokens. Due to the architecture the bug will likely persist across all chains.  

## Proof of Concept
#### Overview

In `BranchBridgeAgent.sol` the function `_normalizeDecimals(uint256 _amount, uint8 _decimals)` is expected to scale a token from a non-18 decimal format to a normalized 18 decimal format. [The function can be found here](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol#L1340). But given the logic:  
```  
    return _decimals == 18 ? _amount : _amount * (10 ** _decimals) / 1 ether;
```  

It means that for a test case of `tokenA` which has `_decimals = 6` and an `_amount = 100 * 10 ** 6` (i.e. 100 tokens):

result  = 100 * (10 ** 6) / 1 * 10 ** 18
        = 100 000 000 / 1 000 000 000 000 000 000
        = 0.0000000001  

The returned `result` will be 0 in Solidity, meaning that transfers that rely on `_normalizeDecimals` (like `depositToPort`) will be fed amounts that are much less than expected (or even zero).
  
  
In `BranchPort.sol` the function `_denormalizeDecimals(uint256 _amount, uint8 _decimals)` is expected to take a scaled 18-decimal amount and scale it back to a `_decimals`-decimal amount. [The code can be found here](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d3
`UlyssesToken.removeAsset` might assign a wrong `assetId` to an asset, resulting in multiple severe issues
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
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L45
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/erc-4626/ERC4626MultiToken.sol#L66-L69
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/erc-4626/ERC4626MultiToken.sol#L77-L80
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L110-L118


# Vulnerability details

## Impact

### Summary of the impact:

When the function `UlyssesToken.removeAsset()` is called, it is possible that the wrong assetId is assigned to an asset. As a result it is possible that:

1. A duplicate asset may be added.

1. When a duplicate asset would be added, the contract might hold less assets than it should hold, potentially resulting in insolvency or other issues.

1. An asset may not be able to be removed.

1. A wrong asset may be removed when `UlyssesToken.removeAsset()` is called next time.

1. The wrong assetId may be returned upon the `UlyssesToken.assetId()` view function.

The `ERC4626MultiToken.assets` and `ERC4626MultiToken.weights` arrays are crucial to determine which assets will be used. Based on `ERC4626MultiToken.assets` and `ERC4626MultiToken.weights` the `UlyssesToken` (which inherits `ERC4626MultiToken`) will pull and push the assets around, therefore when these arrays contain wrong information, the `UlyssesToken` will receive and give wrong assets. Therefore this issue is of high severity.

### Detail of the impact:

When the function `UlyssesToken.removeAsset()` is called, the `assetIndex` is assigned to `assetId[lastAsset]` which is wrong:

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

This means that potentially a wrong assetId gets assigned to an asset.

Example for how a duplicate asset can be added:

- There are 3 assets: A, B, C in the `ERC4626MultiToken.assets` array

- Now `UlyssesToken.removeAsset()` is called with the address of asset A.

- The assetIndex of asset A is determined which is 0 (line 62 UlyssesToken.sol).

- The assetId of C is set to 0 (line 72 UlyssesToken.sol).

- C is then assigned to `assets[0]` on line 73 in UlyssesToken.sol.

- Now due to the fact that asset C has an assetId of 0, you can add asset C again via `UlyssesToken.addAsset()` as a duplicate. This will not revert, because the assetId of asset C is 0:
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L45

- Also, asset C can't be removed, because when calling `UlyssesToken.removeAsset()`, because C's assetId is 0, and the calculation on line 62 will revert due to an underflow.

Example for how a w
Owner can steal assets in `UlyssesToken`
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

By setting the weight of any asset to 0, the owner can replace the balance of one token with another.


## Assessed type

Access Control
`addVault` doesn't work properly
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#L70


# Vulnerability details

## Impact
id was not set in `vaultIds` in the `addVault` function.


## Proof of Concept

```solidity
File: src/maia/factories/PartnerManagerFactory.sol
67:    function addVault(IBaseVault newVault) external onlyOwner {
68:        uint256 id = vaults.length;
69:        vaults.push(newVault);
70:        vaultIds[newVault] == id; // @audit should be =
71:
72:        emit AddedVault(newVault, id);
73:    }
```

## Tools Used
Manual review

## Recommended Mitigation Steps
The operator `==` in line 70 should be replaced with `=`.

```solidity
File: src/maia/factories/PartnerManagerFactory.sol
67:    function addVault(IBaseVault newVault) external onlyOwner {
68:        uint256 id = vaults.length;
69:        vaults.push(newVault);
70:        vaultIds[newVault] = id;
71:
72:        emit AddedVault(newVault, id);
73:    }
```


## Assessed type

Other
`addPartner` doesn't work properly
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#L61


# Vulnerability details


## Impact
id was not set in `partnerIds` in the `addPartner` function.


## Proof of Concept

```solidity
File: src/maia/factories/PartnerManagerFactory.sol
58:    function addPartner(PartnerManager newPartnerManager) external onlyOwner {
59:        uint256 id = partners.length;
60:        partners.push(newPartnerManager);
61:        partnerIds[newPartnerManager] == id; // @audit should be =
62:
63:        emit AddedPartner(newPartnerManager, id);
64:    }
```

## Tools Used
Manual review

## Recommended Mitigation Steps


```solidity
File: src/maia/factories/PartnerManagerFactory.sol
58:    function addPartner(PartnerManager newPartnerManager) external onlyOwner {
59:        uint256 id = partners.length;
60:        partners.push(newPartnerManager);
61:        partnerIds[newPartnerManager] = id;
62:
63:        emit AddedPartner(newPartnerManager, id);
64:    }
```


## Assessed type

Other
If admin `toggleBridgeAgentFactory` on a non BridgeAgentFactory address, that address will be able to directly call `addBridgeAgent`.
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

 https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L413


# Vulnerability details

## Impact
By illegally setting `isBridgeAgentFactory` mapping for a particular address(EOA or contract) to true, the following can happen:

- the address will be able to call `RootPort#addBridgeAgent`, passing in any account(EOA or contract) he wishes, which would allow that account to call `RootPort#burn` or `RootPort#bridgeToRoot` to burn or mint any amount of hTokens he wishes.

## Proof of Concept
Here is the `RootPort#toggleBridgeAgentFactory` function:

```solidity
function toggleBridgeAgentFactory(address _bridgeAgentFactory) external onlyOwner {
    isBridgeAgentFactory[_bridgeAgentFactory] = !isBridgeAgentFactory[_bridgeAgentFactory];

    emit BridgeAgentFactoryToggled(_bridgeAgentFactory);
}
```

The function does not perform any checks, and simply sets `isBridgeAgentFactory` for an address to the opposite of its former value.
This means that calling `toggleBridgeAgent` on an EOA account for example, sets `isBridgeAgentFactory` for that account to !false because the default boolean value in mappings is false.
So now, `isBridgeAgentFactory` for that EOA account has been set to true, so EOA can directly call `addBridgeAgent` function.
Here is `addBridgeAgent` function:

```solidity
function addBridgeAgent(address _manager, address _bridgeAgent) external requiresBridgeAgentFactory {
    if (isBridgeAgent[_bridgeAgent]) revert AlreadyAddedBridgeAgent();

    bridgeAgents.push(_bridgeAgent);
    bridgeAgentsLenght++;
    getBridgeAgentManager[_bridgeAgent] = _manager;
    isBridgeAgent[_bridgeAgent] = !isBridgeAgent[_bridgeAgent];

    emit BridgeAgentAdded(_bridgeAgent, _manager);
}
```

So the EOA account(which has been made a factory), would call `addBridgeAgent`, inputting as `_bridgeAgent` parameter, another EOA address(could also be his address).
The address he inputs will be given `isBridgeAgent` role, which would allow it to `RootPort#bridgeToRoot` or `RootPort#burn` to mint or burm any amount of hTokens it wants.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider implementing one of these:

- Only allow admin to remove, not add BridgeAgentFactory. Therefore, this check should be present:

```solidity
if (!isBridgeAgentFactory[_bridgeAgent])
            revert UnrecognizedBridgeAgentFactory();
```

- Admin should only be able to call `toggleBridgeAgentFactory` on a BridgeAgentFactory address that the Port recognizes. This can be done by checking if the BridgeAgentFactory address is present in `bridgeAgentFactories` array using a for loop (No need to worry about gas since it is only admin that would call it)

## Extra Alpha
Implementing the fix above(especially the first fix which is recommended), will cause `addBridgeAgentFactory` to be ineffective because `isBridgeAgentFactory` for the input address is not toggled and won't be able to be toggled after the fix above:
```solidit
If admin `toggleBridgeAgent` on a non BridgeAgent address, that address will be able to directly call some RootPort critical functions.
Audit: 2023-05-maia-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L400


# Vulnerability details

## Impact
By illegally setting `isBridgeAgent` mapping for a particular address(EOA or contract) to true, the following can happen:

- the address will be able to call `RootPort#burn` to burn any amount of global hTokens from any address he wishes
- The address will also be able to mint any amount of global hTokens by calling `RootPort#bridgeToRoot` which calls `mint` internal function

## Proof of Concept
Here is the `RootPort#toggleBridgeAgent` function:

```solidity
function toggleBridgeAgent(address _bridgeAgent) external onlyOwner {
    isBridgeAgent[_bridgeAgent] = !isBridgeAgent[_bridgeAgent];

    emit BridgeAgentToggled(_bridgeAgent);
}
```

The function does not perform any checks, and simply sets `isBridgeAgent` for an address to the opposite of its former value.
This means that calling `toggleBridgeAgent` on an EOA account for example, sets `isBridgeAgent` for that account to !false because the default boolean value in mappings is false.
So now, `isBridgeAgent` for that EOA has been set to true, so EOA can directly mint or burn hTokens

## Tools Used
Manual Review
## Recommended Mitigation Steps
Consider implementing one of these:

- Only allow admin to remove, not add BridgeAgents. Therefore, this check should be present:

```solidity
if (!isBridgeAgent[_bridgeAgent])
            revert UnrecognizedBridgeAgent();
```

- Admin should only be able to call `toggleBridgeAgent` on a BridgeAgent address that the Port recognizes. This can be done by checking if the BridgeAgent address is present in `bridgeAgents` array using a for loop (No need to worry about gas since it is only admin that would call it)



## Assessed type

Invalid Validation