1. 기본적인 가스 절약 방법

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract GasSaving {
    uint public total;

    // 가스 최적화를 안한 경우의 일반적인 표현
    // function sumIfEvenAndLessThan99(uint[] memory nums) external {
    //     for (uint i = 0; i < nums.length; i += 1) {
    //         bool isEven = nums[i] % 2 == 0;
    //         bool isLessThan99 = nums[i] < 99;
    //         if (isEven && isLessThan99) {
    //             total += nums[i];
    //         }
    //     }
    // }

    /** 가스 최적화 */
    // start : 50908 gas
    // calldata 사용 : 49163 gas
    // memory에 상태변수 로딩 : 48952 gas 
    // 하나의 실행 내에서 상태변수의 값이 여러번 변경될 경우 memory에서 실행하고 최종 결과만 상태변수에 저장한다
    // short circuit - 48634 gas
    // loop increments - 48244 gas
    // cache array length - 48209 gas
    // load array elements to memory - 48047 gas
    // uncheck i overflow/underflow - 47309 gas
    function sumIfEvenAndLessThan99(uint[] calldata nums) external {
        uint _total = toal;
        uint len = nums.length;

        for( uint i = 0 ; i < len ; ){
            uint num = nums[i];
            if(num % 2 == 0 && num < 99){
                _total += num;
            }
            unchecked {
                ++i;
            }
        }
        total = _total;
    }
}

2. 값이 변하지 않는 변수에 대해 constant 및 immutable 변수 사용

//SPDX-License-Identifier:MIT
pragma solidity ^0.8.3;

interface IERC20 {    
		function transfer(address to, uint256 value) external returns (bool);    
		function approve(address spender, uint256 value) external returns (bool);    
		function transferFrom(address from, address to, uint256 value) external returns (bool);   
		//rest of the interface code
}

//Gas used: 187302
contract Expensive {
   IERC20 public token;
   uint256 public timeStamp = 566777;

   constructor(address token_address) {
       token = IERC20();
   }

}

//Gas used: 142890
contract GasSaver {
   IERC20 public immutable i_token;
   uint256 public constant TIMESTAMP = 566777;

   constructor(address token_address) {
       i_token = IERC20(token_address);
   }
}

3. Cache read variables in memory

//SPDX-License-Identifier:MIT;

pragma solidity ^0.8.3;

contract Expensive {
   uint256[] public numbers;

   constructor(uint256[] memory _numbers) {
       numbers = _numbers;
   }

   //Gas used: 40146
   function sum() public view returns (uint256 ){
       uint256 total = 0;
       for (uint256 i=0; i < numbers.length; i++) {
          total += numbers[i];
       }
       return total;
   }
}

contract GasSaver {
   uint256[] public numbers;

   constructor(uint256[] memory _numbers) {
       numbers = _numbers;
   }

   //39434
   function sum() public view returns (uint256 ){
       uint256 total = 0;
       uint256 arrLength = numbers.length;
       uint256[] memory _numbersInMemory = numbers;
       for (uint256 i=0; i < arrLength; i++) {
           total += _numbersInMemory[i];
       }
       return total;
   }
}