xanoutas/solidity-security-findings huggingface dataset:
Contrary to sponsor's intent, the gauge's weight will not be updated properly if governance do not change weight before any voting
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
https://discord.com/channels/810916927919620096/1136715595342172222/1138933047123783740

This is the intended procedure based on Sponsor's answer in discord:
```
Q:
when we add new gauge we don't set any weight point or any data for it, did the new gauge will get the weight and power by time when users vote for it ?!
A:
Yes, the idea is that they originally have a weight 0 and this increases as soon as people vote for it
```
Based on the above, the expected procedure is that all gauges' weight start from the default 0, which means the governance doesn't need to pre-set a gauge to any weight value before user voting on the gauge weights. 

However, the current GaugeController.sol implementation will not behave properly - gauge weights that users voted for will not be updated properly if governance does not set a base weight for each gauge ahead of any user voting.

## Proof of Concept
A user will vote for a gauge weight through `vote_for_gauge_weights()`, this function will write to storage and schedule gauge change for the coming round week - `next_time`, which is working as intended. 

However, when the coming week has passed, the gauge weight user voted will stop updating and erase its own weight back to 0. The weight user voted for will always be erased after one week. This is not intended.

The vulnerability is that when a new checkpoint is created through `checkpoint_gauge()`, it calls `_get_weight()`. In `_get_weight()`, `time_weight[_gauge_addr]` will be first accessed. But `time_weight[_gauge_addr]` will always be 0, if the governance doesn't set a gauge weight beforehand. This means, `_get_weight()` logic will be directly bypassed with the statement `if(t>0)` and return the weight as 0. And because of the bypass, no new `points_weight[_gauge_addr][t]` will be written.

```solidity
//GaugeController.sol
    function checkpoint_gauge(address _gauge) external {
        _get_weight(_gauge);
        _get_sum();
    }

    function _get_weight(address _gauge_addr) private returns (uint256) {
 |>     uint256 t = time_weight[_gauge_addr];
 |>     if (t > 0) {
            Point memory pt = points_weight[_gauge_addr][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_weight[_gauge_addr][t];
                    pt.slope -= d_slope;
                } else {
                    pt.bias = 0;
                    pt.slope = 0;
                }
                points_weight[_gauge_addr][t] = pt;
                if (t > block.timestamp) time_weight[_gauge_addr] = t;
            }
            return pt.bias;
        } else {
            return
lendingMarketTotalBalance isn't updated for first call to _checkpoint_market in markets created in first epoch
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/LendingLedger.sol#L64-L67
https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/LendingLedger.sol#L91


# Vulnerability details

## Impact

This bricks the ability to update the market balance because when the function tries to update the lendingMarketTotalBalance it uses the value of `lastMarketBalance = lendingMarketTotalBalance[_market][lastMarketUpdateEpoch]` and since the value in `lendingMarketTotalBalance[_market][lastMarketUpdateEpoch]` is never updated in the first call to `checkpoint_market` for markets where `lastMarketUpdateEpoch == 0` this query will return 0 and subsequently always set the market’s total balance to 0.

Any calculation of rewards for users participating in a market will therefore cause a divide by 0 revert in `LendingLedger::claim` making users unable to receive rewards.

Anyone can call the `checkpoint_market` function to do this and only spend the gas costs associated with the function call.

## Proof of Concept

1. Alice calls `checkpoint_market` for a market created in the first epoch (within the span of the first week that protocol is deployed) that has never been checkpointed
2. Since the market has never been checkpointed the assignment on L85 gives `uint256 lastMarketUpdateEpoch = lendingMarketTotalBalanceEpoch[_market] = 0`
3. This triggers the if statement on L87: https://github.com/code-423n4/2023-08-verwa/blob/main/src/LendingLedger.sol#L87C9-L88C65 which sets `lendingMarketTotalBalanceEpoch[_market] = currEpoch`
4. When Alice calls `checkpoint_market` again `lastMarketUpdateEpoch` is then set to `currEpoch` from the previous call
5. Because the mapping `lendingMarketTotalBalance[_market][lastMarketUpdateEpoch]` was never updated for `currEpoch`, when `lastMarketBalance` is set on L91, it gets set to the default value of 0 https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/LendingLedger.sol#L91:

```solidity
uint256 lastMarketBalance = lendingMarketTotalBalance[_market][lastMarketUpdateEpoch]

