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: 50/77
Findings: 1
Award: $25.95
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
VRFNFTRandomDrawFactory.sol
function makeNewDraw(IVRFNFTRandomDraw.Settings memory settings) external returns (address) { address admin = msg.sender; // Clone the contract address newDrawing = ClonesUpgradeable.clone(implementation); // Setup the new drawing IVRFNFTRandomDraw(newDrawing).initialize(admin, settings); // Emit event for indexing emit SetupNewDrawing(admin, newDrawing); // Return address for integration or testing return newDrawing; }
Change it with
function makeNewDraw(IVRFNFTRandomDraw.Settings memory settings) external returns (address) { // Clone the contract address newDrawing = ClonesUpgradeable.clone(implementation); // Setup the new drawing IVRFNFTRandomDraw(newDrawing).initialize(msg.sender, settings); // Emit event for indexing emit SetupNewDrawing(msg.sender, newDrawing); // Return address for integration or testing return newDrawing; }
newDrawing
with a return variable newDrawing
function makeNewDraw(IVRFNFTRandomDraw.Settings memory settings) external returns (address) { address admin = msg.sender; // Clone the contract address newDrawing = ClonesUpgradeable.clone(implementation); // Setup the new drawing IVRFNFTRandomDraw(newDrawing).initialize(admin, settings); // Emit event for indexing emit SetupNewDrawing(admin, newDrawing); // Return address for integration or testing return newDrawing; }
Replace it with
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); }
calldata
instead of memory
to save gas on function input parametersfunction initialize(address admin, Settings memory _settings) public initializer {
function makeNewDraw(IVRFNFTRandomDraw.Settings memory settings) external returns (address) {
Replace it with
function initialize(address admin, Settings calldata _settings) public initializer {
function makeNewDraw(IVRFNFTRandomDraw.Settings calldata settings) external returns (address) {
settings
initialization after the validations to save gas if any validation is not fullfilledfunction initialize(address admin, Settings memory _settings) public initializer { // Set new settings settings = _settings; // ... _settings VALIDATIONS
Change it with:
function initialize(address admin, Settings memory _settings) public initializer { // ... _settings VALIDATIONS // Set new settings settings = _settings;
user
function winnerClaimNFT() external { // Assume (potential) winner calls this fn, cache. address user = msg.sender; // Check if this user has indeed won. if (!hasUserWon(user)) { revert USER_HAS_NOT_WON(); } // Emit a celebratory event emit WinnerSentNFT( user, address(settings.token), settings.tokenId, settings ); // Transfer token to the winter. IERC721EnumerableUpgradeable(settings.token).transferFrom( address(this), msg.sender, settings.tokenId ); }
Replace it with:
function winnerClaimNFT() external { // Check if this user has indeed won. if (!hasUserWon(msg.sender)) { revert USER_HAS_NOT_WON(); } // Emit a celebratory event emit WinnerSentNFT( msg.sender, address(settings.token), settings.tokenId, settings ); // Transfer token to the winter. IERC721EnumerableUpgradeable(settings.token).transferFrom( address(this), msg.sender, settings.tokenId ); }
settings
in memoryfunction winnerClaimNFT() external { // Assume (potential) winner calls this fn, cache. address user = msg.sender; // Check if this user has indeed won. if (!hasUserWon(user)) { revert USER_HAS_NOT_WON(); } // Emit a celebratory event emit WinnerSentNFT( user, address(settings.token), settings.tokenId, settings ); // Transfer token to the winter. IERC721EnumerableUpgradeable(settings.token).transferFrom( address(this), msg.sender, settings.tokenId ); }
Replace it with
function winnerClaimNFT() external { // Assume (potential) winner calls this fn, cache. address user = msg.sender; Settings memory settings_ = settings; // Check if this user has indeed won. if (!hasUserWon(user)) { revert USER_HAS_NOT_WON(); } // Emit a celebratory event emit WinnerSentNFT( user, address(settings_.token), settings_.tokenId, settings_ ); // Transfer token to the winter. IERC721EnumerableUpgradeable(settings_.token).transferFrom( address(this), msg.sender, settings_.tokenId ); }
constant
instead of immutable
if such variables won't be reassigned on construction tiem/// @notice Our callback is just setting a few variables, 200k should be more than enough gas. uint32 immutable callbackGasLimit = 200_000; /// @notice Chainlink request confirmations, left at the default uint16 immutable minimumRequestConfirmations = 3; /// @notice Number of words requested in a drawing uint16 immutable wordsRequested = 1; /// @dev 60 seconds in a min, 60 mins in an hour uint256 immutable HOUR_IN_SECONDS = 60 * 60; /// @dev 24 hours in a day 7 days in a week uint256 immutable WEEK_IN_SECONDS = (3600 * 24 * 7); // @dev about 30 days in a month uint256 immutable MONTH_IN_SECONDS = (3600 * 24 * 7) * 30;
Replace it with
/// @notice Our callback is just setting a few variables, 200k should be more than enough gas. uint32 constant callbackGasLimit = 200_000; /// @notice Chainlink request confirmations, left at the default uint16 constant minimumRequestConfirmations = 3; /// @notice Number of words requested in a drawing uint16 constant wordsRequested = 1; /// @dev 60 seconds in a min, 60 mins in an hour uint256 constant HOUR_IN_SECONDS = 60 * 60; /// @dev 24 hours in a day 7 days in a week uint256 constant WEEK_IN_SECONDS = (3600 * 24 * 7); // @dev about 30 days in a month uint256 constant MONTH_IN_SECONDS = (3600 * 24 * 7) * 30;
_requestRoll()
call below the nft transfer call// Request initial roll _requestRoll(); // Attempt to transfer token into this address try IERC721EnumerableUpgradeable(settings.token).transferFrom( msg.sender, address(this), settings.tokenId ) {} catch { revert TOKEN_NEEDS_TO_BE_APPROVED_TO_CONTRACT(); }
Replace it with
// Attempt to transfer token into this address try IERC721EnumerableUpgradeable(settings.token).transferFrom( msg.sender, address(this), settings.tokenId ) {} catch { revert TOKEN_NEEDS_TO_BE_APPROVED_TO_CONTRACT(); } // Request initial roll _requestRoll();
// Emit setup draw user event emit SetupDraw(msg.sender, settings); // Request initial roll _requestRoll(); // Attempt to transfer token into this address try IERC721EnumerableUpgradeable(settings.token).transferFrom( msg.sender, address(this), settings.tokenId ) {} catch { revert TOKEN_NEEDS_TO_BE_APPROVED_TO_CONTRACT(); }
Replace it with
// Request initial roll _requestRoll(); // Attempt to transfer token into this address try IERC721EnumerableUpgradeable(settings.token).transferFrom( msg.sender, address(this), settings.tokenId ) {} catch { revert TOKEN_NEEDS_TO_BE_APPROVED_TO_CONTRACT(); } // Emit setup draw user event emit SetupDraw(msg.sender, settings);
initialize
call// Set new settings settings = _settings; // ... // Setup owner as admin __Ownable_init(admin); // Emit initialized event for indexing emit InitializedDraw(msg.sender, settings); // Get owner of raffled tokenId and ensure the current owner is the admin try IERC721EnumerableUpgradeable(_settings.token).ownerOf( _settings.tokenId ) returns (address nftOwner) { // Check if address is the admin address if (nftOwner != admin) { revert DOES_NOT_OWN_NFT(); } } catch { revert TOKEN_BEING_OFFERED_NEEDS_TO_EXIST(); }
Move the try catch block above the storage initialization and event trigger
// Get owner of raffled tokenId and ensure the current owner is the admin try IERC721EnumerableUpgradeable(_settings.token).ownerOf( _settings.tokenId ) returns (address nftOwner) { // Check if address is the admin address if (nftOwner != admin) { revert DOES_NOT_OWN_NFT(); } } catch { revert TOKEN_BEING_OFFERED_NEEDS_TO_EXIST(); } // Set new settings settings = _settings; // Setup owner as admin __Ownable_init(admin); // Emit initialized event for indexing emit InitializedDraw(msg.sender, settings);
#0 - c4-judge
2022-12-17T17:48:31Z
gzeon-c4 marked the issue as grade-b