Platform: Code4rena
Start Date: 27/11/2023
Pot Size: $36,500 USDC
Total HM: 0
Participants: 22
Period: 8 days
Judge: 0xA5DF
Id: 308
League: ETH
Rank: 13/22
Findings: 1
Award: $72.79
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: hunter_w3b
Also found by: 0x11singh99, 0xVolcano, IllIllI, Sathish9098
72.7885 USDC - $72.79
Number | Issue | Instances |
---|---|---|
[G-01] | State variables can be packed into fewer storage slots | 2 |
[G-02] | Make variable outside of the loop | 8 |
[G-03] | Don't cache any value if it is used only once | 4 |
[G-04] | Check amount for zero before transferring | 1 |
[G-05] | Use named returns value in pure functions | 4 |
State variables
can be packed into fewer storage slots.The EVM works with 32 byte words. Variables less than 32 bytes can be declared next to eachother in storage and this will pack the values together into a single 32 byte storage slot (if the values combined are <= 32 bytes). If the variables packed together are retrieved together in functions, we will effectively save ~2000 gas with every subsequent SLOAD for that storage slot. This is due to us incurring a Gwarmaccess (100 gas) versus a Gcoldsload (2100 gas).
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
2 Instances in 1 File
_ERC1155InteractionStatus
and _ERC721InteractionStatus
to uint8
and can be packed together to save 1 SLOT (~2000 Gas)Since _ERC1155InteractionStatus
and _ERC721InteractionStatus
used only to store 2 constant values NOT_INTERACTION
and INTERACTION
whose values are 1 and 2 respectively. So uint8
is more than sufficient to hold value if it
is 1 Or 2. We can see in entire contract Ocean.sol
either NOT_INTERACTION
Or INTERACTION
assigned to both those
state variables. So it will save 1 SLOT by reducing the size of _ERC1155InteractionStatus
and
_ERC721InteractionStatus
to uint8
and pack them into 1 SLOT.
File : src/ocean/Ocean.sol 108: uint256 _ERC1155InteractionStatus; //@audit reduce these to uint8 109: uint256 _ERC721InteractionStatus; //@audit reduce these to uint8
File : src/ocean/Ocean.sol - uint256 _ERC1155InteractionStatus; - uint256 _ERC721InteractionStatus; + uint8 _ERC1155InteractionStatus; + uint8 _ERC721InteractionStatus;
Declaring variables outside of a loop can help save gas in Solidity by reducing gas consumption because it prevents re-declaration of the variable in each iteration of the loop. When variables are declared outside the loop, the gas cost associated with declaration occurs only once, outside the loop's scope.
8 Instances in 1 File
File : src/ocean/Ocean.sol 501: for (uint256 i = 0; i < interactions.length;) { 502: interaction = interactions[i]; 503: 504: (InteractionType interactionType, address externalContract) = 506: _unpackInteractionTypeAndAddress(interaction); 507: 508: // specifiedToken is the token whose amount the user specifies 509: uint256 specifiedToken = _getSpecifiedToken(interactionType, externalContract, interaction); ... 514: uint256 specifiedAmount; 515: if (interaction.specifiedAmount == GET_BALANCE_DELTA) { 516: specifiedAmount = balanceDeltas.getBalanceDelta(interactionType, specifiedToken); 517: } else { 518: specifiedAmount = interaction.specifiedAmount; 519: } 520: 521: (uint256 inputToken, uint256 inputAmount, uint256 outputToken, uint256 outputAmount) = 522: _executeInteraction( 523: interaction, interactionType, externalContract, specifiedToken, specifiedAmount, userAddress_ 524: );
File : src/ocean/Ocean.sol + InteractionType interactionType; + address externalContract; + uint256 specifiedToken; + uint256 specifiedAmount; + uint256 inputToken; + uint256 inputAmount; + uint256 outputToken; + uint256 outputAmount for (uint256 i = 0; i < interactions.length;) { interaction = interactions[i]; - (InteractionType interactionType, address externalContract) = - _unpackInteractionTypeAndAddress(interaction); + (interactionType, externalContract) = + _unpackInteractionTypeAndAddress(interaction); - uint256 specifiedToken = _getSpecifiedToken(interactionType, externalContract, interaction); + specifiedToken = _getSpecifiedToken(interactionType, externalContract, interaction); - uint256 specifiedAmount; + specifiedAmount; if (interaction.specifiedAmount == GET_BALANCE_DELTA) { specifiedAmount = balanceDeltas.getBalanceDelta(interactionType, specifiedToken); } else { specifiedAmount = interaction.specifiedAmount; } - (uint256 inputToken, uint256 inputAmount, uint256 outputToken, uint256 outputAmount) = + (inputToken, inputAmount, outputToken, outputAmount) = _executeInteraction( interaction, interactionType, externalContract, specifiedToken, specifiedAmount, userAddress_ );
cache
any value if it is used only onceWhen variable used only once there is no need to cache it. It wastes extra gas of creating stack variable and writing value to it.
4 Instances in 3 Files
File : src/ocean/Ocean.sol 867: uint256 amountRemaining = amount - feeCharged; //@audit don't cache amount - feeCharged
File : src/adapters/Curve2PoolAdapter.sol 103: address tokenAddress = underlying[tokenId]; //@audit don't cache underlying[tokenId] 122: address tokenAddress = underlying[tokenId]; //@audit don't cache underlying[tokenId]
File : src/adapters/OceanAdapter.sol 71: uint256 unwrappedAmount = inputAmount - unwrapFee; //@audit don't cache inputAmount - unwrapFee
amount
for zero
before transferringChecking if a value is zero before executing a transfer in Ethereum smart contracts can help prevent unnecessary operations and potentially save gas. And transferring zero amount doesn't change anything and waste gas. Note: These instance missed by bot-report
1 Instances in 1 File
File : src/ocean/Ocean.sol 982: payable(userAddress).transfer(transferAmount); //@audit check transferAmount for 0
pure
functionslibrary NoNamedReturnArithmetic { function sum(uint256 num1, uint256 num2) internal pure returns (uint256) { return num1 + num2; } } contract NoNamedReturn { using NoNamedReturnArithmetic for uint256; uint256 public stateVar; function add2State(uint256 num) public { stateVar = stateVar.sum(num); } }
test for test/NoNamedReturn.t.sol:NamedReturnTest [PASS] test_Increment() (gas: 27639)
library NamedReturnArithmetic { function sum(uint256 num1, uint256 num2) internal pure returns (uint256 theSum) { theSum = num1 + num2; } } contract NamedReturn { using NamedReturnArithmetic for uint256; uint256 public stateVar; function add2State(uint256 num) public { stateVar = stateVar.sum(num); } }
test for test/NamedReturn.t.sol:NamedReturnTest [PASS] test_Increment() (gas: 27613)
4 Instances in 2 Files
File : src/ocean/Ocean.sol 353: function onERC1155BatchReceived( 354: address, 355: address, 356: uint256[] calldata, 357: uint256[] calldata, 358: bytes calldata 359: ) 360: external 361: pure 362: override 363: returns (bytes4) 364: { 365: return 0; 366: }
File : src/ocean/Ocean.sol function onERC1155BatchReceived( address, address, uint256[] calldata, uint256[] calldata, bytes calldata ) external pure override - returns (bytes4) + returns (bytes4 zero) { - return 0; + zero = bytes4(0); }
File : src/adapters/OceanAdapter.sol 99: function _fetchInteractionId(address token, uint256 interactionType) internal pure returns (bytes32) { 100: uint256 packedValue = uint256(uint160(token)); 101: packedValue |= interactionType << 248; 102: return bytes32(abi.encode(packedValue)); 103: }
File : src/adapters/OceanAdapter.sol - function _fetchInteractionId(address token, uint256 interactionType) internal pure returns (bytes32) { + function _fetchInteractionId(address token, uint256 interactionType) internal pure returns (bytes32 _packedValue) { uint256 packedValue = uint256(uint160(token)); packedValue |= interactionType << 248; - return bytes32(abi.encode(packedValue)); + _packedValue = bytes32(abi.encode(packedValue)); }
File : src/adapters/OceanAdapter.sol 108: function _calculateOceanId(address tokenAddress, uint256 tokenId) internal pure returns (uint256) { 109: return uint256(keccak256(abi.encodePacked(tokenAddress, tokenId))); 110: }
File : src/adapters/OceanAdapter.sol - function _calculateOceanId(address tokenAddress, uint256 tokenId) internal pure returns (uint256) { - return uint256(keccak256(abi.encodePacked(tokenAddress, tokenId))); + function _calculateOceanId(address tokenAddress, uint256 tokenId) internal pure returns (uint256 _tokenId) { + _tokenId = uint256(keccak256(abi.encodePacked(tokenAddress, tokenId))); }
File : src/adapters/OceanAdapter.sol 117: function onERC1155Received(address, address, uint256, uint256, bytes memory) public pure returns (bytes4) { 118: return this.onERC1155Received.selector; 119: }
File : src/adapters/OceanAdapter.sol - function onERC1155Received(address, address, uint256, uint256, bytes memory) public pure returns (bytes4) { - return this.onERC1155Received.selector; + function onERC1155Received(address, address, uint256, uint256, bytes memory) public pure returns (bytes4 _selector) { + _selector = this.onERC1155Received.selector; }
#0 - c4-pre-sort
2023-12-10T17:14:20Z
raymondfam marked the issue as sufficient quality report
#1 - 0xA5DF
2023-12-16T14:45:39Z
G1 is significant
#2 - c4-judge
2023-12-17T09:35:52Z
0xA5DF marked the issue as grade-b