```

6. This results in the updates of `lendingMarketTotalBalance` for each epoch to all be set to 0 for each epoch up to `updateUntilEpoch` https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/LendingLedger.sol#L92C13-L94C14
7. Anyone who then tries to call `checkpoint_market` to update the market balance will be able to do so but the balance for all subsequent epochs will be 0 set to 0.

## Tools Used

Manual Review

## Recommended Mitigation Steps

The if statement on L87 should be made to stand alone and the else-if on L89 should be removed so that the first call to `_checkpoint_market` also updates `lendingMarketTotalBalance`:
```solidity
if (lastMarketUpdateEpoch == 0) {
  lendingMarketTotalBalanceEpoch[_market] = currEpoch;
}
// Fill in potential gaps in the market total balance
No need to stack lend pool to take lendlaunger rewards
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Lending lenger give reward tokens to users if they lend their tokens to selected pools. Lendingledger give rewards as weekly and it records user's balance until end of the week(espacially thursday because 1 jan 1970 was thursday). But protocol records can be any record in the week anyvalue, protocol just look at the latest updated value in this week . In short a user can withdraw his tokens after thursday but if he stake his token before next thursday 00.00 he will benefit from tokens like stake tokens all week.  
## Proof of Concept
https://imgur.com/a/qIrEoOa
pls paste this test to lendingLEdgerTest.sol and it can be seen that a malicious user can take same reward like all time staker when just stake his token just for two block time.
## Tools Used

## Recommended Mitigation Steps


## Assessed type

Invalid Validation
Delegate system can be used to increase voting power without resetting the lockEnd
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/main/src/VotingEscrow.sol#L301
https://github.com/code-423n4/2023-08-verwa/blob/main/src/VotingEscrow.sol#L317
https://github.com/code-423n4/2023-08-verwa/blob/main/src/VotingEscrow.sol#L384


# Vulnerability details

## Impact
The added delegate mechanism allows increasing the voting power without resetting the `lockEnd`.

## Proof of Concept
`IncreaseAmount()` increases the locked amount of a `msg.sender` staked native in `VotingEscrow` and resets its `lockEnd` to `block.timestamp + LOCK_TIME`. Users can bypass this feature by generating several wallets, creating locks for these wallets and delegating to the first lock. There is a check in the code that requires that a lock can only be delegated to a more recent lock, but it can be bypassed by delegating in the same block that the first lock is created.

A POC was carried out in Foundry, add the following test to `VotingEscrow.t.sol`:
```solidity
function testIncreaseVotingPowerWithoutResettingLockEnd() public {
    vm.deal(user1, 100 ether);

    vm.prank(user1);
    ve.createLock{value: 1 ether}(1 ether);

    // generate some wallets to delegate in the future if needed to increase
    // voting power without resetting the lock end
    for (uint256 i_; i_ < 10; i_++) {
        address fakeUser1_ = vm.addr(i_ + 27); // random number
        vm.deal(fakeUser1_, 100 ether);
        vm.startPrank(fakeUser1_);
        ve.createLock{value: 1}(1); // just create the lock
        ve.delegate(user1);
        vm.stopPrank();
    }

    (int128 amount_, uint256 lockEnd_,,) = ve.locked(user1);

    // 100 days later, user wants to increase voting power without resetting the lock end
    vm.warp(block.timestamp + 100 days);

    vm.prank(vm.addr(27)); // one of the fake users
    ve.increaseAmount{value: 1 ether}(1 ether);

    (int128 finalAmount_, uint256 finalLockEnd_, int128 delegated_, ) = ve.locked(user1);

    // amount remains equal
    assertEq(amount_, finalAmount_);

    // same lock end, but as can be seen below, the voting power will increase
    assertEq(lockEnd_, finalLockEnd_);

    // 1e18 initial amount + 1e18 increase amount from one of the fake delegates
    assertEq(delegated_, 2e18 + 10);
}
```
As can be seen, the user is able to increase their voting power without increasing the `lockEnd` of the original stake.

## Tools Used
Vscode, Foundry

## Recommended Mitigation Steps
The current requirement that a lock must be delegated to a longer lock is the following:
```solidity
require(toLocked.end >= fromLocked.end, "Only delegate to longer lock");
```
Change the requirement to be strictly bigger as following:
```solidity
require(toLocked.end > fromLocked.end, "Only delegate to longer lock");
```


## Assessed type

Invalid Validation
User funds can be lost
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
User funds sent by users can be lost forever

## Proof of Concept
The contract `LendingLedger.sol` implements the function `receive() external payable` to allow to the protocol to deposit the `Canto` used in the expected behaviour.

But since the `receive() external payable` has not any checks for the origin (the `msg.sender`) of those funds, any user that by error sends some `Canto`to the contract won´t be able to withdraw it effectively losing it.

