Platform: Code4rena
Start Date: 04/02/2022
Pot Size: $30,000 USDC
Total HM: 3
Participants: 37
Period: 3 days
Judge: leastwood
Id: 84
League: ETH
Rank: 23/37
Findings: 2
Award: $130.06
🌟 Selected for report: 0
🚀 Solo Findings: 0
95.9596 USDC - $95.96
the contract can be initialized with tokenInLimit = 0, which DoS the system. Users won’t be able to buy any tokens and the system won’t work well in that case.
Before the declaration of both the TokenSaleUpgradeable and BadgerGuestlistApi contrcats there is a TODO comment which is good to mention that you forgot to remove (or to do).
The saleStart and saleDuration can be set after the sale has begun, which can lead to the sale to be over / not started, which might be an unwanted behavior.
When the sale is paused, the tokens will be locked in the contract. This happens because the claim function requires that the sale isn’t paused, and the sweep function won’t transfer to the owner any tokens belonging to the users. This can be a very big problem if the contract is in a dangerous situation, for example under attack and can’t be unpaused.
The contract doesn’t promise that there will be enough tokenOut for the users to claim, so maybe it is correct to check it in the initialize function or something similar. If that is an expected behavior (for example you want to transfer tokens to the contract only after the sale) that it’s fine, but in my opinion doing it before makes the project more reliable and make the users trust it more.
#0 - GalloDaSballo
2022-02-14T12:48:36Z
I think the report is pretty good, don't necessarily agree with all, but the warden did a good job in summarizing what could be done better
34.1036 USDC - $34.10
The expression 10**tokenOut.decimals() is calculated every time that the getAmountOut() function is called. The value of the expression doesn’t change and it can be saved to save the gas wasted on this calculation. This can be also seen in the buy function, where the expression saleStart + saleDuration is calculated every time we call it. Those 2 variables are set in the initialize function and can be set through the set functions, so the value expression mentioned above can be saved and be updated every time a new value is assigned to one of these variables.
The call to getAmountOut in the buy function can be replaced with the code of the function itself in order to save gas (it doesn’t mean that the getAmountOut function must be deleted). This is done to save the gas wasted on the function call when calling the getAmountOut function.
The getTokenInLimitLeft function do the calculation of limitLeft_ = tokenInLimit - totalTokenIn only when totalTokenIn < tokenInLimit, which promises us that there won’t be underflow, so unchecked can be used here to save the gas spending on underflow checks.
#0 - GalloDaSballo
2022-02-14T12:49:50Z
Not convinced by the idea of inlining the public function, while the runtime savings would save a jump and some small stack operations, the bytecode would actually increase
Other findings are well thought out