xanoutas/solidity-security-findings huggingface dataset:
A single host can vote on a proposal, then abdicate to another address and vote again to wrongly increase numHostsAccepted, and make a proposal ready for execution against the desire of the other hosts
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L465
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L636-L638


# Vulnerability details

## Impact
Protocol's intended functionality is: If all hosts accept a passed proposal, then veto period for that proposal will be skipped, and Proposal will immediately be "Ready" for execution.
Note that the requirement is "ALL HOSTS accept a proposal", but a single host can maliciously increase the numHostsAccepted count, and make it look like all hosts actually accepted the proposal, which will make the proposal ready for execution against the intention of the other hosts.
This allows malicious proposals to skip veto period and immediately become ready for execution, which may negatively affect other hosts and the Party in general

## Proof of Concept
Looking at [LINE 636](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L636-L638) of PartyGovernance#accept function, we can see that if the caller is a host, the numHostsAccepted count is incremented. [this line](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L627-L629) prevents the host from voting twice on a proposal.

The problem is, the caller, who is a host can call [PartyGovernance#abdicateHost](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L457) to transfer host status to another address, and then vote again with that address, which would increment the numHostsAccepted count. He can repeat this `numHosts` times so that [this check](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L1098) passes, and [_getProposalStatus](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L602) returns "Ready", which will allow execution of the proposal against the intention of the other hosts

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider implementing the following:
Snapshot the Hosts status each time abdicateHost is called, and instead of [checking](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L636) the current host status of msg.sender, the snapshotted host status as at proposalCreationTime should be used.


## Assessed type

Invalid Validation
Fund Drainage Due to  Reentrancy Vulnerability in Crowdfunding 
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/InitialETHCrowdfund.sol#L328-L344
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L263-L307


# Vulnerability details

## Impact
in the refund function, there is a problem, this function is reads the isAuthority state variable from the PartyGovernanceNFT contract after making an external call to the burn function of the same contract.
The burn function in the PartyGovernanceNFT contract is called from the refund function in the InitialETHCrowdfund contract. After the burn function is called, the InitialETHCrowdfund contract reads the votingPowerByTokenId mapping from the PartyGovernanceNFT contract. This creates a reentrancy vulnerability because the burn function could potentially call back into the refund function before the state variable votingPowerByTokenId is updated :
- here is the part https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/InitialETHCrowdfund.sol#L328C1-L344C1 :
```solidity
        uint96 votingPower = party.votingPowerByTokenId(tokenId).safeCastUint256ToUint96();
        amount = convertVotingPowerToContribution(votingPower);

        if (amount > 0) {
            // Get contributor to refund.
            address payable contributor = payable(party.ownerOf(tokenId));

            // Burn contributor's party card.
            party.burn(tokenId);

            // Refund contributor.
            contributor.transferEth(amount);

            emit Refunded(contributor, tokenId, amount);
        }
    }

```
- and here is  the corresponding code from PartyGovernanceNFT contract : https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L263C1-L307C9
```solidity
   function burn(uint256[] memory tokenIds) public {
        _assertAuthority();
        _burnAndUpdateVotingPower(tokenIds, false);
    }

    function _burnAndUpdateVotingPower(
        uint256[] memory tokenIds,
        bool checkIfAuthorizedToBurn
    ) private returns (uint96 totalVotingPowerBurned) {
        for (uint256 i; i < tokenIds.length; ++i) {
            uint256 tokenId = tokenIds[i];
            address owner = ownerOf(tokenId);

            // Check if caller is authorized to burn the token.
            if (checkIfAuthorizedToBurn) {
                if (
                    msg.sender != owner &&
                    getApproved[tokenId] != msg.sender &&
                    !isApprovedForAll[owner][msg.sender]
                ) {
                    revert NotAuthorized();
                }
            }

            // Must be retrieved before updating voting power for token to be burned.
            uint96 votingPower = votingPowerByTokenId[tokenId].safeCastUint256ToUint96();

            totalVotingPowerBurned += votingPow
Incorrectly set totalVotingPower can allow users to pass arbitrary Proposals and steal all the parties NFTs
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L255


# Vulnerability details

# Bug Description
The recent implementation update empowers the authority to decrease the `totalVotingPower` arbitrarily using the `decreaseTotalVotingPower()` function. This authority-exclusive function allows the reduction of `totalVotingPower` by a specified amount.

One of the main invariants of the governance logic is, that at any time, besides the minting of party cards for the initial crowdfund, `totalVotingPower >= mintedVotingPower`.  This is done because the decision if a vote has passed is done based upon (simplified) dividing the amount of votes by the `totalVotingPower` and checking if that percentage is above the percentage set as the voting threshold. So if `totalVotingPower` exceeds `mintedVotingPower` it leads to the voting system not working as intended. 

The issue arises as the `decreaseTotalVotingPower()` function lacks a check to prevent the new `totalVotingPower` from falling below `mintedVotingPower`. If the authority inadvertently decreases `totalVotingPower` too much, users or groups without majority voting power could pass proposals. If the `totalVotingPower` is reduced to a value less than the voting power of a single user or group, they could unanimously pass proposals, bypassing the veto period where hosts could otherwise prevent the proposal execution.

# Impact

## Majority
If the authority unintentionally reduces `totalVotingPower` below `mintedVotingPower`, users or groups lacking sufficient votes to pass a proposal gain the ability to pass proposals at will. This allows malicious users to perform various harmful actions, such as adding themselves as authorities through the `AddAuthorityProposal` and subsequently using the `burn()` function to remove all other users from the party. This could lead to the unauthorized sale of NFTs and the misappropriation of party funds.

It's worth noting that the execution delay provides a safeguard, allowing hosts to cancel a malicious proposal before execution.

## Unanimous Passing
If the `totalVotingPower`  gets decreased so far that it falls below the voting power of a single user/group, proposal can get unanimously passed due to the [check](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L1111-L1120) for `(totalVotes * 1e4) / totalVotingPower >= 0.9999e4` in the function `_isUnanimousVotes()`. This leads to the proposal ignoring the veto period and getting executed directly, without needing a single host to have accepted the proposal. 

If, through his vulnerability, a user is able to unanimously pass proposals, he can steal all NFT's of the party using a `ArbitraryCallsProposal`. The `ArbitraryCallsProposal` actually implements a safeguard against it being used to transfer NFT's, by a majority vote. This is done by checking at the [start](https://github.com/code-423n4/2023-10-party/blob/main
`totalVotingPower` is not being decreased in burn function in `PartyGovernanceNFT`.
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L263-L266


# Vulnerability details

## Impact
In PartyGovernanceNFT.burn function, `_burnAndUpdateVotingPower` is being used which will reduce the votingPower from the burned nft and the `mintedVotingPower`. The `totalVotingPower` should be explicitly decreased in burn function (which was missing here).
Similar to the `rageQuit` function, where `totalVotingPowerBurned` is being removed from the `totalVotingPower`.
Link of rageQuit: https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L404

## Recommended Mitigation Steps
The burnedVotingPower amount from the `_burnAndUpdateVotingPower` function, should be decreased from the `totalVotingPower` in `burn` function explicitly.


## Assessed type

Math
Users of ReraiseCrowdfund will potentially not receive appropriate voting power
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L255


# Vulnerability details

# Bug Description

The recent code update introduces the functionality for authorities to reduce the total voting power by invoking the `decreaseTotalVotingPower()` function of the party. However, this functionality can lead to issues when used in the time frame after a `ReraiseETHCrowdfund` is finalized, but users have not yet claimed their voting power. The problem arises if the total voting power gets decreased after being increased during the finalization of the `reraiseCrowdfund`, and users attempt to claim voting power for one of their existing NFTs. To claim their voting power the users will call the `claim()` function which will then subsequently call to `increaseVotingPower()`. In such cases, the capping of `mintedVotingPower` to `totalVotingPower` is utilized, leading to users receiving less voting power than they paid for.

The issue is rooted in the check within the `increaseVotingPower()` function:
```solidity
// Cap voting power to remaining unminted voting power supply. Allow
// minting past total voting power if minting party cards for initial
// crowdfund when there is no total voting power.
if (totalVotingPower != 0 && totalVotingPower - mintedVotingPower_ < votingPower) {
	unchecked {
		votingPower = totalVotingPower - mintedVotingPower_;
	}
}
```

In this case the user will only receive the difference between `totalVotingPower` and `mintedVotingPower` instead of the actual voting power he payed for. This can be very close to the amount that the user paid for, or in worst case 0 if the `totalVotingPower`got reduced to the `mintedVotingPower.

# Impact
In the edge case where, between finalization of a `reraiseCrowdfund` and a user claiming their voting power, an authority calls `decreaseTotalVotingPower()`, the user will receive less voting power than they paid for. The user will also not get a refund for the voting power they have not received, as it would be handled in other cases where a user receives less voting power than they paid for. Additionally, the user will not be able to claim their missing voting power later on, as their claim fully expires. This results in a loss of user funds.

# Proof of Concept
The provided proof of concept (POC) demonstrates a scenario where a user contributes to a `ReraiseETHCrowdfund` but ultimately receives less voting power than intended. The outline of the test case is:
1. Party gets generated
2. User gets a NFT to simulate him already donating in the initial crowdfund
3. A `ReraiseETHCrowdfund` gets started
4. The user deposits the full amount
5. The crowdfund gets finalized
6. An authority decreases the `totalVotingPower`
7. The user tries to claim his voting power, but receives less than he paid for.

<details>

```solidity
function test_claim_afterDecreasedTotalVotingPower() public {
	//------------ SETUP START ------------//
	ReraiseETHCrowdfun
wrong check in `batchContributeFor ` always leads to revert and loose of ETH
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/InitialETHCrowdfund.sol#L255-L273


# Vulnerability details


## Bug Description
The batchContributeFor function allows a caller to contribute to a number of recipients (or tokenIds) on behalf of the owner of tokenIds. This can happen by having a for loop which calls _contribute each time for a specific address. However, in the loop, we can see that we set the values[i] each iteration to valuesSum (valuesSum += args.values[i]), and then we have a check to make sure the msg.value the caller provides is equal to the sum of values. If not, then the call reverts. The issue here is that msg.value never or most of the time will not be equal to the sum of the values because of the gas costs of the transaction. This is due to using msg.value to update the values[i], which leads to a revert and loss of the ETH that the caller provides.

## Impact
In the function [batchContributeFor](https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/InitialETHCrowdfund.sol#L255-L273), we can see that this function uses the msg.value that the caller provides to update the contribute amount/value of the recipients. Then it checks if `(msg.value != valuesSum)`, which will always revert because msg.value is decreased. This is because we set it as the value for the recipient plus the gas costs of each iteration, it decreases. Additionally, it is not saved in a variable at the first line when we call the `batchContributeFor` function to save it.

## Proof of Concept
f we take a look at the test in intialEthCrowdFund.t.sol, we can see that the protocol expects that the balance of the caller will decrease:

```solidity
 function test_batchContributeFor_works() public {
        //first thing create a party
        InitialETHCrowdfund crowdfund = _createCrowdfund(
            CreateCrowdfundArgs({
                initialContribution: 0,
                initialContributor: payable(address(0)),
                initialDelegate: address(0),
                minContributions: 1 ether,
                maxContributions: type(uint96).max,
                disableContributingForExistingCard: false,
                minTotalContributions: 3 ether,
                maxTotalContributions: 5 ether,
                duration: 7 days,
                exchangeRateBps: 1e4,
                fundingSplitBps: 0,
                fundingSplitRecipient: payable(address(0)),
                gateKeeper: IGateKeeper(address(0)),
                gateKeeperId: bytes12(0)
            })
        );
        Party party = crowdfund.party();
        // get random address
        address sender = _randomAddress();
        //set the balance of the sender to 4 ether
        vm.deal(sender, 4 ether);

        // Batch contribute for
        //@0xkazim  here we set each values[i] to 1 eth
        vm.prank(sender);
        //get and intial th
Increasing of totalVotingPower during crowdfund can lead to users losing funds.
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L208


# Vulnerability details

# Bug Description
The recent code update allows authorities to be declared at the start of the crowdfunding phase. This change allows authority-restricted functions to be called during the crowdfunding phase by the authorities added in the party constructor. However, this leads to an issue where a user does not receive the appropriate voting power corresponding to their contributed funds.

The problem arises because an authority can set the `totalVotingPower` variable during the crowdfunding phase using `increaseTotalVotingPower()`. If this value is set to a value lower than `maxTotalContributions`, it can result in users losing funds

To understand this issue, we must consider how users gain voting power when they already have a card and wish to deposit more. The process involves the following steps:

1. User calls `contribute()` with the `tokenId` of their NFT.
2. contribute()` invokes the internal function `_contribute()`
3. `_contribute()` checks if the deposit exceeds the minimum and maximum contributions.
4. The function then calls `party.increaseVotingPower()` to increase the voting power of the user's NFT.
5. As the newly minted voting power can exceed the total voting power, the actual voting power minted is capped to the difference.

```solidity
if (totalVotingPower != 0 && totalVotingPower - mintedVotingPower_ < votingPower) {
	unchecked {
		votingPower = totalVotingPower - mintedVotingPower_;
	}
}
```

This functionality itself is acceptable because an admin may need to set the `totalVotingPower` during this period. However, in such cases, the user who receives less voting power than they paid for should receive back the difference.

# Impact
If the described situation occurs, users will not receive the amount of voting power they paid for, and they will not receive the extra funds back. This leads to users effectively losing funds.

# Proof of Concept
The provided proof of concept (POC) demonstrates an example where a user loses half an ether due to the described change. The POC shows how a user, despite fully complying with the system, loses funds due to this issue.

<details>

```solidity
function test_contribute_increaseVotingPowerToExistingCardCapped() public {
    //initialize the crowdfund with us as authority
    crowdfundOpts.initialContributor = payable(address(0));
    crowdfundOpts.initialDelegate = payable(address(0));
    crowdfundOpts.minContribution = 0;
    crowdfundOpts.maxContribution = type(uint96).max;
    crowdfundOpts.disableContributingForExistingCard = false;
    crowdfundOpts.minTotalContributions = 3 ether;
    crowdfundOpts.maxTotalContributions = 5 ether;
    crowdfundOpts.duration = 7 days;
    crowdfundOpts.exchangeRateBps = 1e4;
    crowdfundOpts.fundingSplitBps = 0;
    crowdfundOpts.fundingSplitRecipient = payable(address(0));
    crowdfundOpts.gateKeeper = IGat
DoS in Providing Voting power after contribution due to minting based on totalVotingPower in PartyGovernanceNFT.increaseVotingPower
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/ETHCrowdfundBase.sol#L330


# Vulnerability details

## Impact
The `PartyGovernanceNFT` contract was designed in such a way that it wont mint, increaseVotingPower if totalMint (mintedVotingPower) wont cross the `totalVotingPower`, if crossing only the difference between them (totalVotingPower - mintedVotingPower) will be given instead. You can observe that on Links (1, 2)
Initially the `totalVotingPower` was 0, so for minting new tokens `mint`, or for increasing the voting power for existing token `increaseVotingPower`, the `totalVotingPower` should be increased first so the new power will be minted. 
When we look at `ETHCrowdfundBase` (Link 3), the totalVotingPower is being updated in the finalize function using the `party.increaseTotalVotingPower` function. 
Whereas the minting is being held at the `_contribute` function in `InitialETHCrowdfund` (Link 4). Since the finalize won't happen (executed) at the time, `totalVotingPower` will be 0. 
Which will result in 0 power minting for the contributors. 

It leads to improper access distribution to the contributors.

## Links 
1) https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L179-L183
2) https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L216-L220
3) https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/ETHCrowdfundBase.sol#L330
4) https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/InitialETHCrowdfund.sol#L302-L307 

