Pack Rule
Pack variables into the same slot (32 bytes) whenever possible, manual packing is most efficient but least readable, EVM packing is okay, but no packing is the least effificient.
Example
pragma solidity ^0.8.17;
contract Pack {
uint256 public packedData;
uint32 public num;
address public secondAddress;
// This function packs a uint32 and an address into packedData
function packData(uint32 data, address addr) external {
// Shift the address 32 bits to the left and bitwise OR it with the uint32 data
// This places the address in the higher bits and the uint32 in the lower bits of the uint256
packedData = (uint256(uint160(addr)) << 32) | uint32(data);
}
// This function unpacks the uint32 from packedData
function unpack() external view returns (uint32, address) {
// Bitwise AND with a mask to isolate the lower 32 bits
uint32 number = uint32(packedData & 0xFFFFFFFF);
address addr = address(uint160(packedData >> 32));
return (number, addr);
}
function storeData(uint32 data, address addr) external{
num = data;
secondAddress = addr;
}
function getData() external view returns(uint32, address){
return (num, secondAddress);
}
}
Calling function storeData
and getData
would cost 23863 gas and calling function packData
and unpack
would cost 23807 gas only, proving that packing variables manually saves gas. Although EVM has already packed the variables for the data structure uint32 and address, but manual packing is still the most efficient way.
The same goes for struct ordering.
Test Environment
- Compiler: solc 0.8.17
- Testing Framework: Foundry
ETH saved over 100 transactions
100 tx * 30 gwei * 0.000000001 * (909-875) = 0.000102 ~= 0.0001 ETH1
USD Saved over 100 transactions
- At \$4000 / ETH: \$0.4
- At \$3000/ETH: \$0.3
- At \$2000/ETH: \$0.2
-
Assumed gas price: 30 gwei ↩