Platform: Code4rena
Start Date: 29/06/2022
Pot Size: $50,000 USDC
Total HM: 20
Participants: 133
Period: 5 days
Judge: hickuphh3
Total Solo HM: 1
Id: 142
League: ETH
Rank: 64/133
Findings: 2
Award: $70.19
π Selected for report: 0
π Solo Findings: 0
π Selected for report: xiaoming90
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xSolus, 0xf15ers, 0xsanson, AmitN, Bnke0x0, BowTiedWardens, Chom, David_, ElKu, Funen, GalloDaSballo, GimelSec, Hawkeye, IllIllI, JC, JohnSmith, Kaiziron, Kenshin, Lambda, Limbooo, MadWookie, Metatron, MiloTruck, Nethermind, Picodes, ReyAdmirado, Sneakyninja0129, StErMi, TomJ, Treasure-Seeker, TrungOre, Waze, Yiko, _Adam, __141345__, antonttc, async, aysha, catchup, cccz, cryptphi, csanuragjain, danb, datapunk, defsec, delfin454000, dirk_y, doddle0x, durianSausage, exd0tpy, fatherOfBlocks, gogo, hake, hansfriese, horsefacts, hubble, itsmeSTYJ, joestakey, oyc_109, pedroais, peritoflores, rajatbeladiya, reassor, robee, rokinot, samruna, saneryee, sashik_eth, shenwilly, shung, simon135, sseefried, unforgiven, zer0dot, zzzitron
47.1306 USDC - $47.13
Redundant import of "openzeppelin/utils/String.sol". Library not used in the context of the file/contract.
Remove the import statement at line 5 of the PuttyV2Nft.sol
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2Nft.sol#L5
π Selected for report: GalloDaSballo
Also found by: 0v3rf10w, 0x1f8b, 0xA5DF, 0xDjango, 0xHarry, 0xKitsune, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xkatana, 0xsanson, ACai, Aymen0909, Bnke0x0, BowTiedWardens, Chom, ElKu, Fitraldys, Funen, Haruxe, Hawkeye, IllIllI, JC, JohnSmith, Kaiziron, Kenshin, Lambda, Limbooo, MadWookie, Metatron, MiloTruck, Picodes, PwnedNoMore, Randyyy, RedOneN, ReyAdmirado, Ruhum, Sm4rty, StErMi, StyxRave, TerrierLover, TomJ, Tomio, UnusualTurtle, Waze, Yiko, _Adam, __141345__, ajtra, ak1, apostle0x01, asutorufos, c3phas, cRat1st0s, catchup, codetilda, cryptphi, datapunk, defsec, delfin454000, durianSausage, exd0tpy, fatherOfBlocks, gogo, grrwahrr, hake, hansfriese, horsefacts, ignacio, jayfromthe13th, joestakey, ladboy233, m_Rassska, mektigboy, minhquanym, mrpathfindr, natzuu, oyc_109, rajatbeladiya, reassor, rfa, robee, rokinot, sach1r0, saian, sashik_eth, simon135, slywaters, swit, z3s, zeesaw, zer0dot
23.0597 USDC - $23.06
One of most the expensive opcodes is SLOAD.
It is better to cache arr.length in a stack variable like uint256 arrLength in order to save gas upon each lookup of the for-loops.
Cache arrays' length in memory in order to save gas from SLOAD on every iteration.
++i costs less gas than i++
Replace each i++ with ++i, especially in for-loops
i
counter in for-loopsUint256's default value is zero so it is useless and a waste of gas to initialize it to 0
Use uint256 i;
instead of uint256 i = 0;
- 497 uint256 feeAmount = 0; + 497 uint256 feeAmount;
++i
in unchecked {}
Since Solidity version 0.8 overflow and underflow checks are implemented in Solidity compiler. \
So it is applied by default on every arithmetic operation and costs additional gas.
In the case of for-loop i
will always be less than the target (e.g. arr.length
) so those checks are unnecessary.
Put each ++i
in unchecked {}
in for-loops
- 556 for (uint256 i = 0; i < orders.length; ++i) { + 556 for (uint256 i = 0; i < orders.length;) { + 557 ... + 558 unchecked { + 559 ++i + 560 } + 561 }
In https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
556 for (uint256 i = 0; i < orders.length; i++)
594 for (uint256 i = 0; i < assets.length; i++)
611 for (uint256 i = 0; i < orders.length; i++)
627 for (uint256 i = 0; i < floorTokens.length; i++)
637 for (uint256 i = 0; i < assets.length; i++)
647 for (uint256 i = 0; i < assets.length; i++)
658 for (uint256 i = 0; i < floorTokens.length; i++)
670 for (uint256 i = 0; i < whitelist.length; i++)
728 for (uint256 i = 0; i < arr.length; i++)
742 for (uint256 i = 0; i < arr.length; i++)
Custom errors introduced in Solidity version 0.8.4 cost less than require()/revert() because of the missing string messages.
Use if
checks with descriptive custom errors instead of require expressions with string parameters.
In https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
- 551 require(orders.length == signatures.length, "Length mismatch in input"); + 551 if(orders.length != signatures.length) revert LengthMismatch();
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
214 require(_weth != address(0), "Unset weth address");
241 require(_fee < 30, "fee must be less than 3%");
278 require(SignatureChecker.isValidSignatureNow(order.maker, orderHash, signature), "Invalid signature");
281 require(!cancelledOrders[orderHash], "Order has been cancelled");
284 require(order.whitelist.length == 0 || isWhitelisted(order.whitelist, msg.sender), "Not whitelisted");
287 require(order.duration < 10_000 days, "Duration too long");
290 require(block.timestamp < order.expiration, "Order has expired");
293 require(order.baseAsset.code.length > 0, "baseAsset is not contract");
297 ? require(floorAssetTokenIds.length == order.floorTokens.length, "Wrong amount of floor tokenIds")
298 : require(floorAssetTokenIds.length == 0, "Invalid floor tokens length");
329 require(msg.value == order.premium, "Incorrect ETH amount sent");
353 require(msg.value == order.strike, "Incorrect ETH amount sent");
395 require(ownerOf(uint256(orderHash)) == msg.sender, "Not owner");
398 require(order.isLong, "Can only exercise long positions");
401 require(block.timestamp < positionExpirations[uint256(orderHash)], "Position has expired");
405 ? require(floorAssetTokenIds.length == order.floorTokens.length, "Wrong amount of floor tokenIds")
406 : require(floorAssetTokenIds.length == 0, "Invalid floor tokenIds length");
429 require(msg.value == order.strike, "Incorrect ETH amount sent");
470 require(!order.isLong, "Must be short position");
475 require(ownerOf(uint256(orderHash)) == msg.sender, "Not owner");
481 require(block.timestamp > positionExpirations[longPositionId] || isExercised, "Must be exercised or expired");
527 require(msg.sender == order.maker, "Not your order");
551 require(orders.length == signatures.length, "Length mismatch in input");
552 require(signatures.length == floorAssetTokenIds.length, "Length mismatch in input");
598 require(token.code.length > 0, "ERC20: Token is not contract");
599 require(tokenAmount > 0, "ERC20: Amount too small");
765 require(_ownerOf[id] != address(0), "URI query for NOT_MINTED token");
Use else if
instead of return expressions
In function fillOrder https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol
345 return positionId;
363 return positionId;
370 return positionId;
378 return positionId;
There is no reason to use a named return varible instead of directly returned value in this cases:
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L689
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L719
Storing function parameters is cheaper if they are located in calldata because copying them in memory cost gas
Store function parameters in calldata where possible and be careful
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L269
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L389
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L466
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L526
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L547
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L574
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L576
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L593
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L610
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L623
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L636
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L646
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L657 floorTokens only!<br>
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L669 <br>
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L683
Functions that are not being called from inside the contract through its lifecycle should be marked as external. External function parameters are located in calldata by default which saves gas comparing to the memory location in public functions.
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L577
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L466
https://github.com/code-423n4/2022-06-putty/blob/main/contracts/src/PuttyV2.sol#L389