xanoutas/solidity-security-findings huggingface dataset:
payable.transfer() call will result in loss of unused Ether
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
```batchContribute``` function carries out funding and transfer in batches by using the payable.transfer() call.
This is unsafe as transfer has hard coded gas budget and can fail when the user is a smart contract. This way programmatical usage of ```batchContribute``` is at risk.
Whenever the user either fails to implement the payable fallback function or cumulative gas cost of the function sequence invoked on a native token transfer exceeds 2300 gas consumption limit the native tokens sent end up undelivered and the corresponding user funds return functionality will fail each time.
Any unused fund will then not be sent to the user leading to direct loss.

## Proof of Concept
InitialETHCrowdfund.sol have ```batchContribute``` function that calls native token payable.transfer

```
    function batchContribute(
        BatchContributeArgs calldata args
    ) external payable onlyDelegateCall returns (uint96[] memory votingPowers) {
        uint256 numContributions = args.tokenIds.length;
        votingPowers = new uint96[](numContributions);


        uint256 ethAvailable = msg.value;
        for (uint256 i; i < numContributions; ++i) {
            ethAvailable -= args.values[i];


            votingPowers[i] = _contribute(
                payable(msg.sender),
                args.delegate,
                args.values[i],
                args.tokenIds[i],
                args.gateDatas[i]
            );
        }


        // Refund any unused ETH.
        if (ethAvailable > 0) payable(msg.sender).transfer(ethAvailable);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
The recommendation is made to use low-level call.value(amount) with the corresponding result check.
```diff
    function batchContribute(
        BatchContributeArgs calldata args
    ) external payable onlyDelegateCall returns (uint96[] memory votingPowers) {
        uint256 numContributions = args.tokenIds.length;
        votingPowers = new uint96[](numContributions);


        uint256 ethAvailable = msg.value;
        for (uint256 i; i < numContributions; ++i) {
            ethAvailable -= args.values[i];


            votingPowers[i] = _contribute(
                payable(msg.sender),
                args.delegate,
                args.values[i],
                args.tokenIds[i],
                args.gateDatas[i]
            );
        }


        // Refund any unused ETH.
-        if (ethAvailable > 0) payable(msg.sender).transfer(ethAvailable);
+        if (ethAvailable > 0) (msg.sender).call{value: ethAvailable}('');
    }
```



## Assessed type

ETH-Transfer
funds stuck in crowdfund
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/ETHCrowdfundBase.sol#L196-L273
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/ETHCrowdfundBase.sol#L317-L336
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/ETHCrowdfundBase.sol#L339-L359


# Vulnerability details


## Vulnerability details:
### Details:

in `ETHCrowdfundBase` the crowdfund deployer can choose to implement fee's that are given to a feeAdress with a certain percentage of the crowdfund ETH. the fee splitting is happening both in `_finalize`:
```solidity=317
function _finalize(uint96 totalContributions_) internal {
        // Finalize the crowdfund.
        delete expiry;

        // Transfer funding split to recipient if applicable.
        address payable fundingSplitRecipient_ = fundingSplitRecipient;
        uint16 fundingSplitBps_ = fundingSplitBps;
        if (fundingSplitRecipient_ != address(0) && fundingSplitBps_ > 0) {
            totalContributions_ -= (totalContributions_ * fundingSplitBps_) / 1e4;
        }

        // Update the party's total voting power.
        uint96 newVotingPower = (totalContributions_ * exchangeRateBps) / 1e4;
        party.increaseTotalVotingPower(newVotingPower);

        // Transfer ETH to the party.
        payable(address(party)).transferEth(totalContributions_);

        emit Finalized();
    }
```
and in `_processContribution`:
```solidity=263
if (fundingSplitRecipient_ != address(0) && fundingSplitBps_ > 0) {
            // Removes funding split from contribution amount in a way that
            // avoids rounding errors for very small contributions <1e4 wei.
            amount = (amount * (1e4 - fundingSplitBps_)) / 1e4;
        }
