xanoutas/solidity-security-findings huggingface dataset:
Allowing multiple checkpoints in a single block can be abused to increase voting power on select proposals
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L242-L256


# Vulnerability details

## Impact
Votes can be multiplied allowing for governance attacks

## Proof of Concept

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L242-L256

    // Get the pointer to store the checkpoint
    Checkpoint storage checkpoint = checkpoints[_account][_id];

    // Record the updated voting weight and current time
    checkpoint.votes = uint192(_newTotalVotes);
    checkpoint.timestamp = uint64(block.timestamp);

    emit DelegateVotesChanged(_account, _prevTotalVotes, _newTotalVotes);

After each token transfer, ERC721Votes.sol#_moveDelegateVote is called, which calls ERC721Votes.sol#_writeCheckpoint (shown above). If called multiple times in one block, it will write multiple checkpoints with the same timestamp as there is no logic to prevent it. Allowing multiple checkpoints per block can be abused using a quirk with how binary search in getPastVotes works. By carefully transferring between accounts and controlling account array lengths getPastVotes can be manipulated to return a number of votes higher than the total number of NFTs owned. First let's break down how getPastVotes selects which checkpoint to return. 

    if (accountCheckpoints[lastCheckpoint].timestamp <= _timestamp) return accountCheckpoints[lastCheckpoint].votes;

When the timestamp <= lastCheckpoint.timestamp getPastVotes immediately short circuits and returns the most recent checkpoint.

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L85-L114

    uint256 high = lastCheckpoint;
    uint256 low;
    uint256 middle;

    // Used to temporarily hold a checkpoint
    Checkpoint memory cp;

    while (high > low) {
        // Find the id of the middle checkpoint
        middle = high - (high - low) / 2;

        // Get the middle checkpoint
        cp = accountCheckpoints[middle];

        // If the timestamp is a match:
        if (cp.timestamp == _timestamp) {
            // Return the voting weight
            return cp.votes;

            // Else if the timestamp is before the one looking for:
        } else if (cp.timestamp < _timestamp) {
            // Update the lower bound
            low = middle;

            // Else update the upper bound
        } else {
            high = middle - 1;
        }
    }

Now if we look at the binary search portion of the code, if middle.timestamp == timestamp, it immediately returns the middle point. With the above code we figure what checkpoint would be the first to return when lastCheckpoint = 4 (array length of 5)

    middle = high - (high - low) / 2 
    middle = 4 - (4 - 0) / 2 = 2

In the case where an array is 5 in length, the first checkpoint that will be returned is accountCheckpoints[2]. If
Deployment flow doesn't work
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L34-L43
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/treasury/Treasury.sol#L25-L34
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L31-L42
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L23-L32
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/metadata/MetadataRenderer.sol#L25-L34


# Vulnerability details

## Impact
The deployment flow is based on the concept that the ZORA team can deploy implementation contracts and users will be able to deploy proxies. However, because manager is immutably set in the implementation contracts, this entire system will not work.

## Proof of Concept
In each of `Token`, `MetadataRenderer`, `Auction`, `Treasury`, and `Governor`, the Manager address is immutably set in the constructor.

Constructors are called when the implementation contract is deployed, and the immutably set value will be hardcoded into the runtime bytecode.

As users deploy their proxies, they will not be able to overwrite this value, and will therefore not have the permissions needed to run their protocol.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Change the deployment flow so that Manager is set in the `initialize()` contract, so that users deploying proxies are able to set their own Manager.

This seems like it slightly reduces security, since there will not be able to be a check that only the Manager can make the `initialize()` call. However, since the proxies are deployed within the same function call that they are initialized (within `Manager:deploy()`) there is no risk here.
NFT owner can double his voting power on the first delegation
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L179-L190


# Vulnerability details

When Mike the NFT owner delegates for the first time and do it to himself, his votes double as _delegate() will reuse minting workflow by obtaining previous delegate from uninitialized `delegation` array and running _moveDelegateVotes() with `_from` being zero address, that corresponds to minting and is used for votes creation. As Mike already have votes equal to his balance (gathered on each mint by the same _moveDelegateVotes() invoked by _afterTokenTransfer()), he obtains x2 votes this way.

Placing severity to be high as ERC721Votes is parent to Token and this allows to substantially bias the governance voting process.

## Proof of Concept

_delegate() uses `delegation` array even if it's not initialized:

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L179-L190

