# MaxContribution check can be bypassed to give a card high voting power

ReraiseETHCrowdfund's maxContribution check runs per-claim, so a user claiming from two addresses then transferring the party NFT stacks voting power on one card.

- Payout: Contest reward
- Severity: medium
- Program: PartyDAO
- Credited: evan
- Disclosed: 2023-06-23
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-04-party-findings/issues/39
- Library page: https://bugbountylibrary.com/r/c4%3A2023-04-party%3AM-02
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic vuln:governance

**Target:** ReraiseETHCrowdfund / PartyGovernanceNFT claim logic
**Root cause:** maxContribution is enforced on the contribution equivalent of voting power at claim time, but addVotingPower on an existing party card does not re-check the card's cumulative voting power, and party cards are transferable.
**Steps:**
1. Set maxContribution to allow at most 3 units of voting power per card.
2. Contribute max amount from two attacker-controlled addresses A and B.
3. After finalization, call `claim()` as A to mint a party card with 3 units.
4. Transfer the partyGovernanceNFT from A to B (crowdfundNFT is non-transferable, party card is).
5. Call `claim()` as B; code path `party.ownerOf(tokenId) == contributor` calls `party.addVotingPower(tokenId, votingPower)`, adding 3 more to the same card for 6 total.
**How found:** Code reading of claim/claimMultiple min/max checks; noticed the `party.ownerOf(tokenId) == contributor` branch adds voting power to an existing card without re-validating cumulative contribution, and that party cards are transferable.
**Exploitation:** Attacker controls multiple contributor addresses, claims sequentially and transfers the party card between them so each claim passes the per-claim maxContribution check while accumulating voting power on a single card, breaking 1-card-1-vote governance.
**Insight:** When a cap is enforced per-transaction or per-claim, check whether the capped quantity is stored on a transferable/mergeable asset that accumulates across calls; per-call checks on cumulative state are bypassable by splitting identities and consolidating afterward.
