PoolTogether — disclosed vulnerability reports and payouts
Every publicly disclosed and closed bug bounty report we hold for PoolTogether, with our own summary of each finding and a link to the original disclosure.
- Reports indexed
- 33
- Total paid
- $0
- Critical
- 0
- Largest payout
- $0
This page collects the 33 closed, publicly disclosed PoolTogether reports indexed on Coin Buggie. Nothing here is active or unpatched — every entry was published by the programme or the researcher after remediation.
The findings concentrate in Front-running / MEV, Logic error, Flash loan attack, Access control, Reentrancy, across EVM-Solidity, Move. Reading a single protocol end to end is the fastest way to see which assumptions its codebase repeatedly gets wrong, which is usually a better predictor of where the next finding lives than the category alone.
Use the vulnerability class links below to compare these findings against the same bug class in other protocols.
What reviewers look for
- Front-running / MEV findings disclosed against PoolTogether
- Logic error findings disclosed against PoolTogether
- Flash loan attack findings disclosed against PoolTogether
- Access control findings disclosed against PoolTogether
- Reentrancy findings disclosed against PoolTogether
Curated highlights
The largest disclosed payouts in this group, with our own summary of each. Every report links back to the original disclosure.
PoolTogether: Increasing reserves breaks PrizePool accounting
An accounting flaw in PoolTogether's `PrizePool` contract allows external reserve contributions to be misattributed and extracted by prize vaults. When `increaseReserve` is invoked, prize tokens are transferred into the contract and added to `_reserve`, but the helper `_accountedBalance()` is not updated. As a result, subsequent calls to `contributePrizeTokens` evaluate the unallocated token delta as total contract balance minus accounted balance, treating the newly deposited reserve funds as excess contributions. Any vault can claim these tokens, inflating its distribution accumulators and causing contract token balances to fall below accounted protocol liabilities.
PoolTogether: The tier odds in `TieredLiquidityDistributor` are incorrect
The TieredLiquidityDistributor contract uses incorrect precomputed probability constants, causing the prize distribution system to deviate from its intended logic. Specifically, the highest standard prize tier should have odds of 1, mirroring the canary tier, but the generation script fails to assign this value. This configuration error means the protocol distributes prizes with incorrect frequencies, directly contradicting the project's documentation.
PoolTogether: Resetting delegation will result in user funds being lost forever
The PoolTogether protocol contained a critical logic vulnerability in the TwabController that allowed users to permanently lose access to their funds. By passing an address(0) as a delegate target, users inadvertently triggered a transfer of their balance to the null address, which could not be recovered or withdrawn. The issue was reachable via standard user interaction with the delegation system and mattered because it resulted in an irreversible loss of capital for the affected user.
PoolTogether: A malicious user can steal other user's deposits from Vault.sol
A critical vulnerability in PoolTogether's Vault contract allowed attackers to steal funds by exploiting unsafe integer type casting. During withdrawals, the contract calculated asset transfers using a uint256 share value but incorrectly downcasted the share amount to a uint96 for the burning process. This truncation permitted users to withdraw large amounts of assets without fully burning their corresponding share balance, effectively allowing them to drain other users' deposits by redeeming the 'remainder' shares.
PoolTogether: `Vault` is not compatible with some ERC4626 vaults
The PoolTogether Vault incorrectly relies on the `maxWithdraw` method from integrated ERC4626 yield vaults to determine its own internal share exchange rate. Because `maxWithdraw` often returns only the currently liquid assets rather than the total value of deposits and yields, temporary liquidity constraints in the underlying vault cause the PoolTogether Vault to miscalculate a lower exchange rate. This artificially deflated rate is then permanently stored, causing an avoidable and erroneous loss of value for users who interact with the vault during such periods.
PoolTogether: Delegated amounts can be forcefully removed from anyone in the `TwabController`
The PoolTogether vault contract contained a critical logic vulnerability in the sponsor function that allowed attackers to forcefully reset any user's delegation status. By calling the function with a zero deposit amount and a victim's address, an attacker could trigger an automatic delegation update to the sponsorship address, effectively stripping the target of their delegated balance in the TwabController. This allowed an attacker to manipulate the total delegated power in the system, potentially skewing lottery results by making their own delegated balance represent a larger share of the total. The protocol addressed this by removing the recipient parameter, restricting sponsorship actions to the msg.sender's own account.
All reports in this group
- PoolTogether: `Vault.mintYieldFee` function can be called by anyone to mint `Vault Shares` to any recipient addressAccess control$0
- PoolTogether: If the underlying asset is a fee on transfer token, it could break the internal accounting of the vaultLogic error$0
- PoolTogether: Unintended or malicious use of prize winners' hooksReentrancy$0
- PoolTogether: `_requireVaultCollateralized()` is called at the beginning of the functions `mintYieldFee()` and `liquidate()`Logic error$0
- PoolTogether: `TwabLib::getTwabBetween` can return inaccurate balances if `_startTime` and `_endTime` aren't safely boundLogic error$0
- PoolTogether: Loss of precision leads to under-collateralizedInteger overflow/underflow$0
- PoolTogether: Vault contribution calculations wrongly include the current round when claiming prizesLogic error$0
- PoolTogether: The threshold check for adding of new tiers is skipped when `_nextNumberOfTiers` is at the maximum amountLogic error$0
- PoolTogether: `Claimer.claimPrizes` can be front-runned in order to make losses for the claim botFront-running / MEV$0
- PoolTogether: Improper handling of cases when withdrawable assets = 0Logic error$0
- PoolTogether: Silent overflow could alter computation when calculating the `vaultPortion` in the `PrizePool` contractInteger overflow/underflow$0
- PoolTogether: Vault does not conform to ERC4626Logic error$0
- PoolTogether: `VaultFactory` allows deployment of vaults with non-authentic `TwabController` and `PrizePool`Logic error$0
- PoolTogether: Balance invariant between the individual and total `twabs` can be brokenLogic error$0
- PoolTogether: Number of prize tiers always increases if just 1 canary prize is claimedLogic error$0
- PoolTogether: `depositWithPermit` and `mintWithPermit` are allowed to be called by the permit creator onlyLogic error$0
- PoolTogether: Tiers can be maintained active to give unfair advantage to user through DoSLogic error$0
- PoolTogether: `Vault.mintWithPermit()` can be DoS'dFront-running / MEV$0
- PoolTogether: `drawManager` can be set to a malicious addressAccess control$0
- PoolTogether: `PrizeVault.maxDeposit()` doesn't take into account produced feesInteger overflow/underflow$0
- PoolTogether: Any fee claim lesser than the total `yieldFeeBalance` as unit of shares is lost and locked in the `PrizeVault` contractLogic error$0
- PoolTogether: Funds locked due to missing transfer checkLogic error$0
- PoolTogether: Permit doesn't work with DAILogic error$0
- PoolTogether: The winner can steal claimer fees, and force him to pay for the gasReentrancy$0
- PoolTogether: `maxDeposit()` uses `yieldVault.maxDeposit()` but `_depositAndMint()` uses `yieldVault.mint()`Integer overflow/underflow$0
- PoolTogether: `yieldFeeBalance` wouldn't be claimed after calling `transferTokensOut()`Oracle manipulation$0
- PoolTogether: `_maxYieldVaultWithdraw()` uses `yieldVault.convertToAssets()`Logic error$0