xanoutas/solidity-security-findings huggingface dataset:
```removeDelegation``` does not remove the delegation to another tokenId
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L210-L216
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L242


# Vulnerability details

The ```removeDelegation``` function in ```VoteEscrowDelegation.sol``` is used to revoke the delegation given to another tokenId in the ```delegate``` function. In other words, ```removeDelegation``` is supposed to remove ```tokenId``` from the ```delegatedTokenIds``` of the ```toTokenId```.

When used in the ```_transferFrom``` function, the ```tokenId``` being transferred should be delegating to any other tokenIds when transferred to the new owner. 

Currently, the ```removeDelegation``` function only removes the ```tokenId``` given as an argument from the ```delegatedTokenIds``` array. 


## Impact

This issue effectively means that users are unable to revoke delegations.

## Proof Of Concept

[Affected code:](https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L210-L216)

```solidity
    function removeDelegation(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
        uint256 nCheckpoints = numCheckpoints[tokenId];
        Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
        removeElement(checkpoint.delegatedTokenIds, tokenId);
        _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
    }
```

## Recommended Mitigation Steps

Consider adding a ```toTokenId``` parameter to the ```removeDelegation``` function which could be used to find the ```toTokenId``` to which the ```tokenId``` delegated to. Then the ```tokenId``` should be removed from the ```toTokenId```'s delegatedTokenIds array. The ```delegates[tokenId]``` should return the ```toTokenId``` delegate of the ```tokenId``` and can be used to remove the delegate from tokenId.
Users can cause other users' delegations to be undone
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L94-L109


# Vulnerability details

## Impact
Users can be blocked by other users, from being able to delegate


## Proof of Concept
The `_writeCheckpoint()` function looks up the existing checkpoint and attempts to update its delegation array if one exists (line 104):
```solidity
File: /contracts/vote-escrow/VoteEscrowDelegation.sol   #1

94       function _writeCheckpoint(
95           uint256 toTokenId,
96           uint256 nCheckpoints,
97           uint256[] memory _delegatedTokenIds
98       ) internal {
99           require(_delegatedTokenIds.length < 500, 'VVDelegation: Cannot stake more');
100  
101          Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];
102  
103          if (nCheckpoints > 0 && oldCheckpoint.fromBlock == block.number) {
104              oldCheckpoint.delegatedTokenIds = _delegatedTokenIds;
105          } else {
106              checkpoints[toTokenId][nCheckpoints] = Checkpoint(block.number, _delegatedTokenIds);
107              numCheckpoints[toTokenId] = nCheckpoints + 1;
108          }
109:     }
```
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L94-L109


However, because the `oldCheckpoint` variable is a `memory` variable and not a `storage` variable, the assignment of `_delegatedTokenIds` does not actually get assigned to contract storage, and only lives in the `memory` variable. This means the first user to delegate to a specific token in a block, is the only one able to actually have the delegation applied.

If the malicious user is a miner, that user can monitor the mempool for checkpoint updates and do a sandwich attack, where the order of transactions is:
1. attacker delegates to token X
2. user's txn delegates to token X (but silently fails due to this bug)
3. attacker calls `removeDelegation()` to reclaim their votes


## Tools Used
Code inspection


## Recommended Mitigation Steps
Use `checkpoints[toTokenId][nCheckpoints]` rather than `oldCheckpoint.delegatedTokenIds` in the assignment on line 104
Vote/delegation history for self-delegations only keeps track of the current delegation - no prior ones 
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L210-L216


# Vulnerability details

## Impact
Queries for historical voting power will have the wrong answer, and any DAO/reward decisions made based off it it will be wrong


## Proof of Concept
`removeDelegation()` is marked as `external`, but is called as if it were `public` by using the `this` address:
```solidity
File: /contracts/vote-escrow/VoteEscrowDelegation.sol   #1

233      function _transferFrom(
234          address _from,
235          address _to,
236          uint256 _tokenId,
237          address _sender
238      ) internal override {
239          require(attachments[_tokenId] == 0 && !voted[_tokenId], 'attached');
240  
241          // remove the delegation
242:         this.removeDelegation(_tokenId);
```
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L233-L242


