Skip to content
Pre-Release: Fe is under active development. This documentation covers the upcoming release. Follow progress on GitHub

Selector Calculation

Function selectors are 4-byte identifiers that the EVM uses to route calls to the correct function. This appendix explains how selectors are calculated and provides common examples.

When you call a smart contract function, the EVM needs to know which function to execute. The first 4 bytes of the calldata contain the function selector, a unique identifier derived from the function signature.

Calldata: 0xa9059cbb000000000000000000000000...
^^^^^^^^
Selector (4 bytes)

Selectors are calculated as the first 4 bytes of the Keccak-256 hash of the function signature:

selector = keccak256("functionName(type1,type2,...)")[0:4]

For the ERC20 transfer function:

  1. Write the signature: transfer(address,uint256)
  2. Hash it: keccak256("transfer(address,uint256)")
  3. Take first 4 bytes: 0xa9059cbb
  1. No spaces - transfer(address,uint256) not transfer(address, uint256)
  2. No parameter names - Just types
  3. Canonical type names - Use uint256 not uint
  4. Array syntax - Use uint256[] for dynamic arrays

When calculating selectors, use these canonical type names:

Fe TypeSignature Type
u256uint256
u128uint128
u64uint64
u32uint32
u16uint16
u8uint8
i256int256
i128int128
boolbool
Addressaddress
String<N>string
[T; N]T[N] (e.g., uint256[3])
FunctionSignatureSelector
name()name()0x06fdde03
symbol()symbol()0x95d89b41
decimals()decimals()0x313ce567
totalSupply()totalSupply()0x18160ddd
balanceOf(address)balanceOf(address)0x70a08231
transfer(address,uint256)transfer(address,uint256)0xa9059cbb
approve(address,uint256)approve(address,uint256)0x095ea7b3
allowance(address,address)allowance(address,address)0xdd62ed3e
transferFrom(address,address,uint256)transferFrom(address,address,uint256)0x23b872dd
FunctionSignatureSelector
balanceOf(address)balanceOf(address)0x70a08231
ownerOf(uint256)ownerOf(uint256)0x6352211e
safeTransferFrom(address,address,uint256)safeTransferFrom(address,address,uint256)0x42842e0e
transferFrom(address,address,uint256)transferFrom(address,address,uint256)0x23b872dd
approve(address,uint256)approve(address,uint256)0x095ea7b3
setApprovalForAll(address,bool)setApprovalForAll(address,bool)0xa22cb465
getApproved(uint256)getApproved(uint256)0x081812fc
isApprovedForAll(address,address)isApprovedForAll(address,address)0xe985e9c5

In Fe, you specify selectors explicitly in message definitions:

msg Erc20 {
#[selector = 0xa9059cbb]
Transfer { to: Address, amount: u256 } -> bool,
#[selector = 0x70a08231]
BalanceOf { account: Address } -> u256,
}

Fe requires explicit selectors because:

  1. ABI compatibility - Ensures your contract matches expected interfaces
  2. Naming freedom - Use Fe’s snake_case while matching camelCase ERCs
  3. No ambiguity - Clear what selector each message variant uses
  4. Verification - Easy to verify against standards

With cast (from Foundry):

Terminal window
$ cast sig "transfer(address,uint256)"
0xa9059cbb
from web3 import Web3
sig = "transfer(address,uint256)"
selector = Web3.keccak(text=sig)[:4].hex()
print(selector) # 0xa9059cbb
const { keccak256, toUtf8Bytes } = require("ethers");
const sig = "transfer(address,uint256)";
const hash = keccak256(toUtf8Bytes(sig));
const selector = hash.slice(0, 10); // "0xa9059cbb"

Many online tools can compute selectors:

Events also have selectors (topic 0), but they use the full 32-byte hash:

event_selector = keccak256("Transfer(address,address,uint256)")
= 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
EventSignatureSelector (topic 0)
TransferTransfer(address,address,uint256)0xddf252ad...
ApprovalApproval(address,address,uint256)0x8c5be1e5...

Different function signatures can produce the same selector (collision). This is rare but possible:

transfer(address,uint256) -> 0xa9059cbb
// Hypothetical collision:
someOtherFunction(bytes32) -> 0xa9059cbb (if it existed)

When designing custom functions, verify your selectors don’t collide with standard interfaces.

ConceptDescription
SelectorFirst 4 bytes of keccak256(signature)
Signature formatfunctionName(type1,type2,...)
No spacestransfer(address,uint256)
Canonical typesuint256 not uint
Event selectorFull 32-byte hash

Explicit selectors in Fe ensure ABI compatibility while giving you naming freedom.