## Tools Used
Manual review.

## Recommended Mitigation Steps
Add a check in the `receive() external payable` to allow only incoming transfers from the addresses the contract should expect fund deposits and revert in any other case.





## Assessed type

Payable
Lock owner DOS and funds stuck
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/VotingEscrow.sol#L398


# Vulnerability details

## Impact
The lock's owner will no longer have the ability to use the delegate function. Meaning the person he chose to delegate can withdraw his funds when lock expires, but won't be able to.

## Proof of Concept
Lets imagine 3 users with a Lock each:
If user 1 delegates to user 2 then user 3 delegates type(int128).max to user 1 (or delegates enough so that user 1 delegated + user 3 delegated > type(int128).max), user 1 can't undelegate / redelegate to someone else because on the \_delegate function, _ ```newLocked.delegated += value;``` will revert with overflow, meaning he can never withdraw his own Lock because it requires him to be the delegatee (```require(locked_.delegatee == msg.sender, "Lock delegated");```).
There is a way for the user to regain control and his situation to return to normal which would be if user 3 undelegated his funds.

## Tools Used
Foundry

## Recommended Mitigation Steps
Not allow other users to delegate any amount they want, so that this condition is always true: user 1 delegated + sum of delegations to him < type(int128).max


## Assessed type

DoS
`GaugeController.sol` isn't updated when voting power is delegated, enabling users to arbitrarily increase gauge weight
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/main/src/GaugeController.sol#L214-L223
https://github.com/code-423n4/2023-08-verwa/blob/main/src/VotingEscrow.sol#L68-L284
https://github.com/code-423n4/2023-08-verwa/blob/main/src/VotingEscrow.sol#L356-L409


# Vulnerability details

## Impact
A malicious user can arbitrarily increase any gauge's weight, resulting in loss of rewards for lenders of other gauges.

## Proof of Concept
Note that the below code snippet from `GaugeController.vote_for_gauge_weights()` (called by users to cast and change votes) contains the only interactions between `GaugeController.sol` and `VotingEscrow.sol`:
```
VotingEscrow ve = votingEscrow;
        (
            ,
            /*int128 bias*/
            int128 slope_, /*uint256 ts*/

        ) = ve.getLastUserPoint(msg.sender); // get the slope of the user's last recorded Point
        require(slope_ >= 0, "Invalid slope");
        uint256 slope = uint256(uint128(slope_));
        uint256 lock_end = ve.lockEnd(msg.sender); // get user's lock expiry time
```
Therefore, `GaugeController.sol` does not know when users lose their voting power in `VotingEscrow.sol` by calling `VotingEscrow.delegate()`. Malicious users can abuse this to gain an arbitrary amount of voting power.

Steps to reproduce:
1. User calls `VotingEscrow.createLock()` and creates a lock.
2. User calls `GaugeController.vote_for_gauge_weights()` to vote for a gauge.
3. User creates a new lock using a different address, locking an economically negligible amount of value.
4. User calls `VotingEscrow.delegate()` and delegates the original lock's voting power to the new address.
5. User calls `GaugeController.vote_for_gauge_weights()` with the new address to vote for a gauge.
6. User repeats steps 3-5 an arbitrary number of times to increase gauge weights by an arbitrary amount.

## Tools Used
Manual Review
## Recommended Mitigation Steps
This may be somewhat difficult to fix due to the way `GaugeController.sol` stores votes and interacts with `VotingEscrow.sol`. A possible mitigation would be to force users to call `VotingEscrow.delegate()` through `GaugeController.sol`, and store delegated slope/bias changes in `GaugeController.sol`'s state so that they can be nullified upon redelegation.


## Assessed type

Other
A user can make sybil attack for manipulate gaugeController dao
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/VotingEscrow.sol#L390-L409
https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/GaugeController.sol#L215-L220


# Vulnerability details

## Impact
When vote_for_gauge_weights() used contract just take info of users slope of exact time. And there is no extra check mechanism in protocol for control is user's delegate amount (so slope too) until user use vote_for_gauge_weights(). So a user can use vote_for_gauge_weights for one account than delegate another contract to his vote power in voteEscrow and use vote_for_gauge_weights() again (he can use this exploit as much as he want) so he can manipulate vote power as a sybil attack.
## Proof of Concept
https://imgur.com/a/WEuU1RN
pls paste this test to gaugeControllertest.sol and it can be seen that how significantly increased of gauge1 by sybil attack.

## Tools Used

