xanoutas/solidity-security-findings huggingface dataset:
QA Report
Audit: 2022-07-golom-findings
Severity: HIGH

VoteEscrowDelegation.sol
--------------------------
delegates mapping is not updated in [removeDelegation](https://github.com/code-423n4/2022-07-golom/blob/7bbb55fca61e6bae29e57133c1e45806cbb17aa4/contracts/vote-escrow/VoteEscrowDelegation.sol#L210-L216). It is updated in function [delegate](https://github.com/code-423n4/2022-07-golom/blob/7bbb55fca61e6bae29e57133c1e45806cbb17aa4/contracts/vote-escrow/VoteEscrowDelegation.sol#L75), but after a call to removeDelegation the mapping still contains the token that it was delegated to.
Impossible to add `ve` in RewardDistributor
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol#L300


# Vulnerability details

## Impact
Starting from `ve == 0`, we call `addVoteEscrow`.
```js
    function addVoteEscrow(address _voteEscrow) external onlyOwner {
        if (address(ve) == address(0)) {
            ve = VE(pendingVoteEscrow);
        } else {
            voteEscrowEnableDate = block.timestamp + 1 days;
            pendingVoteEscrow = _voteEscrow;
        }
    }
```
The function sets `ve` to `pendingVoteEscrow` which is also `address(0)`. There's no other way to change `pendingVoteEscrow`, so `ve` will always be unset.

Without `ve` it's impossible to claim staker rewards.

## Proof of Concept
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/rewards/RewardDistributor.sol#L300

## Recommended Mitigation Steps
Clearly, `ve = VE(_voteEscrow);` at line 300.
`_writeCheckpoint` not working correctly if `oldCheckpoint.fromBlock == block.number`
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#L104


# Vulnerability details

## Impact
```js
    function _writeCheckpoint(
        uint256 toTokenId,
        uint256 nCheckpoints,
        uint256[] memory _delegatedTokenIds
    ) internal {
        require(_delegatedTokenIds.length < 500, 'VVDelegation: Cannot stake more');

        Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];

        if (nCheckpoints > 0 && oldCheckpoint.fromBlock == block.number) {
            oldCheckpoint.delegatedTokenIds = _delegatedTokenIds;
        } else {
            checkpoints[toTokenId][nCheckpoints] = Checkpoint(block.number, _delegatedTokenIds);
            numCheckpoints[toTokenId] = nCheckpoints + 1;
        }
    }
```
Inside the first `if`, we are writing to `memory` instead of `storage`. This means that if `nCheckpoints > 0 && oldCheckpoint.fromBlock == block.number`, the function will absolutely do nothing.

## Proof of Concept

Alice flashloans `tokenId1`. She then calls `delegate(tokenId1, tokenId2)` where `tokenId2` is a token she owns permanently. 
When she returns the first token, there will be an attempt to remove `tokenId1` from the delegates of `tokenId2` (not considering other issues in the `removeDelegation` function). However, since `oldCheckpoint.fromBlock == block.number`, `_writeCheckpoint` will fail to change the state.

Using this trick, Alice is able then to bypass any `removeDelegation`, leading to possible governance attacks.

Also the contract state will be messed up going forward, since the checkpoint of `tokenId2` will have `tokenId1`, but `delegates[tokenId1]` is deleted. If `delegates` was used as check against delegating the same token multiple times, this exploit can be used to continue adding the same token as delegate, increasing limitlessly the governance power of an exploiter.

## Recommended Mitigation Steps

`Checkpoint memory oldCheckpoint` to `storage`.
Impossible to transfer VE tokens
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
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L242


# Vulnerability details

## Impact
In VoteEscrowDelegation.sol, function `_transferFrom` will attempt to remove the delegations to the token transferred.
```js
// remove the delegation
this.removeDelegation(_tokenId);
```
However this external call will change `msg.sender` to `address(this)`, reverting the call when checking the owner
```js
    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);
    }
```

Also, even if the call was made internal, another revert is possible: if the token doesn't have any checkpoints yet, `nCheckpoints - 1` will revert for underflow.

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

## Recommended Mitigation Steps

1) Change `removeDelegation` to `public`, and write `removeDelegation(_tokenId)` instead of `this.removeDelegation(_tokenId)`;
2) Add a `if (nCheckpoints == 0) return;` in `removeDelegation` before the possible underflow.
Possible to `delegate` same token
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#L71-L89


# Vulnerability details

## Impact
It's possible to call `delegate(tokenId1, tokenId2)` using the same token `tokenId1` multiple times. The previous delegated value isn't deleted, and all delegations stack up.

This way `tokenId2` will have all these tokens as delegated:
```js
checkpoints[tokenId2][nCheckpoints-1].delegatedTokenIds = [tokenId1, tokenId1, ...]
```
When calculating the votes for this token, there's no check that the delegated tokens are different, so the values all sum together.

## Proof of Concept
Alice has two tokens. She calls `delegate(tokenId1, tokenId2)` multiple times, accruing the voting power of `tokenId2` over her true token balance.
During a governance proposal, `GovernorAlpha#castVote` will call `VoteEscrow#getPriorVotes` getting an artificially high voting power. She can use this power to pass whatever proposal she likes.

## Recommended Mitigation Steps
Consider adding a `require(delegates[tokenId] == address(0))` in the function `delegate`. Changing a delegation this way requires the user to call `removeDelegation` beforehand.
`removeDelegation` acts on wrong token
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


# Vulnerability details

## Impact
The function `removeDelegation(tokenId)` currently tries to remove `tokenId` from the list of tokens delegated to itself.

The correct behavior would be to get which token `tokenId` is delegated to, and remove `tokenId` from the delegations to that token.

Since `removeDelegation` is important in resetting delegations during token transfers, this malfunctions allows some users to gain more governance votes.

## Proof of Concept
Alice flashloans (or simply loans) some tokens with high balance and, before returning them, she `delegate` their voting power to another token she controls.
Since `removeDelegation` malfunctions, she keeps the votes of all these tokens, leading to a possible governance attack.

## Recommended Mitigation Steps
Use `delegates[tokenId]` to get the correct token from which getting the `checkpoint`.
```js
    function removeDelegation(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
        uint256 toTokenId = delegates[tokenId];      //@audit Add this
        //uint256 nCheckpoints = numCheckpoints[tokenId];
        uint256 nCheckpoints = numCheckpoints[toTokenId];
        //Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
        Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1]; 
        removeElement(checkpoint.delegatedTokenIds, tokenId);
        //_writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
        _writeCheckpoint(toTokenId, nCheckpoints, checkpoint.delegatedTokenIds);
    }
```
Impossible to `delegate`
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#L85
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L101


# Vulnerability details

## Impact
It's impossible to delegate to a token, because the first delegation will always revert.

Consider there's no delegation so far, meaning that every token will have `numCheckpoints[token] = 0`. When calling `delegate`, we will have `nCheckpoints = 0` and the call will revert in `_writeCheckpoint` since `nCheckpoints - 1` underflows.
```js
    function delegate(uint256 tokenId, uint256 toTokenId) external {
    	...
        uint256 nCheckpoints = numCheckpoints[toTokenId];

        if (nCheckpoints > 0) {
        	...
        } else {
            uint256[] memory array = new uint256[](1);
            array[0] = tokenId;
            _writeCheckpoint(toTokenId, nCheckpoints, array);
        }
    }
    
    function _writeCheckpoint(
        uint256 toTokenId,
        uint256 nCheckpoints,
        uint256[] memory _delegatedTokenIds
    ) internal {
        require(_delegatedTokenIds.length < 500, 'VVDelegation: Cannot stake more');

        Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];  //@audit Reverts here
        
        ...
    }
```

## Proof of Concept
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L85
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L101

## Recommended Mitigation Steps
Instead of
```js
Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];
```
write something like:
```js
Checkpoint memory oldCheckpoint;
if (nCheckpoints > 0) oldCheckpoint  = checkpoints[toTokenId][nCheckpoints - 1];
```
Wrong arithmetic operation upon filling a bid order, lead to lost amount of ether on user funds
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L389-L394
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L396-L399


# Vulnerability details

