Platform: Code4rena
Start Date: 13/12/2022
Pot Size: $36,500 USDC
Total HM: 5
Participants: 77
Period: 3 days
Judge: gzeon
Total Solo HM: 1
Id: 191
League: ETH
Rank: 14/77
Findings: 2
Award: $346.08
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 9svR6w
Also found by: 0xdeadbeef0x, BAHOZ, codeislight, deliriusz, gasperpre, trustindistrust
320.1346 USDC - $320.13
no tools were used, just manual analysis
a practical mitigation would be to have a mapping to check the keyHash validity in VRFNFTRandomDrawFactory:
mapping (bytes32 => bool) public isValidKeyHash; /// @notice Constructor to set the implementation constructor(address _implementation, bytes32[] calldata _keyHashes) initializer { ... for(uint i = 0; i < _keyHashes.length;){ isValidKeyHash[_keyHashes[i]] = true; unchecked { ++i; } } } ... error InvalidKeyHash(); /// @notice Function to make a new drawing /// @param settings settings for the new drawing function makeNewDraw(IVRFNFTRandomDraw.Settings memory settings) external returns (address) { if(!isValidKeyHash[settings.keyHash]) revert InvalidKeyHash(); ... }
#0 - c4-judge
2022-12-17T16:26:29Z
gzeon-c4 marked the issue as duplicate of #194
#1 - c4-judge
2023-01-23T16:51:17Z
gzeon-c4 marked the issue as satisfactory
🌟 Selected for report: c3phas
Also found by: 0x1f8b, Aymen0909, Bnke0x0, Bobface, IllIllI, PaludoX0, Rahoz, RaymondFam, ReyAdmirado, Rolezn, Sathish9098, adriro, chaduke, codeislight, ctrlc03, indijanc, izhelyazkov, kuldeep, nadin, neko_nyaa, nicobevi, rvierdiiev, shark
25.9485 USDC - $25.95
in makeNewDraw function, newDrawing variable could be defined as a variable in the return area to save some gas
function makeNewDraw(IVRFNFTRandomDraw.Settings memory settings) external returns (address newDrawing) { address admin = msg.sender; // Clone the contract newDrawing = ClonesUpgradeable.clone(implementation); // Setup the new drawing IVRFNFTRandomDraw(newDrawing).initialize(admin, settings); // Emit event for indexing emit SetupNewDrawing(admin, newDrawing); }
use unchecked in calculations that is impossible to overflow:
function _requestRoll() internal { ... unchecked{ // Setup re-draw timelock request.drawTimelock = block.timestamp + settings.drawBufferTime; } ... } function fulfillRandomWords( uint256 _requestId, uint256[] memory _randomWords ) internal override { ... unchecked { // Get total token range uint256 tokenRange = settings.drawingTokenEndId - settings.drawingTokenStartId; // Store a number from it here (reduce number here to reduce gas usage) // We know there will only be 1 word sent at this point. request.currentChosenTokenId = (_randomWords[0] % tokenRange) + settings.drawingTokenStartId; } ... }
assign state variables after revert checks, to save gas on SSTORE in case it reverts:
function initialize(address admin, Settings memory _settings) public initializer { // Check values in memory: if (_settings.drawBufferTime < HOUR_IN_SECONDS) { revert REDRAW_TIMELOCK_NEEDS_TO_BE_MORE_THAN_AN_HOUR(); } if (_settings.drawBufferTime > MONTH_IN_SECONDS) { revert REDRAW_TIMELOCK_NEEDS_TO_BE_LESS_THAN_A_MONTH(); } ... // Set new settings settings = _settings; ... }
emit events at the end of the functions to save gas on reverts, the following are the instances:
emit InitializedDraw(msg.sender, settings); emit SetupDraw(msg.sender, settings); emit WinnerSentNFT( user, address(settings.token), settings.tokenId, settings ); emit OwnerReclaimedNFT(owner());
#0 - c4-judge
2022-12-17T17:47:28Z
gzeon-c4 marked the issue as grade-b