```solidity
    function _delegate(address _from, address _to) internal {
        // Get the previous delegate
        address prevDelegate = delegation[_from];

        // Store the new delegate
        delegation[_from] = _to;

        emit DelegateChanged(_from, prevDelegate, _to);

        // Transfer voting weight from the previous delegate to the new delegate
        _moveDelegateVotes(prevDelegate, _to, balanceOf(_from));
    }
```

_moveDelegateVotes() doesn't track zero address vote balance, treating it as minting all the time. I.e. when `_from == address(0)` the votes are created from the thin air:

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L196-L233

```solidity
    function _moveDelegateVotes(
        address _from,
        address _to,
        uint256 _amount
    ) internal {
        unchecked {
            // If voting weight is being transferred:
            if (_from != _to && _amount > 0) {
                // If this isn't a token mint:
                if (_from != address(0)) {
                    // Get the sender's number of checkpoints
                    uint256 nCheckpoints = numCheckpoints[_from]++;

                    // Used to store the sender's previous voting weight
                    uint256 prevTotalVotes;

                    // If this isn't the sender's first checkpoint: Get their previous voting weight
                    if (nCheckpoints != 0) prevTotalVotes = checkpoints[_from][nCheckpoints - 1].votes;

                    // Update their voting weight
                    _writeCheckpoint(_from, nCheckpoints, prevTotalVotes, prevTotalVotes - _amount);
                }

                // If this isn't a token burn:
                if (_to != address(0)) {
                    // Get the recipients's number of checkpoints
                    uint256 nCheckpoints = numCheckpoints[_to]++;
ERC721Votes's delegation disables NFT transfers and burning
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L262-L268


# Vulnerability details

If Alice the NFT owner first delegates her votes to herself, second delegates to anyone else with delegate() or delegateBySig() then all her NFT ids will become stuck: their transfers and burning will be disabled.

The issue is _afterTokenTransfer() callback running the _moveDelegateVotes() with an owner instead of her delegate. As Alice's votes in the checkpoint is zero after she delegated them, the subtraction _moveDelegateVotes() tries to perform during the move of the votes will be reverted.

As ERC721Votes is parent to Token and delegate is a kind of common and frequent operation, the impact is governance token moves being frozen in a variety of use cases, which interferes with governance voting process and can be critical for the project.

## Proof of Concept

Suppose Alice delegated all her votes to herself and then decided to delegate them to someone else with either delegate() or delegateBySig() calling _delegate():

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L179-L190

```solidity
    function _delegate(address _from, address _to) internal {
        // Get the previous delegate
        address prevDelegate = delegation[_from];

        // Store the new delegate
        delegation[_from] = _to;

        emit DelegateChanged(_from, prevDelegate, _to);

        // Transfer voting weight from the previous delegate to the new delegate
        _moveDelegateVotes(prevDelegate, _to, balanceOf(_from));
    }
