Solidity Gas Optimizations - Function Name
Description
You may not think that the name of the function will also have an impact on gas consumption. In fact, in the worst case, there may be more than a thousand of gas impacts. Let’s take a look at the following code:
1 | contract Test { |
The above execution b()
will consume 125 gas, then change to the following:
1 | contract Test { |
This time b()
becomes 147 gas consumption. How does the same empty function increase consumption? Trying to execute a()
will only consume 125 Gas. This is because in smart contracts, there is a difference in the order of the functions. The later the sort will consume more. Each position will have an extra 22 gas. At this point you may want to change it to the following:
1 | contract Test { |
But the result has not changed. b()
still consumes 147 gas. Because the order of the function is based on Method ID. In this example, their Method ID is as follows:
1 | a: 0x0dbe671f |
According to this rule we have added one more function:
1 | contract Test { |
Their Method ID is as follows:
1 | a: 0x0dbe671f |
After sorting we can get a < f < b order. Guess how much gas will be spent on b()
this time? Yes, the answer is 169.
Members Participating in the Sort
All public (external) members will be included. In addition to the functions, the properties is also included and also contains the constant. For example:
1 | contract Test { |
b()
will consume 147 gas
Of course, inherited public properties and functions are also included. Therefore, when the contract becomes complex, there may be dozens of public members. Assuming there are 50, the last function consumes a thousand more.
Function Signature
Before knowing the method ID generation rule, we must first know the rules of the function signature. The function signature contains the function name and parameter type. For example:
1 | function func() public { |
The signatures of the above two functions are:
1 | func() |
Properties also have signatures, and the Method ID is generated in the same way. The general property is treated as a function without parameters, and only consider the name and ignore the type, either uint256
, address
or else. Type mapping
treats key as the first parameter. For example:
1 | uint256 public a; |
signatures
1 | a() |
Method ID
The Method ID generation rules are as follows:
1 | keccack256(Function signature) and take the first four bytes |
We can get the following results by keccak256
the above a()
, b()
and f()
functions signature (try this online Keccak-256 tool I developed):
1 | 0dbe671f81a573cff601b9c227db0ed2e5339d3e0a07edc45c42f315a9cb8f0f |
We can see that the first four bytes are Method IDs.
Conclusions
After understanding these principles, we can try the following reactions:
- Reduce public members
- Prioritize functions most used
The first point is easy to understand and implement; the second point we can use the named way to achieve. It’s not easy to name the right hash manually, so I developed this function name optimization tool to handle this problem. In the above example, assume that b()
is most used. You can enter the function signature b()
, the tool will automatically find a new name that Method ID starts with two zero bytes:
1 | b_A6Q(): 0x0000e3fa |
In addition, since the Method ID appears in the input data, there is an additional 128 Gas savings. (Please refer to the previous post Solidity Gas Optimizations).
Further Reading
Previous Post: Solidity Gas Optimizations
Next Post: Solidity Gas Optimizations - Data Compression