Platform: Code4rena
Start Date: 05/10/2023
Pot Size: $33,050 USDC
Total HM: 1
Participants: 54
Period: 6 days
Judge: hansfriese
Id: 294
League: ETH
Rank: 42/54
Findings: 1
Award: $8.19
š Selected for report: 0
š Solo Findings: 0
š Selected for report: windhustler
Also found by: 0xhex, 0xta, JCK, K42, MatricksDeCoder, MrPotatoMagic, SAQ, SY_S, SovaSlava, aslanbek, d3e4, danb, hunter_w3b, lukejohn
8.1878 USDC - $8.19
no | Issue | Instances | |
---|---|---|---|
[G-01] | Not using the named return variable when a function returns, wastes deployment gas | 1 | - |
[G-02] | Can make the variable outside the loop to save gas | 1 | - |
[G-03] | The result of function calls should be cached rather than re-calling the function | 1 | - |
[G-04] | Use constants instead of type(uintx).max | 1 | - |
[G-05] | abi.encode() is less efficient than abi.encodepacked() | 1 | - |
[G-06] | Use hardcode address instead address(this) | 1 | - |
[G-07] | Non efficient zero initialization | 1 | - |
[G-08] | Use assembly to validate msg.sender | 5 | - |
[G-09] | No need to evaluate all expressions to know if one of them is true | 1 | - |
[G-10] | Make 3 event parameters indexed when possible | 1 | - |
[G-11] | Low levelĀ callĀ can be optimized with assembly | 1 | - |
When you execute a function that returns values in Solidity, the EVM still performs the necessary operations to execute and return those values. This includes the cost of allocating memory and packing the return values. If the returned values are not utilized, it can be seen as wasteful since you are incurring gas costs for operations that have no effect.
file: /contracts/ERC20MultiDelegate.sol ///@audit the ' address ' type is declared in function return, in here should not mention if possible. 214 return address(uint160(uint256(hash)));
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L214
Consider making the stack variables before the loop which gonna save gas
file: /contracts/ERC20MultiDelegate.sol 96 uint256 amount = amounts[transferIndex];
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L96
The instances below point to the second+ call of the function within a single function
file: /contracts/ERC20MultiDelegate.sol ///@audit the ' retrieveProxyContractAddress ' function is called tow time in one function. 168 address proxyAddressFrom = retrieveProxyContractAddress(token, from); 169 address proxyAddressTo = retrieveProxyContractAddress(token, to);
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L168-L169
type(uint120).max or type(uint128).max, etc. it uses more gas in the distribution process and also for each transaction than constant usage.
file: /contracts/ERC20MultiDelegate.sol 17 _token.approve(msg.sender, type(uint256).max);
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L17
In terms of efficiency, abi.encodePacked() is generally considered to be more gas-efficient than abi.encode(), because it skips the step of adding function signatures and other metadata to the encoded data. However, this comes at the cost of reduced safety, as abi.encodePacked() does not perform any type checking or padding of data.
Refference: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison
file: /contracts/ERC20MultiDelegate.sol 204 abi.encode(_token, _delegate)
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L204
Instead of usingĀ address(this), it is more gas-efficient to pre-calculate and use the hardcodedĀ address. Foundryās script.sol and solmateāsĀ LibRlp.solĀ contracts can help achieve this. References:Ā https://book.getfoundry.sh/reference/forge-std/compute-create-address
an example :
contract MyContract { address constant public CONTRACT_ADDRESS = 0x1234567890123456789012345678901234567890; function getContractAddress() public view returns (address) { return CONTRACT_ADDRESS; } }
file: /contracts/ERC20MultiDelegate.sol 209 address(this),
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L209
Solidity does not recognize null as a value, so uint variables are initialized to zero. Setting a uint variable to zero is redundant and can waste gas.
file: /contracts/ERC20MultiDelegate.sol 86 uint transferIndex = 0;
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L86
We can use assembly to efficiently validate msg.sender for the didPay and uniswapV3SwapCallback functions with the least amount of opcodes necessary. Additionally, we can use xor() instead of iszero(eq()), saving 3 gas. We can also potentially save gas on the unhappy path by using scratch space to store the error selector, potentially avoiding memory expansion costs.
file: /contracts/ERC20MultiDelegate.sol 17 _token.approve(msg.sender, type(uint256).max); 111 _burnBatch(msg.sender, sources, amounts[:sourcesLength]); 148 token.transferFrom(proxyAddressFrom, msg.sender, amount); 160 token.transferFrom(msg.sender, proxyAddress, amount); 195 return ERC1155(this).balanceOf(msg.sender, uint256(uint160(delegate)));
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L17
When we have a code expressionA || expressionB if expressionA is true then expressionB will not be evaluated and gas saved
file: /contracts/ERC20MultiDelegate.sol 74 require( sourcesLength > 0 || targetsLength > 0, "Delegate: You should provide at least one source or one target delegate" 77 );
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L74-L77
It's the most gas efficient to make up to 3 event parameters indexed. If there are less than 3 parameters, you need to make all parameters indexed.
file: /contracts/ERC20MultiDelegate.sol 33 event DelegationProcessed( address indexed from, address indexed to, uint256 amount 37 );
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L33-L37
When using low-level calls, theĀ returnDataĀ is copied to memory even if the variable is not utilized. The proper way to handle this is through a low level assembly call. For example:
(bool success,) = payable(receiver).call{gas: gas, value: value}(""); can be optimized to: bool success; assembly { success := call(gas, receiver, value, 0, 0, 0, 0) }
file: /contracts/ERC20MultiDelegate.sol 195 return ERC1155(this).balanceOf(msg.sender, uint256(uint160(delegate)));
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L195
#0 - c4-pre-sort
2023-10-13T14:39:21Z
141345 marked the issue as sufficient quality report
#1 - c4-judge
2023-10-24T16:58:52Z
hansfriese marked the issue as grade-b