```

_moveDelegateVotes() will set her votes to `0` as `_from == Alice` and `prevTotalVotes = _amount = balanceOf(Alice)` (as _afterTokenTransfer() incremented Alice's vote balance on each mint to her):

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L196-L217

```solidity
    function _moveDelegateVotes(
        address _from,
        address _to,
        uint256 _amount
    ) internal {
        unchecked {
            // If voting weight is being transferred:
            if (_from != _to && _amount > 0) {
                // If this isn't a token mint:
                if (_from != address(0)) {
                    // Get the sender's number of checkpoints
                    uint256 nCheckpoints = numCheckpoints[_from]++;

                    // Used to store the sender's previous voting weight
                    uint256 prevTotalVotes;

                    // If this isn't the sender's first checkpoint: Get their previous voting weight
                    if (nCheckpoints != 0) prevTotalVotes = checkpoints[_from][nCheckpoints - 1].votes;

                    // Update their voting weight
                    _writeCheckpoint(_from, nCheckpoints, prevTotalVotes, prevTotalVotes - _amount);
Malicious adversary can trigger double vote attack on governance proposals
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L101


# Vulnerability details

## Impact
Governance protocol does not implement function `getPastVotes()` correctly, resulting in timestamps that contain more votes than tokens.
This effectively allows a malicious adversary to double vote, which directly impacts variables within the broader protocol that are controlled by
governance. 

The ERC721Votes contract makes the following assumptions:
1. The midpoint will find the latest checkpoint all the time
2. This checkpoint prevents users transferring tokens without their voting power changing.

Both of these assumptions are broken when the following conditions are satisfied:
1. A user places two transactions mined in the same block (therefore both have the same timestamp)
2. The transaction `transaction_timestamp == proposal.timeCreated` , where `transaction_timestamp` is the same for both transactions

Assumption 2 can be seen in the following code block: 

```
                if (cp.timestamp == _timestamp) { // here the checkpoint will ignore later, more accurate snapshots if the midpoint timestamp directly matches the expected value
                    // Return the voting weight
                    return cp.votes;
```

## Proof of Concept
A possible attack under these conditions would look like this:

Prelim: Malicious adversary is a miner or colludes with a miner to monitor for proposal creations, and submit the following two transactions in the same block as the proposal:
1. Transaction 1 increases the balance of Mal's Account (Mal-1) and therefore increases there voting power
2. Transaction 2 Mal transfers the balance to his second account Mal-2
The malicious actor has leveraged condition 1. In a situation where transaction_timestamp != proposal.timeCreated  (condition 2 fails) the results of the checkpoint update would show that Mal-1 now has 0 voting power, whereas Mal-2 has 100% of the voting power held by Mal. This is the guarantee set by the use of the midpoint approximation formula used to find the latest checkpoint. 

However, in the event that condition (2) is true and the `transaction_timestamp == proposal.timeCreated`, the conditionala check on `LINE 101` will instantly return the cp without checking for another more recent state change. This could lead to a double vote depending on number of checkpoints stored for Mal, but it can strategically crafted to cause damage....

So say there are 7 updates to the checkpoint for mal: 0,1,2,3,4,5,6
assume that transactions that updated checkpoint ID 3 and 4 were mined in the same block. Therefore, the midpoint is 3. 

If transactions 3,4 were put in the same block as the proposal creation transaction then line 101 is true and the value of the checkpoint 3 is returned. This checkpoint may have contained an increase in voting power whilst transaction 4 transferred this vote to another account. If
An unchecked msg.value can be an overflow
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/treasury/Treasury.sol#L160-L169


# Vulnerability details

## Impact
.call value takes the maximum value of ```uint256```, however, because overflow is not checked with ```unchecked```, an overflow occurs if a higher ```msg.value```  is given.

```js
 unchecked {
            // For each target:
            for (uint256 i = 0; i < numTargets; ++i) {
                // Execute the transaction
                (bool success, ) = _targets[i].call{ value: _values[i] }(_calldatas[i]);

                // Ensure the transaction succeeded
                if (!success) revert EXECUTION_FAILED(i);
            }
        }
```


## Proof of Concept
```js
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import "../../lib/test.sol";

contract Test is DSTest {
  
    Contract c0;
    
    function setUp() public {
        c0 = new Contract();
 
    }
    
    function test() public {
        uint256 MAX_INT = 2**256-1;
        c0.execute(MAX_INT+1);
   
    }
}

contract Contract {
    address _targets = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
    error EXECUTION_FAILED();

   function execute(uint256 _values) external payable {
                unchecked {  
               
                (bool success, ) = _targets.call{ value: _values }(" ");
                if (!success) revert EXECUTION_FAILED();
	}
   }  
}

Running 1 test for src/test/GasTest.t.sol:Test
[FAIL. Reason: Arithmetic over/underflow] test() (gas: 2362)
Traces:
  [198206] Test::setUp() 
    ├─ [160719] → new Contract@"0xce71…c246"
    │   └─ ← 692 bytes of code
    └─ ← ()

  [2362] Test::test() 
    └─ ← "Arithmetic over/underflow"

Test result: FAILED. 0 passed; 1 failed; finished in 197.42µs


Failing tests:
Encountered 1 failing test in src/test/GasTest.t.sol:Test
[FAIL. Reason: Arithmetic over/underflow] test() (gas: 2362)

```


## Tools Used
Manual Code review

## Recommended Mitigation Steps
Remove unchecked in the part where msg.value is used
With lower  ```createBid```  offer, it is possible to prevent higher  ```createBid```
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/auction/Auction.sol#L90-L127


# Vulnerability details

## Impact


The createBid function in the Auction contract creates a bid for the current token . The highest bidder by the end of the bidding period wins the tender.
Due to the formula below, there is a certain rate requirement in the increases of the bids given to the createBid function.
```js
     minBidIncrement = 10; // state variable

    // Compute the minimum bid
    minBid = highestBid + ((highestBid * settings.minBidIncrement) / 100);
```

minBid = highestBid + ((highestBid * settings.minBidIncrement) / 100);
for instance ; If highestBid : 75 ETH and minBidIncrement : 10
minBid = 75 + (( 75 * 10) / 100 = 82.5-ETH
75 highestBid is min. bid should be 82.5 ETH

However, someone following Mempool  can see the bid given, bid less than the current bidder, and make the transaction revert to the person who bids more because of the min bid limit with frontrun

Simple prototype of monitoring Mempool ; 
```js
var ethers = require("ethers");
var url = "wss://mainnet.infura.io/…….

var init = function () {
  var customWsProvider = new ethers.providers.WebSocketProvider(url);

  customWsProvider.on("pending", (tx) => { 
//customWsProvider.on("pending", (tx) => { //block pending 
    customWsProvider.getTransaction(tx).then(function (transaction) {
      console.log(transaction);
    });
  });

  customWsProvider._websocket.on("error", async () => {
    console.log(`Unable to connect to ${ep.subdomain} retrying in 3s...`);
    setTimeout(init, 3000);
  });
  customWsProvider._websocket.on("close", async (code) => {
    console.log(
      `Connection lost with code ${code}! Attempting reconnect in 3s...`
    );
    customWsProvider._websocket.terminate();
    setTimeout(init, 3000);
  });
};

init();

// In this section, the transactions requested in the above query 
// (the createBid function is processed for this project) are processed

```


## Proof of Concept

1 - createBid is almost time to end and ```highestBid``` is 75 ETH and ```minBidIncrement``` 82.5 ETH
2 - Alice offers 92 ETH (Min Offer : 82.5 ETH so condition ok)
3 - Bob follows the ```createBid``` function in Mempool and by seeing this offer more fees or using Flashbots; Offers 85 ETH (Min Offer : 82.5 ETH so condition ok)
4 - Bob, who took the lead with FrontRun, became the highest bidder with 85 ETH and the transaction was completed.
5 - It's Alice's turn to trade and now her minBid is 93.5 ETH, so her offer of 92-ETH will be rejected.
6 - In the end, Alice 92-ETH , although Bob offered 85-ETH , Bob bought the NFT

Although it is due to the mathematics of the auction, this is not acceptable, especially the existence of such mathematics is clearly a serious problem for the investor

Front running probabilities of bots in NFT projects is an important criterion by investors.


Example of FrontRunning operations in NFT Sales and offers;
https://twitter.c
User can get infinite voting power by abusing faulty checkpoint logic
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/lib/token/ERC721Votes.sol#L93-L114


# Vulnerability details

## Impact
Because multiple checkpoints can be made within the same block, users can move tokens around and trigger multiple checkpoints with different balances, then architect the later binary search to hit the higher balances, gaming the voting system to give themselves more votes.

## Proof of Concept
Checkpoints within the same block should overwrite each other, as they do in other forks of Compound Governor Bravo (including Nouns). Instead, they are appended in the array.

This can lead to consecutive checkpoints having the same block.timestamp.

Because of this and the fact that `getPastVotes()` function makes a binary search, this function can read the wrong checkpoint, which enables a possible sybil attack with 1 NFT. An attacker can send an NFT to many of his accounts within the same block in a way that can influence the binary search so that it selects malicious checkpoints.

```javascript
function getPastVotes(address _account, uint256 _timestamp) public view returns (uint256) {
        [...]

        // Get the account's number of checkpoints
        uint256 nCheckpoints = numCheckpoints[_account];

        [...]

        unchecked {
            // Get the latest checkpoint id
            // Cannot underflow as `nCheckpoints` is ensured to be greater than 0
            uint256 lastCheckpoint = nCheckpoints - 1;

            [...]

            // Otherwise, find a checkpoint with a valid timestamp
            // Use the latest id as the initial upper bound
            uint256 high = lastCheckpoint;
            uint256 low;
            uint256 middle;

            // Used to temporarily hold a checkpoint
            Checkpoint memory cp;

            // While a valid checkpoint is to be found:
            while (high > low) {
                // Find the id of the middle checkpoint
                middle = high - (high - low) / 2;

                // Get the middle checkpoint
                cp = accountCheckpoints[middle];

                // If the timestamp is a match:
                if (cp.timestamp == _timestamp) {
                    // Return the voting weight
                    return cp.votes;

                    // Else if the timestamp is before the one looking for:
                } else if (cp.timestamp < _timestamp) {
                    // Update the lower bound
                    low = middle;

                    // Else update the upper bound
                } else {
                    high = middle - 1;
                }
            }

            return accountCheckpoints[low].votes;
        }
    }
```

## Recommended Mitigation Steps
If multiple checkpoints are created in the same block, they should be overwritten (the way there are in the live Nouns token): 
```solidity
if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
      c