`removeDelegation()` fetches the current checkpoints, removes the delegated token (if it can find it), and then writes a new checkpoint. However, because it uses a `storage` variable for the lookup, and then passes it to `removeElement()`, which also takes in a storage variable, any changes made to the array are immediately applied to the contract's state variable, meaning that the old checkpoint is having its entry being removed, and and then a new checkpoint is written with a copy of the same storage array:
```solidity
File: /contracts/vote-escrow/VoteEscrowDelegation.sol   #2

210      function removeDelegation(uint256 tokenId) external {
211          require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
212          uint256 nCheckpoints = numCheckpoints[tokenId];
213          Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
214          removeElement(checkpoint.delegatedTokenIds, tokenId);
215          _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
216:     }
```
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L210-L216

In other words, the token is removed from the old checkpoint, the old checkpoint is saved, and a new checkpoint is added, which has the same values as the old checkpoint. This means that any prior delegations are always removed. This happens any time `_transferFrom()` is called, which is any time the token is transferred.


## Tools Used
Code inspection


## Recommended Mitigation Steps
Use `memory` for the `checkpoint` variable, as well as the argument to `removeElement()`, so that a copy of the state array is made and operated upon
Non-self-delegation breaks delgation accounting
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L212-L214


# Vulnerability details

## Impact
Delegation, and thus vote tracking, accounting breaks as soon as anyone delegates to a different address than the owner address. Broken accounting means invalid vote tallies, leading to the wrong outcomes for all voting decisions


## Proof of Concept
`delegate()` sets up delegation by fetching the checkpoints of the token _to which a token is being delegated_, and adds the delegated token to that checkpoint on lines 80 and 84:
```solidity
File: /contracts/vote-escrow/VoteEscrowDelegation.sol   #1

71       function delegate(uint256 tokenId, uint256 toTokenId) external {
72           require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
73           require(this.balanceOfNFT(tokenId) >= MIN_VOTING_POWER_REQUIRED, 'VEDelegation: Need more voting power');
74   
75           delegates[tokenId] = toTokenId;
76           uint256 nCheckpoints = numCheckpoints[toTokenId];
77   
78           if (nCheckpoints > 0) {
79               Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1];
80               checkpoint.delegatedTokenIds.push(tokenId);
81               _writeCheckpoint(toTokenId, nCheckpoints, checkpoint.delegatedTokenIds);
82           } else {
83               uint256[] memory array = new uint256[](1);
84               array[0] = tokenId;
85               _writeCheckpoint(toTokenId, nCheckpoints, array);
86           }
87   
88           emit DelegateChanged(tokenId, toTokenId, msg.sender);
89:      }
```
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L71-L89


Note that above, `delgate()` assigns `tokenId` to `toTokenId`'s checkpoints. `removeDelegation()` on the other hand, removes `tokenId` from its own checkpoints:
```solidity
File: /contracts/vote-escrow/VoteEscrowDelegation.sol   #2

208      /// @notice Remove delegation
209      /// @param tokenId TokenId of which delegation needs to be removed
210      function removeDelegation(uint256 tokenId) external {
211          require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
212          uint256 nCheckpoints = numCheckpoints[tokenId];
213          Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
214          removeElement(checkpoint.delegatedTokenIds, tokenId);
215          _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
216:     }
```
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L208-L216


