xanoutas/solidity-security-findings huggingface dataset:
setBooster() function may be used to steal unclaimed rewards in FlywheelCore contract
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#L137-L141


# Vulnerability details

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

# Vulnerability details

## Impact
A malicious owner can steal all unclaimed rewards and break the reward accounting mechanism

## Proof of Concept

Even if the owner is a good guy but the fact that there exists a rug vector available may negatively impact the protocol's reputation. Or maybe the hotkeys of a multi-sig wallet [may be stolen](https://twitter.com/Mudit__Gupta/status/1675584195798913024). Furthermore, since this contract is meant to be used by other projects, the trustworthiness of every project cannot be vouched for.

The problem lies in the fact that the `flywheelRewards` is not immutable. Let's check a hypothetical process a malicious owner can take.
The `boostedBalanceOf()` function is used to calculate the boosted balance of a user in a given strategy. By creating and setting a booster contract that returns zero when users call `boostedBalanceOf()` in a situation where the user address is not under the attacker's control, and returning arbitrary values for those under his/her control, an attacker can choose specific amounts of rewardToken to assign to himself/herself. The attacker can then call `claimRewards()` to withdraw the funds. Any amounts that the attacker assigns to himself/herself over the amount that normally would have been assigned, upon claiming, is taken from other users' unclaimed balances since tokens are custodied by the flywheelRewards address rather than per-user accounts.

```
    function setBooster(IFlywheelBooster newBooster) external onlyOwner {
        flywheelBooster = newBooster;

        emit FlywheelBoosterUpdate(address(newBooster));
    }
```
In the function `accrueUser()` we have the `supplierToken` variable defined in a way that calls `boostedBalanceOf()`:

```
    function accrueUser(ERC20 strategy, address user, uint256 index) private returns (uint256) {
        // load indices
        uint256 supplierIndex = userIndex[strategy][user];

        // sync user index to global
        userIndex[strategy][user] = index;

        // if user hasn't yet accrued rewards, grant them interest from the strategy beginning if they have a balance
        // zero balances will have no effect other than syncing to global index
        if (supplierIndex == 0) {
            supplierIndex = ONE;
        }

        uint256 deltaIndex = index - supplierIndex;
        // use the booster or token balance to calculate reward balance multiplier
        uint256 supplierTokens = address(flywheelBooster) != address(0)
            ? flywheelBooster.boostedBalanceOf(strategy, user)
            : strategy.balanceOf(user);

        // accumulate rewards by multiplying user tokens by rewardsPerToken index and adding on unclaimed
        uint256 supplierDelta = (supplierTokens *
Not using slippage parameter when interacting with AMMs
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#L144-L145
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L206-L207
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L267-L268
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L357-L358
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyVanilla.sol#L146-L147
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/libraries/PoolActions.sol#L82-L83


# Vulnerability details

## Impact
The slippage parameters are hardcoded to 0, meaning the minimum amount can be 0. The absence of slippage protection causes transactions to be vulnerable to front running. This can result in users potentially losing their funds.

## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L144-L145
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L206-L207
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L267-L268
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L357-L358
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyVanilla.sol#L146-L147
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/libraries/PoolActions.sol#L82-L83

## Tools Used
Manual review

## Recommended Mitigation Steps
Set a reasonable minimum amount


## Assessed type

Uniswap
Tokens with multiple addresses can be stolen due to reliance on balanceOf()
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
Some ERC20 tokens have multiple valid contract addresses that serve as entrypoints for manipulating the same underlying storage (such as Synthetix tokens like SNX and sBTC and the TUSD stablecoin). The accrueUser function holds all rewards for all pools, assumes that a contract address is a unique identifier for a token, and relies on the return value of balanceOf for manipulated tokens to determine what transfers are needed during transaction settlement, multiple entrypoint tokens are not safe to be used in pools. 
An attacker can atomically create a second pool using the alternate address, allocate liquidity, and then immediately deallocate it. 
During execution of the _ accrueUser function, supplierTokens will return a positive net balance for the double entrypoint token, crediting the attacker and transferring them more rewards.
This attack only costs gas, as the allocation and deallocation of non-double entrypoint tokens will cancel out.

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

## Tools Used
VSCode

## Recommended Mitigation Steps
An explicit blacklist should be added to prevent any address other than an "official" one from being used to create pairs and pools for such tokens (potentially fixed at deployment time, as double entrypoint tokens are rare and now widely known to be dangerous).


## Assessed type

Invalid Validation
DateTimeLib.isTuesday can't guarantee the first Tuesday of the month
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/libraries/DateTimeLib.sol#L55-L61
https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/vMaia.sol#L109-L110


# Vulnerability details

## Impact
`DateTimeLib.isTuesday` serves to determine whether or not the input `timestamp` is a Tuesday of a month. This can easily be bricked since there are 4 or 5 Tuesdays in every calendar month which allows users withdraw from their vMaia positions 4 to 5 times instead of just once a month.

## Proof of Concept
The following code block on line 109 is the only place where `DateTimeLib.isTuesday` is called after ensuring `_currentMonth != currentMonth`:

https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/vMaia.sol#L102-L114

```
    function beforeWithdraw(uint256, uint256) internal override {
        /// @dev Check if unstake period has not ended yet, continue if it is the case.
        if (unstakePeriodEnd >= block.timestamp) return;

        uint256 _currentMonth = DateTimeLib.getMonth(block.timestamp);
        if (_currentMonth == currentMonth) revert UnstakePeriodNotLive();

109:        (bool isTuesday, uint256 _unstakePeriodStart) = DateTimeLib.isTuesday(block.timestamp);
        if (!isTuesday) revert UnstakePeriodNotLive();

        currentMonth = _currentMonth;
        unstakePeriodEnd = _unstakePeriodStart + 1 days;
    }
