Proposal to add compiler tip for function selector order

Summary -
Developers currently have no way to affect the ordering of function selectors outside of changing the selector itself which is not possible for functions that must match EIP standards. Each potential selector that is checked before finding a match costs 22 gas units. Contracts with a large number of function selectors could save substantial amounts of gas by prioritizing the selectors that would be most used by external or internal contract calls.

Proposal -
Add a method for developers to be able to signal function selector priority for a function.
This could be a comment before the function or part of the function descriptor like below…

/// @solidity selector-priority 1
function transfer(address to, uint256 value) public returns (bool success) {
function transfer(address to, uint256 value) public selector-priority(1) returns (bool success) {

Unmarked functions would sort after marked functions. Functions with the same priority value would sort with current rules.

image

3 Likes

Why not just sort them by the same order in which the functions appear in the source code?
This would give the user (developer) full control over the ordering of function selectors.

1 Like

Contract inheritance would make it less than ideal, some selectors may come from storage variables so you wouldn’t want those to all float to the top, and you may want to group functions in a particular order for readability but have them sorted in the function selector differently for gas efficiency.

This is a count of all internal and external calls to ERC20 token contracts over the past year by function selector -
image

If all ERC20’s had optimized selectors, it could save tens of billions of gas units yearly.

Can only add one image per post…

This is the order that a standard OZ ERC20 with 200 runs of optimization (ApeCoin) gets built -

The typical solution for this (unrelated to this issue, BTW), is to declare all storage variables as private or internal (depending on your requirements), and implement their getter functions explicitly instead of leaving it for the compiler to implement them implicitly.

It helps with a bunch of things, such as:

  • The ability to add nat-spec documentation to these functions
  • The ability to reduce contract byte-code size, when these functions are not needed by anyone
  • The ability to declare these functions in the contract interface, in order to allow functions in other contracts to interact with them (at least in previous compiler versions, doing so would lead to compilation errors)
1 Like

I think what really matters (i.e., what users really care about) is gas per transaction, not gas accumulation (for example, gas per year, as you’ve described it).

For example, if 1 million individuals spend 22 gas each, then no one’s gonna care about 22 million gas being spent in total.

1 Like

Agreed that it’s possible and there can be benefits to getter functions but it doesn’t solve selector ordering for inheritance and code readability. Adding a tip allows developers to be explicit without sacrificing anything.

RE: individual transaction vs cumulative gas - overall gas consumption affects everyone through base fee calculation and TPS that Ethereum can handle, small amounts at a large scale should be targets for optimizing.

2 Likes