15,000 USDC
View results
Submission Details
Severity: gas
Valid

Use a do while loop instead of for loop in constructor

Summary

Using a do while instead of a for loop can reduce gas costs on deployment of the contract.

Vulnerability Details

The standard for loop will check the condition before executing the statement. Whereas a do while will execute the statement at least once, then check the conditions, allowing for gas savings.

Impact

Original Example:
Gas: 587157
Transaction Cost: 510571
Execution Cost: 419451

Gas optimisation Example:
Gas: 586978 (gas saving -179)
Transaction Cost: 510415 (gas saving -156)
Execution Cost: 419271 (gas saving -180)

Input used for Remix deployment: duplicated addresses for simplicity

[0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2],
[0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2],
0xd9145CCE52D386f254917e481eB44e9943F39138

Contracts Used in Remix IDE

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExampleGasOpt {
mapping(address => address) public s_priceFeeds;
address[] public s_collateralTokens;
DecentralizedStableCoin public i_dsc;
error TokenAddressesAndPriceFeedAddressesMustBeSameLength();
constructor(
address[] memory tokenAddresses,
address[] memory priceFeedAddresses,
address dscAddress
) {
uint tokenArrLength = tokenAddresses.length;
uint pricefeedArrLength = priceFeedAddresses.length;
if (tokenArrLength != pricefeedArrLength) {
revert TokenAddressesAndPriceFeedAddressesMustBeSameLength();
}
uint256 i;
do {
s_priceFeeds[tokenAddresses[i]] = priceFeedAddresses[i];
s_collateralTokens.push(tokenAddresses[i]);
++i;
} while (i < tokenArrLength);
i_dsc = DecentralizedStableCoin(dscAddress);
}
}
contract ExampleOrg {
mapping(address => address) public s_priceFeeds;
address[] public s_collateralTokens;
DecentralizedStableCoin public i_dsc;
error TokenAddressesAndPriceFeedAddressesMustBeSameLength();
constructor(
address[] memory tokenAddresses,
address[] memory priceFeedAddresses,
address dscAddress
) {
if (tokenAddresses.length != priceFeedAddresses.length) {
revert TokenAddressesAndPriceFeedAddressesMustBeSameLength();
}
for (uint256 i = 0; i < tokenAddresses.length; i++) {
s_priceFeeds[tokenAddresses[i]] = priceFeedAddresses[i];
s_collateralTokens.push(tokenAddresses[i]);
}
i_dsc = DecentralizedStableCoin(dscAddress);
}
}
contract DecentralizedStableCoin {
// Contract implementation...
}

Tools Used

Manual review, Remix

Recommendations

Implement a do while loop for the constructor to reduce the gas costs, along with the additional gas saving opportunities identified in the "known issues" repo (provided by Cyfrin) to benefit from a gas saving upon initialisation of the contract, especially in the case there are multiple token contracts and price feeds to add to the arrays upon deployment. There is a caveat with do while loops that its always going to execute at least once, so ensuring that the code is protected against infinite loops. The constructor has an if statement to check the arrays are of the same length, which would imply there are valid addresses in there for the loop to execute at least once.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.