```

which is a problem because the fees are basically taken twice. but also the function `sendFundingSplit` which is responsible for taking the fee split only takes half of the fees: 

```solidity=339
function sendFundingSplit() external returns (uint96 splitAmount) {
        // Check that the crowdfund is finalized.
        CrowdfundLifecycle lc = getCrowdfundLifecycle();
        if (lc != CrowdfundLifecycle.Finalized) revert WrongLifecycleError(lc);

        if (fundingSplitPaid) revert FundingSplitAlreadyPaidError();

        address payable fundingSplitRecipient_ = fundingSplitRecipient;
        uint16 fundingSplitBps_ = fundingSplitBps;
        if (fundingSplitRecipient_ == address(0) || fundingSplitBps_ == 0) {
            revert FundingSplitNotConfiguredError();
        }

        fundingSplitPaid = true;

        // Transfer funding split to recipient.
        splitAmount = (totalContributions * fundingSplitBps_) / 1e4;//@audit-issue doesn't take into account the fees taken from deposits -> funds stuck = total fees, can be saved by dao, but if disabled then it's stuck
        payable(fundingSplitRecipien
decreaseTotalVotingPower - If totalVotingPower becomes less than mintedVotingPower, the protocol crashes(even can lose assets)
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L255-L258
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L393
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L404
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L157
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L1134
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L1115-L1119


# Vulnerability details

## Impact

If `totalVotingPower < mintedVotingPower`, the contract would loses tokens deposited in the contract, the Authority can stop `rageQuit` (Authority doesn’t have permission to do this), and the protocol stops functioning properly.

## Proof of Concept

The `decreaseTotalVotingPower` function does not check if the decreased `totalVotingPower` is less than the `mintedVotingPower`. This can cause a number of problems.

```solidity
function decreaseTotalVotingPower(uint96 votingPower) external {
    _assertAuthority();
@>  _getSharedProposalStorage().governanceValues.totalVotingPower -= votingPower;
}
```

1. Taking more tokens when running `rageQuit` 
    1. User can take more tokens than the percentage of votingPower user actually have. 
    2. In the worst case, user can remove all the tokens deposited into the contract. (In an extreme example, if Authority call `decreaseTotalVotingPower` to go from totalVotingPower : 100, mintedVotingPower: 50 to totalVotingPower : 25, mintedVotingPower: 50, user can take the entire amount with only 25 voting power.)
    
    ```solidity
    function rageQuit(
            uint256[] calldata tokenIds,
            IERC20[] calldata withdrawTokens,
            uint256[] calldata minWithdrawAmounts,
            address receiver
        ) external {
        ...
        uint256[] memory withdrawAmounts = new uint256[](withdrawTokens.length);
        {
            IERC20 prevToken;
            for (uint256 i; i < withdrawTokens.length; ++i) {
                // Check if order of tokens to transfer is valid.
                // Prevent null and duplicate transfers.
                if (prevToken >= withdrawTokens[i]) revert InvalidTokenOrderError();
    
                prevToken = withdrawTokens[i];
    
                // Check token's balance.
                uint256 balance = address(withdrawTokens[i]) == ETH_ADDRESS
                    ? address(this).balance
                    : withdrawTokens[i].balanceOf(address(this));
    
                // Add fair share of tokens from the party to total.
                for (uint256 j; j < tokenIds.length; ++j) {
Host(s) + majority can steal assets of minority+other hosts without any option for minority(+otherhosts) to ragequit.
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L602
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L1097-L1100


# Vulnerability details

## Impact
The ragequit functionality is put in place to protect the rights of the minority, who are not in support of a proposal, and allow them to withdraw their share of assets and exit the party.
In some cases, minority will not have the opportunity to ragequit when a proposal will cheat them

## Proof of Concept
Before now, if majority accept a proposal, and the proposal passes, there would be a delay before execution during which host can veto the proposal. During this period, minority can ragequit from the party if proposal does not favour them.

Current changes allow veto period to be skipped once all hosts accept a proposal, but this also means that minority will not have the opportunity to rageQuit if they are not in support of the proposal.

```solidity
function _getProposalStatus(
    ProposalStateValues memory pv
) private view returns (ProposalStatus status) {
    ...
    if (_hostsAccepted(pv.numHosts, pv.numHostsAccepted)) {      
        return ProposalStatus.Ready;
    }
    ...
}
```

This allows host(s) + majority to cheat minority.

In addition, as was reported in another issue, all hosts may not accept a proposal, but a single host can maliciously increase the numHostsAccepted count for a proposal. So this means that: "A SINGLE HOST + MAJORITY CAN EXECUTE A PROPOSAL, GIVING NO CHANCE FOR MINORITY+OTHER HOSTS TO RAGEQUIT"

## Tools Used
Manual Review

## Recommended Mitigation Steps
Even when all hosts accept a proposal, let there be a brief delay to allow minority to ragequit


## Assessed type

Context
Vulnerability in Token Withdrawal Function
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

Flawed logic in token withdrawal function allows for selective withdrawal of high-value tokens and fails in single-token scenarios.

```
// Sum up total amount of each token to withdraw.
uint256[] memory withdrawAmounts = new uint256[](withdrawTokens.length);
{
    IERC20 prevToken;
    for (uint256 i; i < withdrawTokens.length; ++i) {
        // Check if order of tokens to transfer is valid.
        // Prevent null and duplicate transfers.
        if (prevToken >= withdrawTokens[i]) revert InvalidTokenOrderError();
        ...
    }
}

```

Specific Concerns:

Selective High-Value Withdrawals: Users can exploit the function to repeatedly withdraw only high-value tokens, which could lead to unfair distribution.

Single-Token Scenario Failure: In cases where only one type of reward token is present, the function's validation logic (if (prevToken >= withdrawTokens[i])) incorrectly prevents successful execution.


## Proof of Concept
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L381
## Tools Used

manual

## Recommended Mitigation Steps


Implement a mechanism to ensure fair and equitable withdrawal of tokens, possibly by randomizing the order or enforcing a proportional withdrawal across all token types.
Adjust the validation logic to accommodate scenarios with a single token type. This could involve modifying or bypassing the order check when only one token type is present.


## Assessed type

Token-Transfer
A single host can approve a proposal for all other hosts in the Party
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Summary

A single host can approve a proposal by transferring the host role to dummy accounts and voting again to increment the number of approvals.

## Impact

When a proposal is created in a Party, the number of active hosts is snapshotted in the proposal state values (`numHosts`). This structure also contains a counter with the number of hosts that have accepted the proposal (`numHostsAccepted`).

https://github.com/code-423n4/2023-10-party/blob/0ce3819de173f7688c9c834ce2cc758dd03c9bd2/contracts/party/PartyGovernance.sol#L553-L573

```solidity
553:     function propose(
554:         Proposal memory proposal,
555:         uint256 latestSnapIndex
556:     ) external returns (uint256 proposalId) {
557:         _assertActiveMember();
558:         proposalId = ++lastProposalId;
559:         // Store the time the proposal was created and the proposal hash.
560:         (
561:             _proposalStateByProposalId[proposalId].values,
562:             _proposalStateByProposalId[proposalId].hash
563:         ) = (
564:             ProposalStateValues({
565:                 proposedTime: uint40(block.timestamp),
566:                 passedTime: 0,
567:                 executedTime: 0,
568:                 completedTime: 0,
569:                 votes: 0,
570:                 totalVotingPower: _getSharedProposalStorage().governanceValues.totalVotingPower,
571:                 numHosts: numHosts,
572:                 numHostsAccepted: 0
573:             }),
```

Each time a host votes for a proposal using `accept()`, this counter is incremented to reflect the state in the proposal values:

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

```solidity
636:         if (isHost[msg.sender]) {
637:             ++values.numHostsAccepted;
638:         }
```

The implementation simply checks if the caller account is a current host. A malicious host can transfer the _host_ feature into another account of his own using `abdicateHost()` and vote again for the proposal, effectively incrementing this counter again.

Any host can repeat this process to account for all the votes of the other hosts. Doing so will enable the `PROPOSAL_FLAG_HOSTS_ACCEPT` flag in the proposal, and transition the proposal status into the `Ready` state, skipping the execution delay and the veto period, and making it ready to be executed.

## Proof of Concept

In the following test, a Party is created with 3 different host accounts. After simulating a passing proposal, the `host1` account votes 3 times by transferring the host role to other dummy accounts using `abdicateHost()`, making the proposal to be in a `Ready` state.

Note: the snippet shows only the relevant code for the test. Full test file can be found [here](https://g
No validation for the partyFactory contract ! 
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Malicious party contract can be deployed ! 

## Proof of Concept
Party contracts are created by the proxyFactory contract . 
In the `initialize` function , the proxyFactory contract address is provided by the user which may bring catastrophic consequences . 

This creates opportunity for the initializer to input a malicious partyFactory address and eventually malicious parties can be deployed . Thus contributors who contributed in that malicious  party may loss all of thier funds . 

```solidity 
    function initialize(
        InitialETHCrowdfundOptions memory crowdfundOpts,
        ETHPartyOptions memory partyOpts,
        bytes memory customMetadata
    ) external payable onlyInitialize {
        // Create party the initial crowdfund will be for.
@>      Party party_ = _createParty(partyOpts, customMetadataProvider, customMetadata); //@audit partyFactory contract address is provided by the initializer . 
 //rest of the code ..
```
`_createParty` function :
```solidity 
   function _createParty(
        ETHPartyOptions memory opts,
       //...
    ) private returns (Party) {
       //...code 
        }
        authorities[authoritiesLength - 1] = address(this);

        if (address(customMetadataProvider) == address(0)) {
            return
@>              opts.governanceOpts.partyFactory.createParty( //@audit---> provided partyFactory address is being used without any validation ! 
                    opts.governanceOpts.partyImpl,
                    authorities,
//Rest of the code 
```
This is concerning because there's no easy  way for a regular contributor to actually know if the party contract file have some malicious code because of the malicious creation . 

## Tools Used
Manual review 
## Recommended Mitigation Steps

Validate the partyFactory address provided is correct or hardcode the partyFactory address to ensure security .


## Assessed type

Invalid Validation
All contributions can get locked in the Crowdfund contract, with no means to refund users or finalize.
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
ETHCrowdfundBase#_finalize assumes that all contributions are intact, but there is an emergencyExecute function that is capable of spending part of crowdfund eth. If as little as one wei gets spent, Users cannot get refunded, and the Crowdfund will not get finalized, leading to locking of all contributions. The contract also lacks a receive() callback, which prevents direct transfer of eth to the contract.

## Proof of Concept
There is an [`emergencyExecute`](https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/ETHCrowdfundBase.sol#L379) in ETHCrowdfundBase which allows party DAO to execute an arbitrary function with native value from the contract.

If the DAO spends any amount (as little as 1 wei) from the contract, the address(this).balance will become lower than `totalContributions`.

So `ETHCrowdfundBase#_finalize` will fail because it tries to [transfer](https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/ETHCrowdfundBase.sol#L333) `totalContributions`, which is greater than the contract's balance.

This means that all the contributions will be forever locked in the contract, as there is also no receive() fallback that can be used to directly send the amount spent to make `finalize` possible.


## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider implementing any of these:
- ETHCrowdfundBase should have a receive() callback
- Instead of transferring totalContributions, finalize should transfer `address(this).balance` or `min(totalContributions,address(this).balance)`


## Assessed type

ETH-Transfer
Users can lose eth when contributing. 
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
When contributing in InitialETHCrowdfund, if `mint`ing adjusts the voting power for that user to cap it at totalVotingPower, the user does not get refunded the eth he lost.

## Proof of Concept
PartyGovernanceNFT#mint caps the mintedVotingPower at totalVotingPower. So if votingPowerToMint+alreadyMintedVotingPower>totalVotingPower, votingPowerToMint gets adjusted to totalVotingPower-alreadyMintedVotingPower:

```solidity
function mint(
    address owner,
    uint256 votingPower,
    address delegate
) external returns (uint256 tokenId) {
    _assertAuthority();
    uint96 mintedVotingPower_ = mintedVotingPower;
    uint96 totalVotingPower = _getSharedProposalStorage()
        .governanceValues
        .totalVotingPower;

    // Cap voting power to remaining unminted voting power supply.
    uint96 votingPower_ = votingPower.safeCastUint256ToUint96();
    // 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_; 
        }
    }
    ...
}
```

Looking at [InitialETHCrowdfund#_contribute](https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/InitialETHCrowdfund.sol#L275) internal function, we can see that the contributor does not get refunded when his votingPower gets reduced within `party.mint(contributor, votingPower, delegate);`
This could lead to significant loss of funds for the contributor

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider implementing the following:
- PartyGovernanceNFT#mint should return(uint256 tokenId, uint256 votingPower). 
- Within InitialETHCrowdfund#_contribute, if votingPower returned from calling party#mint is less than votingPower returned from _processContribution, `convertVotingPowerToContribution(delta_change_in_votingPower)` should be refunded to the contributor


## Assessed type

Other