Skip to the content.

← Back to Homepage

Gas Optimisations : This doc suggests some optimisations to save gas. Sources are mostly from Solidity Docs, StackExchange and more.

Read/Write costs :

Because solidity operates in 32 bytes at once, read/write to an element less than 32 bytes costs more as EVM needs to do extra OPs retrieve and pad extra bytes to the element in that 32 byte slot.

So, operations with 32 bytes costs less as no extra operations are involved. So it is beneficial to use reduced size elements like uint8 if you read them all at once, like a whole 32 byte slots at once as there isn’t any padding operation involved **. So reading 4 uint64 which are declared contiguosly is better **as there aren’t any discard operations involved.

Solution :

Recall that structs and arrays start at a diff slot. So inside a struct declaring uint128, uint128, uint256 is better optimised as just 2 slots in storage.

But, uint128, uint256, uint128 is worst due to 3 slots as storage make a new slot if a variable can’t fit.

Calculating function selectors at runtime:

The compiler can precalculate a function selector at compilation time when the function signature is known. So if your function signature is known beforehand then do it in the encoding statement itself. But if in some rare cases you have multiple places in code where you are computing selectors dynamically, then use a function for that to save some gas.

function getSelector(bytes memory _func) returns (bytes4) {
    return bytes4(keccak256(_func));
}

function doStuff() {
    addr.call(abi.encodeWithSelector(getSelector("transfer(address,uint256)"), 0xSomeAddress, 123));
}

getSelector() will always run keccak256() on its argument because it cannot assume that it’s always a constant. With abi.encodeWithSignature(), on the other hand in the constant selector case, the compiler is smart enough to generate more efficient code when you use a string literal:

addr.call(abi.encodeWithSignature("transfer(address,uint256)", 0xSomeAddress, 123));

Note: Calculating selectors by hand is very error-prone and should be avoided. A very common mistake, for example, is to insert a space after the comma, which will result in a completely different selector. The example above does such a calculation on purpose, to illustrate a point, but in practice the language provides a more type-safe way to achieve the same result. The .selector member:

addr.call(abi.encodeWithSelector(IERC20.transfer.selector, 0xSomeAddress, 123));

Use Events for storing something user related/temporary :

Use External functions when using old compiler versions:

Revert strings :

Don’t repeat yourself :

Short Circuiting:

Mappings are cheaper than arrays:

bytes vs byte1[]:

Limit external calls:

Make your contract upgradeable :

Divide your code into multiple contracts: