Platform: Code4rena
Start Date: 17/03/2023
Pot Size: $36,500 USDC
Total HM: 10
Participants: 98
Period: 3 days
Judge: leastwood
Total Solo HM: 5
Id: 223
League: ETH
Rank: 46/98
Findings: 2
Award: $34.80
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Sathish9098
Also found by: 0xAgro, 0xSmartContract, 0xdaydream, 0xnev, Awesome, Aymen0909, BRONZEDISC, Bauchibred, Deathstore, Diana, IceBear, Jerry0x, Kresh, Matin, Rolezn, Stryder, T1MOH, Udsen, adriro, alejandrocovrr, atharvasama, codeslide, cryptonue, descharre, igingu, jack, joestakey, libratus, lukris02, luxartvinsec, nadin, nasri136, reassor, scokaf, shark, slvDev, tnevler
22.7749 USDC - $22.77
Context:
SafeTransferLib.safeTransferFrom(note, msg.sender, revenueAddress, _amount * trayPrice);
L157Description:
The following code will not revert in case the token doesn’t exist.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
L9
Recommendation:
Add isContract() function:
function isContract(address _addr) private returns (bool isContract) { isContract = _addr.code.length > 0; }
Context:
Recommendation:
Add non-zero address checks when set address state variables.
Context:
Description:
"solmate/auth/Owned.sol" implements transferOwnership function. Lack of two-step procedure for transfer of ownership makes it error-prone. It’s possible that the owner mistakenly transfers ownership to the uncontrolled account and it will break all functions with onlyOwner() modifier.
Recommendation:
Implement a two step process for transfer of ownership. Owner call transferOwnership() function where nominates an account. Nominated account needs to call an acceptOwnership() function for the transfer of ownership to fully succeed. This will confirm that the nominated EOA account is a valid and active account.
Context:
Description: Checks Effects Interactions pattern reduce the attack surface for malicious contracts trying to hijack control flow after an external call.
Recommendation: Change to:
if (ERC721(_nftContract).ownerOf(_nftID) != msg.sender) revert PFPNotOwnedByCaller(msg.sender, _nftContract, _nftID); uint256 tokenId = ++numMinted;
Context:
note = ERC20(_newNoteAddress);
L198revenueAddress = _newRevenueAddress;
L206note = ERC20(_newNoteAddress);
L212revenueAddress = _newRevenueAddress;
L220Recommendation:
The best practice is to use two-step procedure for critical changes to make them less error-prone.
Context:
// uint256 constant EMOJIS_LE_FOURTEEN_BYTES = 420;
L55
Context:
trayPrice = _trayPrice;
L106
Recommendation:
Define the minimum and maximum allowable price per tray to prevent setting the wrong trayPrice value, and validate it in constructor.
Context:
ICidNFT private immutable cidNFT;
L14 (cidNFT should start with _)mapping(uint256 => ProfilePictureData) private pfp;
L32 (pfp should start with _)mapping(uint256 => Tray.TileData[]) private nftCharacters;
L49 (nftCharacters should start with _)uint256 private constant TILES_PER_TRAY = 7;
L19 (TILES_PER_TRAY should start with _)uint256 private constant SUM_ODDS = 109;
L22 (SUM_ODDS should start with _)uint256 private constant NUM_CHARS_EMOJIS = 420;
L25 (NUM_CHARS_EMOJIS should start with _)uint256 private constant NUM_CHARS_LETTERS = 26;
L28 (NUM_CHARS_LETTERS should start with _)uint256 private constant NUM_CHARS_LETTERS_NUMBERS = 36;
L31 (NUM_CHARS_LETTERS_NUMBERS should start with _)uint256 private constant PRE_LAUNCH_MINT_CAP = 1_000;
L34 (PRE_LAUNCH_MINT_CAP should start with _)address private revenueAddress;
L44 (revenueAddress should start with _)mapping(uint256 => TileData[TILES_PER_TRAY]) private tiles;
L67 (tiles should start with _)uint256 private prelaunchMinted = type(uint256).max;
L73 (prelaunchMinted should start with _)bytes private constant FONT_SQUIGGLE =
L16 (FONT_SQUIGGLE should start with _)bytes private constant ZALGO_ABOVE_LETTER =
L20 (ZALGO_ABOVE_LETTER should start with _)uint256 private constant ZALGO_NUM_ABOVE = 46;
L24 (ZALGO_NUM_ABOVE should start with _)bytes private constant ZALGO_BELOW_LETTER =
L27 (ZALGO_BELOW_LETTER should start with _)uint256 private constant ZALGO_NUM_BELOW = 47;
L31 (ZALGO_NUM_BELOW should start with _)bytes private constant ZALGO_OVER_LETTER = hex"CCB4CCB5CCB6CCB7CCB8";
L34 (ZALGO_OVER_LETTER should start with _)uint256 private constant ZALGO_NUM_OVER = 5;
L37 (ZALGO_NUM_OVER should start with _)bytes private constant EMOJIS =
L42 (EMOJIS should start with _)uint256 private constant EMOJIS_LE_THREE_BYTES = 17;
L50 (EMOJIS_LE_THREE_BYTES should start with _)uint256 private constant EMOJIS_LE_FOUR_BYTES = 383;
L51 (EMOJIS_LE_FOUR_BYTES should start with _)uint256 private constant EMOJIS_LE_SIX_BYTES = 409;
L52 (EMOJIS_LE_SIX_BYTES should start with _)uint256 private constant EMOJIS_LE_SEVEN_BYTES = 416;
L53 (EMOJIS_LE_SEVEN_BYTES should start with _)uint256 private constant EMOJIS_LE_EIGHT_BYTES = 419;
L54 (EMOJIS_LE_EIGHT_BYTES should start with _)uint256 private constant EMOJIS_MOD_SKIN_TONE_THREE_BYTES = 2;
L58 (EMOJIS_MOD_SKIN_TONE_THREE_BYTES should start with _)uint256 private constant EMOJIS_MOD_SKIN_TONE_FOUR_BYTES = 47;
L59 (EMOJIS_MOD_SKIN_TONE_FOUR_BYTES should start with _)uint256 private constant EMOJIS_BYTE_OFFSET_FOUR_BYTES = 51; // 17 * 3
L63 (EMOJIS_BYTE_OFFSET_FOUR_BYTES should start with _)uint256 private constant EMOJIS_BYTE_OFFSET_SIX_BYTES = 1515; // 17 * 3 + 366 * 4
L64 (EMOJIS_BYTE_OFFSET_SIX_BYTES should start with _)uint256 private constant EMOJIS_BYTE_OFFSET_SEVEN_BYTES = 1671; // 17 * 3 + 366 * 4 + 26 * 6
L65 (EMOJIS_BYTE_OFFSET_SEVEN_BYTES should start with _)uint256 private constant EMOJIS_BYTE_OFFSET_EIGHT_BYTES = 1720; // 17 * 3 + 366 * 4 + 26 * 6 + 7 * 7
L66 (EMOJIS_BYTE_OFFSET_EIGHT_BYTES should start with _)uint256 private constant EMOJIS_BYTE_OFFSET_FOURTEEN_BYTES = 1744; // 17 * 3 + 366 * 4 + 26 * 6 + 7 * 7 + 3 * 8
L67 (EMOJIS_BYTE_OFFSET_FOURTEEN_BYTES should start with _)Description:
The above codes don't follow Solidity's standard naming convention.
Context:
function _beforeTokenTransfers(
L231function _drawing(uint256 _seed) private pure returns (TileData memory tileData) {
L245Context:
/// @dev Generates an on-chain SVG with a new line after 40 bytes. Line splitting generally supports UTF-8 multibyte characters and emojis, but is not tested for arbitrary UTF-8 characters
L41// Because we do not split on zero-width joiners, line in bytes can technically be much longer. Will be shortened to the needed length afterwards
L53bytes memory bName = new bytes(numCharacters * 33); // Used to convert into a string. Can be 33 times longer than the string at most (longest zalgo characters is 33 bytes)
L117uint256 charRandValue = Utils.iteratePRNG(_seed); // Iterate PRNG to not have any biasedness / correlation between random numbers
L247Description:
Maximum suggested line length is 120 characters.
#0 - c4-judge
2023-04-11T05:43:03Z
0xleastwood marked the issue as grade-b
🌟 Selected for report: 0xSmartContract
Also found by: 0xdaydream, 0xnev, Aymen0909, Deekshith99, Diana, EvanW, Fanz, JCN, Jerry0x, K42, Kresh, Madalad, MiniGlome, Polaris_tow, Rageur, ReyAdmirado, Rolezn, SAAJ, SaeedAlipoor01988, Sathish9098, Shubham, Udsen, Viktor_Cortess, Walter, anodaram, arialblack14, atharvasama, caspersolangii, codeslide, descharre, fatherOfBlocks, felipe, ginlee, igingu, lukris02, nadin, slvDev, tnevler, turvy_fuzz, viking71
12.034 USDC - $12.03
Context:
if ((i > 0 && (i + 1) % 40 == 0) || prevByteWasContinuation || i == lengthInBytes - 1) {
L60 (lengthInBytes - 1)if (i != lengthInBytes - 1) {
L62bioTextBytes[i - 2] == 0xE2 &&
L80bioTextBytes[i - 1] == 0x80 &&
L81uint256 fusingCosts = 2**(13 - numCharacters) * 1e18;
L113prelaunchMinted = _nextTokenId() - 1;
L227tileData.fontClass = 3 + uint8((res - 80) / 8);
L259tileData.fontClass = 5 + uint8((res - 96) / 4);
L261tileData.fontClass = 7 + uint8((res - 104) / 2);
L263supportsSkinToneModifier = _characterIndex >= EMOJIS_LE_THREE_BYTES - EMOJIS_MOD_SKIN_TONE_THREE_BYTES;
L86byteOffset = EMOJIS_BYTE_OFFSET_FOUR_BYTES + (_characterIndex - EMOJIS_LE_THREE_BYTES) * 4;
L89supportsSkinToneModifier = _characterIndex >= EMOJIS_LE_FOUR_BYTES - EMOJIS_MOD_SKIN_TONE_FOUR_BYTES;
L90byteOffset = EMOJIS_BYTE_OFFSET_SIX_BYTES + (_characterIndex - EMOJIS_LE_FOUR_BYTES) * 6;
L93byteOffset = EMOJIS_BYTE_OFFSET_SEVEN_BYTES + (_characterIndex - EMOJIS_LE_SIX_BYTES) * 7;
L96byteOffset = EMOJIS_BYTE_OFFSET_EIGHT_BYTES + (_characterIndex - EMOJIS_LE_SEVEN_BYTES) * 8;
L99byteOffset = EMOJIS_BYTE_OFFSET_FOURTEEN_BYTES + (_characterIndex - EMOJIS_LE_EIGHT_BYTES) * 14;
L102Description:
Some gas can be saved by using an unchecked {} block if an underflow isn't possible because of a previous require() or if-statement.
Context:
if (ERC721(_nftContract).ownerOf(_nftID) != msg.sender)
L81
Recommendation: Change to:
if (ERC721(_nftContract).ownerOf(_nftID) != msg.sender) revert PFPNotOwnedByCaller(msg.sender, _nftContract, _nftID); uint256 tokenId = ++numMinted;
#0 - c4-judge
2023-04-10T23:57:27Z
0xleastwood marked the issue as grade-b