``` 
And. as can be seen in `DateTimeLib.isTuesday`, the function simply returns the weekday from the unix timestamp determining whether or not `((day + 3) % 7) + 1 == 2` or `isTuesday` is true to the caller. No where in the logic is seen as making sure it is the first week (or 7 days) of the month:

https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/libraries/DateTimeLib.sol#L53-L61

```
    /// @dev Returns the weekday from the unix timestamp.
    /// Monday: 1, Tuesday: 2, ....., Sunday: 7.
    function isTuesday(uint256 timestamp) internal pure returns (bool result, uint256 startOfDay) {
        unchecked {
            uint256 day = timestamp / 86400;
            startOfDay = day * 86400;
            result = ((day + 3) % 7) + 1 == 2;
        }
    }
```
## Recommended Mitigation Steps
A simple solution to this problem could be to check that the day of the month is less than or equal to 7. This is because the first Tuesday of the month will always fall within the first seven days of the month.

To implement this, you might add another function to your DateTimeLib library that calculates the day of the month, and then use this function in the `beforeWithdraw` method.

Here is a rough outline of what this function could look like:

```
/// @dev Returns the day of the month from the unix timestamp.
function getDay(uint256 timestamp) internal pure returns (uint256 day) {
    uint256 epochDay = timestamp / 86400;

    /// @solidity memory-safe-assembly
    assembly {
        // ... similar calculations as in getMonth ...

        day := add(sub(doy, mul(5, mp)), 123)
    }
}
``
Only the state() of the latest proposal can be checked
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
`state()` function cannot view the state from any proposal except for the latest one.
## Proof of Concept
```
proposalCount >= proposalId && proposalId > initialProposalId,"GovernorBravo::state: invalid proposal id"
```
Currently proposalCount needs to be bigger or equal to proposalId.
Assuming proposalId is incremented linearly in conjunction with proposalCount, this implies only the most recent proposalId will pass the require() check above. All other proposals will not be able to have their states checked via this function.

## Tools Used
Manual Review.
## Recommended Mitigation Steps
Change above function to proposalCount <= proposalId (assuming proposalId is set linearly, which currently is not enforced by code).


## Assessed type

Other
Time cycle can return wrong value due to downcasting
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Time cycle of reward queue can act unexpectedly due to downcasting block.timestamp to uint32. If the value of block.timestamp exceeds uint32 it will overflow resulting in returning wrong cycle time for rewards queuing.

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

## Tools Used
VSCode

## Recommended Mitigation Steps
Use uint256 to avoid potential overflow of timecycle in the future.


## Assessed type

Under/Overflow
Downcasting result in loss of fees for protocol and user
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Downcasting fees collection value from uint256 to uint128 may result in loss of fees for protocol. User and protocol both will get less value in fees when value potentially exceeds uint128 limit and overflow.

## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyVanilla.sol#L111

## Tools Used
VSCode

## Recommended Mitigation Steps
Avoid downcasting by using method type(uint256).max instead of type(uint128).max.


## Assessed type

