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: 18/28
Findings: 2
Award: $619.15
🌟 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
181.0202 USDC - $181.02
The inheritance of NFTmarketOffer.sol is not aligned with other contracts, and it is put in single line even though it exceeds more than 80 characters.
abstract contract NFTMarketOffer is FoundationTreasuryNode, NFTMarketCore, ReentrancyGuardUpgradeable, NFTMarketFees {
The inheritance of NFTMarketCreators.sol is not set at single line even though it only inherits 2 contracts.
abstract contract NFTMarketCreators is Constants, ReentrancyGuardUpgradeable // Adding this unused mixin to help with linearization {
Hence, it is reasonable that the inheritance goes to next line for consistency.
abstract contract NFTMarketOffer is FoundationTreasuryNode, NFTMarketCore, ReentrancyGuardUpgradeable, NFTMarketFees {
Assembly code chainid()
can be replaced with block.chainid
at NFTMarketPrivateSale.sol.
uint256 chainId; // solhint-disable-next-line no-inline-assembly assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(NAME)), // Incrementing the version can be used to invalidate previously signed messages. keccak256(bytes("1")), block.chainid, marketProxyAddress ) );
Here is the updated code which simply uses block.chainid.
DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(NAME)), // Incrementing the version can be used to invalidate previously signed messages. keccak256(bytes("1")), block.chainid, marketProxyAddress ) );
#0 - HardlyDifficult
2022-03-03T12:19:46Z
block.chainid
existed - I'm all for removing assembly where ever possible. We have made the recommended change.Thanks for the feedback.
#1 - alcueca
2022-03-17T09:30:46Z
Unadjusted score: 10
🌟 Selected for report: Dravee
Also found by: 0x1f8b, CertoraInc, TerrierLover, csanuragjain, gzeon, iain, kenta, rfa, robee, thankthedark
Gas cost reduction by logic change at _getMinIncrement
function at NFTMarketCore.sol
_getMinIncrement
function at NFTMarketCore.sol seems to have an unnecessary logic which ends up adding more gas cost.
minIncrement
can be gotten by currentAmount * MIN_PERCENT_INCREMENT_IN_BASIS_POINTS / BASIS_POINTS
as well. Hence, using MIN_PERCENT_INCREMENT_IN_BASIS_POINTS and BASIS_POINTS seems unnecessary.
The current implementation is readable, but unnecessary which would increase gas cost.
function _getMinIncrement(uint256 currentAmount) internal pure returns (uint256) { uint256 minIncrement = currentAmount * MIN_PERCENT_INCREMENT_IN_BASIS_POINTS; unchecked { minIncrement /= BASIS_POINTS; if (minIncrement == 0) { // Since minIncrement reduces from the currentAmount, this cannot overflow. // The next amount must be at least 1 wei greater than the current. return currentAmount + 1; } } return minIncrement + currentAmount; }
yarn test
After rewriting the logic as follows, it has same logic while being able to reduce the gas.
function _getMinIncrement(uint256 currentAmount) internal pure returns (uint256) { uint256 minIncrement = currentAmount; unchecked { minIncrement /= MIN_PERCENT_INCREMENT; // <--- COMMENT: MIN_PERCENT_INCREMENT=10 if (minIncrement == 0) { // Since minIncrement reduces from the currentAmount, this cannot overflow. // The next amount must be at least 1 wei greater than the current. return currentAmount + 1; } } return minIncrement + currentAmount; }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5232850
Max=5232886
Avg=5232882
As a result, the above change can reduce 3405 gas cost on average.
Missing uncheck directory can reduce gas fee at NFTMarketCreators.sol
NFTMarketCreators.sol uses unchecked
directory at where it can be applied, but following part is not wrapped by unchecked
directory.
for (uint256 i = 0; i < _recipients.length; ++i) { if (_recipients[i] != address(0)) { hasRecipient = true; if (_recipients[i] == seller) { return (_recipients, recipientBasisPoints, true); } } }
unchecked
directory would reduce the gas fee
$ yarn test
By wrapping by unchecked
directory, it can reduce the gas fee.
unchecked { for (uint256 i = 0; i < _recipients.length; ++i) { if (_recipients[i] != address(0)) { hasRecipient = true; if (_recipients[i] == seller) { return (_recipients, recipientBasisPoints, true); } } } }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5234530
Max=5234566
Avg=5234562
As a result, the above change can reduce 1725 gas cost on average.
Duplicates condition check recipients.length == 0
increases gas fee at NFTMarketCreators.sol
NFTMarketCreators.sol has duplicate condition check recipients.length == 0
at multiple places which increase gas fee.
NFTMarketCreators.sol has up to 7th priority. 2th, 3rd, 4th, and 5th logic does recipients.length == 0
condition check, which can be removed by simply wrapping 2th, 3rd, 4th, and 5th logic by if (recipients.length == 0)
and remove the duplicate checks.
$ yarn test
This is an example of the modified code where the gas reduction was confirmed. https://gist.github.com/TerrierLover/53bb75cdb57e8237e2339682e2083298
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5229802
Max=5229838
Avg=5229834
As a result, the above change can reduce 6453 gas cost on average.
Using != 0
instead of > 0
can reduce gas fee at NFTMarketCreators.sol
NFTMarketCreators.sol uses _recipients.length > 0
at multiple places, and replacing this with _recipients.length != 0
can decrease the gas fee.
Following codes use _recipients.length > 0
which can be replaced by != 0
.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCreators.sol#L90
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCreators.sol#L153
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketCreators.sol#L185
_recipients
is an array of address, so it will never be less than 0. Hence, just checking _recipients.length != 0
is suffice.
$ yarn test
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After removing _recipients.length > 0
and using _recipients.length != 0
instead at NFTMarketCreators.sol, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5235907
Max=5235943
Avg=5235939
As a result, the above change can reduce 348 gas cost on average.
Improvement of _toAsciiString
and _char
function at AccountMigrationLibrary.sol
There are some unnecessary converstion, initialization, and calculation at _toAsciiString
and _char
function AccountMigrationLibrary.sol.
$ yarn test
Here is an optimization of _toAsciiString
and _char
function to reduce the gas usage.
function _toAsciiString(address x) private pure returns (string memory) { bytes memory s = new bytes(42); s[0] = "0"; s[1] = "x"; uint256 inAddress = uint256(uint160(x)); unchecked { for (uint256 i = 2; i < 42; i += 2) { uint8 b = uint8(inAddress >> (4 * (40 - i))); s[i] = _char(b >> 4); s[i + 1] = _char(b & 0x0f); } } return string(s); } function _char(uint8 b) private pure returns (bytes1 c) { unchecked { if (b < 10) return bytes1(b + 0x30); else return bytes1(b + 0x57); } }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5219693
Max=5219729
Avg=5219725
As a result, the above change can reduce 16562 gas cost on average.
_getMinIncrement
function at is NFTMarketReserveAuction.sol called twice
_getMinIncrement
function at is NFTMarketReserveAuction.sol called twice but calling twice seems not necessary. _getMinIncrement
function can be heavy, and just calling it once by changing the logic is beneficial.
if (auction.endTime < block.timestamp) { // The auction has already ended. revert NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(auction.endTime); } else if (auction.bidder == msg.sender) { // We currently do not allow a bidder to increase their bid unless another user has outbid them first. revert NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid(); } else if (amount < _getMinIncrement(auction.amount)) { // If this bid outbids another, it must be at least 10% greater than the last bid. revert NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(_getMinIncrement(auction.amount)); }
At the third else if
check, _getMinIncrement
function is called twice.
$ yarn test
Here is the example of the optimization.
if (auction.endTime < block.timestamp) { // The auction has already ended. revert NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(auction.endTime); } else if (auction.bidder == msg.sender) { // We currently do not allow a bidder to increase their bid unless another user has outbid them first. revert NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid(); } uint256 minIncrement = _getMinIncrement(auction.amount); if (amount < minIncrement) { // If this bid outbids another, it must be at least 10% greater than the last bid. revert NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(minIncrement); }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5234770
Max=5234806
Avg=5234802
As a result, the above change can reduce 1485 gas cost on average.
placeBidOf
function has unnecessary variable and logic at NFTMarketReserveAuction.sol
placeBidOf
function has unnecessary variable uint256 delta
and else if (amount > msg.value)
at NFTMarketReserveAuction.sol which can increase the gas cost.
unchecked { // The if above ensures delta will not underflow. uint256 delta = amount - msg.value; // Withdraw ETH from the buyer's account in the FETH token contract. feth.marketWithdrawFrom(msg.sender, delta); }
delta
variable is not needed to be defined.
$ yarn test
Here is the example of the optimization.
unchecked { // The if above ensures delta will not underflow. // Withdraw ETH from the buyer's account in the FETH token contract. feth.marketWithdrawFrom(msg.sender, amount - msg.value); }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5235403
Max=5235439
Avg=5235435
As a result, the above change can reduce 852 gas cost on average.
Duplicate logic to store the bid details at NFTMarketReserveAuction.sol
There is duplicate logic to store the bid details at NFTMarketReserveAuction.sol.
Following codes are duplicated at placeBidOf
funciton at NFTMarketReserveAuction.sol
.
auction.amount = amount; auction.bidder = payable(msg.sender);
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L420-L422 https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L444-L445
$ yarn test
if (auction.endTime == 0) { ... // Remove duplicate logic } else { ... // Remove duplicate logic } auction.amount = amount; // duplicate logic is moved here auction.bidder = payable(msg.sender); //
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5233078
Max=5233090
Avg=5233086
As a result, the above change can reduce 3201 gas cost on average.
Can use local variable for the addition block.timestamp + auction.extensionDuration
at NFTMarketReserveAuction.sol.
unchecked { // When a bid outbids another, check to see if a time extension should apply. // We confirmed that the auction has not ended, so endTime is always >= the current timestamp. if (auction.endTime - block.timestamp < auction.extensionDuration) { // Current time plus extension duration (always 15 mins) cannot overflow. auction.endTime = block.timestamp + auction.extensionDuration; } }
Subtraction and addition happen here, but can combine into one addition.
yarn test
Here is an example modification.
unchecked { // When a bid outbids another, check to see if a time extension should apply. // We confirmed that the auction has not ended, so endTime is always >= the current timestamp. uint256 endTime = block.timestamp + auction.extensionDuration; if (auction.endTime < endTime) { // Current time plus extension duration (always 15 mins) cannot overflow. auction.endTime = endTime; } }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236123
Max=5236135
Avg=5236131
As a result, the above change can reduce 156 gas cost on average.
placeBidOf
function has unnecessary variable and logic at NFTMarketReserveAuction.sol
placeBidOf
function has unnecessary variable uint256 delta
and else if (amount > msg.value)
at NFTMarketReserveAuction.sol which can increase the gas cost.
if (amount > msg.value) { // Withdraw additional ETH required from their available FETH balance. unchecked { // The if above ensures delta will not underflow uint256 delta = amount - msg.value; feth.marketWithdrawFrom(msg.sender, delta); } } else if (amount < msg.value) { // The terms of the sale cannot change, so if too much ETH is sent then something went wrong. revert NFTMarketPrivateSale_Too_Much_Value_Provided(); }
delta
variable is not needed to be defined.
$ yarn test
Here is the example of the optimization.
if (amount > msg.value) { // Withdraw additional ETH required from their available FETH balance. unchecked { // The if above ensures delta will not underflow feth.marketWithdrawFrom(msg.sender, amount - msg.value); } } else if (amount < msg.value) { // The terms of the sale cannot change, so if too much ETH is sent then something went wrong. revert NFTMarketPrivateSale_Too_Much_Value_Provided(); }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5235427
Max=5235439
Avg=5235435
As a result, the above change can reduce 852 gas cost on average.
LockBalance.sol contains some duplicated logic which increase gas cost
LockBalance.sol contains some duplicated logic which increase gas cost. Namely, index /= 2
or others.
Here are target functions which can reduce gas cost by refactoring.
function del(Lockups storage lockups, uint256 index) internal { unchecked { if (index % 2 == 0) { index /= 2; lockups.lockups[index] = (lockups.lockups[index] & last128BitsMask); } else { index /= 2; lockups.lockups[index] = (lockups.lockups[index] & first128BitsMask); } } }
function set( Lockups storage lockups, uint256 index, uint256 expiration, uint256 totalAmount ) internal { unchecked { uint256 lockedBalanceBits = totalAmount | (expiration << 96); if (index % 2 == 0) { // set first 128 bits. index /= 2; lockups.lockups[index] = (lockups.lockups[index] & last128BitsMask) | (lockedBalanceBits << 128); } else { // set last 128 bits. index /= 2; lockups.lockups[index] = (lockups.lockups[index] & first128BitsMask) | lockedBalanceBits; } } }
function setTotalAmount( Lockups storage lockups, uint256 index, uint256 totalAmount ) internal { unchecked { if (index % 2 == 0) { index /= 2; lockups.lockups[index] = (lockups.lockups[index] & firstAmountBitsMask) | (totalAmount << 128); } else { index /= 2; lockups.lockups[index] = (lockups.lockups[index] & secondAmountBitsMask) | totalAmount; } } }
yarn test
Here are example changes:
function del(Lockups storage lockups, uint256 index) internal { unchecked { bool isEven = index % 2 == 0; index /= 2; lockups.lockups[index] = (lockups.lockups[index] & (isEven ? last128BitsMask: first128BitsMask)); } }
function set( Lockups storage lockups, uint256 index, uint256 expiration, uint256 totalAmount ) internal { unchecked { uint256 lockedBalanceBits = totalAmount | (expiration << 96); bool isEven = index % 2 == 0; index /= 2; if (isEven) { // set first 128 bits. lockups.lockups[index] = (lockups.lockups[index] & last128BitsMask) | (lockedBalanceBits << 128); } else { // set last 128 bits. lockups.lockups[index] = (lockups.lockups[index] & first128BitsMask) | lockedBalanceBits; } } }
function setTotalAmount( Lockups storage lockups, uint256 index, uint256 totalAmount ) internal { unchecked { bool isEven = index % 2 == 0; index /= 2; if (isEven) { lockups.lockups[index] = (lockups.lockups[index] & firstAmountBitsMask) | (totalAmount << 128); } else { lockups.lockups[index] = (lockups.lockups[index] & secondAmountBitsMask) | totalAmount; } } }
Before the change, $ yarn test
returns gas cost for FETH:
Min=1545162
Max=1545174
Avg=1545174
After the change, $ yarn test
returns gas cost for FETH:
Min=1540631
Max=1540643
Avg=1540643
As a result, the above change can reduce 4531 gas cost on average.
constructor
of FETH.sol has some logic which can be wrapped by unchecked directory.
constructor
of FETH.sol has some logic which can be wrapped by unchecked directory. By doing so, it can reduce gas cost.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L191-L194
lockupInterval = _lockupDuration / 24; if (lockupInterval * 24 != _lockupDuration || _lockupDuration == 0) { revert FETH_Invalid_Lockup_Duration(); }
yarn test
Here is an example modifiction.
constructor(address payable _foundationMarket, uint256 _lockupDuration) { if (!_foundationMarket.isContract()) { revert FETH_Market_Must_Be_A_Contract(); } foundationMarket = _foundationMarket; lockupDuration = _lockupDuration; unchecked { lockupInterval = _lockupDuration / 24; if (lockupInterval * 24 != _lockupDuration || _lockupDuration == 0) { revert FETH_Invalid_Lockup_Duration(); } } }
_lockupDuration / 24
will not be underflown since _lockupDuration
is uint256.
lockupInterval * 24
is equivalent with (_lockupDuration / 24) * 24
so this will not be overflown.
Before the change, $ yarn test
returns gas cost for FETH:
Min=1545162
Max=1545174
Avg=1545174
After the change, $ yarn test
returns gas cost for FETH:
Min=1543566
Max=1543578
Avg=1543578
As a result, the above change can reduce 1596 gas cost on average.
Missing unchecked directory at _removeFromLockedBalance
function in FETH.sol
Potential gas reduction.
escrowIndex + 1
at below code can be wrapped by unchecked directory. According to the comment The number of escrow buckets will never overflow 32 bits
, it is safe to assume that escrowIndex
which is accountInfo.lockupStartIndex
will not overflown.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/FETH.sol#L611-L616
if (accountInfo.lockups.get(escrowIndex + 1).expiration != 0) { // The number of escrow buckets will never overflow 32 bits. unchecked { ++accountInfo.lockupStartIndex; } }
yarn test
Simply wrap escrowIndex + 1
by unchecked directory.
// Bump the escrow start index unless it's the last one unchecked { // The number of escrow buckets will never overflow 32 bits. if (accountInfo.lockups.get(escrowIndex + 1).expiration != 0) { ++accountInfo.lockupStartIndex; } }
Before the change, $ yarn test
returns gas cost for FETH:
Min=1545162
Max=1545174
Avg=1545174
After the change, $ yarn test
returns gas cost for FETH:
Min=1543410
Max=1543422
Avg=1543422
As a result, the above change can reduce 1752 gas cost on average.
Refactoring _getFees
function at NFTMarketFees.sol
can reduce gas cost
Gas reduction
Following codes can be wrapped by unchecked directory since they will not be underflown based on the values of constants (e.g., CREATOR_ROYALTY_BASIS_POINTS or others). https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketFees.sol#L199
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketFees.sol#L204
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketFees.sol#L209
yarn test
Here is an example code which can reduce gas cost.
foundationFee = (price * fee) / BASIS_POINTS; if (creatorRecipients.length > 0) { if (isCreator) { // When sold by the creator, all revenue is split if applicable. unchecked { creatorRev = price - foundationFee; } } else { // Rounding favors the owner first, then creator, and foundation last. creatorRev = (price * CREATOR_ROYALTY_BASIS_POINTS) / BASIS_POINTS; ownerRevTo = seller; unchecked { ownerRev = price - foundationFee - creatorRev; } } } else { // No royalty recipients found. ownerRevTo = seller; unchecked { ownerRev = price - foundationFee; } }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5229802
Max=5229838
Avg=5229834
As a result, the above change can reduce 6453 gas cost on average.
Remove unnecessary call of _sendValueWithFallbackWithdraw
from _distributeFunds
function in NFTMarketFees.sol
Gas reducation.
Following code does not need to be called when creatorFee - totalDistributed does not fullfill the condition. https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketFees.sol#L107-L112
// Send the remainder to the 1st creator, rounding in their favor _sendValueWithFallbackWithdraw( creatorRecipients[0], creatorFee - totalDistributed, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS );
yarn test
Here is an example of the modified code:
// Send the remainder to the 1st creator, rounding in their favor uint256 reminderAmount = creatorFee - totalDistributed; if (reminderAmount != 0) { _sendValueWithFallbackWithdraw( creatorRecipients[0], reminderAmount, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS ); }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5234566
Max=5234578
Avg=5234574
Wrapping ++i
with unchecked directory can reduce gas cost at _distributeFunds
function in NFTMarketFees.sol
gas cost reduction
++i
in the following code is not wrapped by unchecked.
https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketFees.sol#L101
for (uint256 i = 1; i <= maxCreatorIndex; ++i) { uint256 share = (creatorFee * creatorShares[i]) / totalShares; totalDistributed += share; _sendValueWithFallbackWithdraw(creatorRecipients[i], share, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS); }
yarn test
Here is an example of modified code:
// Send payouts to each additional recipient if more than 1 was defined uint256 totalDistributed; for (uint256 i = 1; i <= maxCreatorIndex; ) { uint256 share = (creatorFee * creatorShares[i]) / totalShares; totalDistributed += share; _sendValueWithFallbackWithdraw(creatorRecipients[i], share, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS); unchecked { ++i; } }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5234578
Max=5234590
Avg=5234586
Change the order of acceptOffer
function in NFTMarketOffer.sol can reduce gas fee.
Gas cost reduction
Currently, offer.buyer != offerFrom
if check is done after offer.expiration < block.timestamp
or offer.amount < minAmount
.
// Validate offer expiry and amount if (offer.expiration < block.timestamp) { revert NFTMarketOffer_Offer_Expired(offer.expiration); } else if (offer.amount < minAmount) { revert NFTMarketOffer_Offer_Below_Min_Amount(offer.amount); } // Validate the buyer if (offer.buyer != offerFrom) { revert NFTMarketOffer_Offer_From_Does_Not_Match(offer.buyer); }
yarn test
Simply changing the order may reduce the gas cost. From the code, if offer.buyer
is different from offerFrom
, it looks OK to change the order.
// Validate the buyer if (offer.buyer != offerFrom) { revert NFTMarketOffer_Offer_From_Does_Not_Match(offer.buyer); } // Validate offer expiry and amount if (offer.expiration < block.timestamp) { revert NFTMarketOffer_Offer_Expired(offer.expiration); } else if (offer.amount < minAmount) { revert NFTMarketOffer_Offer_Below_Min_Amount(offer.amount); }
Before the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5236255
Max=5236291
Avg=5236287
After the change, $ yarn test
returns gas cost for FNDNFTMarket:
Min=5235226
Max=5235262
Avg=5235258
As a result, the above change can reduce 1029 gas cost on average.
#0 - HardlyDifficult
2022-03-10T15:06:44Z
Thanks! This is a good report with several useful suggestions. We have made a number of changes you recommended here.
The gas numbers stated above are a bit misleading. You are quoting the deployment cost, which is not the goal for our gas optimizations. We are much more interested in saving gas for users. However many of the suggestions made apply to both. And reductions in the deployment cost typically signifies a size savings which was of interest as well.
!= 0
: It's surprising that this makes a difference, but yes we have made this change._getMinIncrement
called twice: Valid, but the second call is only for the revert case. To keep the code simple we have opted to not make this change.placeBidOf
variable: Valid. We have made this change since it seems to improve readability a bit, but it did not have a measurable impact.constructor
: Valid, but we are not interested in optimizing the constructors._removeFromLockedBalance
: Yes valid, made the change._getFees
: Yes a good point, we have made some changes here.acceptOffer
: Potentially valid but we are preferring to check the condition which we think will be more common first (expired).#1 - alcueca
2022-03-17T15:37:53Z
Score: 1200. Shame that the correct gas savings were not included.
#2 - alcueca
2022-03-17T15:39:56Z
Adding 500 points from #67