## Recommended Mitigation Steps
`party.increaseTotalVotingPower` should be done at `_contribute` function, or else change the 


## Assessed type

Access Control
A malicious contributor can increase voting power maliciously and eventually steal funds!  
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/InitialETHCrowdfund.sol#L194


# Vulnerability details

## Impact
Unlimited voting power for attacker and stealing of funds !
## Proof of Concept
All of the contribute functions uses `msg.value` to calculate the votingpower . For example , `contribute` function looks like this :  
```solidity 
 function contribute(
        uint256 tokenId,
        address delegate,
        bytes memory gateData
    ) public payable onlyDelegateCall returns (uint96 votingPower) {
        return
            _contribute(
                payable(msg.sender),
                delegate,
                msg.value.safeCastUint256ToUint96(), //@audit using msg.value for accounting ! 
                tokenId, 
                gateData
            );
    }
```
Using msg.value for accounting imposes a significant security risk . While delegatecalling from a payable multicall function  , given msg.value is looped throughout the calls without actually providing eth !

```solidity 
 contract Multicall {
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = CrowdFundAddress.delegatecall(data[i]);

            if (!success) {
              revert();
            }
            results[i] = result;
        }
    }
}
```
An exploit scenario could be this : 

The core idea behind this exploit is using msg.value inside a loop.  
Exploiter will  use a payable multicall function to delegatecall  `contribute` multiple times in a single call.  
For the first subcall  `contribute` function works perfectly without any issue. 
But in the second subcall,  msg.value will still be the same as before but no Eth is actually sent .  But  `contribute` function will execute without any issue and increase the contributors voting power as it uses msg.value for accounting purpose and doesnot check if the asset was actually provided or not. 
Afterwards, every subcall will execute  without issue and increase voting power of the contibutor like the second subcall .  

Impact: 
1. If a crowdfund is lost,  exploiter will be able to refund all of the assets before anyone refunds with his increased voting power. And other legitimate contributors will fail to refund as there's not enough asset left in the contract.  
2. If a crowdfund still wins and finalizes somehow , the exploiter gets the votingpower 
## Tools Used
manual review ! 
Inspired by The $6M [Aurora Bug ](https://medium.com/immunefi/aurora-infinite-spend-bugfix-review-6m-payout-e635d24273d)
## Recommended Mitigation Steps
Implement a book keeping balance check Lock to ensure that eth is actually paid . 


## Assessed type

ETH-Transfer