xanoutas/solidity-security-findings huggingface dataset:
Voter can vote without owning any NFT if they can borrow NFT on the creationBlock and hold it for 1 block then return it
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#L170
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L275
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L462


# Vulnerability details

## Impact
Voter can vote without owning any NFT if they can borrow NFT on the creationBlock and hold it for 1 block then return it. 

Voters can get too much voting power while using a little ETH (Borrowing fee), Then voters can spam voting NO or YES to the targeted proposals. If NO voting has been spammed, it may cause a valid proposal to be rejected.

Hackers can monitor mempool to know when the proposal is created and bribe a validator to attach NFT borrowing transactions into the same block as the proposal creation block.

## Proof of Concept
```
proposal.timeCreated = uint32(block.timestamp);
...
weight = getVotes(_voter, proposal.timeCreated);
...
    function getVotes(address _account, uint256 _timestamp) public view returns (uint256) {
        return settings.token.getPastVotes(_account, _timestamp);
    }
```

proposal.timeCreated is set to the proposal creation timestamp which can be known by monitoring the mempool

weight is voting power at the proposal creation timestamp

Once hackers have seen the proposal creation transaction, they can sandwich the transaction with the NFT borrowing transaction. The voting power will get recorded and after 1 block, they return the borrowed NFT immediately. Once voting has started, the borrowed voting power remains there.

## Recommended Mitigation Steps
Get voting power at proposal creation time - 1 second

```
weight = getVotes(_voter, proposal.timeCreated - 1);
```
Incorrect delegation of tokens in `_afterTokenTransfer` allows users to gain maximum voting weight
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
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L189


# Vulnerability details

## Impact
In `ERC721Votes.sol`, when an ERC721 token is minted and transferred to a user who has delegated their tokens, the token voting weight is incorrectly added to the voting weight of the user in `_afterTokenTransfer()`. This means that tokens are inconsistently delegated, as part of the user's tokens are delegated towards themselves and the rest to another user. When a user then tries to change their delegation through `delegate()`, the contract calls `balanceOf()` and assumes all user tokens are delegated to the delegatee which can cause an underflow and allow a user to gain a very large number of votes.

This can be exploited to vote for malicious proposals or DOS the governance and vote against all proposals.

High severity is suitable as this can be done by any user at any time and allows a user to gain complete control over the governance protocol

## Proof of Concept
1. Alice buys token#1 and delegates it to Bob
2. Alice buys token#2
3. `Auction.sol` calls `token.transferFrom(address(this), alice.address, tokenId);`
4. The balances are updated and `_afterTokenTransfer()` is called which then subsequently calls `_moveDelegateVotes(_from, _to, 1);` where `_to` is Alice's address
5. Alice and Bob now have a vote each when Bob should instead have both
6. Alice calls `delegate(_to)` =>  `_moveDelegateVotes(prevDelegate, _to, balanceOf(_from);`
7. As Alice's balance is 2, Bob's weight underflows (as `unchecked` is used) and becomes `type(uint256).max`

## Tools Used
VS Code
## Recommended Mitigation Steps
Modify `_afterTokenTransfer()` to:
```solidity
    function _afterTokenTransfer(
        address _from,
        address _to,
        uint256 _tokenId
    ) internal override {
        //if sender or recipient have not delegated, then automatically self-delegate
        if(delegation[_from] == address(0) && _from != address(0)) {
            delegation[_from] = _from;
        }
        if(delegation[_to] == address(0) &&  _to != address(0)) {
            delegation[_to] = _to;
        }
        // Transfer 1 vote from the sender's delegatee to the recipient's delegatee
        _moveDelegateVotes(delegation[_from], delegation[_to], 1);


        super._afterTokenTransfer(_from, _to, _tokenId);
    }
```

Additionally, ensure that a user is not able to delegate to `address(0)` as that will break the above logic.
Low level call can return true even if the action has failed
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/treasury/Treasury.sol#L162-L168


# Vulnerability details

