PartyGovernanceNFT.sol#increaseTotalVotingPower - Can be exploited by front running/read-only reentrancy with rageQuit to retrieve more funds for the quitter and less for the rest of the users
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The protocol allows for trusted actors (we're assuming the hosts) to use smart contracts, as authorities, to allow for flexibility and more functionality at the disposal of the hosts.

Two of the functions that are available are `mint` and `increaseTotalVotingPower`. Both of the functions are used by `InitialETHCrowdfund` during the party's crowdfunding phase, but the functions can still be called by authorities as they see fit to mint new cards, and increase the total voting power of the Party respectively.

The two functions are supposed to be used in tandem. First `mint` is called, then we call `increaseTotalVotingPower`. For this issue we will focus on `increaseTotalVotingPower`.
```js
    /// @notice Increase the total voting power of the party. Only callable by
    ///         an authority.
    /// @param votingPower The new total voting power to add.
    function increaseTotalVotingPower(uint96 votingPower) external {
        _assertAuthority();
        _getSharedProposalStorage().governanceValues.totalVotingPower += votingPower;
    }
```
The function is quite simple, we just add `votingPower` to `totalVotingPower`.

Now let's explore how `rageQuit` works, which if enabled, allows users to burn a Card and proportionally to the Card's voting power, retrieve either ETH of ERC20 tokens.

In our case we will focus on where `withdrawAmounts` is calculated.
```js
     withdrawAmounts[i] += (balance * getVotingPowerShareOf(tokenIds[j])) / 1e18;
```
Let's see what's inside `getVotingPowerShareOf`.
```js
    /// @notice Return the voting power share of a token. Denominated
    ///         fractions of 1e18. I.e., 1e18 = 100%.
    /// @param tokenId The token ID to query.
    /// @return share The voting power percentage of `tokenId`.
    function getVotingPowerShareOf(uint256 tokenId) public view returns (uint256) {
        uint256 totalVotingPower = _getSharedProposalStorage().governanceValues.totalVotingPower;
        return
            totalVotingPower == 0 ? 0 : (votingPowerByTokenId[tokenId] * 1e18) / totalVotingPower;
    }
```
As you can see, we use the `votingPowerByTokenId` and divide it by the `totalVotingPower` to get how many shares the card holds over the Party's balance. The user that rage quits, specifies which balance should be used, either an ERC20 token balance or the ETH balance of the contract. In any case, this function returns shares of that balance.

Note that for all calculations we are assuming `exchangeRateBps = 1e4`, so 1:1 conversion between ETH and voting power.

Going back to the calculation of the `withdrawAmounts`.
```js
     withdrawAmounts[i] += (balance * getVotingPowerShareOf(tokenIds[j])) / 1e18;
```
`balance` represents either the ETH balance or the ERC20 balance, as stated above.

Let's replace `getVotingPowerShareOf` w