xanoutas/solidity-security-findings huggingface dataset:
the state variables votingPower and tokenId being read after an external call
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
- The  _contribute function it's first calculates the votingPower and then makes an external call to either party.mint or party.increaseVotingPower. operation as this is not recommend it's a problem , the correct way that all external calls should be made at the end of a function, this allow an attacker to manipulate the votingPower calculation in the _contribute function. This could potentially allow the attacker to mint new party cards without any voting power, or to increase the voting power of existing party cards without actually contributing any ETH
## Proof of Concept
this is scenario represent the vulnerability :
- first An attacker creates a malicious contract that has a fallback function which calls the _contribute function.
- The attacker then sends some ETH to the malicious contract. This triggers the fallback function, which calls the _contribute function.
- In the _contribute function, before the party.mint or party.increaseVotingPower external call is made, the votingPower is calculated based on the amount passed in.
- The party contract, which is maliciously designed, calls back into the _contribute function during the party.mint or party.increaseVotingPower calls. This allows the party contract to manipulate the votingPower calculation by calling back into the _contribute function with a different amount.
- As a result, the votingPower calculation is manipulated, which could lead to potential exploitation of the contract.
## Tools Used
- manual review
## Recommended Mitigation Steps
- it's should move the external calls to the end of the function, after all state variables have been read.```


## Assessed type

Other
`minAmount` is ignored when `amount` is zero during rage quit
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/053fb9345b0739b3c26d12e1eae1eefbfd70b223/contracts/party/PartyGovernanceNFT.sol#L426-L432


# Vulnerability details



During rage quit the rage quitter supplies a list of tokens to take a fair share of and the expected minimum amount out. If the share of any of the tokens is less than the minimum then the function reverts.
However, if the share is zero then the minimum amount is ignored and the function doesn't revert.
A user might be rage quitting during a proposal execution that empties out a token (e.g. the party is purchasing an NFT) expecting the function to revert if the proposal was executed already. But in that case the function wouldn't revert and the user would end up losing a part of their share.

## Impact
Rage quitter might get less than the fair share they deserve

## Proof of Concept

