Over the past few years, Ethereum Virtual Machine (EVM) networks have seen remarkable growth. Daily, more users are joining these networks and partaking in a multitude of transactions. However, this surge in activity has resulted in increasing transaction fees, which has led to a pressing need to find ways to lower these fees to enhance the accessibility and user experience of Web3 applications.
An effective approach to address this issue is optimizing the gas execution of smart contracts. By adopting appropriate implementation strategies, developers can craft more efficient smart contracts, thus minimizing gas fees. This optimization not only lowers transaction costs but also improves the overall user experience within EVM networks. As these optimizations progress, the outlook for Web3 applications appears increasingly bright.
Solidity Development
Solidity reigns as the primary programming language for creating smart contracts on Ethereum Virtual Machine (EVM) ecosystems. Smart contracts operate on-chain, and every action associated with a contract transaction incurs a gas charge. Generally, more complex operations or those demanding substantial resources consume higher gas amounts.
The operations that consume the most gas are primarily those involving storage. Improper handling of data addition and retrieval from storage can result in exorbitant costs if all available storage spaces are utilized. An analysis of EVM Codes reveals that STORE opcodes for storage are notably more costly than those for memory manipulation, specifically being 33 times more expensive.
Opcode |
Gas |
Description |
SLOAD |
100 |
Load word from storage |
SSTORE |
100 |
Save word to storage |
MSTORE |
3 |
Load word from memory |
MLOAD |
3 |
Save word to memory |
Storage Areas
The EVM provides five distinct storage areas: storage, memory, calldata, stack, and logs. In Solidity, interaction with the first three areas is primarily conducted, given that stack access is not directly available to the code. The stack serves as the processing location for EVM, necessitating low-level programming methods for access. Logs are utilized for events by Solidity, but contract access to log data post-creation is not permitted.
Storage
- A key-value repository mapping 256-bit words to 256-bit words;
- Retains all state variables of the smart contract which are mutable (constants are part of the contract bytecode);
- Defined per contract upon deployment.
Memory
Calldata
- A temporary holding area for function arguments;
- Read-only and not modifiable.
Gas Optimization Strategies
To diminish gas expenses linked to storage, prioritize utilizing memory instead of storage. Consider the following smart contract that exclusively employs the storage area:
contract GasCostComparison {
uint256[] private s_numbers;
uint256 private s_sum;
function numberSum()public returns(uint256) {
for(uint i=0; i< s_numbers.length; i++){
s_sum+=s_numbers[i];
}
return s_sum;
}
function initNumbers(uint256 n)public {
for(uint i=0; i < n; i++){
s_numbers.push(i);
}
}
}
If s_numbers is initialized by invoking initNumbers with n=10, the gas consumption for numberSum would be 53,010 gas.
Minimize Frequent Reads from Storage
In the `for` loop, the index i is compared with s_numbers.length. While it might seem like the array length is read from storage just once, it is actually read each time the comparison is made. To optimize, read the length just once:
function numberSum()public returns(uint256) {
uint256 l = s_numbers.length;
for(uint i=0; i< l; i++){
s_sum+=s_numbers[i];
}
return s_sum;
}
Here, the length fetched from storage is stored in the l variable, which resides in the memory area of the numberSum() function.
This adjustment reduces gas consumption to 51,945 gas, achieving a savings of 1,065 gas.
Reduce Frequent Writes to Storage
In a similar vein, saving the final sum only at the conclusion of the for loop in the s_sum state variable (which is stored) is more energy-efficient. Instead, create a temporary sum variable in memory:
function numberSum()public view returns(uint256) {
uint256 l = s_numbers.length;
uint256 sum = 0;
for(uint i=0; i< l; i++){
sum+=s_numbers[i];
}
return sum;
}
This time, gas execution amounts to 27,770 gas, nearly half of the previous instances.
Selecting the appropriate storage type can substantially lower blockchain gas fees, as illustrated in the examples above. Optimizing data storage and retrieval is vital for reducing costs and improving the efficiency of smart contracts on Ethereum Virtual Machine (EVM) ecosystems.
By favoring memory over storage for mutable data and grasping the intricacies of gas costs linked to various operations, developers can greatly enhance the performance and cost-efficiency of their applications within the Web3 landscape.