Platform: Code4rena
Start Date: 11/11/2022
Pot Size: $36,500 USDC
Total HM: 5
Participants: 62
Period: 3 days
Judge: berndartmueller
Id: 181
League: ETH
Rank: 49/62
Findings: 1
Award: $22.22
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: ReyAdmirado
Also found by: 0x4non, 0xRoxas, 0xab00, Awesome, Aymen0909, Bnke0x0, Deivitto, Diana, IllIllI, Rahoz, RaymondFam, Rolezn, Sathish9098, ajtra, aphak5010, aviggiano, c3phas, carlitox477, ch0bu, cryptostellar5, erictee, lukris02, martin, rotcivegaf, saian, shark, trustindistrust, zaskoh
22.2155 USDC - $22.22
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.
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L66 function _authorizeUpgrade(address) internal override onlyOwner {}
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Pool.sol#L15 function _authorizeUpgrade(address) internal override onlyOwner {}
Remove empty blocked or emit something / revert / add logic.
require()
check should be refactoredduplicated require()
/ revert()
checks should be
refactored to a modifier or function to save gas
Event appears twice and can be reduced
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L327 require(address(_executionDelegate) != address(0), "Address cannot be zero");
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L336 require(address(_policyManager) != address(0), "Address cannot be zero");
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L345 require(_oracle != address(0), "Address cannot be zero");
refactor this checks to different functions to save gas
Unchecked operations as the ++i on for loops are cheaper than checked one.
In Solidity 0.8+, there’s a default overflow check on unsigned integers. It’s possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline..
The code would go from:
for (uint256 i; i < numIterations; i++) { // ... }
to
for (uint256 i; i < numIterations;) { // ... unchecked { ++i; } } The risk of overflow is inexistent for a uint256 here.
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L177 for (uint8 i=0; i < executionsLength; i++) {
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L184 for (uint8 i = 0; i < executionsLength; i++) {
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L307 for (uint8 i = 0; i < orders.length; i++) {
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L598 for (uint8 i = 0; i < fees.length; i++) {
Add unchecked ++i
at the end of all the for loop where it's not expected to overflow and remove them from the for header
Variables read more than once improves gas usage when cached into local variable
In loops or state variables, this is even more gas saving
Cache variables used more than one into a local variable.
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
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L56 function open() external onlyOwner {
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L60 function close() external onlyOwner {
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L66 function _authorizeUpgrade(address) internal override onlyOwner {}
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Pool.sol#L15 function _authorizeUpgrade(address) internal override onlyOwner {}
Consider adding payable to save gas
>=
cheaper than >
Strict inequalities ( >
) are more expensive than non-strict ones ( >=
). This is due to some supplementary checks (ISZERO
, 3 gas)
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L412 if (order.order.extraParams.length > 0 && order.order.extraParams[0] == 0x01) {
Consider using >= 1
instead of > 0
to avoid some opcodes
<X> += <Y>
costs more gas than <X> = <X> + <Y>
for state variablesx+=y
costs more gas than x=x+y for state variables
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L316 nonces[msg.sender] += 1;
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Pool.sol#L36 _balances[msg.sender] += msg.value;
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Pool.sol#L74 _balances[to] += amount;
-=
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L574
remainingETH -= price;https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Pool.sol#L46 _balances[msg.sender] -= amount;
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Pool.sol#L73 _balances[from] -= amount;
Don't use +=
for state variables as it cost more gas.
Not inlining costs 20
to 40
gas because of two extra JUMP
instructions and additional stack operations needed for function calls.
https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L565 https://github.com/code-423n4/2022-11-non-fungible/blob/8eaeb007b81d3c89ecf6a5dc2a67eecbe94cefaf/contracts/Exchange.sol#L653
Consider changing internal function only called once to inline code for gas savings
A value which value is already known can be used directly rather than reading it from the storage
function setExecutionDelegate(IExecutionDelegate _executionDelegate) external onlyOwner { require(address(_executionDelegate) != address(0), "Address cannot be zero"); executionDelegate = _executionDelegate; emit NewExecutionDelegate(executionDelegate); }
_executionDelegate
parameter of setExecutionDelegate
can be used directly to avoid unnecessary storage read to save some gas.
Recommendation Change to:
function setExecutionDelegate(IExecutionDelegate _executionDelegate) external onlyOwner { require(address(_executionDelegate) != address(0), "Address cannot be zero"); executionDelegate = _executionDelegate; emit NewExecutionDelegate(_executionDelegate); }
Set directly the value to avoid unnecessary storage read to save some gas
#0 - c4-judge
2022-11-17T12:53:25Z
berndartmueller marked the issue as grade-b