Open code423n4 opened 1 year ago
asselstine marked the issue as sponsor confirmed
Picodes marked the issue as duplicate of #145
Picodes marked the issue as satisfactory
Picodes marked the issue as selected for report
Superceded by simplification of tier expansion logic: https://github.com/GenerationSoftware/pt-v5-prize-pool/pull/17
Lines of code
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L361 https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L446-L448 https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L781-L810
Vulnerability details
Impact
As mentioned in the docs, "The Canary Tier is a special prize tier that receives a smaller amount of prize liquidity. The Canary Tier informs the Prize Pool as to whether it's worth increasing the number of prize tiers. The canary tier's role is to let us know whether it's worth offering n+1 tiers of prizes".
The intended behaviour is that the number of tiers only increase if a high enough portion of both the highest non-canary tier prizes and the canary tier prizes are claimed, so that prizes scale with demand and liquidity. However, there is a bug that causes the number of tiers to increase if at least 1 canary prize is claimed. Therefore it is highly likely that it will take just 12 draws to reach the cap of 15 prize tiers (or an attacker could force the situation by claiming canary prizes), at which point the prize sizes will be broken based on the liquidity provided and the protocol will become almost unusable.
Proof of Concept
When a draw has finished/elapsed, the draw manager closes the draw by calling
closeDraw
. During this closing process the next number of tiers is computed:Before we dive into this computation, it's worth exploring the
claimPrize
method. Whenever a prize is claimed there is alargestTierClaimed
state variable that is set to the highest tier that has been claimed for (which can include the canary tier):Now, the issue titled in this report exists because of how the next number of tiers is calculated in the
_computeNextNumberOfTiers
function:For the sake of argument let's say the prize pool had 3 tiers, so the canary tier is tier 2. If there is at least 1 canary tier prize that has been claimed for the previous draw then
largestTierClaimed = 2
and therefore_nextNumberOfTiers = 4
. What's interesting here is that even if the check to expand the number of tiers fails, we still return_nextNumberOfTiers
which has just been set to 4. So we're expanding the number of tiers even if the claim count isn't higher than the claim expansion thresholds.I have made a tiny change to the existing test suite to demonstrate that the number of tiers will increase with just 1 canary prize claim. It can be executed with
forge test -vvv --match-path test/PrizePool.t.sol
:Tools Used
Manual review + foundry
Recommended Mitigation Steps
The
_computeNextNumberOfTiers
logic should be updated to the below:Assessed type
Math