## Impact
Low level call returns true if the contract doesn't exist (code.length == 0), for example in case of a destructed contract or an EOA. You can read about it in the [solidity documentation](https://docs.soliditylang.org/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions). This makes a proposal to be executed successfully even if some of the operations in it failed.

## Proof of Concept
1. A proposal has passed and is ready to be executed. The proposal contains 3 targets - A, B, C. B is a destructed contract.
2. The execute function is called. A is execute successfully, B is not executed but returns true so it is treated as it was successfully executed, and C is executed successfully. The execute function hasn't reverted, even when B wasn't executed at all.

## Tools Used
manual audit

## Recommended Mitigation Steps
Make sure (both in the propose function and before the execution) that the target is an existing contract, by checking that `target.code.length > 0` or using the [OpenZepplin implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322/contracts/utils/Address.sol#L36-L42) for the `isContract` function.
The same founder can be defined multiple times, which causes biased base token distribution
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L71-L126


# Vulnerability details

## Impact
The `Token._addFounders` function doesn't check that the founders are unique, which can lead to the same founder receiving base tokens in different distributions.

## Proof of Concept
To verify this bug I created a foundry test. You can add it to the test folder and run it with `forge test --match-test testNonUniqueFounders`.

This test deploys a token implementation and an `ERC1967` proxy that points to it, and initializes 2 proxies - one with 10 owners of 10% each, where 2 of them are the same address, and the other one with 9 owners - 8 of 10% and one of 20%.

After the call to initialize, the test checks what is the first and second base token ids that the double address founder will get in every token. This is to show that the distribution actually changes.

In the token where he appears once with 20%, the first base token id he gets is 0 and the second is 5, which is the expected result. However, in the second configuration where he appears twice, his 2 first token ids are 0 and 1, which is an unwanted behavior.

```sol
// The relative path of this file is "test/NonUniqueFounders.t.sol"

// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { Test } from "forge-std/Test.sol";
import { console } from "forge-std/console.sol";

import { IManager } from "../../src/manager/Manager.sol";
import { IToken, Token } from "../../src/token/Token.sol";

import { TokenTypesV1 } from "../../src/token/types/TokenTypesV1.sol";

import { ERC1967Proxy } from "../../src/lib/proxy/ERC1967Proxy.sol";

contract NonUniqueFounders is Test, TokenTypesV1 {

    Token imp;
    address proxy1;
    address proxy2;
    
    function setUp() public virtual {
        // Deploying the implementation and the proxy
        imp = new Token(address(this));
        proxy1 = address(new ERC1967Proxy(address(imp), ""));
        proxy2 = address(new ERC1967Proxy(address(imp), ""));

    }

    function testNonUniqueFounders() public {

        IToken token1 = IToken(proxy1);
        IToken token2 = IToken(proxy2);

        address doubleFounder = address(0xDF);

        // Creating 3 founders with 25%, 25%, 50% ownership
        IManager.FounderParams[] memory founders = new IManager.FounderParams[](10);
        founders[0] = IManager.FounderParams({
            wallet: doubleFounder,
            ownershipPct: 10,
            vestExpiry: 1 weeks
        });
        founders[0] = IManager.FounderParams({
            wallet: doubleFounder,
            ownershipPct: 10,
            vestExpiry: 1 weeks
        });
        for (uint i = 2; i < 10; ++i) {
            founders[i] = IManager.FounderParams({
                wallet: address(uint160(i + 0xF)),
                ownershipPct: 10,
                vestExpiry: 1 weeks
            });
        }

        // Initializing the proxy with the founder
No withdraw() for ETH in Treasury.sol
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#L269


# Vulnerability details

## Impact
The contract could receive ETH via `receive()` but no withdraw() is implemented.

## Proof of Concept
address(this).balance will be greater than zero but it will stuck in the contract forever.

## Recommended Mitigation Steps
Implement an access controlled (governor being the owner has the discretion of where and how much to send) `withdraw()` with the following code embedded:
```
    function withdraw(address _to, uint256 _amount) external onlyOwner {
        if (_amount > address(this).balance) revert BALANCE_EXCEEDED();
        (bool sent, ) = _to.call{ value: _amount }('');

        emit Withdraw(amount, sent);
    }
```
Note: You would need to include the following event, and custom error in ITreasury.sol:
```
event Withdraw(uint256 amount, bool sent);

error BALANCE_EXCEEDED();
```
H2 - An underflow can occur in the delegation process
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#L189
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L216


# Vulnerability details

When someone delegates its voting power, the current balance is used as reference to increment or decrement the voting power the users involved, instead of the voting power actually delegated. So if someone  obtains a token after delegating its voting power, and then changes the person they delegate to, the user that had the delegation before will see its voting power decreased by more than the amount he received before. Because everything is in an `unchecked` block, an underflow can occur. So one can obtain an infinite voting power.

## Proof of Concept


The error occurs in the contract `ERC721Votes`, a contract Token inherits from. To simplify the test that presents the behaviour, we created a simple contract `MyToken` that inherits from `ERC721Votes`. The behaviour about the delegation and the transfer will be exactly the same.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import "forge-std/console.sol";
import { NounsBuilderTest } from "./utils/NounsBuilderTest.sol";
import { ERC721Votes } from "../src/lib/token/ERC721Votes.sol";


contract MyToken is ERC721Votes {
    function mint(address _to, uint256 _tokenId) external {
        _mint(_to, _tokenId);
    }
}

contract TestMEP_H2 is NounsBuilderTest {

    function testMEP_H2() public {
        MyToken token = new MyToken();

        address alice = address(0x0a);
        address bob = address(0x0b);
        address carol = address(0x0c);

        // to fix the bug about the first delegation
        vm.prank(alice);
        token.delegate(alice);
        vm.prank(bob);
        token.delegate(bob);
        vm.prank(carol);
        token.delegate(carol);


        token.mint(alice, 1);
        token.mint(carol, 2);


        vm.prank(alice);
        token.delegate(bob);

        vm.prank(carol);
        token.transferFrom(carol, alice, 2);

        vm.prank(alice);
        token.delegate(alice);

        console.log("Bob's voting power:");
        console.logUint(token.getVotes(bob));
    }
}

```

In the scenario, Alice and Carol both have 1 token. If Alice delegates to Bob (that has 0 token), then Carol transfers her token to Alice, and then Alice delegates to herself (she denies the delegate to bob), the function `_moveDelegateVotes` will be called with `_amount` set to 2 (the current Alice's current balance in token). But Bob only has a voting power of 1 (that has been delegated to him by Alice at the begining). The decrement will thus cause an underflow because it is in an `unchecked` block, and Bob will have an infinite voting power. Even if it was not in an `unchecked` block, Alice would be able to remove more voting power to Bob that she should be able to.
H1 - Delegating a token after a mint does not decrements the voting power of the delegator
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#L181
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L203


# Vulnerability details

When a person delegates its voting power to another person, the receiver's voting power is incremented, but the voting power of the person that delegated its token is not decremented. So one can duplicate it's voting power.

## Proof of Concept

The error occurs in the contract `ERC721Votes`, a contract `Token` inherits from. To simplify the test that presents the behaviour, we created a simple contract `MyToken` that inherits from `ERC721Votes`. The behaviour about the delgation will be exactly the same.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import "forge-std/console.sol";
import { NounsBuilderTest } from "./utils/NounsBuilderTest.sol";
import { ERC721Votes } from "../src/lib/token/ERC721Votes.sol";


contract MyToken is ERC721Votes {
    function mint(address _to, uint256 _tokenId) external {
        _mint(_to, _tokenId);
    }
}

contract TestMEP_H1 is NounsBuilderTest {

    function testMEP_H1() public {
        MyToken token = new MyToken();

        address alice = address(0x0a);
        address bob = address(0x0b);

        token.mint(alice, 1);

        vm.prank(alice);
        token.delegate(bob);

        console.log("Alice's voting power:");
        console.logUint(token.getVotes(alice));
        console.log("Bob's voting power:");
        console.logUint(token.getVotes(bob));
    }
}

```

If ther token 1 is minted to Alice, and she transfers this token to Bob, they will both have a voting power of 1. In fact, the function `_moveDelegateVotes` is called with `_from` set to `address(0)` (because there was no previous delegation), so the block that decrements the voting power is not called, and Alice keeps 1 of voting power.

In fact, it is slightly different because it is the contract `Auction` that mints the tokens for itself, and then sends them to the minter. So the contract itself will keep the voting power. But, in the case of founders, it is minted to them direct. So they can use this bug to duplicate their voting power.

## Recommended Mitigation Steps

A simple trick could be to set `delegation[user] = user` when a token is minted to `user`. It works fine, but I'm not sure if resetting the delegation of an user at each token minted is a behaviour that the devs would want.
_handleOutgoingTransfer should have access control. 
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L344-L365


# Vulnerability details

## Impact
function should have access control as much as it is private. it could lead to loss of funds 

## Proof of Concept
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L344-L365

## Tools Used
none 
## Recommended Mitigation Steps
only auction contract should call that function
getPastVotes function doesnt exist 
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#L462


# Vulnerability details

## Impact
the function doesnt exist so we are unable to get past votes which are used as the weight in voting 
## Proof of Concept
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L462

## Tools Used
none 
## Recommended Mitigation Steps

create the function with all it logic
Bidding on paused auctions results in full refund for auction winner
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


# Vulnerability details

## Impact

Affected code: [https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/auction/Auction.sol#L90](https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/auction/Auction.sol#L90)

The function `createBid()` allows users to bid on the latest active auction; when an user raises the latest bid, the previous highest bidder gets a refund.

The auction is settled when `settleAuction()` (if the contract is in paused state) or `settleCurrentAndCreateNewAuction()` (if the contract is fully operational) are called. The settlement results in auction the token being awarded to the highest bidder.

`createBid()` is currently missing important safety checks, as it allows any user to bid on settled auctions, and it allows bidding when contract is paused too. If an user were to bid on a settled auction, the auction winner would get a full refund of their highest bid (along with the already won auction token) and the original user would lose their money

## Proof of Concept

There are two cases in which users can bid on a settled auction:

1) The contract is paused, which allows anyone to just call `settleAuction()` without creating a new, following auction

2) The contract is fully operational but `_createAuction()` fails to mint a new token

## Tools Used

Editor

## Recommended Mitigation Steps

`createBid()` should have the `whenNotPaused()` modifier, and it should also prevent betting on already-settled auctions
Possible attack on governance
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#L340
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/treasury/Treasury.sol#L164


# Vulnerability details

## Impact
It is possible that the government can try to execute a proposal and that someone attacks it to prevent its correct execution, despite having been approved.

## Proof of Concept
As can be seen in the following code, the `execute` method sends the `msg.value` to the treasury to execute the approved proposal.

```javascript
settings.treasury.execute{ value: msg.value }(_targets, _values, _calldatas, _descriptionHash);
```

This proposal was marked as executed in order to avoid the replay.

```javascript
        if (state(proposalId) != ProposalState.Queued) revert PROPOSAL_NOT_QUEUED(proposalId);
        // Mark the proposal as executed
        proposals[proposalId].executed = true;
```

And anyone can execute an approved proposal, because it lacks authorization.

The problem comes when the `msg.value` is set by the one running the proposal, and in the `Governor` nor the `Treasury` make sure that the `msg.value` is related to the `_values` array.

So if the treasury contract have 1 ether, and the proposal is for send 1 new ether to an arbitrary address (not the current balance), if an attacker sends 0 ether and executes the proposal, the treasury ether will be sent to the destination, and the treasury will lose an ether that was not contemplated.

This is just an example, but any conversions to WETH, or calls to contracts that expect that `msg.value` to do something, will be susceptible to attack.

## Recommended Mitigation Steps
- Send the ether during the proposal creation, and refund if it was canceled.