In [the following code snippet](https://github.com/code-423n4/2023-10-party/blob/053fb9345b0739b3c26d12e1eae1eefbfd70b223/contracts/party/PartyGovernanceNFT.sol#L426-L432) it can be seen that the check is done only if `amount > 0`


```solidity
                if (amount > 0) {
                    uint256 minAmount = minWithdrawAmounts[i];

                    // Check amount is at least minimum.
                    if (amount < minAmount) {
                        revert BelowMinWithdrawAmountError(amount, minAmount);
                    }
```

## Recommended Mitigation Steps

In case that amount is zero - revert if the minimum amount is greater than zero


## Assessed type

Other
Single tokenId can posses entire voting power for a party proposal
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
It is possible for a single user to posses voting power equal to the totalvotingpower of a proposal intrinsically. 

## Proof of Concept
```
(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);

        uint40 timestampBefore = uint40(block.timestamp);
        skip(10);
        uint40 timestampAfter = uint40(block.timestamp);

        uint96 votingPower = 91;

        address authority = address(partyAdmin);
        vm.prank(authority);
        party.increaseVotingPower(tokenId, votingPower);

        assertEq(party.votingPowerByTokenId(tokenId), 100);
        assertEq(party.mintedVotingPower(), 100);
        assertEq(party.getVotingPowerAt(recipient, timestampAfter), 100);
        assertEq(party.getVotingPowerAt(recipient, timestampBefore), 10);
```
In the above test it shows that it is possible for a single user to posses the entire voting power for a proposal, although in the poc above it is done by the increaseVotingPower capability of the partyAdmin which is a trusted role, this still shows that if some kind of attack were to happen a user can successfully have all the voting power without any reverts in code (It is best to mitigate casualties in the event of an unforseen attack). A user should only be able to posses that much voting power through delegation and should in no way be able to have intrinsic voting power that exceeds a certain threshold.

## Tools Used
Manual Review

## Recommended Mitigation Steps
1.Introduce a check in the increaseVotingPower that ensures that a single user can not have intrinsic voting power exceeding a certain number.
2.Introduce a new party configuration ```maxUserIntrinsicVotingPower``` that defines how much voting undelegated power a user can have. The maxUserIntrinsicVotingPower can be the result of dividing the totalVotingPower by the number of party participants.


## Assessed type

Governance
Decreasing voting power can lead to deadlocking the contract
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

# Bug Description
The update introduces a new functionality that allows the authority to decrease the voting power of a chosen NFT by calling the `decreaseVotingPower()` function with the NFT's ID.

An essential invariant within the party protocol, specifically for parties created using an ETHCrowdFund, is that the cumulative voting power of all minted NFTs should always be equal to the `totalVotingPower`. This invariant is crucial for the proper operation of the governance system. If the `totalVotingPower` exceeds the sum of all NFT voting powers, it becomes nearly impossible to achieve unanimous decisions, and in some cases, it can lead to complete governance blockage.

The issue arises because when the authority calls `decreaseVotingPower()`, it [reduces](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L239), the voting power of the specified NFT. This reduction affects the sum of all NFTs' voting powers, but the `totalVotingPower` does not decrease. This results in the invariant being violated. Although an authority can call `decreaseTotalVotingPower()` to re-establish the invariant by [decreasing](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L257)  the `totalVotingPower` by the same amount, this step is not required or clearly documented.

If the invariant breaks, as it can happen in this case, the voting system becomes skewed and in the worst case totally broken so that no proposals can be passed anymore.
# Impact

The impact of this issue is significant and can disrupt the governance system of the party. Depending on the extent of the difference between the `totalVotingPower` and the sum of NFT voting powers, two scenarios can occur:
1. **`totalVotingPower <= mintedVotingPower * 1/percentageNeededToPass`**: In this case, more votes than the actual percentage required are needed to pass proposals. Unless the difference between the `totalVotingPower` and minted voting power is very small, unanimous voting becomes unattainable.
2. **`totalVotingPower > mintedVotingPower * 1/percentageNeededToPass`**: Here, the governance system becomes entirely blocked. No proposals can be passed, leading to a deadlock in the governance system.

To comprehend the severity of this issue, consider a worst-case scenario where the following conditions align:
- Emergency Execute and Rage Quit are disabled.
- There is only one authority apart from the Crowdfund, which is a smart contract.
- This smart contract, controlled by a DAO, reduces users' NFT voting powers if they do not comply with DAO guidelines. It exclusively uses the `decreaseVotingPower()` function and never invokes `decreaseTotalVotingPower()`.

In this extreme situation, if the voting powers of NFTs are reduced **`totalVotingPower > mintedVotingPower * 1/percentageNeed
contributeFor, batchContributeFor - attacker can delegate someone else's vote freely
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/InitialETHCrowdfund.sol#L235-L249
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/InitialETHCrowdfund.sol#L302
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/ETHCrowdfundBase.sol#L201-L208
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/crowdfund/InitialETHCrowdfund.sol#L255-L273


# Vulnerability details

## Impact

Attacker can freely delegate other people's votes. Attacker can delegate votes without the user's knowledge. In the worst case, attacker delegate all votes and execute proposal immediately by unanimous vote. Then the attacker can be the Authority, steal assets, or crash the party.

## Proof of Concept

If the attacker calls `contributeFor` or `batchContributeFor`, attacker can delegate votes of any user.

```solidity
function contributeFor(
    uint256 tokenId,
    address payable recipient,
    address initialDelegate,
    bytes memory gateData
) external payable onlyDelegateCall returns (uint96 votingPower) {
    return
        _contribute(
            recipient,
@>          initialDelegate,
            msg.value.safeCastUint256ToUint96(),
            tokenId,
            gateData
        );
}

function batchContributeFor(
    BatchContributeForArgs calldata args
) external payable onlyDelegateCall returns (uint96[] memory votingPowers) {
    votingPowers = new uint96[](args.recipients.length);
    uint256 valuesSum;
    for (uint256 i; i < args.recipients.length; ++i) {
@>      votingPowers[i] = _contribute(
            args.recipients[i],
@>          args.initialDelegates[i],
            args.values[i],
            args.tokenIds[i],
            args.gateDatas[i]
        );
        valuesSum += args.values[i];
    }
    if (msg.value != valuesSum) {
        revert InvalidMessageValue();
    }
}

function _contribute(
    address payable contributor,
    address delegate,
    uint96 amount,
    uint256 tokenId,
    bytes memory gateData
) private returns (uint96 votingPower) {
    // Require a non-null delegate.
    if (delegate == address(0)) {
        revert InvalidDelegateError();
    }

    ...

@>  votingPower = _processContribution(contributor, delegate, amount);

    // OK to contribute with zero just to update delegate.
    if (amount == 0) return 0;

    if (tokenId == 0) {
        // Mint contributor a new party card.
@>      party.mint(contributor, votingPower, delegate);
    } else if (disableContributingForExistingCard) {
        revert ContributingForExistingCardDisabledError();
    } else if (party.ownerOf(tokenId) == contributor) {
        // Increase voting power of contributor's existing party card.
        party.increaseVotingPower(tokenId, votingPower);
    } else {
        revert NotOwnerError(tokenId);
    }
}
`
Malicious Completion of Party Proposals via Voting Power Delegation and Transfer
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L451-L453
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L451-L455


# Vulnerability details

## Impact
Party Proposals can be maliciously completed, allowing for the approval of unwanted proposals within the Party governance system.

## Explanation
In the Party, members can delegate their intrinsic voting power to a new address, removing it from the old one (if any) via [`delegateVotingPower()`](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L451-L453) function and transfer their governance NFTs (voting power) via [`transferFrom`](https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L451-L455) function.

```js
    /// @notice Pledge your intrinsic voting power to a new delegate, removing it from
    ///         the old one (if any).
    /// @param delegate The address to delegate voting power to.
    function delegateVotingPower(address delegate) external {
        _adjustVotingPower(msg.sender, 0, delegate);
    }
```

```js
    function transferFrom(address owner, address to, uint256 tokenId) public override {
        // Transfer voting power along with the token.
        _transferVotingPower(owner, to, votingPowerByTokenId[tokenId]);
        super.transferFrom(owner, to, tokenId);
    }
```

The issue arises when there is an active proposal, and Party members delegate or transfer their governance NFTs (voting power). Currently, the voting power is not immediately removed from the total votes of the active proposal, potentially leading to the malicious completion of a proposal that may not align with the Party's consensus.

## Proof of Concept
Let's consider the following example:

| Member               | Voting Power |
|----------------------|--------------|
| Alice (party member) | 100          |
| Bob (party member)   | 200          |
| Kevin (party member) | 50           |
| Steve (party member) | 50           |
| totalVotingPower     | 400          |
| passThresholdBps     | 1000         |

1. Alice creates a proposal. At this point, Alice's proposal has `100 votes`.
2. Kevin and Steve do not support Alice's proposal and do not approve it.
3. Bob supports Alice's proposal and `accepts` it. Now, Alice's proposal has `300 votes`.
4. However, Bob wants this proposal to be completed at any cost. He creates a second account named Danny, delegates his `200 voting power` to Danny (his second address), and calls the `accept()` function again. As a result, Alice's proposal now has `500 votes` and can be completed.

The core issue lies in the fact that when voting power is `delegated` or `transferred` within the Party between addresses, the `voting power` of these users is not immediately deducted from the total votes of the current active proposal.

## Tool Used
Manual Inspection

## Recommendation and Migration St
function rngComplete is unpprotected
Audit: 2023-08-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L131-L176


# Vulnerability details

## Impact
The rngComplete is a function Called by the relayer to complete the Rng relay auction.
However it has zero access control.

## Proof of Concept
The function makes calls to the prizepool to close a draw, it also withdraws from a reserve. All these are done with the data passed in as parameters. Hence it should not be left unguarded
```solidity
 function rngComplete(
    uint256 _randomNumber,
    uint256 _rngCompletedAt,
    address _rewardRecipient,
    uint32 _sequenceId,
    AuctionResult calldata _rngAuctionResult
  ) external returns (bytes32) {
    if (_sequenceHasCompleted(_sequenceId)) revert SequenceAlreadyCompleted();
    uint64 _auctionElapsedSeconds = uint64(block.timestamp < _rngCompletedAt ? 0 : block.timestamp - _rngCompletedAt);
    if (_auctionElapsedSeconds > (_auctionDurationSeconds-1)) revert AuctionExpired();
    // Calculate the reward fraction and set the draw auction results
    UD2x18 rewardFraction = _fractionalReward(_auctionElapsedSeconds);
    _auctionResults.rewardFraction = rewardFraction;
    _auctionResults.recipient = _rewardRecipient;
    _lastSequenceId = _sequenceId;

    AuctionResult[] memory auctionResults = new AuctionResult[](2);
    auctionResults[0] = _rngAuctionResult;
    auctionResults[1] = AuctionResult({
      rewardFraction: rewardFraction,
      recipient: _rewardRecipient
    });

    uint32 drawId = prizePool.closeDraw(_randomNumber);

    uint256 futureReserve = prizePool.reserve() + prizePool.reserveForOpenDraw();
    uint256[] memory _rewards = RewardLib.rewards(auctionResults, futureReserve);

    emit RngSequenceCompleted(
      _sequenceId,
      drawId,
      _rewardRecipient,
      _auctionElapsedSeconds,
      rewardFraction
    );

    for (uint8 i = 0; i < _rewards.length; i++) {
      uint104 _reward = uint104(_rewards[i]);
      if (_reward > 0) {
        prizePool.withdrawReserve(auctionResults[i].recipient, _reward);
        emit AuctionRewardDistributed(_sequenceId, auctionResults[i].recipient, i, _reward);
      }
    }

    return bytes32(uint(drawId));
  }
```
## Tools Used
Manual Review
## Recommended Mitigation Steps
Since it says that it is a relayer function. Then require that the msg.sender is the rngAuctionRelayer address


## Assessed type

Access Control
INCORRECT ACCESS CONTROL
Audit: 2023-08-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuction.sol#L170-L208
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuction.sol#L288-L297
https://github.com/GenerationSoftware/pt-v5-vault-boost/blob/9d640051ab61a0fdbcc9500814b7f8242db9aec2/src/VaultBooster.sol#L131-L134
https://github.com/GenerationSoftware/pt-v5-vault-boost/blob/9d640051ab61a0fdbcc9500814b7f8242db9aec2/src/VaultBooster.sol#L171-L176
https://github.com/GenerationSoftware/pt-v5-vault-boost/blob/9d640051ab61a0fdbcc9500814b7f8242db9aec2/src/VaultBooster.sol#L181-L183
https://github.com/GenerationSoftware/pt-v5-vault-boost/blob/9d640051ab61a0fdbcc9500814b7f8242db9aec2/src/VaultBooster.sol#L201-L203


# Vulnerability details

## Impact
Access control plays an important role in segregation of privileges in smart contracts and other applications. If this is misconfigured or not properly validated on sensitive functions, it may lead to loss of funds, tokens and in some cases compromise of the smart contract.

The contracts `RngAuction` and `VaultBooster` are importing an access control library `@openzeppelin/contracts/access/Ownable.sol` but the functions (`startRngRequest`, `getRngResults`) and  (`getBoost`, `deposit`, `accrue`, `liquidatableBalanceOf`) are missing the modifier onlyOwner.

Note: The functions in first set of brackets belong to the first contract and so on.

## Proof of Concept
Read the following 2min read blog for understanding the concept.
https://blog.solidityscan.com/access-control-vulnerabilities-in-smart-contracts-a31757f5d707

## Tools Used
Manual Review

## Recommended Mitigation Steps
It is recommended to go through the contract and observe the functions that are lacking an access control modifier. If they contain sensitive administrative actions, it is advised to add a suitable modifier to the same


## Assessed type

Access Control
Lack of authentication in rngComplete
Audit: 2023-08-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L131-L176


# Vulnerability details

## Impact
In RngRelayAuction.sol the rngComplete function is meant to be called only by the rngAuctionRelayer contract, however, it can currently be called by any user. Consequently any random number can be used to close a prize pool draw, benefiting the malicious user. Furthermore, other function parameters such as the _rewardRecipient can be altered to further benefit the attacker and steal from the protocol and its users.

## Proof of Concept
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L131-L176
We can see above that there are no checks for who the msg.sender is and the prize pool is closed with the _randomNumber parameter that can be set to any value by the attacker.

## Tools Used
Manual review
## Recommended Mitigation Steps
Add an authentication check for msg.sender in the rngComplete function so that it is equal to the rngAuctionRelayer variable.


## Assessed type

Access Control