Platform: Code4rena
Start Date: 14/06/2022
Pot Size: $50,000 USDC
Total HM: 19
Participants: 99
Period: 5 days
Judge: HardlyDifficult
Total Solo HM: 4
Id: 136
League: ETH
Rank: 19/99
Findings: 3
Award: $604.27
π Selected for report: 1
π Solo Findings: 0
π Selected for report: hyh
Also found by: 0x29A, 0xNineDec, 0xf15ers, 0xkowloon, GreyArt, IllIllI, KIntern, Kenshin, Lambda, WatchPug, Wayne, berndartmueller, byterocket, cccz, codexploder, horsefacts, kenzo, obront, obtarian, oyc_109, peritoflores, rajatbeladiya, rfa, saian, unforgiven, zer0dot
11.084 USDC - $11.08
Judge has assessed an item in Issue #187 as High risk. The relevant finding follows:
rescueETH()
cannot rescue EtherrescueETH()
sends msg.value
to the destination
address, which means it requires the caller of rescueETH()
to provide the Ether to send. Essentially the owner is directly paying the destination
address, and the Ether in the contract remains untouched
There is 1 instance of this issue:
File: contracts/staking/InfinityStaker.sol #1 345 function rescueETH(address destination) external payable onlyOwner { 346 (bool sent, ) = destination.call{value: msg.value}(''); 347 require(sent, 'Failed to send Ether'); 348: }
#0 - HardlyDifficult
2022-07-14T01:09:26Z
π Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xf15ers, 0xkowloon, 0xmint, 8olidity, BowTiedWardens, Chom, Cityscape, Czar102, ElKu, FSchmoede, Funen, GimelSec, GreyArt, IllIllI, KIntern, Kaiziron, Kenshin, Lambda, MadWookie, MiloTruck, PPrieditis, Picodes, Ruhum, Sm4rty, StErMi, TerrierLover, TomJ, Treasure-Seeker, VAD37, WatchPug, Wayne, _Adam, a12jmx, abhinavmir, antonttc, apostle0x01, asutorufos, berndartmueller, cccz, cloudjunky, codexploder, cryptphi, csanuragjain, defsec, delfin454000, fatherOfBlocks, georgypetrov, hake, hansfriese, horsefacts, hyh, k, kenta, nxrblsrpr, oyc_109, peritoflores, rajatbeladiya, reassor, rfa, robee, sach1r0, saian, samruna, shenwilly, simon135, sorrynotsorry, sseefried, throttle, unforgiven, wagmi, zzzitron
287.6424 USDC - $287.64
Issue | Instances | |
---|---|---|
1 | rescueETH() cannot rescue Ether | 1 |
2 | Vulnerable to cross-chain replay attacks due to static DOMAIN_SEPARATOR | 1 |
3 | Unused/empty receive() /fallback() function | 4 |
4 | Missing checks for address(0x0) when assigning values to address state variables | 6 |
5 | abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256() | 2 |
Total: 14 instances over 5 issues
Issue | Instances | |
---|---|---|
1 | public functions not called by the contract should be declared external instead | 1 |
2 | constant s should be defined rather than using magic numbers | 56 |
3 | Large multiples of ten should use scientific notation (e.g. 1e6 ) rather than decimal literals (e.g. 1000000 ), for readability | 1 |
4 | Missing event and timelock for critical parameter change | 4 |
5 | Expressions for constant values such as a call to keccak256() , should use immutable rather than constant | 4 |
6 | Use scientific notation (e.g. 1e18 ) rather than exponentiation (e.g. 10**18 ) | 3 |
7 | Inconsistent spacing in comments | 3 |
8 | Variable names that consist of all capital letters should be reserved for constant /immutable variables | 18 |
9 | NatSpec is incomplete | 4 |
10 | Event is missing indexed fields | 11 |
11 | Not using the named return variables anywhere in the function is confusing | 6 |
Total: 111 instances over 11 issues
rescueETH()
cannot rescue EtherrescueETH()
sends msg.value
to the destination
address, which means it requires the caller of rescueETH()
to provide the Ether to send. Essentially the owner is directly paying the destination
address, and the Ether in the contract remains untouched
There is 1 instance of this issue:
File: contracts/staking/InfinityStaker.sol #1 345 function rescueETH(address destination) external payable onlyOwner { 346 (bool sent, ) = destination.call{value: msg.value}(''); 347 require(sent, 'Failed to send Ether'); 348: }
See this issue from a prior contest for details
There is 1 instance of this issue:
File: contracts/core/InfinityExchange.sol #1 57: bytes32 public immutable DOMAIN_SEPARATOR;
receive()
/fallback()
functionIf the intention is for the Ether to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth))
)
There are 4 instances of this issue:
File: contracts/staking/InfinityStaker.sol #1 55: fallback() external payable {}
File: contracts/staking/InfinityStaker.sol #2 57: receive() external payable {}
File: contracts/core/InfinityExchange.sol #3 119: fallback() external payable {}
File: contracts/core/InfinityExchange.sol #4 121: receive() external payable {}
address(0x0)
when assigning values to address
state variablesThere are 6 instances of this issue:
File: contracts/staking/InfinityStaker.sol 50: INFINITY_TOKEN = _tokenAddress; 51: INFINITY_TREASURY = _infinityTreasury; 376: INFINITY_TREASURY = _infinityTreasury;
File: contracts/core/InfinityExchange.sol 115: WETH = _WETH; 116: MATCH_EXECUTOR = _matchExecutor; 1256: MATCH_EXECUTOR = _matchExecutor;
abi.encodePacked()
should not be used with dynamic types when passing the result to a hash function such as keccak256()
Use abi.encode()
instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456)
=> 0x123456
=> abi.encodePacked(0x1,0x23456)
, but abi.encode(0x123,0x456)
=> 0x0...1230...456
). "Unless there is a compelling reason, abi.encode
should be preferred". If there is only one argument to abi.encodePacked()
it can often be cast to bytes()
or bytes32()
instead.
If all arguments are strings and or bytes, bytes.concat()
should be used instead
There are 2 instances of this issue:
File: contracts/core/InfinityExchange.sol #1 1197: bytes32 nftsHash = keccak256(abi.encodePacked(hashes));
File: contracts/core/InfinityExchange.sol #2 1213: bytes32 tokensHash = keccak256(abi.encodePacked(hashes));
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
There is 1 instance of this issue:
File: contracts/token/InfinityToken.sol #1 117: function getTimelock() public view returns (uint256 timelock) {
constant
s should be defined rather than using magic numbersEven assembly can benefit from using readable constants instead of hex/numeric literals
There are 56 instances of this issue:
File: contracts/staking/InfinityStaker.sol /// @audit 3 236: (userstakedAmounts[user][Duration.SIX_MONTHS].amount * 3) + /// @audit 4 237: (userstakedAmounts[user][Duration.TWELVE_MONTHS].amount * 4)) / (10**18); /// @audit 18 237: (userstakedAmounts[user][Duration.TWELVE_MONTHS].amount * 4)) / (10**18); /// @audit 4 246: StakeAmount[] memory stakingInfo = new StakeAmount[](4); /// @audit 3 250: stakingInfo[3] = userstakedAmounts[user][Duration.TWELVE_MONTHS]; /// @audit 90 277: return 90 days; /// @audit 180 279: return 180 days; /// @audit 360 281: return 360 days;
File: contracts/core/InfinityOrderBookComplication.sol /// @audit 3 38: bool _isTimeValid = makerOrder2.constraints[3] <= block.timestamp && /// @audit 4 39: makerOrder2.constraints[4] >= block.timestamp && /// @audit 3 40: makerOrder1.constraints[3] <= block.timestamp && /// @audit 4 41: makerOrder1.constraints[4] >= block.timestamp; /// @audit 3 91: manyMakerOrders[i].constraints[3] <= block.timestamp && /// @audit 4 92: manyMakerOrders[i].constraints[4] >= block.timestamp; /// @audit 3 102: makerOrder.constraints[3] <= block.timestamp && /// @audit 4 103: makerOrder.constraints[4] >= block.timestamp; /// @audit 3 160: return (makerOrder.constraints[3] <= block.timestamp && /// @audit 4 161: makerOrder.constraints[4] >= block.timestamp && /// @audit 3 175: sell.constraints[3] <= block.timestamp && /// @audit 4 176: sell.constraints[4] >= block.timestamp && /// @audit 3 177: buy.constraints[3] <= block.timestamp && /// @audit 4 178: buy.constraints[4] >= block.timestamp; /// @audit 4 332: uint256 duration = order.constraints[4] - order.constraints[3]; /// @audit 3 332: uint256 duration = order.constraints[4] - order.constraints[3]; /// @audit 3 337: uint256 elapsedTime = block.timestamp - order.constraints[3]; /// @audit 4 338: uint256 PRECISION = 10**4; // precision for division; similar to bps
File: contracts/core/InfinityExchange.sol /// @audit 20000 202: uint256 startGasPerOrder = gasleft() + ((startGas + 20000 - gasleft()) / ordersLength); /// @audit 5 216: isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.constraints[5]] = true; /// @audit 5 230: isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.constraints[5]] = true; /// @audit 3 311: bool isTimeValid = makerOrders[i].constraints[3] <= block.timestamp && /// @audit 4 312: makerOrders[i].constraints[4] >= block.timestamp; /// @audit 1000000 381: require(minNonce < userMinOrderNonce[msg.sender] + 1000000, 'too many'); /// @audit 5 515: bool orderExpired = isUserOrderNonceExecutedOrCancelled[order.signer][order.constraints[5]] || /// @audit 5 516: order.constraints[5] < userMinOrderNonce[order.signer]; /// @audit 5 723: isUserOrderNonceExecutedOrCancelled[sell.signer][sell.constraints[5]] = true; /// @audit 5 724: isUserOrderNonceExecutedOrCancelled[buy.signer][buy.constraints[5]] = true; /// @audit 10000 725: uint256 protocolFee = (protocolFeeBps * execPrice) / 10000; /// @audit 5 774: isUserOrderNonceExecutedOrCancelled[buy.signer][buy.constraints[5]] = true; /// @audit 10000 775: uint256 protocolFee = (protocolFeeBps * execPrice) / 10000; /// @audit 5 818: isUserOrderNonceExecutedOrCancelled[sell.signer][sell.constraints[5]] = true; /// @audit 10000 819: uint256 protocolFee = (protocolFeeBps * execPrice) / 10000; /// @audit 10000 873: uint256 protocolFee = (protocolFeeBps * execPrice) / 10000; /// @audit 5 878: sell.constraints[5], /// @audit 5 879: buy.constraints[5], /// @audit 5 970: isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.constraints[5]] = true; /// @audit 5 995: isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.constraints[5]] = true; /// @audit 0x80ac58cd 1067: if (IERC165(item.collection).supportsInterface(0x80ac58cd)) { /// @audit 0xd9b67a26 1069: } else if (IERC165(item.collection).supportsInterface(0xd9b67a26)) { /// @audit 10000 1135: uint256 protocolFee = (PROTOCOL_FEE_BPS * amount) / 10000; /// @audit 4 1155: uint256 duration = order.constraints[4] - order.constraints[3]; /// @audit 3 1155: uint256 duration = order.constraints[4] - order.constraints[3]; /// @audit 3 1160: uint256 elapsedTime = block.timestamp - order.constraints[3]; /// @audit 4 1161: uint256 PRECISION = 10**4; // precision for division; similar to bps /// @audit 0x7bcfb5a29031e6b8d34ca1a14dd0a1f5cb11b20f755bb2a31ee3c4b143477e4a 1170: bytes32 ORDER_HASH = 0x7bcfb5a29031e6b8d34ca1a14dd0a1f5cb11b20f755bb2a31ee3c4b143477e4a; /// @audit 0xf73f37e9f570369ceaab59cef16249ae1c0ad1afd592d656afac0be6f63b87e0 1187: bytes32 ORDER_ITEM_HASH = 0xf73f37e9f570369ceaab59cef16249ae1c0ad1afd592d656afac0be6f63b87e0; /// @audit 0x88f0bd19d14f8b5d22c0605a15d9fffc285ebc8c86fb21139456d305982906f1 1203: bytes32 TOKEN_INFO_HASH = 0x88f0bd19d14f8b5d22c0605a15d9fffc285ebc8c86fb21139456d305982906f1;
1e6
) rather than decimal literals (e.g. 1000000
), for readabilityThere is 1 instance of this issue:
File: contracts/core/InfinityExchange.sol #1 381: require(minNonce < userMinOrderNonce[msg.sender] + 1000000, 'too many');
Events help non-contract tools to track changes, and events prevent users from being surprised by changes
There are 4 instances of this issue:
File: contracts/staking/InfinityStaker.sol #1 351 function updateStakeLevelThreshold(StakeLevel stakeLevel, uint16 threshold) external onlyOwner { 352 if (stakeLevel == StakeLevel.BRONZE) { 353 BRONZE_STAKE_THRESHOLD = threshold; 354 } else if (stakeLevel == StakeLevel.SILVER) { 355 SILVER_STAKE_THRESHOLD = threshold; 356 } else if (stakeLevel == StakeLevel.GOLD) { 357 GOLD_STAKE_THRESHOLD = threshold; 358 } else if (stakeLevel == StakeLevel.PLATINUM) { 359 PLATINUM_STAKE_THRESHOLD = threshold; 360 } 361: }
File: contracts/staking/InfinityStaker.sol #2 364 function updatePenalties( 365 uint16 threeMonthPenalty, 366 uint16 sixMonthPenalty, 367 uint16 twelveMonthPenalty 368 ) external onlyOwner { 369 THREE_MONTH_PENALTY = threeMonthPenalty; 370 SIX_MONTH_PENALTY = sixMonthPenalty; 371 TWELVE_MONTH_PENALTY = twelveMonthPenalty; 372: }
File: contracts/staking/InfinityStaker.sol #3 375 function updateInfinityTreasury(address _infinityTreasury) external onlyOwner { 376 INFINITY_TREASURY = _infinityTreasury; 377: }
File: contracts/core/InfinityExchange.sol #4 1255 function updateMatchExecutor(address _matchExecutor) external onlyOwner { 1256 MATCH_EXECUTOR = _matchExecutor; 1257: }
keccak256()
, should use immutable
rather than constant
There are 4 instances of this issue:
File: contracts/token/InfinityToken.sol #1 25: bytes32 public constant EPOCH_INFLATION = keccak256('Inflation');
File: contracts/token/InfinityToken.sol #2 26: bytes32 public constant EPOCH_DURATION = keccak256('EpochDuration');
File: contracts/token/InfinityToken.sol #3 27: bytes32 public constant EPOCH_CLIFF = keccak256('Cliff');
File: contracts/token/InfinityToken.sol #4 28: bytes32 public constant MAX_EPOCHS = keccak256('MaxEpochs');
1e18
) rather than exponentiation (e.g. 10**18
)There are 3 instances of this issue:
File: contracts/staking/InfinityStaker.sol #1 237: (userstakedAmounts[user][Duration.TWELVE_MONTHS].amount * 4)) / (10**18);
File: contracts/core/InfinityOrderBookComplication.sol #2 338: uint256 PRECISION = 10**4; // precision for division; similar to bps
File: contracts/core/InfinityExchange.sol #3 1161: uint256 PRECISION = 10**4; // precision for division; similar to bps
Some lines use // x
and some use //x
. The instances below point out the usages that don't follow the majority, within each file
There are 3 instances of this issue:
File: contracts/staking/InfinityStaker.sol #1 22: ///@dev Storage variable to keep track of the staker's staked duration and amounts
File: contracts/staking/InfinityStaker.sol #2 26: ///@dev Infinity treasury address - will be a EOA/multisig
File: contracts/staking/InfinityStaker.sol #3 38: ///@dev Penalties if staked tokens are rageQuit early. Example: If 100 tokens are staked for twelve months but rageQuit right away,
constant
/immutable
variablesIf the variable needs to be different based on which class it comes from, a view
/pure
function should be used instead (e.g. like this).
There are 18 instances of this issue:
File: contracts/staking/InfinityStaker.sol 25: address public INFINITY_TOKEN; 27: address public INFINITY_TREASURY; 33: uint16 public BRONZE_STAKE_THRESHOLD = 1000; 34: uint16 public SILVER_STAKE_THRESHOLD = 5000; 35: uint16 public GOLD_STAKE_THRESHOLD = 10000; 36: uint16 public PLATINUM_STAKE_THRESHOLD = 20000; 40: uint16 public THREE_MONTH_PENALTY = 2; 41: uint16 public SIX_MONTH_PENALTY = 3; 42: uint16 public TWELVE_MONTH_PENALTY = 4;
File: contracts/core/InfinityOrderBookComplication.sol 338: uint256 PRECISION = 10**4; // precision for division; similar to bps
File: contracts/core/InfinityExchange.sol 59: address public MATCH_EXECUTOR; 61: uint32 public WETH_TRANSFER_GAS_UNITS = 50000; 63: uint16 public PROTOCOL_FEE_BPS = 250; 104: constructor(address _WETH, address _matchExecutor) { 1161: uint256 PRECISION = 10**4; // precision for division; similar to bps 1170: bytes32 ORDER_HASH = 0x7bcfb5a29031e6b8d34ca1a14dd0a1f5cb11b20f755bb2a31ee3c4b143477e4a; 1187: bytes32 ORDER_ITEM_HASH = 0xf73f37e9f570369ceaab59cef16249ae1c0ad1afd592d656afac0be6f63b87e0; 1203: bytes32 TOKEN_INFO_HASH = 0x88f0bd19d14f8b5d22c0605a15d9fffc285ebc8c86fb21139456d305982906f1;
There are 4 instances of this issue:
File: contracts/core/InfinityExchange.sol #1 /// @audit Missing: '@param verifySellOrder' 452 /** 453 * @notice Checks whether orders are valid 454 * @dev Checks whether currencies match, sides match, complications match and if each order is valid (see isOrderValid) 455 * @param orderHash hash of the order 456 * @param sell the sell order 457 * @param buy the buy order 458 * @return whether orders are valid 459 */ 460 function verifyMatchOneToManyOrders( 461 bytes32 orderHash, 462 bool verifySellOrder, 463 OrderTypes.MakerOrder calldata sell, 464 OrderTypes.MakerOrder calldata buy 465: ) public view returns (bool) {
File: contracts/core/InfinityExchange.sol #2 /// @audit Missing: '@return' 512 * @param orderHash computed hash of the order 513 */ 514: function isOrderValid(OrderTypes.MakerOrder calldata order, bytes32 orderHash) public view returns (bool) {
File: contracts/core/InfinityExchange.sol #3 /// @audit Missing: '@return' 640 * @param protocolFeeBps exchange fee 641 */ 642 function _matchOneMakerBuyToManyMakerSells( 643 bytes32 buyOrderHash, 644 OrderTypes.MakerOrder calldata sell, 645 OrderTypes.MakerOrder calldata buy, 646 uint16 protocolFeeBps 647: ) internal returns (uint256) {
File: contracts/core/InfinityExchange.sol #4 /// @audit Missing: '@param execPrice' 661 /** 662 * @notice Internal helper function to match orders specified via a higher order intent 663 * @param sell the sell order 664 * @param buy the buy order 665 * @param constructedNfts the nfts constructed by an off chain matching that are guaranteed to intersect 666 with the user specified signed intents (orders) 667 * @param startGasPerOrder start gas when this order started execution 668 * @param protocolFeeBps exchange fee 669 * @param wethTransferGasUnits gas units that a WETH transfer will use 670 * @param weth WETH address 671 */ 672 function _matchOrders( 673 OrderTypes.MakerOrder calldata sell, 674 OrderTypes.MakerOrder calldata buy, 675 OrderTypes.OrderItem[] calldata constructedNfts, 676 uint256 startGasPerOrder, 677 uint256 execPrice, 678 uint16 protocolFeeBps, 679 uint32 wethTransferGasUnits, 680: address weth
indexed
fieldsEach event
should use three indexed
fields if there are three or more fields
There are 11 instances of this issue:
File: contracts/token/InfinityToken.sol 35: event EpochAdvanced(uint256 currentEpoch, uint256 supplyMinted);
File: contracts/staking/InfinityStaker.sol 44: event Staked(address indexed user, uint256 amount, Duration duration); 45: event DurationChanged(address indexed user, uint256 amount, Duration oldDuration, Duration newDuration); 46: event UnStaked(address indexed user, uint256 amount); 47: event RageQuit(address indexed user, uint256 totalToUser, uint256 penalty);
File: contracts/core/InfinityExchange.sol 80: event CancelAllOrders(address user, uint256 newMinNonce); 81: event CancelMultipleOrders(address user, uint256[] orderNonces); 82: event NewWethTransferGasUnits(uint32 wethTransferGasUnits); 83: event NewProtocolFee(uint16 protocolFee); 85 event MatchOrderFulfilled( 86 bytes32 sellOrderHash, 87 bytes32 buyOrderHash, 88 address seller, 89 address buyer, 90 address complication, // address of the complication that defines the execution 91 address currency, // token address of the transacting currency 92 uint256 amount // amount spent on the order 93: ); 95 event TakeOrderFulfilled( 96 bytes32 orderHash, 97 address seller, 98 address buyer, 99 address complication, // address of the complication that defines the execution 100 address currency, // token address of the transacting currency 101 uint256 amount // amount spent on the order 102: );
Consider changing the variable to be an unnamed one
There are 6 instances of this issue:
File: contracts/token/InfinityToken.sol /// @audit admin 113: function getAdmin() public view returns (address admin) { /// @audit timelock 117: function getTimelock() public view returns (uint256 timelock) { /// @audit inflation 121: function getInflation() public view returns (uint256 inflation) { /// @audit cliff 125: function getCliff() public view returns (uint256 cliff) { /// @audit totalEpochs 129: function getMaxEpochs() public view returns (uint256 totalEpochs) { /// @audit epochDuration 133: function getEpochDuration() public view returns (uint256 epochDuration) {
#0 - nneverlander
2022-06-22T19:02:00Z
Thank you!!
#1 - HardlyDifficult
2022-07-14T01:10:17Z
π Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xAsm0d3us, 0xDjango, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 0xkowloon, BowTiedWardens, Chom, ElKu, FSchmoede, Funen, GimelSec, Kaiziron, Kenshin, Lambda, MadWookie, MiloTruck, PPrieditis, Picodes, PwnedNoMore, StErMi, Tadashi, TerrierLover, TomJ, Tomio, Wayne, Waze, _Adam, antonttc, apostle0x01, asutorufos, c3phas, codexploder, defsec, delfin454000, fatherOfBlocks, hake, hansfriese, hyh, joestakey, k, kenta, oyc_109, peritoflores, reassor, rfa, robee, sach1r0, simon135, slywaters, zer0dot
305.5469 USDC - $305.55
Issue | Instances | |
---|---|---|
1 | Multiple address mappings can be combined into a single mapping of an address to a struct , where appropriate | 1 |
2 | State variables only set in the constructor should be declared immutable | 2 |
3 | State variables can be packed into fewer storage slots | 1 |
4 | State variables should be cached in stack variables rather than re-reading them from storage | 9 |
5 | Multiple accesses of a mapping/array should use a local variable cache | 13 |
6 | internal functions only called once can be inlined to save gas | 22 |
7 | Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if -statement | 3 |
8 | require() /revert() strings longer than 32 bytes cost extra gas | 3 |
9 | Using bool s for storage incurs overhead | 1 |
10 | Using > 0 costs more gas than != 0 when used on a uint in a require() statement | 1 |
11 | >= costs less gas than > | 1 |
12 | It costs more gas to initialize non-constant /non-immutable variables to zero than to let the default of zero be applied | 26 |
13 | Splitting require() statements that use && saves gas | 2 |
14 | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead | 39 |
15 | Using private rather than public for constants, saves gas | 5 |
16 | Duplicated require() /revert() checks should be refactored to a modifier or function | 6 |
17 | Empty blocks should be removed or emit something | 4 |
18 | Use custom errors rather than revert() /require() strings to save gas | 47 |
19 | Functions guaranteed to revert when called by normal users can be marked payable | 13 |
Total: 199 instances over 19 issues
address
mappings can be combined into a single mapping
of an address
to a struct
, where appropriateSaves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
There is 1 instance of this issue:
File: contracts/core/InfinityExchange.sol #1 70 mapping(address => uint256) public userMinOrderNonce; 71 72 /// @dev This records already executed or cancelled orders to prevent replay attacks. 73: mapping(address => mapping(uint256 => bool)) public isUserOrderNonceExecutedOrCancelled;
immutable
Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmacces (100 gas) with a PUSH32
(3 gas).
There are 2 instances of this issue:
File: contracts/token/InfinityToken.sol #1 31: uint256 public currentEpochTimestamp;
File: contracts/staking/InfinityStaker.sol #2 25: address public INFINITY_TOKEN;
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 can also be cheaper
There is 1 instance of this issue:
File: contracts/staking/InfinityStaker.sol #1 /// @audit Variable ordering with 3 slots instead of the current 4: /// @audit mapping(32):userstakedAmounts, address(20):INFINITY_TOKEN, uint16(2):BRONZE_STAKE_THRESHOLD, uint16(2):SILVER_STAKE_THRESHOLD, uint16(2):GOLD_STAKE_THRESHOLD, uint16(2):PLATINUM_STAKE_THRESHOLD, uint16(2):THREE_MONTH_PENALTY, uint16(2):SIX_MONTH_PENALTY, address(20):INFINITY_TREASURY, uint16(2):TWELVE_MONTH_PENALTY 23: mapping(address => mapping(Duration => StakeAmount)) public userstakedAmounts;
The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.
There are 9 instances of this issue:
File: contracts/token/InfinityToken.sol /// @audit currentEpoch on line 61 66: uint256 epochsLeft = getMaxEpochs() - currentEpoch; /// @audit currentEpoch on line 72 81: emit EpochAdvanced(currentEpoch, supplyToMint); /// @audit previousEpochTimestamp on line 63 65: uint256 epochsPassedSinceLastAdvance = (block.timestamp - previousEpochTimestamp) / getEpochDuration();
File: contracts/staking/InfinityStaker.sol /// @audit INFINITY_TOKEN on line 69 74: IERC20(INFINITY_TOKEN).safeTransferFrom(msg.sender, address(this), amount); /// @audit INFINITY_TOKEN on line 141 142: IERC20(INFINITY_TOKEN).safeTransfer(INFINITY_TREASURY, penalty); /// @audit BRONZE_STAKE_THRESHOLD on line 213 215: } else if (totalPower > BRONZE_STAKE_THRESHOLD && totalPower <= SILVER_STAKE_THRESHOLD) { /// @audit SILVER_STAKE_THRESHOLD on line 215 217: } else if (totalPower > SILVER_STAKE_THRESHOLD && totalPower <= GOLD_STAKE_THRESHOLD) { /// @audit GOLD_STAKE_THRESHOLD on line 217 219: } else if (totalPower > GOLD_STAKE_THRESHOLD && totalPower <= PLATINUM_STAKE_THRESHOLD) {
File: contracts/core/InfinityExchange.sol /// @audit WETH_TRANSFER_GAS_UNITS on line 197 231: uint256 gasCost = (startGas - gasleft() + WETH_TRANSFER_GAS_UNITS) * tx.gasprice;
The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage
or calldata
variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations. Caching an array's struct avoids recalculating the array offsets into memory/calldata
There are 13 instances of this issue:
File: contracts/core/InfinityOrderBookComplication.sol /// @audit manyMakerOrders[i] on line 81 83: numItems += manyMakerOrders[i].nfts[j].tokens.length; /// @audit manyMakerOrders[i] on line 83 91: manyMakerOrders[i].constraints[3] <= block.timestamp && /// @audit manyMakerOrders[i] on line 91 92: manyMakerOrders[i].constraints[4] >= block.timestamp; /// @audit manyMakerOrders[i] on line 92 94: itemsIntersect = itemsIntersect && doItemsIntersect(makerOrder.nfts, manyMakerOrders[i].nfts);
File: contracts/core/InfinityExchange.sol /// @audit makerOrders1[i] on line 150 151: (bool canExec, uint256 execPrice) = IComplication(makerOrders1[i].execParams[0]).canExecMatchOneToOne( /// @audit makerOrders[0] on line 303 304: bool isMakerSeller = makerOrders[0].isSellOrder; /// @audit makerOrders[i] on line 311 312: makerOrders[i].constraints[4] >= block.timestamp; /// @audit makerOrders[i] on line 312 314: require(currency == makerOrders[i].execParams[1], 'cannot mix currencies'); /// @audit makerOrders[i] on line 314 315: require(isMakerSeller == makerOrders[i].isSellOrder, 'cannot mix order sides'); /// @audit makerOrders[0] on line 344 345: bool isMakerSeller = makerOrders[0].isSellOrder; /// @audit makerOrders[i] on line 350 351: require(isMakerSeller == makerOrders[i].isSellOrder, 'cannot mix order sides'); /// @audit nfts[i] on line 1191 1191: bytes32 hash = keccak256(abi.encode(ORDER_ITEM_HASH, nfts[i].collection, _tokensHash(nfts[i].tokens))); /// @audit tokens[i] on line 1207 1207: bytes32 hash = keccak256(abi.encode(TOKEN_INFO_HASH, tokens[i].tokenId, tokens[i].numTokens));
internal
functions only called once can be inlined to save gasNot inlining costs 20 to 40 gas because of two extra JUMP
instructions and additional stack operations needed for function calls.
There are 22 instances of this issue:
File: contracts/staking/InfinityStaker.sol 275: function _getDurationInSeconds(Duration duration) internal pure returns (uint256) { 290 function _updateUserStakedAmounts( 291 address user, 292 uint256 amount, 293 uint256 noVesting, 294 uint256 vestedThreeMonths, 295 uint256 vestedSixMonths, 296: uint256 vestedTwelveMonths 328: function _clearUserStakedAmounts(address user) internal {
File: contracts/core/InfinityOrderBookComplication.sol 317: function _sumCurrentPrices(OrderTypes.MakerOrder[] calldata orders) internal view returns (uint256) {
File: contracts/core/InfinityExchange.sol 574 function _matchOneToOneOrders( 575 OrderTypes.MakerOrder calldata makerOrder1, 576 OrderTypes.MakerOrder calldata makerOrder2, 577 uint256 startGasPerOrder, 578 uint256 execPrice, 579 uint16 protocolFeeBps, 580 uint32 wethTransferGasUnits, 581: address weth 611 function _matchOneMakerSellToManyMakerBuys( 612 bytes32 sellOrderHash, 613 OrderTypes.MakerOrder calldata sell, 614 OrderTypes.MakerOrder calldata buy, 615 uint256 startGasPerOrder, 616 uint16 protocolFeeBps, 617 uint32 wethTransferGasUnits, 618: address weth 642 function _matchOneMakerBuyToManyMakerSells( 643 bytes32 buyOrderHash, 644 OrderTypes.MakerOrder calldata sell, 645 OrderTypes.MakerOrder calldata buy, 646 uint16 protocolFeeBps 647: ) internal returns (uint256) { 672 function _matchOrders( 673 OrderTypes.MakerOrder calldata sell, 674 OrderTypes.MakerOrder calldata buy, 675 OrderTypes.OrderItem[] calldata constructedNfts, 676 uint256 startGasPerOrder, 677 uint256 execPrice, 678 uint16 protocolFeeBps, 679 uint32 wethTransferGasUnits, 680: address weth 712 function _execMatchOneToOneOrders( 713 bytes32 sellOrderHash, 714 bytes32 buyOrderHash, 715 OrderTypes.MakerOrder calldata sell, 716 OrderTypes.MakerOrder calldata buy, 717 uint256 startGasPerOrder, 718 uint256 execPrice, 719 uint16 protocolFeeBps, 720 uint32 wethTransferGasUnits, 721: address weth 763 function _execMatchOneMakerSellToManyMakerBuys( 764 bytes32 sellOrderHash, 765 bytes32 buyOrderHash, 766 OrderTypes.MakerOrder calldata sell, 767 OrderTypes.MakerOrder calldata buy, 768 uint256 startGasPerOrder, 769 uint256 execPrice, 770 uint16 protocolFeeBps, 771 uint32 wethTransferGasUnits, 772: address weth 810 function _execMatchOneMakerBuyToManyMakerSells( 811 bytes32 sellOrderHash, 812 bytes32 buyOrderHash, 813 OrderTypes.MakerOrder calldata sell, 814 OrderTypes.MakerOrder calldata buy, 815 uint256 execPrice, 816 uint16 protocolFeeBps 817: ) internal returns (uint256) { 861 function _execMatchOrders( 862 bytes32 sellOrderHash, 863 bytes32 buyOrderHash, 864 OrderTypes.MakerOrder calldata sell, 865 OrderTypes.MakerOrder calldata buy, 866 OrderTypes.OrderItem[] calldata constructedNfts, 867 uint256 startGasPerOrder, 868 uint256 execPrice, 869 uint16 protocolFeeBps, 870 uint32 wethTransferGasUnits, 871: address weth 905 function _execMatchOrder( 906 address seller, 907 address buyer, 908 uint256 sellNonce, 909 uint256 buyNonce, 910 OrderTypes.OrderItem[] calldata constructedNfts, 911 address currency, 912: uint256 amount 941 function _takeOrders( 942 OrderTypes.MakerOrder calldata makerOrder, 943 OrderTypes.OrderItem[] calldata takerItems, 944: uint256 execPrice 963 function _execTakeOrders( 964 bytes32 makerOrderHash, 965 OrderTypes.MakerOrder calldata makerOrder, 966 OrderTypes.OrderItem[] calldata takerItems, 967 bool isMakerSeller, 968: uint256 execPrice 989 function _execTakeOneOrder( 990 bytes32 makerOrderHash, 991 OrderTypes.MakerOrder calldata makerOrder, 992 bool isMakerSeller, 993: uint256 execPrice 1062 function _transferNFTs( 1063 address from, 1064 address to, 1065: OrderTypes.OrderItem calldata item 1080 function _transferERC721s( 1081 address from, 1082 address to, 1083: OrderTypes.OrderItem calldata item 1101 function _transferERC1155s( 1102 address from, 1103 address to, 1104: OrderTypes.OrderItem calldata item 1128 function _transferFees( 1129 address seller, 1130 address buyer, 1131 uint256 amount, 1132: address currency 1185: function _nftsHash(OrderTypes.OrderItem[] calldata nfts) internal pure returns (bytes32) { 1201: function _tokensHash(OrderTypes.TokenInfo[] calldata tokens) internal pure returns (bytes32) {
unchecked {}
for subtractions where the operands cannot underflow because of a previous require()
or if
-statementrequire(a <= b); x = b - a
=> require(a <= b); unchecked { x = b - a }
There are 3 instances of this issue:
File: contracts/staking/InfinityStaker.sol #1 /// @audit if-condition on line 298 301: amount = amount - noVesting;
File: contracts/staking/InfinityStaker.sol #2 /// @audit if-condition on line 302 305: amount = amount - vestedThreeMonths;
File: contracts/staking/InfinityStaker.sol #3 /// @audit if-condition on line 306 309: amount = amount - vestedSixMonths;
require()
/revert()
strings longer than 32 bytes cost extra gasEach extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas
There are 3 instances of this issue:
File: contracts/staking/InfinityStaker.sol #1 92 require( 93 userstakedAmounts[msg.sender][oldDuration].amount >= amount, 94 'insufficient staked amount to change duration' 95: );
File: contracts/staking/InfinityStaker.sol #2 96: require(newDuration > oldDuration, 'new duration must be greater than old duration');
File: contracts/core/InfinityExchange.sol #3 395: require(!isUserOrderNonceExecutedOrCancelled[msg.sender][orderNonces[i]], 'nonce already executed or cancelled');
bool
s for storage incurs overhead// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27
Use uint256(1)
and uint256(2)
for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past
There is 1 instance of this issue:
File: contracts/core/InfinityExchange.sol #1 73: mapping(address => mapping(uint256 => bool)) public isUserOrderNonceExecutedOrCancelled;
> 0
costs more gas than != 0
when used on a uint
in a require()
statementThis change saves 6 gas per instance. The optimization works until solidity version 0.8.13 where there is a regression in gas costs.
There is 1 instance of this issue:
File: contracts/core/InfinityExchange.sol #1 392: require(numNonces > 0, 'cannot be empty');
>=
costs less gas than >
The compiler uses opcodes GT
and ISZERO
for solidity code that uses >
, but only requires LT
for >=
, which saves 3 gas
There is 1 instance of this issue:
File: contracts/token/InfinityToken.sol #1 67 epochsPassedSinceLastAdvance = epochsPassedSinceLastAdvance > epochsLeft 68 ? epochsLeft 69: : epochsPassedSinceLastAdvance;
constant
/non-immutable
variables to zero than to let the default of zero be appliedNot overwriting the default for stack variables saves 8 gas. Storage and memory variables have larger savings
There are 26 instances of this issue:
File: contracts/core/InfinityOrderBookComplication.sol 76: for (uint256 i = 0; i < ordersLength; ) { 82: for (uint256 j = 0; j < nftsLength; ) { 197: uint256 numConstructedItems = 0; 199: for (uint256 i = 0; i < nftsLength; ) { 214: uint256 numTakerItems = 0; 216: for (uint256 i = 0; i < nftsLength; ) { 244: uint256 numCollsMatched = 0; 246: for (uint256 i = 0; i < order2NftsLength; ) { 247: for (uint256 j = 0; j < order1NftsLength; ) { 289: uint256 numTokenIdsPerCollMatched = 0; 290: for (uint256 k = 0; k < item2TokensLength; ) { 291: for (uint256 l = 0; l < item1TokensLength; ) { 318: uint256 sum = 0; 320: for (uint256 i = 0; i < ordersLength; ) {
File: contracts/core/InfinityExchange.sol 148: for (uint256 i = 0; i < numMakerOrders; ) { 200: for (uint256 i = 0; i < ordersLength; ) { 219: for (uint256 i = 0; i < ordersLength; ) { 272: for (uint256 i = 0; i < numSells; ) { 308: for (uint256 i = 0; i < numMakerOrders; ) { 349: for (uint256 i = 0; i < ordersLength; ) { 393: for (uint256 i = 0; i < numNonces; ) { 1048: for (uint256 i = 0; i < numNfts; ) { 1086: for (uint256 i = 0; i < numTokens; ) { 1109: for (uint256 i = 0; i < numNfts; ) { 1190: for (uint256 i = 0; i < numNfts; ) { 1206: for (uint256 i = 0; i < numTokens; ) {
require()
statements that use &&
saves gasSee this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper
There are 2 instances of this issue:
File: contracts/core/InfinityExchange.sol #1 264: require(numSells == buys.length && numSells == constructs.length, 'mismatched lengths');
File: contracts/core/InfinityExchange.sol #2 949: require(makerOrderValid && executionValid, 'order not verified');
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
There are 39 instances of this issue:
File: contracts/staking/InfinityStaker.sol 33: uint16 public BRONZE_STAKE_THRESHOLD = 1000; 34: uint16 public SILVER_STAKE_THRESHOLD = 5000; 35: uint16 public GOLD_STAKE_THRESHOLD = 10000; 36: uint16 public PLATINUM_STAKE_THRESHOLD = 20000; 40: uint16 public THREE_MONTH_PENALTY = 2; 41: uint16 public SIX_MONTH_PENALTY = 3; 42: uint16 public TWELVE_MONTH_PENALTY = 4; 351: function updateStakeLevelThreshold(StakeLevel stakeLevel, uint16 threshold) external onlyOwner { 365: uint16 threeMonthPenalty, 366: uint16 sixMonthPenalty, 367: uint16 twelveMonthPenalty
File: contracts/core/InfinityExchange.sol 61: uint32 public WETH_TRANSFER_GAS_UNITS = 50000; 63: uint16 public PROTOCOL_FEE_BPS = 250; 82: event NewWethTransferGasUnits(uint32 wethTransferGasUnits); 83: event NewProtocolFee(uint16 protocolFee); 145: uint16 protocolFeeBps = PROTOCOL_FEE_BPS; 146: uint32 wethTransferGasUnits = WETH_TRANSFER_GAS_UNITS; 196: uint16 protocolFeeBps = PROTOCOL_FEE_BPS; 197: uint32 wethTransferGasUnits = WETH_TRANSFER_GAS_UNITS; 269: uint16 protocolFeeBps = PROTOCOL_FEE_BPS; 270: uint32 wethTransferGasUnits = WETH_TRANSFER_GAS_UNITS; 423: (bytes32 r, bytes32 s, uint8 v) = abi.decode(order.sig, (bytes32, bytes32, uint8)); 518: (bytes32 r, bytes32 s, uint8 v) = abi.decode(order.sig, (bytes32, bytes32, uint8)); 579: uint16 protocolFeeBps, 580: uint32 wethTransferGasUnits, 616: uint16 protocolFeeBps, 617: uint32 wethTransferGasUnits, 646: uint16 protocolFeeBps 678: uint16 protocolFeeBps, 679: uint32 wethTransferGasUnits, 719: uint16 protocolFeeBps, 720: uint32 wethTransferGasUnits, 770: uint16 protocolFeeBps, 771: uint32 wethTransferGasUnits, 816: uint16 protocolFeeBps 869: uint16 protocolFeeBps, 870: uint32 wethTransferGasUnits, 1260: function updateWethTranferGas(uint32 _wethTransferGasUnits) external onlyOwner { 1266: function setProtocolFee(uint16 _protocolFeeBps) external onlyOwner {
private
rather than public
for constants, saves gasIf needed, the value can be read from the verified contract source code. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
There are 5 instances of this issue:
File: contracts/token/InfinityToken.sol 25: bytes32 public constant EPOCH_INFLATION = keccak256('Inflation'); 26: bytes32 public constant EPOCH_DURATION = keccak256('EpochDuration'); 27: bytes32 public constant EPOCH_CLIFF = keccak256('Cliff'); 28: bytes32 public constant MAX_EPOCHS = keccak256('MaxEpochs');
File: contracts/core/InfinityExchange.sol 57: bytes32 public immutable DOMAIN_SEPARATOR;
require()
/revert()
checks should be refactored to a modifier or functionSaves deployment costs
There are 6 instances of this issue:
File: contracts/staking/InfinityStaker.sol 117: require(amount != 0, 'stake amount cant be 0');
File: contracts/core/InfinityExchange.sol 183: require(msg.sender == MATCH_EXECUTOR, 'OME'); 347: require(currency != address(0), 'offers only in ERC20'); 350: require(currency == makerOrders[i].execParams[1], 'cannot mix currencies'); 351: require(isMakerSeller == makerOrders[i].isSellOrder, 'cannot mix order sides'); 362: require(msg.value >= totalPrice, 'invalid total price');
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{...}}
)
There are 4 instances of this issue:
File: contracts/staking/InfinityStaker.sol #1 55: fallback() external payable {}
File: contracts/staking/InfinityStaker.sol #2 57: receive() external payable {}
File: contracts/core/InfinityExchange.sol #3 119: fallback() external payable {}
File: contracts/core/InfinityExchange.sol #4 121: receive() external payable {}
revert()
/require()
strings to save gasCustom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hitby avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
There are 47 instances of this issue:
File: contracts/token/InfinityToken.sol 61: require(currentEpoch < getMaxEpochs(), 'no epochs left'); 62: require(block.timestamp >= currentEpochTimestamp + getCliff(), 'cliff not passed'); 63: require(block.timestamp >= previousEpochTimestamp + getEpochDuration(), 'not ready to advance');
File: contracts/staking/InfinityStaker.sol 68: require(amount != 0, 'stake amount cant be 0'); 69: require(IERC20(INFINITY_TOKEN).balanceOf(msg.sender) >= amount, 'insufficient balance to stake'); 91: require(amount != 0, 'amount cant be 0'); 92 require( 93 userstakedAmounts[msg.sender][oldDuration].amount >= amount, 94 'insufficient staked amount to change duration' 95: ); 96: require(newDuration > oldDuration, 'new duration must be greater than old duration'); 117: require(amount != 0, 'stake amount cant be 0'); 123: require(totalVested >= amount, 'insufficient balance to unstake'); 193: require(totalStaked >= 0, 'nothing staked to rage quit'); 347: require(sent, 'Failed to send Ether');
File: contracts/core/InfinityOrderBookComplication.sol 255: require(tokenIdsIntersect, 'tokenIds dont intersect');
File: contracts/core/InfinityExchange.sol 138: require(msg.sender == MATCH_EXECUTOR, 'OME'); 139: require(numMakerOrders == makerOrders2.length, 'mismatched lengths'); 150: require(_complications.contains(makerOrders1[i].execParams[0]), 'invalid complication'); 155: require(canExec, 'cannot execute'); 183: require(msg.sender == MATCH_EXECUTOR, 'OME'); 184: require(_complications.contains(makerOrder.execParams[0]), 'invalid complication'); 185 require( 186 IComplication(makerOrder.execParams[0]).canExecMatchOneToMany(makerOrder, manyMakerOrders), 187 'cannot execute' 188: ); 190: require(isOrderValid(makerOrder, makerOrderHash), 'invalid maker order'); 263: require(msg.sender == MATCH_EXECUTOR, 'OME'); 264: require(numSells == buys.length && numSells == constructs.length, 'mismatched lengths'); 279: require(executionValid, 'cannot execute'); 306: require(currency != address(0), 'offers only in ERC20'); 310: require(isOrderValid(makerOrders[i], makerOrderHash), 'invalid maker order'); 313: require(isTimeValid, 'invalid time'); 314: require(currency == makerOrders[i].execParams[1], 'cannot mix currencies'); 315: require(isMakerSeller == makerOrders[i].isSellOrder, 'cannot mix order sides'); 326: require(msg.value >= totalPrice, 'invalid total price'); 342: require(ordersLength == takerNfts.length, 'mismatched lengths'); 347: require(currency != address(0), 'offers only in ERC20'); 350: require(currency == makerOrders[i].execParams[1], 'cannot mix currencies'); 351: require(isMakerSeller == makerOrders[i].isSellOrder, 'cannot mix order sides'); 362: require(msg.value >= totalPrice, 'invalid total price'); 380: require(minNonce > userMinOrderNonce[msg.sender], 'nonce too low'); 381: require(minNonce < userMinOrderNonce[msg.sender] + 1000000, 'too many'); 392: require(numNonces > 0, 'cannot be empty'); 394: require(orderNonces[i] >= userMinOrderNonce[msg.sender], 'nonce too low'); 395: require(!isUserOrderNonceExecutedOrCancelled[msg.sender][orderNonces[i]], 'nonce already executed or cancelled'); 587: require(verifyMatchOneToOneOrders(sellOrderHash, buyOrderHash, sell, buy), 'order not verified'); 621: require(verifyMatchOneToManyOrders(buyOrderHash, false, sell, buy), 'order not verified'); 649: require(verifyMatchOneToManyOrders(sellOrderHash, true, sell, buy), 'order not verified'); 684: require(verifyMatchOrders(sellOrderHash, buyOrderHash, sell, buy), 'order not verified'); 949: require(makerOrderValid && executionValid, 'order not verified'); 1141: require(sent, 'failed to send ether to seller'); 1231: require(sent, 'failed');
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. The extra opcodes avoided are
CALLVALUE
(2),DUP1
(3),ISZERO
(3),PUSH2
(3),JUMPI
(10),PUSH1
(3),DUP1
(3),REVERT
(0),JUMPDEST
(1),POP
(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost
There are 13 instances of this issue:
File: contracts/staking/InfinityStaker.sol 345: function rescueETH(address destination) external payable onlyOwner { 351: function updateStakeLevelThreshold(StakeLevel stakeLevel, uint16 threshold) external onlyOwner { 364 function updatePenalties( 365 uint16 threeMonthPenalty, 366 uint16 sixMonthPenalty, 367 uint16 twelveMonthPenalty 368: ) external onlyOwner { 375: function updateInfinityTreasury(address _infinityTreasury) external onlyOwner {
File: contracts/core/InfinityExchange.sol 1220 function rescueTokens( 1221 address destination, 1222 address currency, 1223 uint256 amount 1224: ) external onlyOwner { 1229: function rescueETH(address destination) external payable onlyOwner { 1235: function addCurrency(address _currency) external onlyOwner { 1240: function addComplication(address _complication) external onlyOwner { 1245: function removeCurrency(address _currency) external onlyOwner { 1250: function removeComplication(address _complication) external onlyOwner { 1255: function updateMatchExecutor(address _matchExecutor) external onlyOwner { 1260: function updateWethTranferGas(uint32 _wethTransferGasUnits) external onlyOwner { 1266: function setProtocolFee(uint16 _protocolFeeBps) external onlyOwner {
#0 - nneverlander
2022-06-22T19:07:53Z
Thank you for the detailed report.
#1 - HardlyDifficult
2022-07-14T18:50:18Z
π₯