claimPrizesForRound() transfers the entire amount of a prize to a winner without considering the total number of winners for that prize.
The prize for a given round and prize index can be set by calling the setPrize() function, which pulls the amounts from the caller (the owner) and stores the prize data in the prizes array:
However, the claimPrizesForRound() function always transfers the full prize amounts to the first caller, regardless of the number of winners for the prize. Once the prize for a specific index is claimed, other winners of that prize cannot claim their share (or winners of other prizes may end up not being able to claim theirs), effectively being denied their winnings:
This approach can lead to scenarios where the amount available to be distributed among prize winners is less than that represented by the prizes stored in the prizes array.
This is considered medium severity because:
claimPrizesForRound() will revert if there aren't enough funds to transfer the prize to the winner, altering the user
the owner can mitigate this by simply "refilling" the prize as many times as needed
Proof of Concept
A prize is set with a certain amount of WETH and USDB for a specific round and prize index, intended for multiple winners.
Multiple users enter the round with tickets that end up winning this prize.
The first user to call claimPrizesForRound() for this round and prize index successfully claims the entire prize amount.
Subsequent winners attempting to claim their share of the prize for the same round and prize index find that they cannot, as the prize has already been fully distributed to the first caller.
Tools Used
Manual review
Recommended Mitigation Steps
It is unclear whether the amounts passed to setPrize() are meant to be distributed among all winners of the given prize or to be paid out to each winner, but the cleaner approach would be the latter. In that case, the amount pulled from the owner can simply be scaled by the number of winners:
Lines of code
https://github.com/code-423n4/2024-02-thruster/blob/3896779349f90a44b46f2646094cb34fffd7f66e/thruster-protocol/thruster-treasure/contracts/ThrusterTreasure.sol#L163-L184 https://github.com/code-423n4/2024-02-thruster/blob/3896779349f90a44b46f2646094cb34fffd7f66e/thruster-protocol/thruster-treasure/contracts/ThrusterTreasure.sol#L102-L134
Vulnerability details
Impact
claimPrizesForRound()
transfers the entire amount of a prize to a winner without considering the total number of winners for that prize.The prize for a given round and prize index can be set by calling the
setPrize()
function, which pulls the amounts from the caller (the owner) and stores the prize data in theprizes
array:ThrusterTreasure.sol#L163-L184
However, the
claimPrizesForRound()
function always transfers the full prize amounts to the first caller, regardless of the number of winners for the prize. Once the prize for a specific index is claimed, other winners of that prize cannot claim their share (or winners of other prizes may end up not being able to claim theirs), effectively being denied their winnings:ThrusterTreasure.sol#L102-L134
This approach can lead to scenarios where the amount available to be distributed among prize winners is less than that represented by the prizes stored in the
prizes
array.This is considered medium severity because:
claimPrizesForRound()
will revert if there aren't enough funds to transfer the prize to the winner, altering the userProof of Concept
claimPrizesForRound()
for this round and prize index successfully claims the entire prize amount.Tools Used
Manual review
Recommended Mitigation Steps
It is unclear whether the amounts passed to
setPrize()
are meant to be distributed among all winners of the given prize or to be paid out to each winner, but the cleaner approach would be the latter. In that case, the amount pulled from the owner can simply be scaled by the number of winners:Assessed type
Other