Closed sherlock-admin4 closed 2 months ago
The TWAB observation limitations are indeed important to consider when deploying a new TwabController and PrizePool and the issue stated is accurate in that if configured incorrectly, some prizes may not be possible to claim. However, the TwabController and PrizePool have complex configurations and are meant to be deployed by experts who know the system's limitations.
The TWAB limitation with regards to historical lookups is documented in the observation lib and was set in such a way to support at least 1 year (almost 2 in reality) of historical lookups when using the minimum TWAB period of 1 hour: https://github.com/sherlock-audit/2024-05-pooltogether/blob/1aa1b8c028b659585e4c7a6b9b652fb075f86db3/pt-v5-twab-controller/src/libraries/ObservationLib.sol#L14
The severity of this issue is much lower than what is claimed since it requires the prize pool to be incorrectly configured with regards to the system's documented limitations.
It is also worth noting that the recommended mitigation would cause a vulnerability in the prize pool since new observations could force the oldest observation out of the ring buffer, allowing depositors to manipulate their observations to gain more chances to win a prize that looks at a time period longer than the supported TWAB history.
0x73696d616f
medium
Some tiers will not be awarded in the PrizePool when the last observation has been erased in the TwabController
Summary
Tiers are awarded based on the past twab supplies registered in the
TwabController
, but it reverts if an observation that has been erased due to the circular buffer is tried to fetch. Thus, some tiers will not be claimed.Vulnerability Detail
In
PrizePool::isWinner()
, thetwab
balances are fetched from theTwabController
since startDrawIdInclusive. This variable is calculated using PrizePool::computeRangeStartDrawIdInclusive(), where_endDrawIdInclusive
is thelastAwardedDrawId_
and_rangeSize
is an estimate of the prize frequency in draws, basically1 / odds
rounded up.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 thetwab
balance from theTwabController
since the opening ofdrawId == 1
.The last awarded draw in the example is
lastAwardedDrawId_ == 366
, which means that at least366
draw periods have passed sincedrawId == 1
. IfdrawPeriodSeconds
is2 days
, this is 732 days in the past. TheTwabController
allows a minimumPERIOD_LENGTH
of1 hour
, and can store up to17520
observations, which is17520 / 24 == 730
days. It stores the observations in a circular buffer, which means that past observations are overwritten after the buffer has been filled.The
PrizeVault
tries to fetch an observation that no longer exists,startDrawIdInclusive == 1
, aslastAwardedDrawId_ == 366
, so the currentdrawId
is at least367
, which means that it has already overriden index0
of the ring buffer. In this case, onDrawAccumulatorLib::getDisbursedBetween()
, it fetches the oldest possible index, which is index1
. Thus, thePrizeVault
is not problematic in this scenario.However, the
TwabController
reverts if an observation older than the oldest in the ring buffer is tried to fetch, as is the case here due to the fact that it can only store730
days, which means that it would be impossible to claim the prize.This will also happen when users are late to claim their shutdown portions, as
twab
values in theTwabController
can keep being registered after thePrizePool
has shutdown, so the timestamp corresponding tostartDrawIdInclusive
in theTwabController
may have been erased (after shutdown values can not be added to the accumulators in thePrizePool
).Impact
Impossibility to claim prizes when the
startDrawIdInclusive
is older than the oldest observation in theTwabController
. Also, impossible to claim the shutdown portion if theTwabController
has been overwritten enough times after the shutdown.Code Snippet
https://github.com/sherlock-audit/2024-05-pooltogether/blob/main/pt-v5-twab-controller/src/libraries/TwabLib.sol#L648-L651
Tool used
Manual Review
Vscode
Recommendation
Instead of reverting in the
TwabController
, use the oldest possible observation, which is the best estimate in this case that still allows claiming prizes.