If `tokenId` wasn't delegated to itself, then the call to `removeElement()` will not fail, and a new checkpoint will be written with the token still being delegated to its old location:
```solidity
File: /contracts/vote-escrow/Vo
`removeDelegation` does not remove all delegators potentially allowing a new owner to vote for malicious proposals
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L214-L215


# Vulnerability details

## Impact
As of now, `removeDelegation` removes the delegation of a token to itself, however leaves all delegators in place. This can be very dangerous because if this token is transferred to a malicious user (for example the malicious user buys the token from the original user on a secondary market) then the token can be used to propose and vote for malicious upgrades/proposals which can hijack the entire protocol. This essentially allows an attacker to buyout votes without the explicit consent of the original delegator.

## Proof of Concept
1. Alice owns a token with 10% of the voting power of the protocol
2. Bob buys the token from alice. Bob still manages to keep all the delegations of that token
3. Bob can now try to propose and vote for a malicious upgrade which steals all reward tokens, nft's, etc...

## Tools Used
VS Code
## Recommended Mitigation Steps
Consider changing `removeDelegation` so that it removes all delegations. This can be done by passing an empty dynamic array for `_writeCheckpoint()`
`VoteEscrow._transferFrom()` always reverts, making veGolom untradable
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L242


# Vulnerability details

## Impact

Quote from Golom documentation regarding veGolom tokens:

    "This further allows locks to be traded on secondary markets, as well as allows participants to borrow against their locks in future lending market places." 

Since function `_transferFrom()` of `VoteEscrow` reverts, `transferFrom()` and `safeTransferFrom()` always revert. Without these functions veGolom cannot be traded on exchanges. 

## Proof of Concept
In `VoteEscrow._transferFrom()`, [#L242](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L242), the external call to `VoteEscrow.removeDelegation()` 

```solidity
    this.removeDelegation(_tokenId);
```
will revert because of the following check in `removeDelegation()` 

```solidity
    require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
```

and the external call changes `msg.sender` from the user calling `transferFrom` to the contract address.

This is true unless the `VoteEscrow` itself owns the tokens, but even in this case the tx will revert at #L245:

```solidity
    require(_isApprovedOrOwner(_sender, _tokenId));
```
since the `VoteEscrow` has not way to approve tokens.

So external functions `transferFrom()` and `safeTransferFrom()` always revert.

## Recommended Mitigation Steps

Note that it could not be enough to make `removeDelegation` public and make the external call `this.removeDelegation(_tokenId)` an internal call.

If `removeDelegation` is intended to be called only by the owner of the veGolom token, then one cannot call it from `_transferFrom` since this function is intended to transfer tokens when called by an approved address. In this case, It is best to remove the delegation in a first call and then transfer separately in a second call, and check in `_transferFrom()` that the token is not delegated:

```diff
-       this.removeDelegation(_tokenId);
+       require(delegates[_tokenId] == 0, 'Token is delegated');
```

where `delegates` is the mapping which maps each delegator to its current delegatee, and must be zeroed out each time a delegator is removed, by adding to the function `removeDelegation`:

```solidity
    delegates[tokenId] = 0;
```
`delegate` function always reverts
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L78-L86
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L101


# Vulnerability details

# `delegate` function always reverts

## Impact
`delegate` function in `VoteEscrow` always reverts, so all the delegation logic implemented in `VoteEscrowDelegation.sol` cannot be used.

## Proof of Concept
The initial state of mapping `numCheckpoints` is such that `nCheckpoints = 0` ([VoteEscrowDelegation.sol#L76](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L76)). Then the `else` block is always entered, where `_writeCheckpoint()` reverts at [#L101](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L101)

```solidity
Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];
```
 because `nCheckpoints = 0` and there is an underflow error.

Since mapping `numCheckpoints` is only modified in `_writeCheckpoint()`, it will always keep its initial state and `delegate()` will always revert.

## Recommended Mitigation Steps

One possible fix is to change `_writeCheckpoint()` like this:

```solidity
    function _writeCheckpoint(
        uint256 toTokenId,
        uint256 nCheckpoints,
        uint256[] memory _delegatedTokenIds
    ) internal {
        require(_delegatedTokenIds.length < 500, 'VVDelegation: Cannot stake more');
        if (nCheckpoints == 0 || 
            checkpoints[toTokenId][nCheckpoints - 1].fromBlock < block.number) {
            checkpoints[toTokenId][nCheckpoints] = Checkpoint(block.number, _delegatedTokenIds);
            numCheckpoints[toTokenId] = nCheckpoints + 1;
        } else {
            Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];
            oldCheckpoint.delegatedTokenIds = _delegatedTokenIds;
        }
    }
