Solidity Method Selectors

This post was originally published on my website.


Interacting with smart contracts is a large part of Ethereum. After all, if you can't interact with a smart contract, what use is it? You probably know how to call one in a framework such as web3.js:

const contract = new Contract(jsonAbi, address);

contract.methods.functionName().send({ from: "0x..." });

However, how is the function encoded? How does the EVM know what to execute? Let's take this apart, one by one.

The Problem

Okay, so we need to encode a function signature. This shouldn't be too hard. If you were going to be designing Ethereum, you might just say that the functions should be referred to (in the EVM) using the raw bytes of the function name. For example, a function named coolFunction would be encoded to 0x63 0x6f 0x6f .... Pretty reasonable.

However, this falls apart when you have overloaded functions. For example, in the following contract:

contract Overloaded {
  function addNumbers(uint a, uint b) public pure returns (uint) {
    return a + b;
  }

  function addNumbers(uint a, uint b, uint c) public pure returns (uint) {
    return a + b + c;
  }
}

The functions both have the name addNumbers, meaning that the raw bytes would both be 0x61 0x64 0x64 ..., which would make it ambiguous as to which function we're calling.

Okay, what if we added the whole signature to the representation? It technically could work, but then the simple addNumbers function would have 26 and 34 bytes (for 2 and 3 arguments respectively). That's a lot!

The Solution

Now that I've hyped this up (more than I should've), here is the solution the people that developed Solidity thought of: get the first 4 bytes (8 hex characters) of the Keccak-256 hash of the function signature.

This method means that it's very unlikely to have a collision, as hashes are practically random. This means that in the 4.3 billion possible 4-byte sequences ($256^4$), you would be hard-put trying to find a collision.

There is a playground you can play with if you view this article on my website.

However, this solution means that you can't find the function name from the selector. This is a problem, as you might be debugging a contract and want to know what function is being called. Luckily, there is a solution to this: the Ethereum Signature Database. This is a database of over a million function signatures and their corresponding function names.

For example, I can search up the selector 0x095ea7b3, and find that, oh look, it's the approve function in the ERC20 standard (among other things):

Searching up 0x095ea7b3 on 4byte.directory
Searching up 0x095ea7b3 on 4byte.directory

Conclusion

So, now you know how Solidity encodes function signatures. This is a very clever method of implementing a way to encode function signatures, and I hope you learned something from this post!

Subscribe to ByteAtATime
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.