ENS - MatricksDeCoder'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: 37/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-11

Awards

8.1878 USDC - $8.19

External Links

GAS-1 abi.encode is less efficient

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L204C24-L204C30

 abi.encode(_token, _delegate)

abi.encode must be replaced with abi.encodePacked as there is little or no chances collision given passing in 2 addresses. abi.encode adds padding which adds to the gas costs as opposed to abi.encodePacked which tightly packs the

GAS-2 Make use of OpenZeppelin Minimal Clones

Minimal proxies, for deploying contract ERC20ProxyDelegator results insubstantial gas savings. Clones are small and inexpensive smart contracts that delegate calls to an implementation contract, eliminating the need to deploy the entire contract repeatedly for each delegateMulti.

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L106

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L173

Instead make use of created proxies, that point to the shared contract logic. This approach reduces gas costs significantly, making the deployment process more efficient and cost-effective. e.g ERC1167 is very gas efficient implementation contract is directly stored in the contract code, eliminating the need for additional storage loads (sload).

GAS-3 Cache values resued more than once in functions e.g Math.max(sourcesLength, targetsLength)

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L80

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L87 In above cases Math.max(sourcesLength, targetsLength) is computed twice in same function instead of just storing the value first time in a memory variable and reuse e.g

uint256 maxSize = Math.max(sourcesLength, targetsLength)

GAS-4 Math.min and Math.max function can be implemented in this contract as internal functions

Instead of importing entire Open Zeppelin Math library can just implement the two needed functions as internal functions or copy and create them in own library and import that library to use in these contracts.

GAS-5 Replace OpenZeppelin Ownable contracts with Solmate Owned

Solmate Owned contracts are generally always more gas efficient and smaller compared to Open Zeppelin contracts. We can see that the Solmate Owned contract is smaller than Open Zeppelin on this link

https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol

GAS-6 Use do while loops instead of for loops

It may be ideal to use do while loops as do while loop will cost less gas since the condition is not being checked for the first iteration leading to them being cheaper than for loops

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L85

\\change from format for (uint i= 0; i<num; i++) { ...doStuff} \\ change to format uint i; do { ...doStuff} while(i < num)

GAS-7 Solidity Gas Optimizor set to default 200 runs

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/hardhat.config.js#L27 There is scope for gas savings by exploring the optimal optimizer runs settings for Solidity

Not setting optimized value can result in using optimizer settings that are no best for reducing gas costs for the users. The more the runs e.g 10,000 the cheaper the gas costs for functions for users. The smaller the runs, the cheaper the deployment costs. It's essential to put users first in order to attract more and better usage of protocol.

GAS-8 Remove unnecessary imported Address library

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L4

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L26

Important library Address which is attached to address type is not important as none of its functionality like sendValue, verifyCallResult etc are used on the address type.

GAS-9 Public variables that can be made private

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L28

ERC20Votes public token; // vs ERC20Votes private token;

Above variable by being public leads to a free getter function which is not really necessary as the value can be read directly from blockchain. Gas savings can be made by making the variable private

GAS-10 No need to take in address inputs as uint256

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L57

function _delegateMulti( uint256[] calldata sources, uint256[] calldata targets, uint256[] calldata amounts )

Taking in address inputs as uint256 just results in further computation that adds to gas costs of casting the inputs into address

address source = transferIndex < sourcesLength ? address(uint160(sources[transferIndex])) : address(0); address target = transferIndex < targetsLength ? address(uint160(targets[transferIndex])) : address(0);

Just take them in as addresses

function _delegateMulti( address[] calldata sources, address[] calldata targets, uint256[] calldata amounts )

GAS-11 No need to initialize variables to default values

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L86C16-L86C16

for ( uint transferIndex = 0; transferIndex < Math.max(sourcesLength, targetsLength); transferIndex++ ) {

Can be rewritten as no need to reassing transferIndex to its default value 0 at start

for ( uint transferIndex; transferIndex < Math.max(sourcesLength, targetsLength); transferIndex++ ) {

GAS-12 Use assembly for retrieveProxyContractAddress

https://github.com/code-423n4/2023-10-ens/blob/1adbe2cce191140657b8bccffab85103953bdccb/contracts/ERC20MultiDelegate.sol#L198 Instead of the below

bytes memory bytecode = abi.encodePacked( type(ERC20ProxyDelegator).creationCode, abi.encode(_token, _delegate) ); bytes32 hash = keccak256( abi.encodePacked( bytes1(0xff), address(this), uint256(0), // salt keccak256(bytecode) ) ); return address(uint160(uint256(hash)));

Use cheaper assembly in the form below updating it appropriately for the correct variables, values and logic

function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } }

Above format in assembly does the create2 and hashing in memory which saves gas

#0 - c4-pre-sort

2023-10-13T14:13:09Z

141345 marked the issue as sufficient quality report

#1 - c4-judge

2023-10-24T17:02:43Z

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