How to Optimize Your Smart Contracts for Ethereum Mainnet Success

Save gas, reduce storage costs, and secure your smart contracts. Learn best practices for optimizing Ethereum code with real Solidity examples.

Optimize Smart Contracts for Ethereum Mainnet block explorer evm dapps

Smart contracts are the backbone of decentralized apps on Ethereum. Making them efficient isn’t just a nice-to-have; it’s essential. If your contract isn’t optimized, you’ll face exorbitant gas fees and wasted storage, potentially jeopardizing your project before it even starts. Additionally, poorly written code not only increases costs but also exposes you to security risks.

In summary, optimizing your smart contracts reduces expenses, enhances performance, and ensures your project remains secure.

This article provides tips for optimizing Ethereum smart contracts, with a focus on minimizing gas consumption, streamlining logic, and adhering to best practices for secure and maintainable code. These techniques benefit both seasoned developers and newcomers.

Smart contract illustration with Ethereum, OpenZeppelin, and Arbitrum logos on a pastel gradient background

1. Write Minimalist Code

When you're coding, keep it simple. Extra lines, functions, or variables can really add up in gas costs, both when you deploy and during use. Get rid of any dead code, unused variables, and repetitive stuff. If you can use a library like OpenZeppelin, go for it. It helps reduce the size of your contract and makes it safer by minimizing potential bugs and vulnerabilities.

Practical Example

Here is an example using a simple setter function.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract GasTest {
    uint public x;

    // Unoptimized: Unnecessary setter, repeated logic
    function setX(uint _x) public {
        x = _x;
    }

    function doubleX() public {
        x = x * 2;
    }

    // Optimized: Combine logic where possible
    function setAndDoubleX(uint _x) public {
        x = _x * 2;
    }
}

Let's run a gas report using forge for this contract.

Foundry Forge gas report for Solidity smart contract showing optimized vs unoptimized gas usage and function gas costs in terminal output

From the image, you can see that the optimized version costs over 35% less gas for the exact same logic.

From the function costs:

  • setX: 43,718 gas
  • doubleX: 26,511 gas
  • Combined: 70,229 gas
  • setAndDoubleX: 43,988 gas
Calling setX and then doubleX means you’re writing to storage twice, which stacks up the costs. The optimized function (setAndDoubleX) only writes once, making it much cheaper.

2: Choose the Right Data Structures

Using mappings instead of arrays for queries and retrieving values can result in substantial cost savings for both developers and users. Since iterating through arrays onchain is often costly, it's advisable to avoid this practice whenever possible. When structuring your data, consider employing structs to group related information, which enhances clarity and readability.

Practical Example

Here, we have a code that loops through an array to find if an address.

//Looping through an array to find if an address exists

address[] public whitelist;

function isWhitelisted(address user) public view returns (bool) {
    for (uint i = 0; i < whitelist.length; i++) {
        if (whitelist[i] == user) return true;
    }
    return false;
}

We would switch it up in this example and ask Claude to optimize the code;

Code optimization prompt for Solidity whitelist function displayed on Claude AI interface with array loop example

Here is the result;

Solidity mapping-based whitelist function for efficient address lookup shown in mapping.sol code snippet

This optimization dramatically improves gas efficiency. The original code becomes increasingly expensive as the whitelist grows, potentially costing 21,000+ gas for large arrays, while the mapping version maintains a constant ~2,100 gas regardless of size.

The mapping eliminates the need to loop through every address, providing instant results instead of scanning the entire array. This makes the contract much more scalable and cost-effective for production use.

3. Minimize using storage

The single biggest gas guzzler in Ethereum is storage operations. Think of writing to storage on Ethereum like sending postcards. Each time you send a postcard (a storage write), it costs you money and takes time. If you want to send multiple messages (updating several values), it makes more sense to gather all of your thoughts first and send them in one big envelope rather than sending each postcard one by one. This way, you save on postage and effort, just like batching state changes saves on gas costs in Ethereum!

Whenever you need to update several values, do so in memory first and then commit the result to storage in a single operation.

Practical Example

This contract compares Looping and Writing to storage for each user Vs Writing to storage only once.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract SimpleStorageGas {
    mapping(address => uint256) public balances;
    address user1;
    address user2;
    address user3;

    // Set users and initialize balances
    function setUpUsers() public {
        user1 = address(0x1);
        user2 = address(0x2);
        user3 = address(0x3);
        balances[user1] = 100;
        balances[user2] = 100;
        balances[user3] = 100;
    }

    // Unoptimized: Multiple storage writes
    function resetAllBalancesUnoptimized() public {
        balances[user1] = 0;
        balances[user2] = 0;
        balances[user3] = 0;
    }

    // Optimized: Only one storage write
    function resetOneBalanceOptimized() public {
        balances[user1] = 0;
    }
}

Let's run a forge test for this code;

Terminal output of Foundry Forge test comparing optimized vs unoptimized Solidity functions with gas usage for SimpleStorage contract

From the result, you can see that the function that resets three user balances (resetAllBalancesUnoptimized) uses more than 2x the gas compared to resetting just one (resetOneBalanceOptimized). Every extra write to storage is costly, the more storage slots you update, the more gas you burn.

4. Prioritize Security

Never prioritize gas savings at the expense of security. An optimized contract is meaningless if it exposes user funds to risk. Security must always come first. Adhere to best practices such as the Checks-Effects-Interactions pattern, and use tools like OpenZeppelin’s ReentrancyGuard for any function that handles Ether or sensitive operations. Cutting corners on security will ultimately cost far more than any gas savings you achieve.

Practical example

Here is a contract that uses the Checks-Effects-Interactions pattern vs one that doesn't.

Solidity smart contract example showing unsafe withdraw function vulnerable to reentrancy in unsafe.sol code snippet

Problem:
If msg.sender is a contract with a fallback function, it can call withdraw again before the balance is reduced, draining funds (classic reentrancy attack).

Solidity smart contract example demonstrating safe withdraw pattern with state update before external call in safe.sol code snippet

Why is this safe?
If msg.sender tries to re-enter, their balance is already set to zero (or reduced) before the external call, so a second withdrawal will fail.

Conclusion

Optimizing smart contracts for gas efficiency is crucial, and ensuring their security is equally important. However, these efforts are meaningless if you skip the final step of code verification and auditing. Without onchain auditing, you risk not fully understanding potential vulnerabilities. Even a "gas-efficient" contract can fail catastrophically due to a single critical bug.

Verification serves as public and cryptographic assurance that the deployed code meets the expectations of both developers and users.

This isn’t optional if you care about adoption or security. In the next guide, I’ll break down exactly how to verify your smart contracts using Blockscout, step by step, from compiling your contract to verifying directly from the Blockscout UI, providing you with a transparent link between source code and deployed bytecode.

💡
To learn how to Deploy and Verify a smart contract using Remix check out this Guide