Platform: Code4rena
Start Date: 06/12/2022
Pot Size: $36,500 USDC
Total HM: 16
Participants: 119
Period: 3 days
Judge: berndartmueller
Total Solo HM: 2
Id: 189
League: ETH
Rank: 44/119
Findings: 3
Award: $66.79
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: RaymondFam
Also found by: 0xdeadbeef0x, 0xhacksmithh, AkshaySrivastav, Awesome, Bnke0x0, CRYP70, HollaDieWaldfee, JC, Parth, Rahoz, Tutturu, __141345__, ahmedov, ajtra, asgeir, aviggiano, bin2chen, btk, carrotsmuggler, cccz, chaduke, cryptonue, dic0de, fatherOfBlocks, fs0c, hansfriese, jonatascm, karanctf, ladboy233, lumoswiz, martin, obront, pashov, pauliax, rvierdiiev, shark, simon135, supernova, tourist, yellowBirdy, zapaz, zaskoh
0.6136 USDC - $0.61
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L105
The original transfer used to send eth uses a fixed stipend of 2300 gas. This was used to prevent reentrancy. However, this limits your protocol to interact with other contracts that need more than that to process the transaction good article about thathttps://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L105
' payable(msg.sender).transfer(owed);'
You used to call instead. For example
(bool success, ) = msg.sender.call{owed}(""); require(success, "Transfer failed.");
#0 - c4-judge
2022-12-10T00:30:21Z
berndartmueller marked the issue as duplicate of #99
#1 - c4-judge
2023-01-03T12:48:03Z
berndartmueller marked the issue as satisfactory
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0x4non, 0xA5DF, 0xNazgul, 0xRobocop, 0xfuje, Bnke0x0, HollaDieWaldfee, Lambda, RaymondFam, TomJ, _Adam, adriro, ajtra, carrotsmuggler, cccz, danyams, gasperpre, hansfriese, helios, immeas, joestakey, ladboy233, nameruse, obront, oyc_109, rvierdiiev, saian, sakshamguruji, seyni, slvDev, yixxas, zaskoh
31.1555 USDC - $31.16
address(0x0)
when assigning values to address
state variables2022-12-escher/src/minters/FixedPrice.sol::79 => sale = _sale; 2022-12-escher/src/minters/LPDA.sol::111 => sale = _sale; 2022-12-escher/src/minters/OpenEdition.sol::83 => sale = _sale; 2022-12-escher/src/uris/Base.sol::11 => baseURI = _baseURI; 2022-12-escher/src/uris/Generative.sol::16 => scriptPieces[_id] = _data;
initialize
functions can be front-runSee this finding from a prior badger-dao contest for details
2022-12-escher/src/Escher721.sol::32 => function initialize( 2022-12-escher/src/Escher721.sol::37 => ) public initializer { 2022-12-escher/src/minters/FixedPrice.sol::78 => function initialize(Sale memory _sale) public initializer { 2022-12-escher/src/minters/LPDA.sol::110 => function initialize(Sale calldata _sale) public initializer { 2022-12-escher/src/minters/OpenEdition.sol::82 => function initialize(Sale memory _sale) public initializer { 2022-12-escher/src/uris/Base.sol::18 => function initialize(address _owner) public initializer {
__gap[50]
storage variable to allow for new storage variables in later versionsSee this link for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.
2022-12-escher/src/minters/FixedPrice.sol::10 => contract FixedPrice is Initializable, OwnableUpgradeable, ISale { 2022-12-escher/src/minters/LPDA.sol::10 => contract LPDA is Initializable, OwnableUpgradeable, ISale { 2022-12-escher/src/minters/OpenEdition.sol::10 => contract OpenEdition is Initializable, OwnableUpgradeable, ISale { 2022-12-escher/src/uris/Base.sol::7 => contract Base is ITokenUriDelegate, OwnableUpgradeable {
address.call{value:x}()
should be used instead of payable.transfer()
The use of payable.transfer()
is heavily frowned upon because it can lead to the locking of funds. The transfer()
call requires that the recipient has a payable
callback, only provides 2300 gas for its operation. This means the following cases can cause the transfer to fail:
payable
callbackpayable
callback spends more than 2300 gas (which is only enough to emit something)2022-12-escher/src/minters/LPDA.sol::105 => payable(msg.sender).transfer(owed);
Avoid floating pragmas for non-library contracts.
While floating pragmas make sense for libraries to allow them to be included with multiple different versions of applications, it may be a security risk for application implementations.
A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version ending up checking a different EVM compilation that is ultimately deployed on the blockchain.
2022-12-escher/src/Escher.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/Escher721.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/Escher721Factory.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/minters/FixedPrice.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/minters/FixedPriceFactory.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/minters/LPDA.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/minters/LPDAFactory.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/minters/OpenEdition.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/minters/OpenEditionFactory.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/uris/Base.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/uris/Generative.sol::2 => pragma solidity ^0.8.17; 2022-12-escher/src/uris/Unique.sol::2 => pragma solidity ^0.8.17;
2022-12-escher/src/uris/Base.sol::15 => return baseURI;
indexed
fieldsEach event should use three indexed fields if there are three or more fields
2022-12-escher/src/minters/FixedPrice.sol::40 => event Buy(address indexed _buyer, uint256 _amount, uint256 _value, Sale _saleInfo); 2022-12-escher/src/minters/LPDA.sol::43 => event Buy(address indexed _buyer, uint256 _amount, uint256 _value, Sale _saleInfo); 2022-12-escher/src/minters/OpenEdition.sol::39 => event Buy(address indexed _buyer, uint256 _amount, uint256 _value, Sale _saleInfo);
2022-12-escher/src/Escher.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/Escher721.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/Escher721Factory.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/minters/FixedPrice.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/minters/FixedPriceFactory.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/minters/LPDA.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/minters/LPDAFactory.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/minters/OpenEdition.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/minters/OpenEditionFactory.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/uris/Base.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/uris/Generative.sol::1 => // SPDX-License-Identifier: MIT 2022-12-escher/src/uris/Unique.sol::1 => // SPDX-License-Identifier: MIT
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents’ functions and change the visibility from public to external .
2022-12-escher/src/Escher.sol::21 => function setURI(string memory _newuri) public onlyRole(DEFAULT_ADMIN_ROLE) { 2022-12-escher/src/Escher.sol::27 => function addCreator(address _account) public onlyRole(CURATOR_ROLE) { 2022-12-escher/src/Escher721.sol::37 => ) public initializer { 2022-12-escher/src/Escher721.sol::51 => function mint(address to, uint256 tokenId) public virtual onlyRole(MINTER_ROLE) { 2022-12-escher/src/Escher721.sol::57 => function updateURIDelegate(address _uriDelegate) public onlyRole(URI_SETTER_ROLE) { 2022-12-escher/src/Escher721.sol::67 => ) public onlyRole(DEFAULT_ADMIN_ROLE) { 2022-12-escher/src/Escher721.sol::72 => function resetDefaultRoyalty() public onlyRole(DEFAULT_ADMIN_ROLE) { 2022-12-escher/src/minters/FixedPrice.sol::78 => function initialize(Sale memory _sale) public initializer { 2022-12-escher/src/minters/FixedPrice.sol::86 => function getPrice() public view returns (uint256) { 2022-12-escher/src/minters/FixedPrice.sol::91 => function startTime() public view returns (uint256) { 2022-12-escher/src/minters/FixedPrice.sol::96 => function editionContract() public view returns (address) { 2022-12-escher/src/minters/FixedPrice.sol::101 => function available() public view returns (uint256) { 2022-12-escher/src/minters/FixedPriceFactory.sol::42 => function setFeeReceiver(address payable fees) public onlyOwner { 2022-12-escher/src/minters/LPDA.sol::99 => function refund() public { 2022-12-escher/src/minters/LPDA.sol::110 => function initialize(Sale calldata _sale) public initializer { 2022-12-escher/src/minters/LPDA.sol::117 => function getPrice() public view returns (uint256) { 2022-12-escher/src/minters/LPDA.sol::128 => function startTime() public view returns (uint256) { 2022-12-escher/src/minters/LPDA.sol::133 => function editionContract() public view returns (address) { 2022-12-escher/src/minters/LPDA.sol::138 => function available() public view returns (uint256) { 2022-12-escher/src/minters/LPDA.sol::142 => function lowestPrice() public view returns (uint256) { 2022-12-escher/src/minters/LPDAFactory.sol::46 => function setFeeReceiver(address payable fees) public onlyOwner { 2022-12-escher/src/minters/OpenEdition.sol::82 => function initialize(Sale memory _sale) public initializer { 2022-12-escher/src/minters/OpenEdition.sol::89 => function finalize() public { 2022-12-escher/src/minters/OpenEdition.sol::97 => function getPrice() public view returns (uint256) { 2022-12-escher/src/minters/OpenEdition.sol::102 => function startTime() public view returns (uint256) { 2022-12-escher/src/minters/OpenEdition.sol::107 => function timeLeft() public view returns (uint256) { 2022-12-escher/src/minters/OpenEdition.sol::112 => function editionContract() public view returns (address) { 2022-12-escher/src/minters/OpenEdition.sol::116 => function available() public view returns (uint256) { 2022-12-escher/src/minters/OpenEditionFactory.sol::42 => function setFeeReceiver(address payable fees) public onlyOwner { 2022-12-escher/src/uris/Base.sol::18 => function initialize(address _owner) public initializer {
Consider defining in only one contract so that values cannot become out of sync when only one location is updated
2022-12-escher/src/Escher.sol::11 => bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE"); 2022-12-escher/src/Escher.sol::13 => bytes32 public constant CURATOR_ROLE = keccak256("CURATOR_ROLE"); 2022-12-escher/src/Escher721.sol::17 => bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE"); 2022-12-escher/src/Escher721.sol::19 => bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
#0 - c4-judge
2023-01-04T10:47:51Z
berndartmueller marked the issue as grade-b
🌟 Selected for report: slvDev
Also found by: 0x4non, Bnke0x0, Diana, Dinesh11G, RaymondFam, ReyAdmirado, adriro, ahmedov, ajtra, c3phas, cryptostellar5, nicobevi, pfapostol, sakshamguruji, tnevler, zaskoh
35.0246 USDC - $35.02
immutable
Avoids a Gusset (20000 gas)
2022-12-escher/src/Escher721.sol::22 => address public tokenUriDelegate; 2022-12-escher/src/minters/FixedPrice.sol::27 => Sale public sale; 2022-12-escher/src/minters/FixedPriceFactory.sol::12 => address payable public feeReceiver; 2022-12-escher/src/minters/LPDA.sol::35 => Sale public sale; 2022-12-escher/src/minters/LPDAFactory.sol::12 => address payable public feeReceiver; 2022-12-escher/src/minters/OpenEdition.sol::26 => Sale public sale; 2022-12-escher/src/minters/OpenEditionFactory.sol::12 => address payable public feeReceiver; 2022-12-escher/src/uris/Base.sol::8 => string public baseURI; 2022-12-escher/src/uris/Generative.sol::7 => bytes32 public seedBase;
If variables occupying the same slot are both written the same function or by the constructor avoids a separate Gsset (20000 gas). Reads of the variables are also cheaper
2022-12-escher/src/Escher721.sol::22 => address public tokenUriDelegate;
++i/i++
should be unchecked{++i}
/unchecked{++i}
when it is not possible for them to overflow, as is the case when used in for
and while
loops2022-12-escher/src/minters/FixedPrice.sol::65 => for (uint48 x = sale_.currentId + 1; x <= newId; x++) { 2022-12-escher/src/minters/LPDA.sol::73 => for (uint256 x = temp.currentId + 1; x <= newId; x++) { 2022-12-escher/src/minters/OpenEdition.sol::66 => for (uint24 x = temp.currentId + 1; x <= newId; x++) {
> 0
costs more gas than != 0
when used on a uint
in a require()
statement2022-12-escher/src/minters/LPDA.sol::103 => require(owed > 0, "NOTHING TO REFUND"); 2022-12-escher/src/minters/LPDAFactory.sol::35 => require(sale.startPrice > 0, "INVALID START PRICE"); 2022-12-escher/src/minters/LPDAFactory.sol::36 => require(sale.dropPerSecond > 0, "INVALID DROP PER SECOND");
2022-12-escher/src/minters/LPDA.sol::36 => uint48 public amountSold = 0;
++i
costs less gas than i++
, especially when it’s used in forloops (--i
/i--
too)2022-12-escher/src/minters/FixedPrice.sol::65 => for (uint48 x = sale_.currentId + 1; x <= newId; x++) { 2022-12-escher/src/minters/LPDA.sol::73 => for (uint256 x = temp.currentId + 1; x <= newId; x++) { 2022-12-escher/src/minters/OpenEdition.sol::66 => for (uint24 x = temp.currentId + 1; x <= newId; x++) {
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadWhen using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed
2022-12-escher/src/Escher721.sol::66 => uint96 feeNumerator 2022-12-escher/src/minters/FixedPrice.sol::16 => uint48 currentId; 2022-12-escher/src/minters/FixedPrice.sol::17 => uint48 finalId; 2022-12-escher/src/minters/FixedPrice.sol::20 => uint96 price; 2022-12-escher/src/minters/FixedPrice.sol::23 => uint96 startTime; 2022-12-escher/src/minters/FixedPrice.sol::46 => sale = Sale(0, 0, address(0), type(uint96).max, payable(0), type(uint96).max); 2022-12-escher/src/minters/FixedPrice.sol::62 => uint48 newId = uint48(_amount) + sale_.currentId; 2022-12-escher/src/minters/FixedPrice.sol::65 => for (uint48 x = sale_.currentId + 1; x <= newId; x++) { 2022-12-escher/src/minters/LPDA.sol::15 => uint48 currentId; 2022-12-escher/src/minters/LPDA.sol::16 => uint48 finalId; 2022-12-escher/src/minters/LPDA.sol::19 => uint80 startPrice; 2022-12-escher/src/minters/LPDA.sol::20 => uint80 finalPrice; 2022-12-escher/src/minters/LPDA.sol::21 => uint80 dropPerSecond; 2022-12-escher/src/minters/LPDA.sol::23 => uint96 endTime; 2022-12-escher/src/minters/LPDA.sol::26 => uint96 startTime; 2022-12-escher/src/minters/LPDA.sol::30 => uint48 amount; 2022-12-escher/src/minters/LPDA.sol::31 => uint80 balance; 2022-12-escher/src/minters/LPDA.sol::36 => uint48 public amountSold = 0; 2022-12-escher/src/minters/LPDA.sol::49 => uint48 x = type(uint48).max; 2022-12-escher/src/minters/LPDA.sol::50 => uint80 y = type(uint80).max; 2022-12-escher/src/minters/LPDA.sol::51 => uint96 z = type(uint96).max; 2022-12-escher/src/minters/LPDA.sol::59 => uint48 amount = uint48(_amount); 2022-12-escher/src/minters/LPDA.sol::67 => uint48 newId = amount + temp.currentId; 2022-12-escher/src/minters/LPDA.sol::71 => receipts[msg.sender].balance += uint80(msg.value); 2022-12-escher/src/minters/LPDA.sol::82 => sale.finalPrice = uint80(price); 2022-12-escher/src/minters/LPDA.sol::101 => uint80 price = uint80(getPrice()) * r.amount; 2022-12-escher/src/minters/LPDA.sol::102 => uint80 owed = r.balance - price; 2022-12-escher/src/minters/LPDA.sol::117 => function getPrice() public view returns (uint256) { 2022-12-escher/src/minters/OpenEdition.sol::15 => uint72 price; 2022-12-escher/src/minters/OpenEdition.sol::16 => uint24 currentId; 2022-12-escher/src/minters/OpenEdition.sol::19 => uint96 startTime; 2022-12-escher/src/minters/OpenEdition.sol::22 => uint96 endTime; 2022-12-escher/src/minters/OpenEdition.sol::46 => type(uint72).max, 2022-12-escher/src/minters/OpenEdition.sol::47 => type(uint24).max, 2022-12-escher/src/minters/OpenEdition.sol::49 => type(uint96).max, 2022-12-escher/src/minters/OpenEdition.sol::51 => type(uint96).max 2022-12-escher/src/minters/OpenEdition.sol::58 => uint24 amount = uint24(_amount); 2022-12-escher/src/minters/OpenEdition.sol::64 => uint24 newId = amount + temp.currentId; 2022-12-escher/src/minters/OpenEdition.sol::66 => for (uint24 x = temp.currentId + 1; x <= newId; x++) { 2022-12-escher/src/minters/OpenEdition.sol::117 => return sale.endTime > block.timestamp ? type(uint24).max : 0;
keccak256()
, should use immutable
rather than constant
See this issue for a detailed description of the issue
2022-12-escher/src/Escher.sol::11 => bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE"); 2022-12-escher/src/Escher.sol::13 => bytes32 public constant CURATOR_ROLE = keccak256("CURATOR_ROLE"); 2022-12-escher/src/Escher721.sol::17 => bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE"); 2022-12-escher/src/Escher721.sol::19 => bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
require()
/revert()
checks should be refactored to a modifier or function2022-12-escher/src/Escher.sol::45 => revert("SoulBound"); 2022-12-escher/src/minters/FixedPrice.sol::51 => require(block.timestamp < sale.startTime, "TOO LATE"); 2022-12-escher/src/minters/LPDA.sol::62 => require(block.timestamp >= temp.startTime, "TOO SOON"); 2022-12-escher/src/minters/LPDAFactory.sol::30 => require(IEscher721(sale.edition).hasRole(bytes32(0x00), msg.sender), "NOT AUTHORIZED");
require()
or revert()
statements that check input arguments should be at the top of the functionChecks that involve constants should come before checks that involve state variables
2022-12-escher/src/minters/LPDAFactory.sol::31 => require(sale.saleReceiver != address(0), "INVALID SALE RECEIVER");
revert()
/require()
strings to save deployment gas2022-12-escher/src/Escher.sol::45 => revert("SoulBound"); 2022-12-escher/src/Escher.sol::56 => revert("SoulBound"); 2022-12-escher/src/Escher.sol::61 => require(balanceOf(_account, uint256(_role)) == 0, "Already Creator"); 2022-12-escher/src/Escher721Factory.sol::32 => require(escher.hasRole(escher.CREATOR_ROLE(), msg.sender), "NOT AUTHORIZED"); 2022-12-escher/src/minters/FixedPrice.sol::51 => require(block.timestamp < sale.startTime, "TOO LATE"); 2022-12-escher/src/minters/FixedPrice.sol::60 => require(block.timestamp >= sale_.startTime, "TOO SOON"); 2022-12-escher/src/minters/FixedPrice.sol::61 => require(_amount * sale_.price == msg.value, "WRONG PRICE"); 2022-12-escher/src/minters/FixedPrice.sol::63 => require(newId <= sale_.finalId, "TOO MANY"); 2022-12-escher/src/minters/FixedPriceFactory.sol::30 => require(IEscher721(_sale.edition).hasRole(bytes32(0x00), msg.sender), "NOT AUTHORIZED"); 2022-12-escher/src/minters/FixedPriceFactory.sol::31 => require(_sale.startTime >= block.timestamp, "START TIME IN PAST"); 2022-12-escher/src/minters/FixedPriceFactory.sol::32 => require(_sale.finalId > _sale.currentId, "FINAL ID BEFORE CURRENT"); 2022-12-escher/src/minters/LPDA.sol::62 => require(block.timestamp >= temp.startTime, "TOO SOON"); 2022-12-escher/src/minters/LPDA.sol::64 => require(msg.value >= amount * price, "WRONG PRICE"); 2022-12-escher/src/minters/LPDA.sol::68 => require(newId <= temp.finalId, "TOO MANY"); 2022-12-escher/src/minters/LPDA.sol::93 => require(block.timestamp < sale.startTime, "TOO LATE"); 2022-12-escher/src/minters/LPDA.sol::103 => require(owed > 0, "NOTHING TO REFUND"); 2022-12-escher/src/minters/LPDAFactory.sol::30 => require(IEscher721(sale.edition).hasRole(bytes32(0x00), msg.sender), "NOT AUTHORIZED"); 2022-12-escher/src/minters/LPDAFactory.sol::31 => require(sale.saleReceiver != address(0), "INVALID SALE RECEIVER"); 2022-12-escher/src/minters/LPDAFactory.sol::32 => require(sale.startTime >= block.timestamp, "INVALID START TIME"); 2022-12-escher/src/minters/LPDAFactory.sol::33 => require(sale.endTime > sale.startTime, "INVALID END TIME"); 2022-12-escher/src/minters/LPDAFactory.sol::34 => require(sale.finalId > sale.currentId, "INVALID FINAL ID"); 2022-12-escher/src/minters/LPDAFactory.sol::35 => require(sale.startPrice > 0, "INVALID START PRICE"); 2022-12-escher/src/minters/LPDAFactory.sol::36 => require(sale.dropPerSecond > 0, "INVALID DROP PER SECOND"); 2022-12-escher/src/minters/OpenEdition.sol::61 => require(block.timestamp >= temp.startTime, "TOO SOON"); 2022-12-escher/src/minters/OpenEdition.sol::62 => require(block.timestamp < temp.endTime, "TOO LATE"); 2022-12-escher/src/minters/OpenEdition.sol::63 => require(amount * sale.price == msg.value, "WRONG PRICE"); 2022-12-escher/src/minters/OpenEdition.sol::76 => require(block.timestamp < sale.startTime, "TOO LATE"); 2022-12-escher/src/minters/OpenEdition.sol::91 => require(block.timestamp >= temp.endTime, "TOO SOON"); 2022-12-escher/src/minters/OpenEditionFactory.sol::30 => require(IEscher721(sale.edition).hasRole(bytes32(0x00), msg.sender), "NOT AUTHORIZED"); 2022-12-escher/src/minters/OpenEditionFactory.sol::31 => require(sale.startTime >= block.timestamp, "START TIME IN PAST"); 2022-12-escher/src/minters/OpenEditionFactory.sol::32 => require(sale.endTime > sale.startTime, "END TIME BEFORE START"); 2022-12-escher/src/uris/Generative.sol::15 => require(bytes(scriptPieces[_id]).length == 0, "ALREADY SET"); 2022-12-escher/src/uris/Generative.sol::20 => require(seedBase == bytes32(0), "SEED BASE SET"); 2022-12-escher/src/uris/Generative.sol::28 => require(seedBase != bytes32(0), "SEED BASE NOT SET");
payable
If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.
2022-12-escher/src/Escher.sol::21 => function setURI(string memory _newuri) public onlyRole(DEFAULT_ADMIN_ROLE) { 2022-12-escher/src/Escher.sol::27 => function addCreator(address _account) public onlyRole(CURATOR_ROLE) { 2022-12-escher/src/Escher721.sol::51 => function mint(address to, uint256 tokenId) public virtual onlyRole(MINTER_ROLE) { 2022-12-escher/src/Escher721.sol::57 => function updateURIDelegate(address _uriDelegate) public onlyRole(URI_SETTER_ROLE) { 2022-12-escher/src/Escher721.sol::72 => function resetDefaultRoyalty() public onlyRole(DEFAULT_ADMIN_ROLE) { 2022-12-escher/src/minters/FixedPrice.sol::50 => function cancel() external onlyOwner { 2022-12-escher/src/minters/FixedPriceFactory.sol::42 => function setFeeReceiver(address payable fees) public onlyOwner { 2022-12-escher/src/minters/LPDA.sol::92 => function cancel() external onlyOwner { 2022-12-escher/src/minters/LPDAFactory.sol::46 => function setFeeReceiver(address payable fees) public onlyOwner { 2022-12-escher/src/minters/OpenEdition.sol::75 => function cancel() external onlyOwner { 2022-12-escher/src/minters/OpenEditionFactory.sol::42 => function setFeeReceiver(address payable fees) public onlyOwner { 2022-12-escher/src/uris/Base.sol::10 => function setBaseURI(string memory _baseURI) external onlyOwner { 2022-12-escher/src/uris/Generative.sol::14 => function setScriptPiece(uint256 _id, string memory _data) external onlyOwner { 2022-12-escher/src/uris/Generative.sol::19 => function setSeedBase() external onlyOwner {
calldata
instead of memory
for function parametersUse calldata instead of memory for function parameters. Having function arguments use calldata instead of memory can save gas.
2022-12-escher/src/Escher.sol::21 => function setURI(string memory _newuri) public onlyRole(DEFAULT_ADMIN_ROLE) { 2022-12-escher/src/minters/FixedPrice.sol::78 => function initialize(Sale memory _sale) public initializer { 2022-12-escher/src/minters/FixedPrice.sol::107 => function _end(Sale memory _sale) internal { 2022-12-escher/src/minters/OpenEdition.sol::82 => function initialize(Sale memory _sale) public initializer { 2022-12-escher/src/minters/OpenEdition.sol::120 => function _end(Sale memory _sale) internal { 2022-12-escher/src/uris/Base.sol::10 => function setBaseURI(string memory _baseURI) external onlyOwner { 2022-12-escher/src/uris/Generative.sol::14 => function setScriptPiece(uint256 _id, string memory _data) external onlyOwner {
Checking non-zero transfer values can avoid an expensive external call and save gas.
While this is done in some places, it’s not consistently done in the solution.
2022-12-escher/src/minters/FixedPrice.sol::109 => ISaleFactory(factory).feeReceiver().transfer(address(this).balance / 20); 2022-12-escher/src/minters/LPDA.sol::85 => ISaleFactory(factory).feeReceiver().transfer(fee); 2022-12-escher/src/minters/LPDA.sol::86 => temp.saleReceiver.transfer(totalSale - fee); 2022-12-escher/src/minters/LPDA.sol::105 => payable(msg.sender).transfer(owed); 2022-12-escher/src/minters/OpenEdition.sol::92 => ISaleFactory(factory).feeReceiver().transfer(address(this).balance / 20);
private
rather than public
for constants, saves gasIf needed, the value can be read from the verified contract source code.
2022-12-escher/src/Escher.sol::11 => bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE"); 2022-12-escher/src/Escher.sol::13 => bytes32 public constant CURATOR_ROLE = keccak256("CURATOR_ROLE"); 2022-12-escher/src/Escher721.sol::17 => bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE"); 2022-12-escher/src/Escher721.sol::19 => bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting. If the contract is meant to be extended, the contract should be abstract and the function signatures be added without any default implementation. If the block is an empty if-statement block to avoid doing subsequent checks in the else-if/else conditions, the else-if/else conditions should be nested under the negation of the if-statement, because they involve different classes of checks, which may lead to the introduction of errors when the code is later modified (if(x){}else if(y){...}else{...} => if(!x){if(y){...}else{...}})
2022-12-escher/src/Escher721.sol::25 => constructor() {}
#0 - c4-sponsor
2022-12-22T22:57:39Z
mehtaculous marked the issue as sponsor disputed
#1 - c4-judge
2023-01-04T11:04:12Z
berndartmueller marked the issue as grade-b