Open sherlock-admin3 opened 6 months ago
The protocol team fixed this issue in the following PRs/commits: https://github.com/GenerationSoftware/pt-v5-prize-pool/pull/115
Fixed.
Now the max range of draws to look for is limited to grandPrizePeriodDraws
The Lead Senior Watson signed off on the fix.
0x73696d616f
high
Vault portion calculation in
PrizePool::getVaultPortion()
is incorrect as_startDrawIdInclusive
has been erasedSummary
The vault portion calculation in
PrizePool::getVaultPortion()
is incorrect because it fetches a_startDrawIdInclusive
that has been overwritten in the total accumulator but not in the vaults accumulator or the donation accumulator.Vulnerability Detail
To illustrate the issue, consider the grand prize, where odds are
1 / g = 0.00273972602e18
withg == 365 days
. The estimated prize frequency is1e18 / 0.00273972602e18 == 365.000000985
rounded up to the nearest integer,366
. IflastAwardedDrawId_
is366
, thenstartDrawIdInclusive
is366 - 366 + 1 == 1
. This means that it will fetch the total, vault and donation accumulators since the opening ofdrawId == 1
.However, when
lastAwardedDrawId_ == 366
, the current opendrawId
is367
, which is written to index 0 in the circular buffer, overwritingdrawId == 1
. But, in the donation and vault cases, the accumulator still hasdrawId == 1
, as the buffer will likely not have been overwritten (draw periods take 1 day, it is highly likely the individual accumulators are not written to every day, but the total accumulator is, or users may exploit this on purpose). As the buffer is circular, this will keep happening every time after the buffer has been filled and indexes start being overwritten.Thus, due to this, the vault portion will be bigger than it should, as the total accumulator is calculated between 365 days (from 2 to 366, as 1 has been erased), but the vault and donation accumulators for 366 days (between 1 and 366). Users will have bigger than expected chances of winning prizes and in the worst case, they may even not be able to claim prizes at all, in case the erased
drawId
had a significant contribution to the prizes through a donation, and then it underflows when doingtotalSupply = totalContributed - totalDonated;
in PrizePool::getVaultShares().Add the following test to
PrizePool.t.sol
and log the vault and total portions inPrizePool::getVaultShares()
. The contribution of the vault will be bigger than the total.Impact
Users get better prize chances than supposed and/or it can lead to permanent reverts when trying to claim prizes.
Code Snippet
https://github.com/sherlock-audit/2024-05-pooltogether/blob/main/pt-v5-prize-pool/src/PrizePool.sol#L1014
Tool used
Manual Review
Vscode
Recommendation
When calculating
PrizePool::computeRangeStartDrawIdInclusive()
, therangeSize
should be capped to365
to ensure the rightstartDrawIdInclusive
is fetched, not one that has already been erased. In the scenario above, if the range is capped at 365, whenlastAwardedDrawId_ == 366
,startDrawIdInclusive
would be366 - 365 + 1 == 2
, which has not been erased as the currentdrawId
is367
, having only overwrittendrawId == 1
, but 2 still has the information.