# "Votes" balance can be increased indefinitely in multiple contracts

Public initializeDistributionRecord() in Tokensoft vesting contracts lacks idempotency, letting a beneficiary re-mint ERC20Votes voting power repeatedly via a valid merkle proof.

- Payout: Contest reward
- Severity: high
- Program: Tokensoft
- Credited: 0xDanielH
- Disclosed: 2023-06-30
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-06-tokensoft-judging/issues/41
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-06-tokensoft%3AH-1
- Tags: domain:web3 impact:integrity kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:governance

**Target:** Solidity vesting/claim contracts (ContinuousVestingMerkle, PriceTierVestingMerkle, TrancheVestingMerkle, CrosschainMerkleDistributor, etc.)
**Root cause:** initializeDistributionRecord() is externally callable with only a merkle proof check and no per-beneficiary idempotency guard; _initializeDistributionRecord mints tokensToVotes(totalAmount) each call, so repeated calls with the same valid proof mint unbounded ERC20Votes balance.
**Steps:**
1. Obtain a valid merkle proof for (beneficiary, amount, domain) as a legitimate beneficiary.
2. Call `initializeDistributionRecord(domain, beneficiary, amount, merkleProof)` repeatedly; each call mints tokensToVotes(amount) to beneficiary via _mint.
3. Call `delegate(beneficiary)` to record the inflated balance as voting power.
4. Use the inflated votes in any DAO/governance that reads this ERC20Votes balance.
**How found:** Manual review of the claim contracts: noticed initializeDistributionRecord() is external with only validMerkleProof and no replay/idempotency guard, then traced _initializeDistributionRecord to _mint of tokensToVotes, concluding repeated calls inflate ERC20Votes balance.
**Exploitation:** A beneficiary with one valid merkle leaf loops initializeDistributionRecord() to mint arbitrary voting power, then delegates to self, capturing disproportionate governance weight in any project using 'voting while vesting'.
**Escalation:** Applies across all inheriting contracts (ContinuousVestingMerkle, PriceTierVestingMerkle, PriceTierVestingSale_2_0, TrancheVestingMerkle, CrosschainMerkleDistributor and children), so any DAO relying on these for voting power is affected.
**Insight:** In merkle-claim + ERC20Votes designs, any externally callable initializer that mints voting power must be idempotent per beneficiary (or reconcile to current balance, not add); check whether re-initialization on merkle root changes lets a user re-mint.