```
Adding (removing) a delegation also adds to (removes from) the previous checkpoint
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L79-L81
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L213-L215


# Vulnerability details

## Impact
Users can add or remove a delegation from the latest checkpoint, altering the stored history of delegations and changing the voting power of the delegatee corresponding to a past period of time.

## Proof of Concept
-  Delegation can be removed retroactively:
    
    `tokenId` is delegated to `toTokenId` using `delegate`. This pushes `tokenId` into the array:
    ``` solidity
    checkpoints[toTokenId][nCheckpoints - 1].delegatedTokenIds
    ```
    (see [VoteEscrowDelegation.sol#L80](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L80))

    Later the owner of `tokenId` wants to remove the delegation and calls `removeDelegation`. `checkpoints[toTokenId][nCheckpoints - 1]` is copied as a reference into local storage variable `checkpoint` ([VoteEscrowDelegation.sol#L213](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L213)) and then `checkpoint.delegatedTokenIds` is passed as a reference to  `removeElement(uint256[] storage _array, uint256 _element)`. So the argument  `_array` is a reference to `checkpoints[toTokenId][nCheckpoints - 1].delegatedTokenIds` and when the delegatee is removed, it is removed from the previous checkpoint. The result is that the voting power for `toTokenId` calculated in function [`getPriorVotes`](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L185) and used by [governorAlpha._castVote](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/governance/GovernerBravo.sol#L381) is reduced by the amount correspoinding to the time passed since the last checkpoint.

- Delegation can be added retroactively:
   
   The same problem with assignment as a reference exists in function `delegate` ([VoteEscrowDelegation.sol#L78-L82](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L78-L82)), where `tokenId` is pushed to the previous checkpoint stored.

```solidity
        if (nCheckpoints > 0) {
            Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1];
            checkpoint.delegatedTokenIds.push(tokenId);
            _writeCheckpoint(toTokenId, nCheckpoints, checkpoint.delegatedTokenIds);
        }
```

## Recommended Mitigation Steps
In functions `delegate` and `removeDelegation`, call first `_writeCheckpoint` to duplicate the last checkpoint, then `push`/`pop` `tokenId`
Cannot remove delegation from a token to another token
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L213


# Vulnerability details

## Impact
A user who has delegated the vote of a veGolom token (that he/she owns) to another veGolom token cannot remove the delegation, so the delegatee token will permanently hold the voting power of the delegator token.


## Proof of Concept
A user tries to remove the delegation from `tokenId` he/she owns to the delegated token, calling `removeDelegation(uint256 tokenId)`. 
The delegation should be removed at the lines:

```solidity
        Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
        removeElement(checkpoint.delegatedTokenIds, tokenId);
```
but the array `checkpoint.delegatedTokenIds` is the list of **delegators** to `tokenId` **itself**. So, unless the delegation was from the token to itself, `removeDelegation` does nothing.

## Recommended Mitigation Steps
Two fixes are proposed:

1. Add the delegatee as an argument to `removeDelegation` and remove `tokenId` from its list of delegators:
   
```diff
-   function removeDelegation(uint256 tokenId) external {
+   function removeDelegation(uint256 tokenId, uint256 toTokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
        uint256 nCheckpoints = numCheckpoints[tokenId];
-       Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
+       Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1];
        removeElement(checkpoint.delegatedTokenIds, tokenId);
        _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
    }
```

or

2. Load the delegatee from the mapping `delegates` which maps each delegator to its current delegatee:

```diff
    function removeDelegation(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
+       uint256 toTokenId = delegates[tokenId];
        uint256 nCheckpoints = numCheckpoints[tokenId];
-       Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
+       Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1];
        removeElement(checkpoint.delegatedTokenIds, tokenId);
        _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
    }
```