# 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.