## Recommended Mitigation Steps
Use vote_for_gauge_weights() in voteescrow for msg.sender when delegate function used.


## Assessed type

Governance
User don't have to deposit for a week into the market to get his weekly reward from the `LendingLedger`
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/main/src/LendingLedger.sol#L134


# Vulnerability details

## Impact
In the `LendingLedger` contract, a user is rewarded with CANTO tokens depending on how long he has his deposit in the market. Rewards are distributed for each week during which the deposit was inside the market. However, the user can cheat this condition because we are rounding down to the start of the week, so the user can deposit at 23:59 at the end of the week and withdraw at 00:00 and still get rewarded as if he had his deposit for the whole week.

## Proof of Concept
Test case for the `LendingLedger.t.sol`
```
    function setupStateBeforeClaim() internal {
        whiteListMarket();

        vm.prank(goverance);
        ledger.setRewards(0, WEEK*10, amountPerEpoch);

        // deposit into market at 23:59 (week 4)
        vm.warp((WEEK * 5) - 1);

        int256 delta = 1.1 ether;
        vm.prank(lendingMarket);
        ledger.sync_ledger(lender, delta);

        // airdrop ledger enough token balance for user to claim
        payable(ledger).transfer(1000 ether);
        // withdraw at 00:00 (week 5)
        vm.warp(block.timestamp + 1);
        vm.prank(lendingMarket);
        ledger.sync_ledger(lender, delta * (-1));
    }

    function testClaimValidLenderOneEpoch() public {
        setupStateBeforeClaim();

        uint256 balanceBefore = address(lender).balance;
        vm.prank(lender);
        ledger.claim(lendingMarket, 0, type(uint256).max);
        uint256 balanceAfter = address(lender).balance;
        assertTrue(balanceAfter - balanceBefore == 1 ether);

        uint256 claimedEpoch = ledger.userClaimedEpoch(lendingMarket, lender);
        assertTrue(claimedEpoch - WEEK*4 == WEEK);
    }
```
## Tools Used
Foundry
## Recommended Mitigation Steps
It's difficult to propose a solution for this exploit without major changes in the contract's architecture. Perhaps we can somehow split the amount based on the time the sync was made inside the week, let's say Alice's `last_sync` was in the middle of week0, she deposited 1 ether, thus her amount for the current epoch will be 1/2 ether. However there is a caveat, how do we fill the gaps? We can't fill them with 1/2 ether. We can use this struct though,
```
Amount {
    uint256 actualAmount,
    uint256 fraction
}
```
so we can use `fraction` for the current epoch and `actualAmount = 1 ether` to fill the gaps. 


## Assessed type

Timing
Delegating older lock to a newer one does not allow to undelegate it
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


# Vulnerability details

## Impact

User who accidentally delegates his lock to the newer one, will get his lock stuck. User won't be able to undelegate his lock, because function `delegate` will always revert.

Please notice, that this is the different issue than previously reported: "Delegatee is able to forbid user from undelegating".
In the previous scenario, it's delegatee who performs the attack by calling `increaseAmount` on their lock.
In the current scenario - delegatee interaction is not required. It's user who has accidentally or without realizing the consequences (this scenario is not documented anywhere) delegated his lock to a newer lock - thus makes his lock impossible to undelegate.


## Proof of Concept

Two locks were created, each with 1 week difference (1849478400 - 1848873600 = 604800 seconds = 7 days):

```
locked[0x5B38Da6a701c568545dCfcB03FcB875f56beddC4]:
    0:
    int128: amount 123
    1:
    uint256: end 1848873600
    2:
    int128: delegated 123
    3:
    address: delegatee 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4

locked[0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2]:
    0:
    int128: amount 123
    1:
    uint256: end 1849478400
    2:
    int128: delegated 123
    3:
    address: delegatee 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
```

The lock 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 was created first, then the lock 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 was created after one week.

Now, we will delegate lock 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 to 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2:
```
From 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, call: delegate(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2)
```

Now, when we will try to undelegate this lock, function will revert:

```
From: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 call: delegate(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4)

The transaction has been reverted to the initial state.
Reason provided by the contract: "Only delegate to longer lock".
Debug the transaction to get more information.
```

This issue occurs, because of line 384:

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

The only way to undelegate the lock - is to call `increaseAmount` on the lock, which will modify its `locked.end` date.

However, this scenario is not desirable:

* calling `increaseAmount` requires spending additional funds, even if it's just calling `increaseAmount(1)` - it's still unnecessary cost for undelegating the lock

* calling `increaseAmount` increases the locktime:

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