Solidity - Tips to save Gas

Search for a command to run...

No comments yet. Be the first to comment.
All about perpetual contracts

A Technical Guide for Beginners to Ethereum's Proof of Stake

Transaction and type of Transactions in the Ethereum blockchain

Structure of an Ethereum Node

Update to ERC1155

Hello Everyone..
Welcome to another day of exploring Web3 Engineering. In today's blog, let us look into the tips and patterns to follow during solidity smart contract development to save gas costs while making transactions. So, without any further ado, let's get started.
As we all know by now, in solidity, every single operation costs some gas. When we run a for loop as shown below.
for(int i = 0; i< array.length(); i++) {
if(array[i] == val) {
array[i] = k;
}
}
We are initializing a variable called count
We are also incrementing it for every iteration
These two operations will increase the gas in linear fashion depending on the length of the array.
Mappings are cheaper than than the Arrays in solidity.
Array values are supposed to saved in serial fashion. Which means, index 1 element will be saved in the memory location right after the index 0 element and so on.
In the storage space, the first memory block contains the length of the array and the values are stored from the 2nd block on wards.
If there is any value appended, it needs to update both values list and the length of the array. The two operations are expensive
Mappings don't have any concept of length. The mapping are stored as key value pairs in the storage space.
Keys in mappings are hashed to a unique identifier that is used to locate the corresponding value in storage.
Which means whenever there is a need to update the mapping, there is only operation taking place.
While writing contracts, we often use multiple global variables such as block.timestamp, msg.sender, msg.value etc. But accessing global variables or storage variables is expensive. So, we need to create a local copy of the variable which will create an in-memory copy of the variable and accessing variables from memory is relatively cheaper. We can look into an example below.
// expensive function
function func1() external {
require(admins[msg.sender], "Not eligible");
changesCounter[msg.sender] += 1;
emit Changed(msg.sender, block.timestamp);
}
// cheaper function
function func2() external {
address caller = msg.sender;
require(admins[caller], "Not eligible");
changesCounter[caller] += 1;
emit Changed(caller, block.timestamp);
}
An external / public returns ABI encoded data while an internal function returns bytes data. So, using a public function inside a function is expensive than caling an internal function.
The code can be rewritten as shown in below in order to save gas.
// Expensive method
function childExpensive() public returns(uint) {
return 10;
}
function expensive() external returns(uint) {
return childExpensive();
}
// Cheaper method
function _childInternal() internal returns(uint) {
return 10;
}
function childCheap() external returns(uint) {
return _childInternal();
}
function cheap() external returns(uint) {
return _childInternal();
}
internal function and created a wrapper called childCheap to make it available for the external sources. And also whenever the logic is required we are calling the internal function.Comment down if you have any more tips to save gas. Thank you