Platform: Code4rena
Start Date: 24/02/2022
Pot Size: $75,000 USDC
Total HM: 21
Participants: 28
Period: 7 days
Judge: alcueca
Total Solo HM: 15
Id: 94
League: ETH
Rank: 21/28
Findings: 2
Award: $442.06
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: leastwood
Also found by: 0x1f8b, 0xliumin, 0xwags, CertoraInc, Dravee, IllIllI, Ruhum, TerrierLover, WatchPug, cmichel, csanuragjain, defsec, gzeon, hubble, jayjonah8, kenta, kirk-baird, rfa, robee
292.7988 USDC - $292.80
2022-02-foundation
1 Use safeTransferFrom instead of transferFrom.
Openzeppelin says that Usage of this method is discouraged, use safeTransferFrom whenever possible. In these cases, you can use safeTransferFrom.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L264 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketPrivateSale.sol#L177 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCore.sol#L86 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCore.sol#L97 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCore.sol#L105
2 wrong description? (I am not sure.) ERC20 funds?
3 delete unnecessary code. I think the following code is not necessary, because this function is internal and called only by _buy. In _buy the related nftContractToTokenIdToBuyPrice will be deleted.
4 delete unused variable name for return value.
For example, the variable name market is not used, so you can delete it.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L693 delete market https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L661 delete amount
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCore.sol#L112 delete fetchAddress
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L358 delete amount
5 missing inputs validation. Length of nftContracts and tokenIds must be same
if (nftContracts.length != tokenIds.length) revert SomethingError();
6 address is not necessary.
Type of param nftContract is address, so you donโt need to convert it with address().
7 emit msg.sender and msg.value if contract receives ether.
For example, receive() external payable { emit EtherReceived(msg.sender, msg.value); }
#0 - HardlyDifficult
2022-03-09T11:07:16Z
transferFrom
because it lowers risk of reentrancy. But we may revisit this in the future.return
.#1 - alcueca
2022-03-17T09:49:41Z
Unadjusted score: 35
๐ Selected for report: Dravee
Also found by: 0x1f8b, CertoraInc, TerrierLover, csanuragjain, gzeon, iain, kenta, rfa, robee, thankthedark
2022-02-foundation gas optimization
1 replace storage with memory.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L126 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L313 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L359 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L388 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L108 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L126 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L205 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L282 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L317 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L338 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L535 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L236
2 Using cache at the beginning of the function to save gas. (before if statement)
function _invalidateOffer(address nftContract, uint256 tokenId) private { Offer memory offer = nftContractToIdToOffer[nftContract][nftContract]; if (offer.expiration >= block.timestamp) {} }
3 No need to use cache.
Cached variable is used only one time in these functions, so you donโt need to use cache.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L296 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L327 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L190 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketBuyPrice.sol#L305 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L236 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L382 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L662
For example,
if (nftContractToIdToOffer[nftContract][tokenId].buyer == msg.sender){}
4 code duplication.
You need to change visibility of getReserveAuctionIdFor to public. However, you can save gas for deployment by using getReserveAuctionIdFor in the following functions.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L667 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L587 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L533
And you can also think of the following cases.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L576 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L657
uint256 auctionId = getReserveAuctionIdFor(nftContract, tokenId);
5 use initial value for uint256.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCreators.sol#L94 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCreators.sol#L155 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCreators.sol#L193 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketFees.sol#L85 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketFees.sol#L101 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketOffer.sol#L161 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L274
for (uint256 i; i < nftContracts.length; ++i){}
#0 - HardlyDifficult
2022-03-09T19:06:46Z
storage
is most often less gas than memory
. The key here seems to be which slots are accessed. When memory
is used it makes a full copy, so all the data is read. However often in logic we are able to return after reading just one of the datapoints in the struct. By using storage we only read the data that's required. As a general best practice we have been using storage unless memory is required in order to access the original data (e.g. to emit after a delete
).memory
line).#1 - alcueca
2022-03-17T16:02:17Z
Without testing the gas savings, it's unfortunately difficult for wardens to ensure that the gas savings are real. Food for thought.
Score: 100