Platform: Code4rena
Start Date: 06/03/2023
Pot Size: $36,500 USDC
Total HM: 8
Participants: 93
Period: 3 days
Judge: cccz
Total Solo HM: 3
Id: 218
League: ETH
Rank: 55/93
Findings: 1
Award: $21.70
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: adriro
Also found by: 0x1f8b, 0xAgro, 0xSmartContract, 0xfuje, 0xkazim, 0xnev, Aymen0909, Bason, Cyfrin, DadeKuma, LethL, Madalad, MohammedRizwan, Rolezn, SAAJ, SunSec, Udsen, Yukti_Chinta, ast3ros, bin2chen, brgltd, bshramin, btk, bugradar, catellatech, cryptostellar5, descharre, dontonka, erictee, fatherOfBlocks, georgits, glcanvas, hl_, horsefacts, igingu, juancito, lukris02, martin, nadin, nomoi, peanuts, pipoca, sakshamguruji, seeu, slvDev, tnevler, zaskoh
21.7018 USDC - $21.70
_safeMint
instead of mint
for ERC721OpenZeppelin recommendation is to use _safeMint()
instead of mint()
for ERC721.
It is possible that the receiving contract does not support the ERC721 protocol, which can result in the minted NFT to be locked when using _mint()
.
_safeMint()
will ensure that the recipient implements IERC721Receiver or is an EOA.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/Ticket.sol#L26
Multiple functions in the project emit an event as the last statement. Wherever possible, consider emitting events before external calls. In case of reentrancy, funds are not at risk (for external call + event ordering), however emitting events after external calls can damage frontends and monitoring tools in case of reentrancy attacks.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L97-L99
StakedTokenLock
constructor for _stakedToken
If a IStaking gets configured with address zero, failure to immediately reset the value can result in unexpected behavior for the project.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/StakedTokenLock.sol#L18
Staking.rewardPerToken()
will multiply the unclaimed rewards by 1e18 and divide by the total supply. For a token created with decimals different than 18, the calculation behave unexpectedly. The likelihood will depend of a token with this behavior being used as reward. Similar problem in present on Staking.earned()
.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L58
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L62
Assume USDC is ever used for reward. Then Staking.rewardPerToken()
will return a bigger value than expected, and rewards will be inflated when calling Staking.getReward()
. Similarly, if a token with more than 18 decimals is used, the rewards will be smaller than expected.
Avoid hardcoding 1e18 in Staking.rewardPerToken()
and Staking.earned()
, and call the .decimals()
function for the token.
Locking the pragma helps to ensure that contracts do not accidentally get deployed using an outdated compiler version.
Note that pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case with contracts in a library or a package.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/VRFv2RNSource.sol#L3
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/StakedTokenLock.sol#L3
Consider transfering the funds after calling the _mint function in Staking.stake()
, similarly to how _burn is called before the transfer in Staking.withdraw()
.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L73-L74
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L85-L86
The project has demonstrated a good level of maturity and security awareness by checking all linter/static analysis items. However, some items can be solved instead of silenced. This can improve the quality of the codebase overall.
For example, it's possible to use safeTransfer instead of transfer for the instances highlighted bellow (even if the staked token is trusted - the cost for using safeTransfer is minimal and it will be in sync with the linter - no need for silencing it).
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/StakedTokenLock.sol#L32-L34
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/StakedTokenLock.sol#L45-L47
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/StakedTokenLock.sol#L53-L55
It is crucial to write tests with possibly 100% coverage for smart contracts.
The following contracts/libraries contain less than 70% branch coverage, after running forge coverage
.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/LotterySetup.sol#L12
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/ReferralSystem.sol#L9
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/LotteryMath.sol#L9
It is recommended to write tests for all possible code flows.
If the value being downcasted is bigger than expected, it can result in a silent overflow and result in accounting errors. Consider using the SafeCast library from OpenZeppelin.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/LotterySetup.sol#L170
As described on the solidity documentation. "The assert function creates an error of type Panic(uint256). … Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix.
Even if the code is expected to be unreachable, consider using a custom error/require statement instead of assert.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/LotterySetup.sol#L147
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/TicketUtils.sol#L99
The solidity documentation recommends placing public view functions bellow external mutative functions.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L46-L63
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L65-L124
If intentional, consider adding a comment near the winTier
loops when ignoring the first item of the array to illustrate why they're not being processed on the loop. The impact of being non-intentional is leaving the first item and causing issues for processing rewards and random numbers.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/LotterySetup.sol#L169
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/Lottery.sol#L211
The solidity documentation recommends a maximum of 120 characters.
Consider adding a limit of 120 characters or less to prevent large lines.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/LotterySetup.sol#L61
Repeated statements used for validation can be converted into a function modifier to improve code reusability.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L70
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L82
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/RNSourceBase.sol#L24
Using scientific notation for multiples of ten will improve code readability. E.g. 1000 => 1e3
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/PercentageMath.sol#L8
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/PercentageMath.sol#L11
Consider adding NATSPEC on all external/public functions to improve documentation.
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L67
It's possible to name the imports to improve code readability. E.g. import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
can be rewritten as import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
https://github.com/code-423n4/2023-03-wenwin/blob/main/src/LotteryToken.sol#L5
#0 - thereksfour
2023-03-12T07:16:26Z
1 L 4 INFO 12 NC
#1 - c4-judge
2023-03-12T07:16:53Z
thereksfour marked the issue as grade-a
#2 - c4-judge
2023-03-12T07:17:41Z
thereksfour marked the issue as grade-b
#3 - c4-sponsor
2023-03-14T09:54:22Z
0xluckydev marked the issue as sponsor disputed
#4 - thereksfour
2023-03-17T12:08:36Z
will treat mint -> safemint as low risk due to the fact that mint may cause NFT to freeze. see https://github.com/code-423n4/2022-09-artgobblers-findings/issues/321#issuecomment-1275317394