xanoutas/solidity-security-findings huggingface dataset:
In the _initialize function of the ETHCrowdfundBase contract, when minTotalContributions is equal to maxTotalContributions, crowdfund will never reach its minimum goal in some specific scenarios
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/ETHCrowdfundBase.sol#L148-L150
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/ETHCrowdfundBase.sol#L256-L258


# Vulnerability details

## Impact
In the `_initialize` function of the `ETHCrowdfundBase` contract, when `minTotalContributions` is equal to `maxTotalContributions`, crowdfund will never reach its minimum goal in some specific scenarios. The `ETH` of users who contribute to this crowdfund will be locked in the contract. They can only call the `refund` function to retrieve their `ETH` after the expiration time.

## Proof of Concept
Suppose there is a new `Party`. During initialization, set the value of `minTotalContributions` equal to `maxTotalContributions`. The specific parameters are assumed to be:

Mintotalcontributions=10 eth
Maxtotalcontributions=10 eth
Mincontribution=2 eth
Maxcontribution=3 eth
Expiry=30 days

Now consider the following scenarios as examples:
1. three users have contributed 3 eth, so the current totalContributions=9 eth.
2. at this time, suppose Alice tries to contribute eth. Because maxTotalContributions=10 eth, he can contribute at most 1 eth, but minContribution=2 eth, so Alice can never complete the contribution. In this code segment, revert will always occur!

```solidity
if (amount < minContribution_) {
    revert BelowMinimumContributionsError(amount, minContribution_);
}
```
Cod link: https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/ETHCrowdfundBase.sol#L256-L258

This crowdfund will never reach 10eth means that the 9eth of the first three users will be locked in the contract for 30 days. Even the host cannot manually call finalize to end this crowdfund.



## Tools Used
Visual Studio Code
Foundry 

## Recommended Mitigation Steps
Optimize the code in line 148 of the `_initialize` function of the `ETHCrowdfundBase` contract, and use `>=` to replace `>`, as follows:

```        
if (opts.minTotalContributions >= opts.maxTotalContributions) {
    revert MinGreaterThanMaxError(opts.minTotalContributions, opts.maxTotalContributions);
}
```



## Assessed type

Other
`PartyGovernanceNFT.burn` is not updating the `totalVotingPower`
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L268-L305


# Vulnerability details

## Proof of Concept
The `_getSharedProposalStorage().governanceValues.totalVotingPower` value defines the voting power cap that can be minted using `mint` in `PartyGovernanceNFT`.

In a previous version of the protocol (2023-05-party) this variable was `_governanceValues.totalVotingPower` which was updated in `burn` and in `rageQuit` by calling `burn` internally:

[PartyGovernanceNFT.sol#L257-L261](https://github.com/code-423n4/2023-05-party/blob/f6f80dde81d86e397ba4f3dedb561e23d58ec884/contracts/party/PartyGovernanceNFT.sol#L257-L261)
```solidity
        if (totalVotingPower != 0) {
            _governanceValues.totalVotingPower = totalVotingPower - votingPower;
        } else {
            if (!authority) revert OnlyAuthorityError();
        }
```

In the current version, `_getSharedProposalStorage().governanceValues.totalVotingPower` is updated directly in `rageQuit` but is not updated anymore in `burn`:

[PartyGovernanceNFT.sol#L263-L301](https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L263-L301)
```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 += votingPower;


            // Update voting power for token to be burned.
            delete votingPowerByTokenId[tokenId];
            emit PartyCardIntrinsicVotingPowerSet(tokenId, 0);
            _adjustVotingPower(owner, -votingPower.safeCastUint96ToInt192(), address(0));


            // Burn token.
            _burn(tokenId);


            emit Burn(msg.sender, tokenId, votingPower);
        }
```
The voting power cap will therefore not be updated when burning voting power.

## Impact
If this change was not intended, burning will not result in a decrease of the voting power cap which will allow to mint more voting power than expected.

## To
The owner is a single point of failure and a centralization risk
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Having a single EOA as the only owner of contracts is a large centralization risk and a single point of failure. A single private key may be taken in a hack, or the sole holder of the key may become unable to retrieve the key when necessary, or the single owner can become malicious and perform a rug-pull. Consider changing to a multi-signature setup, and or having a role-based authorization model.

## Proof of Concept

```solidity
file: /contracts/party/PartyGovernanceNFT.sol

479    function addAuthority(address authority) external onlySelf {

```


## Assessed type

Access Control
Unchecked return value of low-level call()/delegatecall()
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/proposals/ProposalStorage.sol#L47-L49


# Vulnerability details

## Impact
The function being called may revert, which will be indicated by the return value to call()/delegatecall(). If the return value is not checked, the code will continue on as if there was no error, rather than reverting with the error encountered.
## Proof of Concept
```solidity
file: /contracts/proposals/ProposalStorage.sol

47        (bool s, bytes memory r) = address(impl).delegatecall(
            abi.encodeCall(IProposalExecutionEngine.initialize, (address(oldImpl), initData))
49        );

```


## Assessed type

call/delegatecall
Offchain signature can be improved
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/signature-validators/OffChainSignatureValidator.sol#L28


# Vulnerability details

## Impact

Offchain signature can be improved

## Proof of Concept

This issue intends to combine a few issues,

1. signature never expires
2. signature schema is too simple, 
	- no chain id for cross-chain replay protection, the protocol does intend to deploy on both ethereum and base network
	-  no nonce increment for signature replay protection, meaning the same signature can be used to bypass the signature validation multiple times as long as the owner is a party member
	- only the message length is in the signature schema, but the same message that have message length can be decoded in different way

relevant [line of code](https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/signature-validators/OffChainSignatureValidator.sol#L28)

```solidity
// Recreate the message pre-hash from the raw data
bytes memory encodedPacket = abi.encodePacked(
	"\x19Ethereum Signed Message:\n",
	Strings.toString(message.length),
	message
);
if (keccak256(encodedPacket) != hash) {
	revert MessageHashMismatch();
}
```

3. because signature never expires and no replay protection, in **the case when the totalWeight is changed and reduced by govnerance**

```solidity
// Either threshold is 0 or signer votes above threshold
if (
	thresholdBps == 0 ||
	(signerVotingPowerBps > totalVotingPower &&
		signerVotingPowerBps / totalVotingPower >= thresholdBps)
) {
	return IERC1271.isValidSignature.selector;
}
```

an outdated signature can be used to bypass the validation without generating new signature

suppose 

signerVotingPowerBps is 1000

totalVotingPower is 500

thresholdBps is 3 

1000 / 500 >= 3 is false

but in totalVotingPower changed and reduced to 200

1000 / 200 >= 3 is true, and signature validation is passed

## Tools Used

Manual Review

## Recommended Mitigation Steps

add nonce to avoid signature reuse

add signature expiration to make sure signature have expiration date





## Assessed type

Invalid Validation
Blacklisted distributor cause funds to be stuck 
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L534


# Vulnerability details

## Impact
It has been stated by the sponsor that a part from rebase/fee on transfer tokens, the protocol will accept any non-ruggable ERC20 token. If one of the tokens used is blocklistable like USDC for example. Then two functionnality are at risk. The function `RageQuit()` and `distribute()`. If the distribute address is blacklisted, the distribute will revert. Same thing if the receiver address in rageQuit is blacklisted.

- Users might be unable to interact with the `distribute` function if the distributor address is blacklisted.

## Proof of Concept
Run the following test in the file PartyGovernanceUnit.t.sol. Command : forge test --mt testDistribute_BlocklistableErc20-vvvv

```javascript
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8;

import "../contracts/vendor/solmate/ERC20.sol";

contract ERC20Mock is ERC20 {
    constructor() ERC20("DummyERC20", "DUM", 18) {}

    function mint(address account, uint256 amount) external {
        _mint(account, amount);
    }

    function burn(address account, uint256 amount) external {
        _burn(account, amount);
    }
}

contract USDCMock is ERC20Mock {
    error USDCMock__AccountBlackListed();

    mapping(address => bool) public blacklisted;

    function blacklist(address _account) external {
        blacklisted[_account] = true;
    }

    modifier notBlacklisted(address _account) {
        require(!blacklisted[_account], "Blacklistable: account is blacklisted");
        _;
    }

    function transfer(address recipient, uint256 amount)
        public
        virtual
        override
        notBlacklisted(recipient)
        returns (bool)
    {
        return super.transfer(recipient, amount);
    }

    function deal(address owner, uint256 amount) external {
        _mint(owner, amount);
    }
}

function testDistribute_BlocklistableErc20() external {
        (
            IERC721[] memory preciousTokens,
            uint256[] memory preciousTokenIds
        ) = _createPreciousTokens(2);
        TestablePartyGovernance gov = _createGovernance(
            false,
            100e18,
            preciousTokens,
            preciousTokenIds
        );

        // Only a member with VP can call distribute().
        address member = _randomAddress();
        gov.rawAdjustVotingPower(member, 1e18, member);

        USDCMock erc20 = new USDCMock();
        erc20.deal(address(gov), 1337e18);

        // Blacklist distributor
        erc20.blacklist(address(tokenDistributor));
        vm.expectRevert();
        vm.prank(member);
        gov.distribute(1337e18, ITokenDistributor.TokenType.Erc20, address(erc20), 0);

               // Results :
    //      │   ├─ [748] DummyERC20::transfer(DummyTokenDistributor: [0xF62849F9A0B5Bf2913b396098F7c7019b51A820a], 1337000000000000000000 [1.337e21]) 
    // │   │   └─ ← "Bl
Ability to burn tokens diminishes fairness of party governance dynamics
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L260-L261
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L307-L308
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L268-L305


# Vulnerability details

As described in the notes for both burn functions, [here](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L260-L261) and [here](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L307-L308), in ```PartyGovernanceNFT.sol```; it should not be possible to burn governance tokens after a party has started.

However, as the POC below shows, it is indeed possible to burn governance tokens after the party has started.

## Impact
Being able to burn tokens, even though the function is locked to an authority, has the capacility to unfairly disrupt the voting power distribution and influence outcomes. 
Users reading the docs [here](https://docs.partydao.org/docs/partys/Party#burn) will be under the impression that their governance token, which they purchased, cannot be burnt but that is not the case.

## Proof of Concept
Copy this test into ```PartyGovernanceNFT.t.sol``` and run:

```
    function testBurn_afterPartyStarted() external {
        (Party party,, ) = partyAdmin.createParty(
            partyImpl,
            PartyAdmin.PartyCreationMinimalOptions({
                host1: address(this),
                host2: address(0),
                passThresholdBps: 5100,
                totalVotingPower: 100,
                preciousTokenAddress: address(toadz),
                preciousTokenId: 1,
                rageQuitTimestamp: 0,
                feeBps: 0,
                feeRecipient: payable(0)
            })
        );
        address recipient = _randomAddress();
        vm.prank(address(partyAdmin));
        uint256 tokenId = party.mint(recipient, 10, recipient);

        vm.prank(address(partyAdmin));
        party.burn(tokenId);

        vm.expectRevert("NOT_MINTED");
        party.ownerOf(tokenId);
    }
```

## Tools Used
Manual Review
Foundry

## Recommended Mitigation Steps
Check in the burn functions whether the party is active by asserting if ```totalVotingPower``` is greater than ```0```. Revert the function if ```True```


## Assessed type

Governance
Unprotected Initializer in ProposalStorage Contract
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/proposals/ProposalStorage.sol#L43-L54


# Vulnerability details

## Impact
there is  an unprotected initializer in the _initProposalImpl function. This function sets the engineImpl state variable to the impl argument and then calls the initialize function of the impl contract. There are no protections to prevent this function from being called multiple times, which could allow an attacker to change the engineImpl state variable and reinitialize the contract.
## Proof of Concept
here is a scenario show the vulnerability :
-let's say attacker deploys a malicious contract that implements the IProposalExecutionEngine interface. This contract overrides the initialize function to perform a malicious action, such as transferring all funds to the attacker's address.
- after that The attacker calls the _initProposalImpl function in the ProposalStorage contract with the address of the malicious contract as the impl argument. This changes the engineImpl state variable to the malicious contract and reinitializes the contract.
- and then When the initialize function of the malicious contract is called, it transfers all funds to the attacker's address.
- as result The attacker can now withdraw the stolen funds from the ProposalStorage contract.
## Tools Used
manual review
## Recommended Mitigation Steps
to fix the problem it must to add a modifier to the function to checks if the contract has already been initialized and If the contract has already been initialized, the modifier should revert the transaction. this can solution and prevent from the vulnerability.


## Assessed type

Other
Last set of users may not be able to get a refund when Crowdfund did not succeed
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
InitialETHCrowdfund#refund assumes that all contributions are intact, but there is an emergencyExecute function that is capable of spending crowdfund eth. When this happens some people will not get their refund when crowdfund fails. The contract also lacks a receive() callback, which prevents direct transfer of eth to the contract

## Proof of Concept
When a Crowdfund does not succeed, that is, "expiry" timestamp has been reached and `totalContributions<minTotalContributions`, Contributors can get their contributed ETH back by calling [InitialETHCrowdfund#refund](https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/InitialETHCrowdfund.sol#L318).

InitialETHCrowdfund#refund attempts to transfer the exact amount that the user contributed, but this may fail for the last set of users if some of the Crowdfund's funds is used by Party DAO in an [emergency operation](https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/ETHCrowdfundBase.sol#L379).

This will lead to locking of some users' funds in InitialETHCrowdfund contract.

Consider the following scenario:
- minContribution=10 eth
- Alice contributes 3 eth
    - eth balance of contract=3 eth
- Bob contributes 5 eth
    - eth balance of contract=8 eth
- Party DAO makes an arbitrary operation via ETHCrowdfundBase#emergencyExecute with amountETH of 0.5 eth
    - eth balance of contract=7.5 eth
- Crowdfund does not succeed
- Alice successfully withdraws her 3 eth contribution via InitialETHCrowdfund#refund
    - eth balance of contract = 4.5 eth
- Bob calls InitialETHCrowdfund#refund, which will revert because contract attempts to transfer 5 eth to Bob, whereas the contract's balance is 4.5 eth
- 4.5 eth gets locked in the contract.

## Tools Used
Manual Review

## Recommended Mitigation Steps
InitialETHCrowdfund#refund should send a prorata share of the contract's balance based on the amount contributed by a user. This will:
- distribute losses among all contributors
- distribute stray ETH in contract among all contributors


## Assessed type

ETH-Transfer
Potential Lock of ETH in emergencyExecute Function
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

Discrepancy between amountEth parameter and msg.value can lead to ETH being locked in the contract.

```
function emergencyExecute(
    address targetAddress,
    bytes calldata targetCallData,
    uint256 amountEth
) external payable { ... }

```

The emergencyExecute function is designed to perform an emergency operation by sending ETH to a target address. However, the function uses the amountEth parameter to determine the amount of ETH to send, but there is no check against the actual ETH sent with the message (msg.value). If amountEth does not match msg.value, there is a risk that more ETH than intended could be locked within the contract, as the excess amount sent is not refunded or accounted for.

Mismatched Values: 

If msg.value is greater than amountEth, the excess ETH is neither used nor returned, effectively becoming locked within the contract.
Lack of Validation: There is no validation to ensure that amountEth equals msg.value, leading to potential discrepancies.

## Proof of Concept

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

## Tools Used

manual

## Recommended Mitigation Steps

implement a validation check to ensure that amountEth matches msg.value. If the values do not match, the function should revert to prevent execution and avoid locking funds.

```
require(msg.value == amountEth, "Mismatch between sent ETH and amountEth parameter.");

```



## Assessed type

Token-Transfer
Vulnerability in cancel Function of Governance Smart Contract
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

Flaw in cancel function logic allows premature cancellation of proposals.

```
function cancel(uint256 proposalId, Proposal calldata proposal) external { ... }

```

Problem Description: 

The cancel function in the governance smart contract is intended to allow the cancellation of proposals under specific conditions. However, there is a logic flaw related to the executedTime and cancelDelay calculations. Since values.executedTime is initialized to 0 upon proposal creation, the sum of executedTime and cancelDelay will always be less than the current block.timestamp. This flaw permits the cancellation of proposals at any time, regardless of the intended delay constraints.

Immediate Cancellation Vulnerability: 

The current logic does not prevent immediate cancellation after proposal creation, undermining the governance process.
Ineffective Delay Mechanism: The intended delay mechanism (cancelDelay) is rendered ineffective due to the initial state of values.executedTime.

## Proof of Concept


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


## Tools Used

manual

## Recommended Mitigation Steps

 Revise the cancellation logic to correctly account for the intended delay period. This can be achieved by ensuring that values.executedTime is set to an appropriate reference time (e.g., proposal creation time or first execution attempt) instead of defaulting to 0.


## Assessed type

Timing