xanoutas/solidity-security-findings huggingface dataset:
A proposer can initiate several proposals at once through delegations
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L160-L164


# Vulnerability details

## Impact
Within the NounsDAOV3Proposals' propose() function, a proposer is only permitted to have one active proposal at a time. Despite this, a proposer has the capability to delegate to various accounts, enabling these accounts to propose on their behalf.

## Proof of Concept
The function checkNoActiveProp() ensures that the proposer does not have an ongoing proposal.
```solidity
    function checkNoActiveProp(NounsDAOStorageV3.StorageV3 storage ds, address proposer) internal view {
        ...
        if (
            proposersLatestProposalState == NounsDAOStorageV3.ProposalState.ObjectionPeriod ||
            proposersLatestProposalState == NounsDAOStorageV3.ProposalState.Active ||
            proposersLatestProposalState == NounsDAOStorageV3.ProposalState.Pending ||
            proposersLatestProposalState == NounsDAOStorageV3.ProposalState.Updatable
        ) revert ProposerAlreadyHasALiveProposal();
        ...
    }
```

Additionally, a proposer can delegate their voting power to other accounts by invoking the delegate() function.

However, this capability could potentially lead to the concentration of power. With a single proposer able to delegate their voting power to several accounts, those with high voting power could control the decision-making process. This scenario can be problematic, particularly if the most powerful parties have conflicting interests or objectives divergent from the DAO's goals.

Moreover, the capability to delegate in this manner could lead to proposals being made without adequate consideration or discourse, as delegated accounts might not share the proposer's level of insight or interest in the proposal. Consequently, decisions might be made that do not reflect the DAO members' best interests.

In conclusion, although delegation can enhance DAO participation and engagement, it must be applied judiciously, with sufficient controls to prevent potential adverse outcomes, such as the excessive concentration of power.

