Solidity external vs public for gas optimization

Solidity external vs public for gas optimization

Wednesday, November 4, 2026, 9 AM

I used to mark every function as public because it felt safer and more flexible. Then I started auditing my gas costs and realized I was throwing away ETH every time I passed a large array to a function.

The main difference between external and public is how the EVM handles the input data. When a function is marked as public, the compiler expects that it might be called from within the contract itself. To handle this, Solidity copies the arguments from calldata into memory. This copy operation is expensive, especially for strings and large arrays.

How external visibility saves gas

When you use the external modifier, the function cannot be called internally without using the 'this' keyword. Because of this restriction, the compiler doesn't bother copying the data into memory. It reads directly from calldata, which is significantly cheaper. If you have a function that takes a large set of data and you only plan to call it from outside the contract, external is the correct choice.

// Less efficient: copies userIds to memory
function processUsersPublic(uint256[] public userIds) public pure returns (uint256) {
    return userIds.length;
}

// More efficient: reads userIds directly from calldata
function processUsersExternal(uint256[] calldata userIds) external pure returns (uint256) {
    return userIds.length;
}

In the example above, calling processUsersExternal with a large array will cost less gas than processUsersPublic because it skips the memory allocation step. The larger the array, the bigger the savings.

The internal call mistake

A common mistake I see is developers making a function external to save gas, then realizing they need to call it from another function in the same contract. They try to fix this by using this.myFunction(). This is a bad idea because using 'this' triggers a real external call via a jump, which costs more gas than a simple internal jump.

If you need to call a function both internally and externally, keep it as public. But if it is strictly an entry point for users or other contracts, use external. I usually start with external by default and only switch to public if I absolutely have to call it from within the same logic flow.

Posted in: Solidity

Comments

No comments yet