Documentation Index
Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
Use this file to discover all available pages before exploring further.
Extracts the function selector (first 4 bytes) from calldata. The selector identifies which function to invoke in a smart contract.
Signature
function getSelector(calldata: CallDataType): [4]u8
calldata.getSelector(): [4]u8
Parameters
- calldata - CallData instance to extract selector from
Returns
[4]u8 - 4-byte array containing function selector
Examples
Basic Usage
Hex Format
Class API
import { CallData } from '@tevm/voltaire';
const calldata = CallData("0xa9059cbb...");
const selector = CallData.getSelector(calldata);
console.log(selector);
// [0xa9, 0x05, 0x9c, 0xbb]
import { CallData, Hex } from '@tevm/voltaire';
const calldata = CallData("0xa9059cbb...");
const selector = CallData.getSelector(calldata);
// Convert to hex string
const hex = Hex.fromBytes(selector);
console.log(hex); // "0xa9059cbb"
import { CallData } from '@tevm/voltaire';
const calldata = CallData("0xa9059cbb...");
const selector = calldata.getSelector(); // Method on instance
console.log(selector);
Function Selectors
Function selector is first 4 bytes of keccak256(signature):
import { Keccak256 } from '@tevm/voltaire';
// Compute selector manually
const signature = "transfer(address,uint256)";
const hash = Keccak256.hashString(signature);
const selector = hash.slice(0, 4);
console.log(selector);
// [0xa9, 0x05, 0x9c, 0xbb]
// Same as extracting from calldata
const calldata = abi.transfer.encode(recipient, amount);
console.log(CallData.getSelector(calldata));
// [0xa9, 0x05, 0x9c, 0xbb] (identical)
Common Selectors
const ERC20_SELECTORS = {
TRANSFER: [0xa9, 0x05, 0x9c, 0xbb], // transfer(address,uint256)
APPROVE: [0x09, 0x5e, 0xa7, 0xb3], // approve(address,uint256)
TRANSFER_FROM: [0x23, 0xb8, 0x72, 0xdd], // transferFrom(address,address,uint256)
BALANCE_OF: [0x70, 0xa0, 0x82, 0x31], // balanceOf(address)
ALLOWANCE: [0xdd, 0x62, 0xed, 0x3e], // allowance(address,address)
} as const;
function matchERC20Function(calldata: CallDataType): string {
const selector = CallData.getSelector(calldata);
if (selectorEquals(selector, ERC20_SELECTORS.TRANSFER)) {
return "transfer";
} else if (selectorEquals(selector, ERC20_SELECTORS.APPROVE)) {
return "approve";
}
// ...
}
const ERC721_SELECTORS = {
SAFE_TRANSFER_FROM: [0x42, 0x84, 0x2e, 0x0e], // safeTransferFrom(address,address,uint256)
TRANSFER_FROM: [0x23, 0xb8, 0x72, 0xdd], // transferFrom(address,address,uint256)
APPROVE: [0x09, 0x5e, 0xa7, 0xb3], // approve(address,uint256)
SET_APPROVAL_FOR_ALL: [0xa2, 0x2c, 0xb4, 0x65], // setApprovalForAll(address,bool)
OWNER_OF: [0x63, 0x52, 0x21, 0x1e], // ownerOf(uint256)
} as const;
Use Cases
Function Routing
import { CallData } from '@tevm/voltaire';
function routeCall(calldata: CallDataType) {
const selector = CallData.getSelector(calldata);
const selectorHex = Hex.fromBytes(selector);
switch (selectorHex) {
case "0xa9059cbb":
return handleTransfer(calldata);
case "0x095ea7b3":
return handleApprove(calldata);
case "0x23b872dd":
return handleTransferFrom(calldata);
default:
throw new Error(`Unknown function: ${selectorHex}`);
}
}
Selector Matching
import { CallData } from '@tevm/voltaire';
function isTransfer(calldata: CallDataType): boolean {
const selector = CallData.getSelector(calldata);
const TRANSFER_SELECTOR = [0xa9, 0x05, 0x9c, 0xbb];
return selector.every((byte, i) => byte === TRANSFER_SELECTOR[i]);
}
// Or use hasSelector helper
const isTransfer = CallData.hasSelector(calldata, "0xa9059cbb");
Transaction Filtering
import { CallData } from '@tevm/voltaire';
interface Transaction {
to: string;
data: string;
}
function filterERC20Transfers(txs: Transaction[]): Transaction[] {
const TRANSFER_SELECTOR = [0xa9, 0x05, 0x9c, 0xbb];
return txs.filter(tx => {
try {
const calldata = CallData(tx.data);
const selector = CallData.getSelector(calldata);
return selector.every((byte, i) => byte === TRANSFER_SELECTOR[i]);
} catch {
return false;
}
});
}
Contract Interface Detection
import { CallData } from '@tevm/voltaire';
const ERC20_SELECTORS = new Set([
"0xa9059cbb", // transfer
"0x095ea7b3", // approve
"0x23b872dd", // transferFrom
"0x70a08231", // balanceOf
]);
function looksLikeERC20(calldata: CallDataType): boolean {
const selector = CallData.getSelector(calldata);
const hex = Hex.fromBytes(selector);
return ERC20_SELECTORS.has(hex);
}
Selector extraction is extremely fast (just array slice):
// Benchmark: 10M iterations
const calldata = CallData("0xa9059cbb...");
console.time("getSelector");
for (let i = 0; i < 10_000_000; i++) {
CallData.getSelector(calldata);
}
console.timeEnd("getSelector");
// Pure JS: ~120ms
// WASM: ~65ms (1.8x faster)
// Effectively zero-cost operation
Selector Collisions
Different functions can have same selector (rare but possible):
// Example collision (contrived)
const sig1 = "transfer(address,uint256)";
const sig2 = "transferFrom(address,address)"; // Hypothetical collision
// Both hash to same first 4 bytes (extremely rare)
// Probability: 1 in 4,294,967,296 (2^32)
Real collisions are virtually impossible but:
- Always validate full function signature when security-critical
- Use ABI decoding to verify parameter types
- Don’t rely solely on selector for authentication
Zero-Copy Access
Direct byte access without allocation:
import { CallData } from '@tevm/voltaire';
const calldata = CallData.fromBytes(
new Uint8Array([0xa9, 0x05, 0x9c, 0xbb, ...])
);
// Zero-copy: Returns view of first 4 bytes
const selector = CallData.getSelector(calldata);
// Mutations visible (don't mutate!)
selector[0] = 0xff;
console.log(calldata[0]); // 0xff (affected!)
For immutability, copy selector:
const selector = CallData.getSelector(calldata).slice(); // Copy