```solidity
    function test_delegate() public {
        vm.prank(address(timelock));
        dao._setProposalThresholdBPS(1000); // 10%
        console2.log("before executing fork, proposal threshold is 2");
        assertEq(dao.proposalThreshold(), 2);
        console2.log("start executing forking");
        dao.executeFork();

        // there are 20 tokens, but 5 are now the DAO's, so adjusted total supply is 15
        assertEq(dao.adjustedTotalSupply(), 15);
        console2.log("after executing fork, , proposal threshold is 1");
        assertEq(dao.proposalThreshold(), 1);

        address Evil = makeAddr("Evil");
        console2.log("holder transfers Noun#13 to Evil");
        vm.prank(tokenHolder);
        nounsToken.transferFrom(tokenHolder, Evil, 13);
        vm.roll(block.num
Missing Storage Gap in Upgradeable Contract
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/token/base/ERC721CheckpointableUpgradeable.sol#L50


# Vulnerability details

## Impact
The current `ERC721CheckpointableUpgradeable` contract doesn't have any reserved storage gap. However, any logic contract that serves as a foundational contract and is expected to be inherited by other upgradable children should preserve a reasonable amount of storage gap. This is necessary to accommodate new state variables that might be introduced during future upgrades.

## Proof of Concept
The contract `ERC721CheckpointableUpgradeable` is intended to be upgradable and used as a base contract, however there is no storage gap in this contract, which will limit the ability to upgrade.

## Tools Used

## Recommended Mitigation Steps
It is better to have a storage gap preserved in base contract `ERC721CheckpointableUpgradeable` in case that new state variables are introduced in future upgrades. For more information, please refer to: https://docs.openzeppelin.com/contracts/3.x/upgradeable#storage_gaps.


## Assessed type

Upgradable
Malicious whale of forked DAO can prevent smaller token holders from creating proposals
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L287-L290
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L678-L689
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L288
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L122
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L785-L787


# Vulnerability details

The proposal threshold on a forked DAO can be set all the way up to 1,000 basis points.  If this were the case, only whales would be able to make proposals on the forked DAO.

## Impact
The likelihood of this is low, because in order to set the `proposalThresholdBps` to 1,000 basis points, it will need to be proposed and succeeded itself.  However, if a few whales were to propose this, they could either forcibly make the proposal succeed by taking the majority of the votes, or they could 'bully' other members of the DAO to also vote in favor of the proposal.  On the contrary, if this were to succeed, the impact would be high as it would prevent anyone with less than 16 tokens to create proposals, resulting in a medium severity.

## Proof of Concept
The total supply of nouns currently is 775.  If 20% are needed to fork, then only 155 nouns are needed in order to escrow to fork.  If the 155 nouns succeeds, then the new forked DAO has a supply of 155 nouns.
On `NounsDAOLogicV1Fork.sol`, there is a check in place on the `propose()` function to see if the proposer has enough votes in place in order to create a proposal.

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L288
```
nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold,
```
In addition, there is a function on the same `NounsDAOLogicV1Fork.sol` contract called `_setProposalThresholdBPS()`

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L678-L689
```
function _setProposalThresholdBPS(uint256 newProposalThresholdBPS) external {
        require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only');
        require(
            newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS &&
                newProposalThreshold
Division before Multiplication could incur unnecessary precision loss
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3DynamicQuorum.sol#L64


# Vulnerability details

## Impact
Division before multiplication could incur unnecessary precision loss causing loss of funds.

## Proof of Concept
In the `dynamicQuorumVotes()` function of `NounsDAOV3DynamicQuorum.sol` contract the value of `againstVotesBPS` is first calculated by dividing a value by `totalSupply` before doing another multiplication with `againstVotesBPS` in the next line.

```
function dynamicQuorumVotes(
        uint256 againstVotes,
        uint256 totalSupply,
        NounsDAOStorageV3.DynamicQuorumParams memory params
    ) public pure returns (uint256) {
@>        uint256 againstVotesBPS = (10000 * againstVotes) / totalSupply; //@audit div first
@>     uint256 quorumAdjustmentBPS = (params.quorumCoefficient * againstVotesBPS) / 1e6; //@audit then mul 
        uint256 adjustedQuorumBPS = params.minQuorumVotesBPS + quorumAdjustmentBPS;
        uint256 quorumBPS = min(params.maxQuorumVotesBPS, adjustedQuorumBPS);
        return bps2Uint(quorumBPS, totalSupply);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
When implementing mathematical operations involving multiplication and division, always ensure all division come last after all multiplications have been implemented to avoid incurring precision loss and subsequent loss of value.


## Assessed type

Math
The NounsDAOLogicV3.sol contract has a receive function but no withdraw function.
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol#L1035


# Vulnerability details

## Impact
All Ether sent to the NounsDAOLogicV3.sol will be locked in the contract because it implements a `receive()` function without a withdraw function.

## Proof of Concept
The `NounsDAOLogicV3.sol` contract implements the `receive()` function but does not have a withdraw function to withdraw Ether sent to the contract making the Ether to be locked and lost forever.

```
receive() external payable {} //@audit no withdraw.
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider adding a withdraw function to the `NounsDAOLogicV3.sol` contract.


## Assessed type

ETH-Transfer
All transactions with Ether to  NounsDAOExecutor::executeTransaction() function will fail.
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOExecutor.sol#L173


# Vulnerability details

## Impact
All transactions with Ether to the `NounsDAOExecutor::executeTransaction()` function will fail because it does not have the `payable` keyword.

## Proof of Concept
The `executeTransaction()` function of the `NounsDAOExecutor` contract does not have the payable keyword so every transaction with Ether to the function will fail.

The `executeTransaction()` has call value without a payable keyword.

```
function executeTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 eta)
        public
        returns (bytes memory)
    {

        ....//omitted code.
        //@audit not payable
        // solium-disable-next-line security/no-call-value
        (bool success, bytes memory returnData) = target.call{value: value}(callData); 
       //@audit no msg.value validation
        require(success, "NounsDAOExecutor::executeTransaction: Transaction execution reverted.");

        emit ExecuteTransaction(txHash, target, value, signature, data, eta);

        return returnData;
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider adding the `payable` keyword to the `executeTransaction()` function.


## Assessed type

Payable
[M-05] `NounsDAOV3DynamicQuorum.quorumVotes()`: Wrong totalSupply parameter used to calculated dynamic votes thresholds
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L912
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3DynamicQuorum.sol#L32-L44


# Vulnerability details

## Impact and Details
It is stated in `NounsDAOV3Fork.adjustedTotalSupply()` that the total supply of nouns exclude those in treasury and escrow contract. 

In `NounsDAOV3Proposals.isDefeated()`, the dynamic threshold is calculated based on adjustedTotalSupply at the time of creation. This used to be only a increasing value from Nouns DAO V2 since totalSupply() increases by 1 everyday with a new Noun minted.

However, with the introduction of the possibility of forking, I think calculation of dynamic threshold should use current `adjustedTotalSupply()` based on the remaining Nouns Token supply not in the new Noun DAO instead of at the time of proposal in the event of forking.

[NounsDAOV3Proposals.sol#L912](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L912)
```solidity
function createNewProposal(
    NounsDAOStorageV3.StorageV3 storage ds,
    uint256 proposalId,
    uint256 proposalThreshold_,
    uint256 adjustedTotalSupply,
    ProposalTxs memory txs
) internal returns (NounsDAOStorageV3.Proposal storage newProposal) {
    uint64 updatePeriodEndBlock = SafeCast.toUint64(block.number + ds.proposalUpdatablePeriodInBlocks);
    uint256 startBlock = updatePeriodEndBlock + ds.votingDelay;
    uint256 endBlock = startBlock + ds.votingPeriod;

    newProposal = ds._proposals[proposalId];
    newProposal.id = proposalId;
    newProposal.proposer = msg.sender;
    newProposal.proposalThreshold = proposalThreshold_;
    newProposal.targets = txs.targets;
    newProposal.values = txs.values;
    newProposal.signatures = txs.signatures;
    newProposal.calldatas = txs.calldatas;
    newProposal.startBlock = startBlock;
    newProposal.endBlock = endBlock;
    /// @audit totalSupply cached at time of proposal
->  newProposal.totalSupply = adjustedTotalSupply;
    newProposal.creationBlock = SafeCast.toUint64(block.number);
    newProposal.updatePeriodEndBlock = updatePeriodEndBlock;
}
```

If the original larger `adjustedTotalSupply()` is used, honest proposals proposed in the original Nouns DAO may never reach the minimum Dynamic Threshold for for votes if many of the original Nouns join the new Noun DAO, thus potentially leaving proposals in Defeated state since there is significantly lower number of Nouns to vote on proposal. 

[NounsDAOV3DynamicQuorum.sol#L32-L44](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3DynamicQuorum.sol#L32-L44)
```solidity
function quorumVotes(Nou
returnTokensToOwner() in NounsDAOForkEscrow.sol can revert unexpectedly due to improper subtraction 
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L116
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L124


# Vulnerability details

## Impact
returnTokensToOwner() in NounsDAOForkEscrow.sol can revert unexpectedly because of the `     numTokensInEscrow -= tokenIds.length;` math in its logic. tokenIds is an externally supplied argument and it can be larger than the storage variable `numTokensInEscrow`. Subtraction of larger uint value from smaller uint value can cause a panic/unexpected revert in solidity.

## Proof of Concept
```
    function returnTokensToOwner(address owner, uint256[] calldata tokenIds) external onlyDAO {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            if (currentOwnerOf(tokenIds[i]) != owner) revert NotOwner();

            nounsToken.transferFrom(address(this), owner, tokenIds[i]);
            escrowedTokensByForkId[forkId][tokenIds[i]] = address(0);
        }

        numTokensInEscrow -= tokenIds.length;
    }
```


## Tools Used
vs code 
## Recommended Mitigation Steps
add checks to prevent subtraction if numTokensInEscrow < tokenIds.length


## Assessed type

Math
Potential for Permanent Lock of Funds in NounsDAOExecutorV2 Contract
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOExecutorV2.sol?#L209-L231


# Vulnerability details

## Impact
The NounsDAOExecutorV2 contract has a potential vulnerability where Ether or ERC20 tokens could become permanently locked in the contract. This could occur if funds are sent to the contract by someone other than the admin, or if the admin loses access to their address or private keys. The impact of this vulnerability is financial loss for the user who sent the funds and potential operational disruption for the contract.

## Description
The NounsDAOExecutorV2 contract has a `fallback` function and functions `sendETH` and `sendERC20` that allow the contract's admin to transfer Ether and ERC20 tokens out of the contract. However, if someone other than the admin sends Ether or ERC20 tokens to the contract, those funds would be stuck because only the admin can execute the `sendETH` and `sendERC20` functions. If the admin role is not properly managed or if the admin loses access to their address or private keys, the funds could be permanently stuck in the contract.

## Proof of Concept
1. An external user sends Ether or ERC20 tokens to the `NounsDAOExecutorV2` contract address. This triggers the `fallback` function and the funds are now in the contract.

2. The external user tries to retrieve their funds. However, they are unable to do so because only the admin can execute the `sendETH` and `sendERC20` functions.

3. If the admin loses access to their address or private keys or if the admin role is not properly managed, the funds become permanently stuck in the contract.

## Tools Used
* Manual Review
* VS code

## Recommended Mitigation Steps
To mitigate this issue, consider implementing a function that allows any user to withdraw their own funds. This function should be carefully designed to prevent unauthorized withdrawals. Additionally, ensure that the admin role is properly managed and that there are procedures in place to recover the admin access if needed.


## Assessed type

Access Control
Token holders can create as many proposals as they want if they already have an active proposal by transferring their token to another wallet.
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L160-L187
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L272-L368
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L172
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L798-L809
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L197-L210
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L220-L259


# Vulnerability details

The `propose()` function on `NounsDAOV3Proposals.sol` has a check called `checkNoActiveProp(ds, msg.sender)`, which exists to prevent token holders from spamming the propose function.  Furthermore, the `proposeOnTimelockV1()` function calls `propose()` directly, making it susceptible, and `proposeBySigs()` makes an internal call to `verifySignersCanBackThisProposalAndCountTheirVotes`, which also calls `checkNoActiveProp(ds, msg.sender)`.  Likewise, the `propose()` function on `NounsDAOLogicV1Fork.sol` has this check which is put in place to also prevent spamming the DAO with proposals:
```
temp.latestProposalId = latestProposalIds[msg.sender];
        if (temp.latestProposalId != 0) {
            ProposalState proposersLatestProposalState = state(temp.latestProposalId);
            require(
                proposersLatestProposalState != ProposalState.Active,
                'NounsDAO::propose: one live proposal per proposer, found an already active proposal'
            );
            require(
                proposersLatestProposalState != ProposalState.Pending,
                'NounsDAO::propose: one live proposal per proposer, found an already pending proposal'
            );
        }
```
However, this is easily bypassed by a tokenholder if they create a proposal, transfer their token to another wallet, wait at least one block to satisfy the `ds.nouns.getPriorVotes(msg.sender, block.number - 1)`, and then create another proposal.  Being as how a new block is created about every 12 seconds, a user can ‘slow spam’ the propose function since there is no cap on the amount of existing proposals at any given time.

## Impact
Since a malicious tokenholder can create as many proposals as they want as long as they wait until the next block, the sheer amount of spammed proposals can grief the members of the DAO, possibly even preventing them from voting on valid proposals because they would have
Vetoer can veto a non-existent proposal
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L536-L564


# Vulnerability details

## Impact
Vetoer is allowed to veto an invalid proposalId, which would prevent that proposal from getting votes, queued or executed.

## Proof of Concept
Here is the veto function:
```solidity
    function veto(NounsDAOStorageV3.StorageV3 storage ds, uint256 proposalId) external {
        if (ds.vetoer == address(0)) {
            revert VetoerBurned();
        }

        if (msg.sender != ds.vetoer) {
            revert VetoerOnly();
        }

        if (stateInternal(ds, proposalId) == NounsDAOStorageV3.ProposalState.Executed) {
            revert CantVetoExecutedProposal();
        }

        NounsDAOStorageV3.Proposal storage proposal = ds._proposals[proposalId];

        proposal.vetoed = true;
        INounsDAOExecutor timelock = getProposalTimelock(ds, proposal);
        for (uint256 i = 0; i < proposal.targets.length; i++) {
            timelock.cancelTransaction(
                proposal.targets[i],
                proposal.values[i],
                proposal.signatures[i],
                proposal.calldatas[i],
                proposal.eta
            );
        }

        emit ProposalVetoed(proposalId);
    }
```
As we can see, the only check done on the proposal is that the proposal must not have been executed.
If the vetoer calls veto with a future proposalId(either mistakenly or deliberately), the status of that proposal will be set to "Vetoed". And when an unlucky proposer creates a proposal which gets assigned the id which vetoer has vetoed, that proposal will not be able to gain votes, get queued or get executed.
So if the proposer created the proposal using `proposeBySigs`, those signatures will now be useless, and proposer would have to obtain new signatures from signers, which is not very user-friendly.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Don't allow vetoer to veto a proposal whose proposer is 0 address.


## Assessed type

Invalid Validation