ENS - SY_S's results

Decentralized naming for wallets, websites, & more.

General Information

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

ENS

Findings Distribution

Researcher Performance

Rank: 41/54

Findings: 1

Award: $8.19

Gas:
grade-b

🌟 Selected for report: 0

šŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: windhustler

Also found by: 0xhex, 0xta, JCK, K42, MatricksDeCoder, MrPotatoMagic, SAQ, SY_S, SovaSlava, aslanbek, d3e4, danb, hunter_w3b, lukejohn

Labels

bug
G (Gas Optimization)
grade-b
sufficient quality report
G-01

Awards

8.1878 USDC - $8.19

External Links

Summary

Gas Optimization

noIssueInstances
[G-01]abi.encode() is less efficient than abi.encodepacked()1
[G-02]Use assembly to validate msg.sender5
[G-03]Use hardcode address instead address(this)1
[G-04]No need to evaluate all expressions to know if one of them is true1
[G-05]Can make the variable outside the loop to save gas1
[G-06]Low levelĀ callĀ can be optimized with assembly1
[G-07]Make 3 event parameters indexed when possible1
[G-08]Not using the named return variable when a function returns, wastes deployment gas1
[G-09]The result of function calls should be cached rather than re-calling the function1
[G-10]Non efficient zero initialization1
[G-11]Use constants instead of type(uintx).max1

Gas Optimizations

[G-1] abi.encode() is less efficient than abi.encodepacked()

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

[G-2] Use assembly to validate msg.sender

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

[G-3] Use hardcode address instead address(this)

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

[G-4] No need to evaluate all expressions to know if one of them is true

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

[G-5] Can make the variable outside the loop to save gas

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

[G-6] Low levelĀ callĀ can be optimized with assembly

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

[G-7] Make 3 event parameters indexed when possible

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-L3

[G-8] Not using the named return variable when a function returns, wastes deployment gas

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

[G-9] The result of function calls should be cached rather than re-calling the function

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

[G-10] Non efficient zero initialization

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

[G-11] Use constants instead of type(uintx).max

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

#0 - c4-pre-sort

2023-10-13T14:53:02Z

141345 marked the issue as sufficient quality report

#1 - c4-judge

2023-10-24T16:58:15Z

hansfriese marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Ā© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter