Solia saysA function selector is the first 4 bytes of keccak256 of its canonical signature; with only 2³² possible selectors, real-world collisions exist and the compiler has to detect them.
When you call transfer(address,uint256), the calldata starts with a 4-byte selector: the first 4 bytes of keccak256("transfer(address,uint256)"). The contract jumps to the matching function based on those 4 bytes. With only 2³² possible selectors and millions of function names in the wild, collisions do exist — the compiler refuses to deploy a contract with two clashing selectors.
Knowing how selectors are computed lets you debug malformed calldata, write low-level dispatchers, and read raw transactions. The demo computes selectors for four standard ERC-20 functions using a toy hash; the same rule holds under real keccak256.
Power-ups you unlock
Selector = first 4 bytes of keccak256(signature)
Signature is the canonical form: name(type,type,...)
Calldata begins with the selector
Selector space is 2³² → real collisions exist
Compiler refuses to deploy clashing selectors
The Reentrancy Reaper attacks — common mistakes
Computing selectors with parameter names (only types matter)
Forgetting that overloaded functions get different selectors
Hardcoding a selector without verifying it
Confusing the 4-byte selector with the full function hash
Boss battleCompute the (toy-hash) selectors for four ERC-20 functions and verify they do not collide.
Example code
<!doctype html><html><head><meta charset="utf-8"></head>
<body style="background:#06040d;color:#e6e0ff;font-family:monospace;padding:20px"><pre id="o"></pre>
<script>
// real selector = keccak256(signature)[0:4]; toy hash here for runnability. determinism property is identical.
const H = (s) => { let h=2166136261>>>0; for(const c of s){ h^=c.charCodeAt(0); h=Math.imul(h,16777619); } return '0x' + (h>>>0).toString(16).padStart(8,'0'); };
const sigs = [
'transfer(address,uint256)',
'transferFrom(address,address,uint256)',
'approve(address,uint256)',
'balanceOf(address)'
];
const rows = sigs.map(s => ' ' + H(s) + ' ' + s);
const selectors = sigs.map(H);
const collisions = selectors.length - new Set(selectors).size;
document.getElementById('o').textContent = [
'function selector = first 4 bytes of keccak256(signature)',
'',
...rows,
'',
'collisions in this set: ' + collisions + ' (compiler refuses to deploy clashing selectors)',
'',
'famous real-world clash: burn(uint256) vs burn(int256) → different selectors despite same intent'
].join('\n');
</script></body></html>