Under/Overflow
RootPort.addBridgeAgentFactory() is taking bridgeAgentLenght into account rather than bridgeAgentFactoriesLength 
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
- This function will increase `bridgeAgentsLenght` rather than `bridgeAgentFactoriesLenght` which will lead to improper state update in `bridgeAgentFactories[]` array.

- Also it will lead to improper state change in `bridgeAgents[]` as `bridgeAgentsLenght` is changed unexpectedly. 

- This can have severe impact while traversing and fetching addresses from both arrays. 

## Proof of Concept

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

## Tools Used
Manual Review

## Recommended Mitigation Steps
- Remove `bridgeAgentsLenght` and add `bridgeAgentFactoriesLenght` instead in the function.






## Assessed type

Error
Pragma non-specification can lead to non-functional / corrupted contract when deployed on Arbitrum
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/hermes/tokens/HERMES.sol#L2


# Vulnerability details

## Impact
Pragma has been set to ^0.8.0 allowing the contracts to be compiled with a compiler equal or greater than 0.8.0. The problem with compiling is that Arbitrum is NOT compatible with 0.8.20 and later.
Contracts compiled with non specified versions will result in a non-functional or potentially damaged version that won't behave as expected. The default behaviour of compiler would be to use the newest version which would mean by default it will be compiled with the 0.8.20 version which will produce broken code.

## Proof of Concept
All contracts in scope.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Lock or Constrain pragma as follows: pragma solidity 0.8.0 or pragma solidity >=0.8.0 <=0.8.19


## Assessed type

Other
Missing deadline check when performing a swap
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

Missing deadline checks allow pending transactions to be maliciously executed in the future. Without deadline parameters, as a consequence, users can have their operations executed at unexpected times, when the market conditions are unfavorable.

## Proof of Concept

The problem occurs  in `RootBridgeAgent.sol`.`_gasSwapOut()` function:
```solidity
(int256 amount0, int256 amount1) = IUniswapV3Pool(poolAddress).swap(
            address(this),
            !zeroForOneOnInflow,
            int256(_amount),
            sqrtPriceLimitX96,
            abi.encode(SwapCallbackData({tokenIn: address(wrappedNativeToken)}))
        );
```

We don't have a deadline check in `_gasSwapOut()`. The deadline check ensures that the transaction can be executed on time and the expired transaction revert.

## Tools Used

Visual Studio Code

## Recommended Mitigation Steps

Introduce a `deadline` parameter in `_gasSwapOut()` function.


## Assessed type

Uniswap
Holders will not have enough time to delegate or cast their votes
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-05-maia/blob/main/src/governance/GovernorBravoDelegateMaia.sol#L18-L27


# Vulnerability details

## Impact

Note: The sponsor [message](https://discord.com/channels/810916927919620096/1110626768223739924/1116673692588589106) on which networks project will be deployed. 

Vote period/delay will not work correctly on L2s due to the use of `block.number`, which may not give holders enough time to delegate or cast before the voting delay or period ends.

## Proof of Concept

```solidity
    uint256 public constant MIN_VOTING_PERIOD = 80640; // About 2 weeks

    /// @notice The max setable voting period
    uint256 public constant MAX_VOTING_PERIOD = 161280; // About 4 weeks

    /// @notice The min setable voting delay
    uint256 public constant MIN_VOTING_DELAY = 40320; // About 1 weeks

    /// @notice The max setable voting delay
    uint256 public constant MAX_VOTING_DELAY = 80640; // About 2 weeks
```

Each of these values represent a specific period in ethereum blocks (.i.e 12s), but all of this values will not give the holders enough time when deploying on L2s due to .

Here is a simple comparison of block times across different networks:

| Network               | Block time |
|-----------------------|------------|
| Ethereum mainnet      | 12 s       |
| Arbitrum              | 2 s        |
| Optimism              | 2 s        |
| Polygon               | 2 s        |
| Binance               | 3 s        |
| Metis                 | 2 s        |
| Fantom                | 1 s        |
| Avalanche             | 3 s        |

## Tools Used

Manual Review

## Recommended Mitigation Steps

We recommend using `block.timestamp` instead of `block.number`.



## Assessed type

Timing
No Deadline check in `UlyssesRouter`, allowing outdated slippage and allow pending transaction to be unexpected executed
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Proof of Concept

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: Uniswap V2 and Uniswap V3). If such an option is not present, users can unknowingly perform bad trades.

Currently there are 3 external methods in `UlyssesRouter`:

1. `addLiquidity`
2. `removeLiquidity`
3. `swap`

2 things are most important while interacting with AMMs:

1. Slippage parameter: Which is controlled here by user by passing the value of `minOutput` as a function parameter.
2. Deadline of Execution: In case, the transaction is submitted to the mempool and transaction fee is too low for miners to be interested in including the transaction in a block immediately, then the transaction can stay in the mempool for some time. If it is executed at a later time, there is high chance that the slippage parameter earlier passed might result in higher slippage than what user originally anticipated. This can result in loss of funds.

A MEV bot can detect the pending transactions. Since the outdated maximum slippage value now allows for high slippage, the bot sandwiches the transaction, resulting in significant profit for the bot and significant loss for user.

This second part is not handled in `UlyssesRouter`.

## Impact

Potential loss of funds

## Tools Used

VS Code

## Recommended Mitigation Steps

Add a deadline check exactly how uniswap does in functions related to adding liquidity, removing liquidity and swapping.


## Assessed type

MEV
More funds can be redeemed than deposited
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The redeem method in removeLiquidity() function does not check for the liquidity provided by the msg.sender. This can result in attacker redeeming more amount than deposited, thus liquidating the whole pool.

## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesRouter.sol#L62

## Tools Used
VSCode

## Recommended Mitigation Steps
CEI should be placed in top which first check amount deposited in the pool, then redeem value should also be checked for being <= current balance of liquidity provided the user.


## Assessed type

Other
In FlywheelCore.sol contract, setBooster() can be used to steal unclaimed rewards
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
A malicious authorized user can steal all unclaimed rewards and break the reward accounting

Even if the authorized user is benevolent the fact that there is a rug vector available may negatively impact the protocol's reputation. Furthermore since this contract is meant to be used by other projects, the trustworthiness of every project cannot be vouched for.

## Proof of Concept
By setting a booster that returns zero for all calls to boostedBalanceOf() where the user address is not under the attacker's control, and returning arbitrary values for those under his/her control, an attacker can choose specific amounts of rewardToken to assign to himself/herself. The attacker can then call claimRewards() to withdraw the funds. Any amounts that the attacker assigns to himself/herself over the amount that normally would have been assigned, upon claiming, is taken from other users' unclaimed balances, since tokens are custodied by the flywheelRewards address rather than per-user accounts.

```Solidity
File: src/rewards/base/FlywheelCore.sol

137    function setBooster(IFlywheelBooster newBooster) external onlyOwner {
138        flywheelBooster = newBooster;
139
140        emit FlywheelBoosterUpdate(address(newBooster));
141    }
```
[Link to code](https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/rewards/base/FlywheelCore.sol#L137-L141)

```Solidity
File: src/rewards/base/FlywheelCore.sol

196        uint256 supplierTokens = address(flywheelBooster) != address(0)
197            ? flywheelBooster.boostedBalanceOf(strategy, user)
198            : strategy.balanceOf(user);
199
200        // accumulate rewards by multiplying user tokens by rewardsPerToken index and adding on unclaimed
201        uint256 supplierDelta = (supplierTokens * deltaIndex) / ONE;
202        uint256 supplierAccrued = rewardsAccrued[user] + supplierDelta;
203
204        rewardsAccrued[user] = supplierAccrued;
```
[Link to code](https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/rewards/base/FlywheelCore.sol#L196-L204)

Projects also using BaseFlywheelRewards or its child contrats, are implicitly approving infinite transfers by the core.

```Solidity
File: src/rewards/base/FlywheelCore.sol

25    constructor(FlywheelCore _flywheel) {
26        flywheel = _flywheel;
27        address _rewardToken = _flywheel.rewardToken();
28        rewardToken = _rewardToken;
29
30        _rewardToken.safeApprove(address(_flywheel), type(uint256).max);
31    }
```
[Link to code](https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/rewards/base/BaseFlywheelRewards.sol#L31-L37)

## References
This is a direct reference of Medium severity found in xtribe audit.
Finding reference link- https://github.
Maximum approve can liquidate
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The getUlyssesLP function approves the recipient contract with the max value instead of only the required amount.


## Proof of Concept
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesRouter.sol#L40


## Tools Used
Hardhat

## Recommended Mitigation Steps
The approval should not be set to the max value, especially if the amount that the contract may use is already known in this call, like this is the case for getUlyssesLP.
Only approve amount.



## Assessed type

Other