Functions `GolomTrader.sol#fillBid(...)` and `GolomTrader.sol#fillCriteriaBid(...)` used to fill a signed order (Golom `order.orderType = 1` or `= 2`) by the buyer after he approved `GolomTrader` contract some wETH. However, in case that the signed order is of type ERC1155 where the value of `order.isERC721 = false` and functions called with param; `amount > 1` this issue will occurs. At the end of both functions an internal function `_settleBalances(...)` called where the issue is handled.
**Lines**
| [contracts/core/GolomTrader.sol#fillBid(...)](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L307)
| [contracts/core/GolomTrader.sol#fillCriteriaBid(...)](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L367)
-> [contracts/core/GolomTrader.sol#_settleBalances(...)](https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L375-L403)

## Impact
Token owner who try to sell his tokens will get less than what he should be getting and the rest of the ether will stuck forever in the `GolomTrader` contract since there is no ether rescue function.

## Tools Used
vscode, hardhat, manual review.

## Proof of Concept
There are more than one arithmetic operations to classify this issue. One is when calculating the `protocolfee` which we multiply by `amount` and used when transferring to the `RewardDistributor` contract and this is what it meant to be. However, in the line `GolomTrader.sol#L393` and `GolomTrader.sol#L400`, `protocolfee` has been used as its the base fee of the order total amount `order.totalAmt` which it's not. Thus it used to subtract the total amount of 1 token and then multiply by the `amount`.
At the end the token seller will get less than what he should get, and the amount of ether has been lost will live forever in the contract. The amount of lost ether scale exponentially upon amount entered by user (Token seller).
```solidity
contracts/core/GolomTrader.sol:
  375      function _settleBalances(
  376          Order calldata o,
  377          uint256 amount,
  378          address referrer,
  379          Payment calldata p
  380      ) internal {
  381:         uint256 protocolfee = ((o.totalAmt * 50) / 10000) * amount;
  ...
  389              payEther(
  390:                 (o.totalAmt - protocolfee - o.exchange.paymentAmt - o.prePayment.paymentAmt - o.refererrAmt) * // @audit
  391                      amount -
  392                      p.paymentAmt,
  393                  msg.sender
  394              );
  ...
  396
Index error
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#L101


# Vulnerability details

## Impact
`delegate` will not work due to the index error.

## Proof of Concept
`VoteEscrowDelegation._writeCheckpoint` will be reverted when `nCheckpoints == 0`. `numCheckpoints` can be increased only when `nCheckpoints == 0`. So `numCheckpoints` will always be zero for any `tokenId` and `delegate` will not work.

## Tools Used
Manually reviewed

## Recommended Mitigation Steps

Remove line 101 of `VoteEscrowDelegation.sol` and change line 103 as follows.

```
if (nCheckpoints > 0 && checkpoints[toTokenId][nCheckpoints - 1].fromBlock == block.number) {
```
Able to delegate multiple times with the same 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#L71-L89
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L168-L175


# Vulnerability details

The ```delegate``` function in ```VoteEscrowDelegation.sol``` is used to delegate voting power from one tokenId to another. The tokenId is added to the toTokenId's delegatedTokenIds array which contains all tokenIds that have delegated to the toTokenId. The amount of votes a token has for a checkpoint is determined by its own balance and the balances of all tokens in delegatedTokenIds.

Since delegatedTokenIds is an array, it may contain the same tokenId more than once. This means that the voting power of a tokenId can be multiplied by just adding the same tokenId to the delegatedTokenIds array.

## Impact

This issue would allow anyone to increase their voting power without staking more tokens or having legitimate delegations. 

A malicous user may also fill the delegatedTokenIds of any tokenId so that when a user wants to delegate to that tokenId the delegate function will revert. This happens when the delegate funtion calls the _writeCheckpoint function which reverts if the delegatedTokenIds array contains 500 or more tokenIds.

## Proof Of Concept

The code below shows how a tokenId delegates to a toTokenId in the ```delegate``` function. If nCheckpoints > 0, then the tokenId will be added to the toTokenId's delegatedTokenIds array for that checkpoint. The function does not check if the tokenId is currently delegating to the toTokenId or another tokenId which allows a user to repeatedly call the function for the same toTokenId

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

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

After the tokenId is added, _writeCheckpoint is called which will either update the current checkpoint of the toTokenId or create a new checkpoint with the updated delegatedTokenIds array.

The malicious user could use a tokenId with very little voting power to fill up the delegatedTokenIds array so that other tokens are unable to delegate to that toTokenId. They could also delegate to a tokenId they own with the same tokenId and multiply its voting power without actually adding more votes. The getVotes function will then just return voting power of the tokenId * 500 (the delegatedTokenIds array cannot contain more than 500 tokenIds).


## Recommended Mitigation Steps

Prevent duplicate tokenIds from being entered in the delegatedTokenIds array when the ```delegate``` function is called. This could be done by checking ```delegates[tokenId] == address(0), which i
The function `_transferFrom` doesn't work
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

In `VoteEscrowDelegation.sol` L242, the function `removeDelegation` is called externally (with the syntax `this.`), but that external call changes the `msg.sender`. So in the function `removeDelegation`, `msg.sender` will be the contract itself, not the user. But this function requires that the owner of the NFT is `msg.sender`, that will always be false for NFTs owned by the user. 

Sggested fix: call `removeDelegation` internally (so make this function `public` instead of `external`).
The function `removeDelegation` doesn't work
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


# Vulnerability details

When a delegation is created (with the function `delegate`, the ID of its NFT (`tokenId`) is added in the array `checkpoints[toTokenId][nCheckpoints - 1];` where `toTokenId` is the ID of NFT the user delegates its NFT to.

When an user removes the delegation of his NFT (with the function `removeDelegation` L210), `tokenId` is removed from `checkpoints[tokenId][nCheckpoints - 1]` istead of `checkpoints[toTokenId][nCheckpoints - 1]`.

`toTokenId` can be retrieved in `removeDelegation` through the mapping `delegates`, but be aware that this mapping is not accurate (reported as a Medium).
The function `delegate` doesn't work
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#L101


# Vulnerability details

In `VoteEscrowDelegation.sol` in the function `delegate` when `nCheckpoint = 0`, which is the case for every account that has not delegated yet, the function `_writeCheckpoint` is called. However, this function computes `(nCheckpoints - 1)` L101 which thus causes an underflow. Contracts are compiled in 8.11.0 so underflow reverts, and no one is then able to delegate his voting power. 

Suggested fix: the function `_writeCheckpoint` should check if `nCheckpoints` is zero and behave accordingly.
A NFT can be delegated to many NFTS
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#L71


# Vulnerability details

In `VoteEscrowDelegation.sol` in the function `delegate`, there is no check about if the tokenId NFT has already been delegated. Thus is it possible to delegate voting power multiple time. It's even possible to self delegate (`tokenId = toTokenId`) multiple times. By doing that, it's possible to arbitrarily increase the voting power related to any NFT. Indeed, the function `getPriorVotes` relies on the size of the array `delegatedTokenIds` for the given `tokenId`, and this array can be filled with multiple time the same token ID.

Suggested fix: check that the NFT has not delegated its power yet (do not use the `delegates` mapping to do so, it's not updated when a delegation is removed, reported as a Medium finding)
Misleading mapping `delegates`
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#L32


# Vulnerability details

The mapping `delegates` in `VoteEscrowDelegation` can be misleading (it is not updated when a delegation is removed). If someone is relying on this value (not the case here, because this mapping is never used in the contracts (?), but we can assume that it has / will have / should have a purpose).

Suggested fix: update the mapping when a delegation is removed.
User is unable to remove delegation and transfer NFT
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#L233-L262


# Vulnerability details

## Impact
The _transferFrom() in VoteEscrowDelegation.sol should be change to an external function. Currently, the function is unable to be called by any user since it is an internal function and there's no call to the function from another Golom contract.

With above, users who want to remove delegation and transfer their NFT will be unable to call the function.

## Proof of Concept
https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L233-L262

## Tools Used
Manual review

## Recommended Mitigation Steps
Change function to external.
delegate will revert for tokenIds which have no prior checkpoints
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#L71-L109


# Vulnerability details

## Impact
For a tokenId without a prior checkpoint, calling `VoteEscrowDelegation.delegate()` would revert due to an underflow in `nCheckpoints`

`nCheckpoints` variable in `uint256 nCheckpoints = numCheckpoints[toTokenId];` is set to 0 since there was no prior numCheckpoints. Thus nCheckpoints - 1 in line 79 would cause an underflow causing a revert.

Due to above, first time delegation on a tokenId will always revert.



## Tools Used
Manual review

## Recommended Mitigation Steps
Change `uint256 nCheckpoints = numCheckpoints[toTokenId];` to `uint256 nCheckpoints = numCheckpoints[toTokenId] + 1;`
Fees are lost after GOLOM token supply limit has been reached
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/rewards/RewardDistributor.sol#L100-L102


# Vulnerability details

## Impact
When the 1 billion supply has been reached for the GOLOM token, `addFee()` in RewardDistributor will automatically return to the start without updating any trades, however will still accept fees being sent from GolomTrader. These fees however cannot be claimed by anyone, thereby permanently locking these fees into the contract. 

## Tools Used
VS Code
## Recommended Mitigation Steps
Consider preventing fees from being sent to the reward distributor after the max supply has been reached or allowing fees to be sent to governance or some other privileged role.