# Gas Saving Techniques In Solidity Contracts
There are several ways in which you can save gas fee while writing/deploying you contract. And as a good solidity/smart contract developer, it helps to know a few.
## Replacing memory with calldata
When passing arrays or strings to external functions:
- memory makes a full copy of the data → costs more gas.
- calldata is read-only and doesn’t copy data → much cheaper.
Use calldata whenever possible in external/public functions.
:::success
// Expensive
function processData(string memory input) external {}
// Cheaper
function processData(string calldata input) external {}
:::
## Loading state variables into memory
- Accessing storage (sload) is expensive.
- If you need to read a state variable multiple times, load it into a local variable (in memory/stack).
:::success
// Expensive
function foo() external view returns (uint) {
return myStorageVar + myStorageVar;
}
// Cheaper
function foo() external view returns (uint) {
uint temp = myStorageVar;
return temp + temp;
}
:::
### Replace i++ with ++i
- i++ (post-increment) requires a temporary variable → extra gas.
- ++i (pre-increment) just increments.
:::success
for (uint i = 0; i < 10; ++i) {
// cheaper
}
:::
## Caching array elements
- Accessing arr.length or arr[i] repeatedly costs extra gas.
- Cache values in a local variable.
:::success
uint length = arr.length;
for (uint i = 0; i < length; ++i) {
uint element = arr[i];
doSomething(element);
}
:::
## Short circuiting
Solidity’s && and || short-circuit:
- In A && B, if A is false, B is not evaluated.
- In A || B, if A is true, B is not evaluated.
Place cheaper conditions first to save gas.
:::success
if (x != 0 && 100 / x > 5) {
// avoids divide by zero and saves gas
}
:::
## Using unchecked
unchecked { ... } tells Solidity:
➡️ “Do not insert overflow/underflow checks inside this block.”
:::success
function add(uint x, uint y) public pure returns (uint) {
unchecked { return x + y; } // no overflow checks, cheaper
}
:::
- Gas saving: ~ 5–30 gas per operation, depending on context. This matters a lot inside loops.
### What happens on overflow?
#### Without unchecked:
:::success
uint a = type(uint).max; // max uint256
uint b = 1;
uint c = a + b; // ❌ Reverts (overflow)
:::
#### With unchecked:
:::success
uint a = type(uint).max;
uint b = 1;
unchecked {
uint c = a + b;
// ✅ No revert, the counter starts again from 0 / wraps around to 0
}
:::
👉 In other words:
With checks → transaction reverts (safe).
Unchecked → behaves like pre-Solidity 0.8 (wrap-around, aka (MAX + 1) mod (2^256) = 0).
#### When to use unchecked safely?
- In loops where overflow is impossible (e.g., for (uint i = 0; i < arr.length; ++i) since i won’t exceed arr.length).
:::success
for (uint i = 0; i < arr.length; ) {
// safe to increment unchecked
unchecked { ++i; }
}
:::
- When logic guarantees no overflow, e.g., subtracting where x >= y is checked before.
:::success
require(x >= y, "underflow");
unchecked { return x - y; }
:::
✅ Summary:
- unchecked removes overflow/underflow checks → saves gas.
- On overflow, the value wraps around instead of reverting.
- Use it only when you’re sure overflow cannot happen.