xanoutas/solidity-security-findings huggingface dataset:
In MulticallRootRouter.sol, approve function can fail for non standard ERC20 tokens like USDT
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/MulticallRootRouter.sol#L111-L126
https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/MulticallRootRouter.sol#L137-L152


# Vulnerability details

## Impact
Some tokens (like USDT) do not work when changing the allowance from an existing non-zero allowance value. For example Tether (USDT)’s approve() function will revert if the current approval is not zero, to protect against front-running changes of approvals. 

[Link to usdt contract reference(SLOC 199-209)](https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#code)

approve is actually vulnerable to a sandwich attack as explained in the following document and this check for allowance doesn't actually avoid it.

Reference document link- https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit

In ERC20, front running attack is possible via approve() function,

Reference link for better understanding- https://blog.smartdec.net/erc20-approve-issue-in-simple-words-a41aaf47bca6

In the protocol, all functions using approve() must be first approved by zero. The  _approveAndCallOut() and _approveMultipleAndCallOut() are called to make ERC20 approvals. But it does not approve 0 first.

```Solidity
File: src/ulysses-omnichain/MulticallRootRouter.sol

    function _approveAndCallOut(
        address owner,
        address recipient,
        address outputToken,
        uint256 amountOut,
        uint256 depositOut,
        uint24 toChain
    ) internal virtual {
        //Approve Root Port to spend/send output hTokens.
        ERC20hTokenRoot(outputToken).approve(bridgeAgentAddress, amountOut);

        // some code

```

```Solidity
File: src/ulysses-omnichain/MulticallRootRouter.sol

    function _approveMultipleAndCallOut(
        address owner,
        address recipient,
        address[] memory outputTokens,
        uint256[] memory amountsOut,
        uint256[] memory depositsOut,
        uint24 toChain
    ) internal virtual {
        //For each output token
        for (uint256 i = 0; i < outputTokens.length;) {
            //Approve Root Port to spend output hTokens.
            ERC20hTokenRoot(outputTokens[i]).approve(bridgeAgentAddress, amountsOut[i]);

        // some code
```

## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/MulticallRootRouter.sol#L111-L126

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

## Tools Used
Manual review

## Recommended Mitigation Steps
approve 0 first OR use openzeppelin safeERC20.sol


## Assessed type

Other
Incorrect Use of Equality Operator in addPartner and addVault Functions
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The `addPartner` and `addVault` functions in the PartnerManagerFactory contract are intended to add new partner managers and vaults respectively. These functions also assign a unique ID to each new partner manager and vault by storing them in the `partnerIds` and `vaultIds` mappings. However, due to the incorrect use of the equality operator `==` instead of the assignment operator `=`, the IDs are not actually stored. This leads to incorrect state management of the contract which can have numerous unforeseen consequences including but not limited to:

- The inability to correctly reference or identify these partners and vaults later, which could lead to operational issues in the contract's logic.
- The `removePartner` and `removeVault` functions will not work as intended since the mappings do not hold the correct data.

## Proof of Concept
Affected code in `addPartner` function on line 61:

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

```
function addPartner(PartnerManager newPartnerManager) external onlyOwner {
    uint256 id = partners.length;
    partners.push(newPartnerManager);
61:    partnerIds[newPartnerManager] == id; // This should be an assignment, not an equality check.
    emit AddedPartner(newPartnerManager, id);
}
```
Affected code in `addVault` function on line 70:

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

```
function addVault(IBaseVault newVault) external onlyOwner {
    uint256 id = vaults.length;
    vaults.push(newVault);
70:    vaultIds[newVault] == id; // This should be an assignment, not an equality check.
    emit AddedVault(newVault, id);
}
```
The lines of code highlighted above should instead use the assignment operator `=` to correctly store the ID of the newly added partner or vault.

## Recommended Mitigation Steps
The code should be updated to use the assignment operator `=` instead of the equality operator `==` in the `addPartner` and `addVault` functions. The corrected code should look like this:

In `addPartner` function:

```
partnerIds[newPartnerManager] = id;
```
In `addVault` function:

```
vaultIds[newVault] = id;
```


## Assessed type

Context
Functions don't update after being called
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/ArbitrumBranchPort.sol#L45-L55
https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/ArbitrumBranchPort.sol#L58-L73
https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/ArbitrumBranchPort.sol#L58-L73


# Vulnerability details

## Impact
Without updating the reserve or vault value of tokens after calling different functions, the contract may be prone to inconsistent state, security issues, financial implications, and bad user experience. It is important to review and update the reserve value to maintain contract integrity and functionality.

## Proof of Concept
From the above functions linked  no explicit code that updates the balances after they're called

## Tools Used
Manual review 
## Recommended Mitigation Steps

Add explicit codes to update the balances after they are called


## Assessed type

Token-Transfer
Reactivated gauges have incorrect accounting for the last cycle’s rewards
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/rewards/rewards/FlywheelGaugeRewards.sol#L185


# Vulnerability details

## Impact
reactivated gauges that previously had queued up rewards have a mismatch between their storedCycle and contract’s gaugeCycle state variable.

Due to this mismatch, there is also a resulting issue with the accounting logic for its completed rewards:

## Proof of Concept

Due to this mismatch, there is also a resulting issue with the accounting logic for its completed rewards:
https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/rewards/rewards/FlywheelGaugeRewards.sol#L185

```
 uint112 completedRewards = queuedRewards.storedCycle == lastCycle ?queuedRewards.cycleRewards : 0;
```
Consequently, this then produces an incorrect value for QueuedRewards.priorCycleRewards:

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/rewards/rewards/FlywheelGaugeRewards.sol#L190
```
 priorCycleRewards: queuedRewards.priorCycleRewards + completedRewards,
```
As now completedRewards will be equal to 0 instead of the previous cycle’s rewards for that gauge. This may cause a loss of rewards accounted for this gauge as this value is later used in getAccruedRewards().

## Tools Used
Code inspection
## Recommended Mitigation Steps

Consider changing the logic of the check so that storedCycle values further in the past than lastCycle may produce the right rewards return for this expression, such as using <= instead of == and adding an explicit check for storedCycle == 0 to account for the initial scenario.


## Assessed type

Other
`RootBridgeAgent.redeemSettlement` can be front-run using `RootBridgeAgent.retrySettlement` causing redeem DoS
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Since [RootBridgeAgent.retrySettlement(...)](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L243-L252) can be called by **anyone** for **any** settlement, a malicious actor can front-run an user trying to redeem his failed settlement via [RootBridgeAgent.redeemSettlement(...)](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L254-L268) by calling [RootBridgeAgent.retrySettlement(...)](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L243-L252) with `_remoteExecutionGas = 0` in order to make sure that this settlement will also fail in the future.  

As a consequnce, the user's subsequent call to [RootBridgeAgent.redeemSettlement(...)](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L254-L268) [will revert](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L260-L261) (DoS) because the settlement was [already marked](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L577) with `SettlementStatus.Success` during the malicious actor's call to [RootBridgeAgent.retrySettlement(...)](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L243-L252). Therefore the user is unable to redeem his assets.

## Proof of Concept
The following PoC modifies an existing test case to confirm the above claims resulting in:
* The settlement being marked with `SettlementStatus.Success`.
* DoS of [RootBridgeAgent.redeemSettlement(...)](https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L254-L268) method for this settlement.
* **The user not being able to redeem his assets.**

Just apply the *diff* below and run the test with `forge test --match-test testRedeemSettlement`:

```diff
diff --git a/test/ulysses-omnichain/RootTest.t.sol b/test/ulysses-omnichain/RootTest.t.sol
index ea88453..ccd7ad2 100644
--- a/test/ulysses-omnichain/RootTest.t.sol
+++ b/test/ulysses-omnichain/RootTest.t.sol
@@ -1299,14 +1299,13 @@ contract RootTest is DSTestPlus {
         hevm.deal(_user, 1 ether);
 
         //Retry Settlement
-        multicallBridgeAgent.retrySettlement{value: 1 ether}(settlementNonce, 0.5 ether);
 
         settlement = multicallBridgeAgent.getSettlementEntry(settlementNonce);
 
         require(settlement.status == SettlementStatus.Success, "S
NO CHECK TO VERIFY THE ELEMENTS OF `assetsAmounts[]` ARRAY IS IN THE SAME ORDER AS `assets[]` ARRAY, IF MISCONFIGURED COULD BREAK THE INTERNAL ACCOUNTING OF SHARE CALCULATION
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/ERC4626MultiToken.sol#L194-L208


# Vulnerability details

## Impact
In the `ERC4626MultiToken.convertToShares()` function, `assetsAmounts` are used to calculate the shares to mint. Here the `assetsAmounts` are expected to be passed in the order of the `assets` array. If there is any misconfiguration in the order, then it will affect the internal `shares` calculation since the `weights[i]` would be different for the respective `assetsAmounts[i]`. Hence the minted `share` amount could be erroneous which could impact the overall accounting of the protocol.

## Proof of Concept

```solidity
    function convertToShares(uint256[] calldata assetsAmounts) public view virtual returns (uint256 shares) {
        uint256 _totalWeights = totalWeights;
        uint256 length = assetsAmounts.length;

        if (length != assets.length) revert InvalidLength();

        shares = type(uint256).max;
        for (uint256 i = 0; i < length;) {
            uint256 share = assetsAmounts[i].mulDiv(_totalWeights, weights[i]);
            if (share < shares) shares = share;
            unchecked {
                i++;
            }
        }
    }
```

https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/ERC4626MultiToken.sol#L194-L208

## Tools Used

VSCode and Manual Review

## Recommended Mitigation Steps

Hence it is recommended to implement a validation to verify that the elements of the `assetsAmounts[]` array are in the correct order as the `assets[]` array. This can be done by passing in a struct array when calling the `ERC4626MultiToken.deposit()` function. Each struct should include the `address` of the respective `asset` and the corresponding `assetsAmounts`. This struct array should be passed into the `ERC4626MultiToken.convertToShares()` function from the `deposit` function. Inside the `convertToShares` function inside the `for` loop it can be checked if each of the `address` of the asset is in the correct order as of `asset[]` array. If the order is different the transaction should revert.




## Assessed type

Other
In VirtualAccount.sol.withdrawERC20(), Transaction revert if the Token does not support 0 value transfer
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In VirtualAccount.sol.withdrawERC20(), Transaction revert if the Token does not support 0 value transfer when transferring tokens to recipient address.

```solidity
File: src/ulysses-omnichain/VirtualAccount.sol

31    function withdrawERC20(address _token, uint256 _amount) external requiresApprovedCaller {
32        _token.safeTransfer(msg.sender, _amount);
33    }
```
The withdrawERC20() function provides the ability to withdraw ANY ERC20 tokens. The issue is there are some token which reverts with zero value transfers. Tokens like LEND token, etc. The issue is at L-167, if the erc20Data.amount is 0, the code would revert if the ERC20 token does not support 0 value transfer.

According to https://github.com/d-xo/weird-erc20#revert-on-zero-value-transfers

Some tokens (e.g. LEND) revert when transferring a zero value amount.

## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/ulysses-omnichain/VirtualAccount.sol#L31-L33

## Tools Used
Manual review

## Recommended Mitigation Steps
Recommend to check if the amount is 0 before performing withdraw in withdrawERC20() function.

```solidity
File: src/ulysses-omnichain/VirtualAccount.sol

    function withdrawERC20(address _token, uint256 _amount) external requiresApprovedCaller {
+        if(_amount != 0){
        _token.safeTransfer(msg.sender, _amount);
+   }
    }
```


## Assessed type

Token-Transfer
changeAdmin function does not have checks 
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/gauges/factories/BaseV2GaugeManager.sol#L146


# Vulnerability details

## Impact
changeAdmin does not check for new address which should be different than the old one.
Also newAdmin should not be same as the carrying out the transaction.

## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/main/src/gauges/factories/BaseV2GaugeManager.sol#L146

## Tools Used
VScode

## Recommended Mitigation Steps
CEI to be placed in context of checking Admin!= newAdmin and newAdmin != msg.sender .


## Assessed type

Invalid Validation
Interactions with Pool do not use valid deadlines for operations
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

Miner can potentially hold the transaction which results in loss of funds for users.

## Proof of Concept

```solidity
File: TalosBaseStrategy.sol

    (liquidityDifference, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity(
        INonfungiblePositionManager.IncreaseLiquidityParams({
            tokenId: _tokenId,
            amount0Desired: amount0Desired,
            amount1Desired: amount1Desired,
            amount0Min: 0,     
            amount1Min: 0,
 @->        deadline: block.timestamp        
        })
    );

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

AMMs provide their users with an option to limit the execution of their pending actions, such as swaps or adding and removing liquidity. The most common solution is to include a deadline timestamp as a parameter (for example in Uniswap V2 and Uniswap V3). If such an option is not present, users can unknowingly perform bad trades as interaction with the pools is very time sensitive, if transaction gets hold for sometime than it can lead to bad trades unknowingly by the Users and can potentially result in loss of funds.

All the functions in the codebase that interact with uniswap pools have a deadline parameter passed as `block.timestamp`, which means that whenever the miner decides to include the transaction in a block, it will be valid at that time, since `block.timestamp` will be the current timestamp at the time of mining. but here, there is a possibility of a malicious miner to hold the transaction how whatever amount he/she wants.

This is tbe reason, instead of `block.timestamp`, an extra arguments should be passed with the functions and user should be allowed to pass a valid deadline for it so that in case a malicious miner tries to hold it, then the transaction reverts as per the deadline specified by the user. Having slippage is an issue for any AMMs but having outdated Slippage protection can also be a dangerous attack vector.

Link to a similar issue reported in [Paraspace Audit](https://code4rena.com/reports/2022-11-paraspace#m-13-interactions-with-amms-do-not-use-deadlines-for-operations) by Code4rena.

## Tools Used

VS Code

## Recommended Mitigation Steps

Add deadline arguments in all functions that interact with uniswap pools, and pass it along to these calls.


## Assessed type

Uniswap
Many `create` methods are suspicious of the reorg attack
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyStaked.sol#L28


# Vulnerability details

## Proof of Concept

There are many instance of this, but to understand things better, taking the example of `createTalosV3Strategy` method.

The `createTalosV3Strategy` function deploys a new `TalosStrategyStaked` contract using the create, where the address derivation depends only on the arguments passed.

At the same time, some of the chains like Arbitrum and Polygon are suspicious of the reorg attack.

```solidity
File: TalosStrategyStaked.sol

  function createTalosV3Strategy(
        IUniswapV3Pool pool,
        ITalosOptimizer optimizer,
        BoostAggregator boostAggregator,
        address strategyManager,
        FlywheelCoreInstant flywheel,
        address owner
    ) public returns (TalosBaseStrategy) {
        return new TalosStrategyStaked( // @audit-issue Reorg Attack
                pool,
                optimizer,
                boostAggregator,
                strategyManager,
                flywheel,
                owner
            );
    }

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

Even more, the reorg can be couple of minutes long. So, it is quite enough to create the `TalosStrategyStaked` and transfer funds to that address using `deposit` method, especially when someone uses a script, and not doing it by hand.

Optimistic rollups (Optimism/Arbitrum) are also suspect to reorgs since if someone finds a fraud the blocks will be reverted, even though the user receives a confirmation.

Same issue can affect factory contracts in Ulysses omnichain contracts as well with more severe consequences.

Can refer this an issue previously report [here](https://code4rena.com/reports/2023-04-frankencoin#m-14-re-org-attack-in-factory) to have more understanding about it.

## Impact

Exploits involving Stealing of funds.

## Tools Used

VS Code

## Recommended Mitigation Steps

Deploy such contracts via `create2` with `salt`.


## Assessed type

Other
Contracts are vulnerable to rebasing accounting-related issues
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/rewards/base/FlywheelCore.sol#L197


# Vulnerability details

## Impact
Rebasing tokens are tokens that have each holder's balanceof() increase over time. Aave aTokens are an example of such tokens. If rebasing tokens are used, rewards accrue to the contract holding the tokens, and cannot be withdrawn by the original depositor. 

## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/main/src/rewards/base/FlywheelCore.sol#L197

## Tools Used
Manual review

## Recommended Mitigation Steps
To address the issue, track 'shares' deposited on a pro-rata basis, and let shares be redeemed for their proportion of the current balance at the time of the withdrawal.



## Assessed type

Token-Transfer
Unable to check state() if proposalId == 0
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/governance/GovernorBravoDelegateMaia.sol#L297-L299


# Vulnerability details

## Impact
`state()` function cannot be called to view proposal state if proposalId == 0.

## Proof of Concept
There is no check to prevent queueing a proposalId with a value of 0 via the queue() function.
However, in the state() function there is a check preventing using a proposalId == 0.
For clarity: initialProposalId must be zero according to _initiate(), therefore, proposalId cannot be 0 according to check below.
```
function state(uint proposalId) public view returns (ProposalState) {
    require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");
```

## Tools Used
Manual Review
## Recommended Mitigation Steps
Implement check to preventing queueing a proposalId == 0.


## Assessed type

Other