xanoutas/solidity-security-findings huggingface dataset:
M-05A Unmitigated
Audit: 2024-04-ai-arena-mitigation-findings
Severity: MEDIUM

# Lines of code

https://github.com/ArenaX-Labs/2024-02-ai-arena-mitigation/blob/1192a55963c92fb4bd9ca8e0453c96af09731235/src/FighterFarm.sol#L366
https://github.com/ArenaX-Labs/2024-02-ai-arena-mitigation/blob/1192a55963c92fb4bd9ca8e0453c96af09731235/src/MergingPool.sol#L142-L175
https://github.com/ArenaX-Labs/2024-02-ai-arena-mitigation/blob/1192a55963c92fb4bd9ca8e0453c96af09731235/src/FighterFarm.sol#L425
https://github.com/ArenaX-Labs/2024-02-ai-arena-mitigation/blob/1192a55963c92fb4bd9ca8e0453c96af09731235/src/FighterFarm.sol#L241



# Vulnerability details

### C4 issue
M-05A: [Users can get benefited from DNA pseudorandomly calculation](https://github.com/code-423n4/2024-02-ai-arena-findings/issues/1017)

### Comments
Before, the pseudorandom derivation of new fighters' DNA was vulnerable to manipulation in several ways:
1. owner address manipulation in `FighterFarm::claimFighters` and `FighterFarm::reRoll`.
1. users could freely choose `mintPassDnas` values to pass to `FighterFarm::redeemMintPass`.
1. users could delay the minting of fighters till `fighters.length` resulted in a beneficial DNA in `FighterFarm::mintFromMergingPool` in `FighterFarm::claimFighters`.
1. it was possible to know in advance which re-roll would result in the best DNA in `FighterFarm::reRoll`, because of `numRerolls[tokenId]` being used to generate the DNA hash.

### Mitigation
[PR #11](https://github.com/ArenaX-Labs/2024-02-ai-arena-mitigation/pull/11)
This issue has been only partially mitigated.

#### `FighterFarm::redeemMintPass`
There's one instance in which the mitigation works. `FighterFarm::redeemMintPass` now relies on a trusted `_delegatedAddress` which controls what parameters are being used, including the `mintPassDnas` values which are `keccak256` hashed into DNAs. This fix was introduced as mitigation of H-03.

#### `FighterFarm::claimFighters`
On the other hand, `FighterFarm::claimFighters` now relies as well on a trusted `_delegatedAddress`, but it signs:

```solidity
bytes32 msgHash = bytes32(keccak256(abi.encode(
    msg.sender, 
    numToMint[0], 
    numToMint[1],
    nftsClaimed[msg.sender][0],
    nftsClaimed[msg.sender][1]
)));
```

and then `msg.sender`, `nftsClaimed[msg.sender][0]` and `nftsClaimed[msg.sender][1]` are used to generate the DNA hash. In all scenarios in which msg.sender could be manipulated, for example if promotional campaigns are run in which users could precompute and use the address resulting in the best DNA, the issue remains unmitigated. Note that `nftsClaimed[msg.sender]` values of newly generated addresses are always 0 and `numToMint` could probably be known or assumed in advance.

Another consequence of the fix that seems incorrect is that all fighters minted within a `FighterFarm::claimFighters` call will have the same DNA, because `msg.sender`, `nftsClaimed[msg.sender][0]` and `nftsClaimed[msg.sender][1]` don't change among iterations in:
```solidity
for (uint16 i = 0; i < totalToMint; i++) {
    _createNewFighter
Sencond hand Delegatee can Withdraw before owner undelegates
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L371-L375
https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L329-L331


# Vulnerability details

## Impact
When an original depositor delegates to another address, the new address can call withdraw before the delegator undelegates.

## Proof of Concept
```
 require(locked_.amount > 0, "No lock");
        require(locked_.end <= block.timestamp, "Lock not expired");
        require(locked_.delegatee == msg.sender, "Lock delegated");
```
All these checks will pass as the new address amount has been increased
```
 if (action == LockAction.DELEGATE) {
            newLocked.delegated += value;
```
locked.end will also pass as long as lock time is yet to expire, ```locked_.delegatee == msg.sender``` since the new address is the new delegatee, the last check psses too.
```(bool success, ) = msg.sender.call{value: amountToSend}("");``` this is then called to transfer to the new address.

When the real owner now decides to undelegate, the process fails due to: https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L400-L401

Attack Scenerio:
    1. When an original depositor creates a lock, they can delegate voting power to another address using the delegate function.
    2. The attacker, who is now the delegatee, can exploit the vulnerability to withdraw funds from the lock even before the original depositor undelegates.
    3. The vulnerability arises due to improper checks in the withdraw function. The function checks if the lock exists, if it has expired, and if the calling address matches the delegatee.
   4.  Since the delegatee's voting power and lock amount have been increased through delegation, these checks will pass successfully.
   5.  The attacker then initiates a withdrawal by calling the withdraw function, and funds are transferred to their address.
    6. However, when the original depositor attempts to undelegate, the system fails 
## Tools Used
Manual review

## Recommended Mitigation Steps
Introduce a mechanism that prevents withdrawals for a certain period after delegation or when a delegation change occurs. This could include cooldown periods or additional steps for approval








## Assessed type

Context
User can claim most of the rewards for a lending market by depositing cNote for just 2 blocks / epoch
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/LendingLedger.sol#L117-L143


# Vulnerability details

## Impact
For a user to receive rewards for supplying cNote in a lending market (LM), he only needs to have supplied the cNote at the end of an epoch.
Users staking for the whole duration of an epoch get 0 benefits, compared to users who supply only at the end of the epoch.

Therefore evRWA provides no incentive for users to supply cNote long-term in a specific LM. 

Since there is no incentive to stick to one LM, this significantly reduces the incentive to lock cNote for evRWA - there is no need to vote for an LM because the user can choose the LM with the highest weight and redeem the rewards for it with 0 commitment.

This will create an unhealthy dynamic of reward-hunting without long-term commitment to the protocol.

Even if a LM provides good incentives for the user to keep tokens supplied, it is a reasonable assumption to make that protocols with even better incentives will exist.

I believe this to be a high issue because it can be detrimental to the success of the veRWA system.
In order to prevent the issue from manifesting, the protocol will have to rely on the LMs to over-incentivize or to enforce long minimum deposit durations. 
No such requirements for the LMs have been described in the audit docs, which leads me to believe that no such requirements have been considered.

## Proof of Concept
1. On deposit, LendingLedger::sync_ledger is called, which updates the deposited amount for the current epoch.
```
function sync_ledger(address _lender, int256 _delta) external { 
        address lendingMarket = msg.sender; 
        require(lendingMarketWhitelist[lendingMarket], "Market not whitelisted");

        _checkpoint_lender(lendingMarket, _lender, type(uint256).max);
        uint256 currEpoch = (block.timestamp / WEEK) * WEEK;
        int256 updatedLenderBalance = int256(lendingMarketBalances[lendingMarket][_lender][currEpoch]) + _delta;
        require(updatedLenderBalance >= 0, "Lender balance underflow"); // Sanity check performed here, but the market should ensure that this never happens

        lendingMarketBalances[lendingMarket][_lender][currEpoch] = uint256(updatedLenderBalance);

        _checkpoint_market(lendingMarket, type(uint256).max);
        int256 updatedMarketBalance = int256(lendingMarketTotalBalance[lendingMarket][currEpoch]) + _delta;
        require(updatedMarketBalance >= 0, "Market balance underflow"); // Sanity check performed here, but the market should ensure that this never happens
        lendingMarketTotalBalance[lendingMarket][currEpoch] = uint256(updatedMarketBalance);
    }
```
2. As soon as the epoch finishes, the LenderBalance becomes final. User can now withdraw everything from the Lending Market and keep any rewards.

## Tools Used
Manual review

## Recommended Mitigation Steps
To encourage supplying tokens over the whole duration of an
Voters can vote on a single pool multiple times by redelegating
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L356-L387
https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/GaugeController.sol#L211-L278


# Vulnerability details

## Impact
Users should be able to have only one concurrent vote on a pool in GaugeController. When a user votes the weight of his vote is calculated using his `_user_weight` parameter and the slope and end time of his balance lock are used to calculate the bias for that pool. 

But nothing stops him from delegating his balance to another voter who can then use it to vote in GaugeController. This means that many delegators can vote individually on a pool and also delegate their balance to another voter who uses all their balances to vote on the same pool. The time of delegation does not matter i.e before or after individual votes. This effectively allows them to have multiple votes on a pool. It can also be carried out by an individual user through multiple iterations of the following proof of concept.

## Proof of Concept
Alice creates an attacker contract to:
1. Vote on a pool.
2. Delegate to another address of hers. The address can lock as low as 1*e-18 CANTO (assuming CANTO has 18 decimal places) to become a valid voter.
3. Vote using that address. This address can be a contract created cheaply using Openzeppelin's [MinimalProxy](https://docs.openzeppelin.com/contracts/3.x/api/proxy#Clones), an implementation of EIP 1167.
4. Repeat from no. 2.

## Tools Used
VsCode

## Recommended Mitigation Steps
Depending on what the team actually wants, two different steps can be taken.
1. Set the slope to zero whenever a user delegates in VotingEscrow. This will ensure the vote weight of a wallet that delegates is zero.
2. Modify GaugeController to check the `delegatee` of a `msg.sender` by calling the `locked` function on VotingEscrow. GaugeController can revert if `delegatee != msg.sender`











## Assessed type

Other
Fixed locking period of 5 years doesn't allow the delegator to withdraw funds for atleast another 5 years
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L383-L384
https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L331


# Vulnerability details

## Details
Let's first understand how the process of delegation, withdraw and increaseAmount works
1. `Rule 1`: To delegate to some user A, `A.lock.end` needs to be longer than `msg.sender.lock.end` and `A.Lock.end > block.timestamp`(Lock mustn't be expired)

[`VotingEscrow.delegate#L383-L384`](https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L383-L384)
```
    require(toLocked.end > block.timestamp, "Delegatee lock expired");
    require(toLocked.end >= fromLocked.end, "Only delegate to longer lock");
```


2. `Rule 2`: To withdraw the locked `Canto`, delegatee must be `msg.sender`

[`VotingEscrow.withdraw#L331`](https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L331)
```
    require(locked_.delegatee == msg.sender, "Lock delegated");
```

3. `Rule 3`: Locking period is fixed, i.e. 5 years. You can't increase the `lock.end` directly but calling `increaseAmount` will reset it to `block.timestamp + 5 years`

[`VotingEscrow.increaseAmount#L301`](https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L301)

```
    newLocked.end = _floorToWeek(block.timestamp + LOCKTIME);
```

Now consider `userA` and `userB`. 

`userA` has delegated to `userB`, which means `userB.lock.end >= userA.lock.end`
And userA wants to withdraw his Canto in 2 different scenarios.
1. `userA.lock.end > block.timestamp`, so userA's lock is not expired yet
- To withdraw `userA.lock.delegatee == userA.address`, so, `userA` need to undelegate  from `userB` to himself. 
  - But `userB.lock.end >= userA.lock.end` as per `Rule2`, so `userA` can't delegate to himself because of `Rule 1`
  - So, he has no option left but to call `increaseAmount` with minimum amount possible and increase his lock time to another 5 years. So won't be able to withdraw until another 5 years

2. `userA.lock.end < block.timestamp`, so `userA` wants to withdraw after his lock is expired.
- To withdraw `userA.lock.delegatee == userA.address` as per `Rule2`, so, `userA` need to undelegate  from `userB` to himself. 
  - But to delegate to self, `A.Lock.end > block.timestamp` as per `Rule1`. So againhe won't be able to call delegate
  - Again no option left but to call `increaseAmount`, reset the lock.end and then call delegate(userA.address).
  - Again won't be able to withdraw until next 5 years
## Impact
After delegating to someone else, User must need to call increaseAmount and wait for another 5 years to withdraw
## Proof of Concept
https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L383-L384
https
If gov use removeGauge users who use votingPower for that gauge will lose their votingPower infinetly.
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/GaugeController.sol#L210-L213
https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/GaugeController.sol#L127-L130


# Vulnerability details

## Impact
When gov use removeGauge gauge's power changed to the 0, however there is no reset for user votePower which stored in vote_user_power mapping. Because of that users will lose their voting power. For example if a user give his 1000 votingPower to removedGauge before removed.His votingpower reduced to 9000(normally everyone's is 10000).Also he cannot decreased votingpower for removedGauge because there is no chance to use vote_user_power for removed gauge.( vote_user_power is the only function for reduce of user's vote power to gauge.)
## Proof of Concept
https://imgur.com/a/sRgCApg pls paste this test to gaugeControllerTest.sol and from test it can be seen that user cannot take his voting power back and his votingPower stuck for nothing

## Tools Used

## Recommended Mitigation Steps


## Assessed type

Invalid Validation
GaugeController._get_sum() returns wrong value, breaking vote accounting
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/GaugeController.sol#L64-L85
https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/GaugeController.sol#L249-L253


# Vulnerability details

## Impact

The function to account for past historic total gauge weights `_get_sum()` is supposed to return the sum for the future week. Instead it returns the sum for the oldest updated week.

The returned value is used in `vote_for_gauge_weights()` to compute the sum after the vote of the user. This value will be incorrect, providing incorrect values for total weights for all future use of the contract.

The problem worsen itself over time as new incorrect values will be incorrectly calculated based on already incorrect values.

The accounting of votes for the protocol is profoundly broken and getting the gauges weight will be return incorrect values over time.

## Proof of Concept

`_get_sum()` is supposed to return the sum for the future week (the last updated value in the loop), however it returns `pt.bias` i.e. the first value considered. That is if the sum is not updated for 5 weeks, it will return the value of 5 weeks ago:

```solidity
    /// @notice Fill historic gauge weights week-over-week for missed checkins and return the sum for the future week
    /// @return Sum of weights
    function _get_sum() internal returns (uint256) {
        uint256 t = time_sum;
        Point memory pt = points_sum[t];
        for (uint256 i; i < 500; ++i) {
            if (t > block.timestamp) break;
            t += WEEK;
            uint256 d_bias = pt.slope * WEEK;
            if (pt.bias > d_bias) {
                pt.bias -= d_bias;
                uint256 d_slope = changes_sum[t];
                pt.slope -= d_slope;
            } else {
                pt.bias = 0;
                pt.slope = 0;
            }
            points_sum[t] = pt;
            if (t > block.timestamp) time_sum = t;
        }
        return pt.bias;
    }
```

This value is used to compute the future sum in `vote_for_gauge_weights()`:

```solidity
    /// @notice Allocate voting power for changing pool weights
    /// @param _gauge_addr Gauge which `msg.sender` votes for
    /// @param _user_weight Weight for a gauge in bps (units of 0.01%). Minimal is 0.01%. Ignored if 0
    function vote_for_gauge_weights(address _gauge_addr, uint256 _user_weight) external {
        ...
        VotingEscrow ve = votingEscrow;
        (
            ,
            /*int128 bias*/
            int128 slope_, /*uint256 ts*/

        ) = ve.getLastUserPoint(msg.sender);
        require(slope_ >= 0, "Invalid slope");
        uint256 slope = uint256(uint128(slope_));
        uint256 lock_end = ve.lockEnd(msg.sender);
        uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK;
        require(lock_end > next_time, "Lock expires too soon");
        VotedSlope memory old_slope = vote_user_slopes[msg.sende
Double voting in GaugeController
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L367-L375
https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L397-L403


# Vulnerability details

## Impact
Voting with the same collateral multiple times by delegating and undelegating, a process that could manipulatively influence(increase) the weight of a particular lending market where the malicious actor is the major Liquidity provider.

## Proof of Concept
The protocol allows depositors to delete to another address, but this feature comes with a problem:
1. It allows double voting with the same collateral, as no prevention for this as confirmed by the sponsor that no mitigation for yet https://drive.google.com/file/d/1_542keetegWF3idpF3bAqnNjxL9r9eUy/view?usp=sharing

1. In ``` function createLock(uint256 _value) external payable nonReentrant {``` user creates a lock by depositing value, in return an amount of voting power in given, which enables the depositor to participate in voting for preferred lending market. According to the documentation.
```
Users can lock up CANTO (for five years) in the VotingEscrow contract to get veCANTO. They can then vote within GaugeController for different lending markets that are white-listed by governance.
```

Also, the protocols allows delegation of voting power to another address ``` function delegate(address _addr) external nonReentrant {```
```
 if (delegatee == msg.sender) {
            // Delegate
            fromLocked = locked_;
            toLocked = locked[_addr];
```
In ```VotingEscrow. _delegate```, the new delegatee amount is updated to that of the delegator:
```
 if (action == LockAction.DELEGATE) {
            newLocked.delegated += value;
            emit Deposit(addr, uint256(int256(value)), newLocked.end, action, block.timestamp);
        
``` 

The implication of this is that, this new address now has the power to vote in GaugeController.vote_for_gauge_weights(), the problem with this is that, the real owner can later reclaim power by undelegating according to https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L385C9-L385C72
```_delegate(delegatee, fromLocked, value, LockAction.UNDELEGATE);``` where value is now retrieved from delegatee and retained by the previous owner who now has same power to vote again for the same lending market. This means a malicious actor can switch address to vote for a lending market of choice(where he has major shares to get more REWARD).

The reason for this attack is that when delegating, only value changes
```
   if (action == LockAction.DELEGATE) {
            newLocked.delegated += value;
            emit Deposit(addr, uint256(int256(value)), newLocked.end, action, block.timestamp);
        } else {
            newLocked.delegated -= value;
            emit Withdraw(addr, uint256(int256(value)), action, block.