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.
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!
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):
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!