Blast the balloon SC Audit
Users are allowed to buy a spin during the existence of a round, meaning between its start and end time (start time plus duration), including the end time.
When a spin is bought, a random number generation is initiated via the pyth entropy contract. It takes the pyth contract exactly 1 block to call the game callback with the newly generate random number. As an Optimism fork, Blast has 2 seconds blocks.
Users that purchase a spin exactly at the end of a round, specifically exactly at the end time (roundInfo.startTime + roundInfo.duration) and 1 second prior, are losing funds since the execution of the entropyCallback function by the pyth contract will be done after the round has been closed, reverting the transaction.
Since blast masters positions can be overwritten, users are incentivize to hold out on buying spins as late as possible in order to protect their potential gains. This increases the likelihood of this issue manifesting.
In the buySpin function, when checking that the current round has not ended, reduce the duration with 2 seconds to compensate for positions opened in the last moment.
Note to deduct only from duration and to also validate if the deduction would underflow.
Hardcoding a time ― block number pairing is not normally indicated but in the case of the Blast blockchain, even if a blockchain upgrade reduces the block time, users at most will lose the possibility to spin for an few fractions of a second.
Example implementation:
uint32 public constant DURATION_TOLERANCE = 2 seconds;
uint32 adjustedDuration = roundInfo.duration < DURATION_TOLERANCE ? 0 : roundInfo.duration - DURATION_TOLERANCE;
require(block.timestamp <= roundInfo.startTime + adjustedDuration, BlastTheBalloonErrors.ROUND_ENDED);
Blast the balloon SC Audit