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.
CallDataDecoded represents the structured form of calldata after parsing the function selector and ABI-decoding the parameters.
Structure
export type CallDataDecoded = {
selector: [4]u8,
signature: ?[]const u8,
parameters: []AbiValue,
}
Fields
selector - 4-byte function identifier
- First 4 bytes of keccak256 hash of function signature
- Required for all function calls
- Immutable once created
signature - Optional function signature string
- Human-readable like
"transfer(address,uint256)"
- Useful for debugging and logging
- Not required for execution (EVM only uses selector)
parameters - Decoded ABI parameter values
- Array of structured ABI values
- Preserves type information
- Enables type-safe parameter access
Decoding CallData
import { CallData, Abi } from '@tevm/voltaire';
const abi = Abi([{
name: "transfer",
type: "function",
inputs: [
{ name: "to", type: "address" },
{ name: "amount", type: "uint256" }
]
}]);
const calldata = CallData("0xa9059cbb...");
const decoded = CallData.decode(calldata, abi);
console.log(decoded.selector); // [0xa9, 0x05, 0x9c, 0xbb]
console.log(decoded.signature); // "transfer(address,uint256)"
console.log(decoded.parameters[0]); // Address
console.log(decoded.parameters[1]); // Uint256
import { CallData } from '@tevm/voltaire';
const calldata = CallData("0xa9059cbb...");
// Can still extract selector
const selector = CallData.getSelector(calldata);
console.log(selector); // [0xa9, 0x05, 0x9c, 0xbb]
// But parameters remain encoded
const rawParams = calldata.slice(4); // Everything after selector
Encoding to CallData
import { CallData, Address, TokenBalance } from '@tevm/voltaire';
const decoded: CallDataDecoded = {
selector: [0xa9, 0x05, 0x9c, 0xbb],
signature: "transfer(address,uint256)",
parameters: [
Address("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
TokenBalance.fromUnits("1", 18)
]
};
const calldata = CallData.encode(decoded);
console.log(CallData.toHex(calldata));
// "0xa9059cbb00000000000000000000000070997970..."
const std = @import("std");
const CallData = @import("primitives").CallData;
pub fn example(allocator: std.mem.Allocator) !void {
var decoded = CallDataDecoded{
.selector = [_]u8{ 0xa9, 0x05, 0x9c, 0xbb },
.signature = "transfer(address,uint256)",
.parameters = &[_]AbiValue{
.{ .address = address_value },
.{ .uint256 = amount_value },
},
.allocator = allocator,
};
const calldata = try decoded.encode(allocator);
defer allocator.free(calldata.data);
}
Working with Parameters
Type-Safe Access
import { CallData, Abi } from '@tevm/voltaire';
const abi = Abi([{
name: "swap",
type: "function",
inputs: [
{ name: "tokenIn", type: "address" },
{ name: "tokenOut", type: "address" },
{ name: "amountIn", type: "uint256" },
{ name: "minAmountOut", type: "uint256" }
]
}]);
const calldata = CallData("0x...");
const decoded = CallData.decode(calldata, abi);
// Type-safe parameter access
const tokenIn: Address = decoded.parameters[0];
const tokenOut: Address = decoded.parameters[1];
const amountIn: Uint256 = decoded.parameters[2];
const minAmountOut: Uint256 = decoded.parameters[3];
Dynamic Parameters
const abi = Abi([{
name: "multicall",
type: "function",
inputs: [
{ name: "calls", type: "bytes[]" }
]
}]);
const decoded = CallData.decode(calldata, abi);
// Access dynamic array
const calls: CallData[] = decoded.parameters[0];
for (const call of calls) {
console.log(CallData.toHex(call));
}
Selector Operations
Matching Selectors
import { CallData } from '@tevm/voltaire';
const TRANSFER_SELECTOR = [0xa9, 0x05, 0x9c, 0xbb];
const APPROVE_SELECTOR = [0x09, 0x5e, 0xa7, 0xb3];
function routeCall(calldata: CallData) {
const selector = CallData.getSelector(calldata);
if (equals(selector, TRANSFER_SELECTOR)) {
return handleTransfer(calldata);
} else if (equals(selector, APPROVE_SELECTOR)) {
return handleApprove(calldata);
} else {
throw new Error("Unknown function");
}
}
Computing Selectors
import { Keccak256 } from '@tevm/voltaire';
function computeSelector(signature: string): [4]u8 {
const hash = Keccak256.hashString(signature);
return hash.slice(0, 4);
}
const selector = computeSelector("transfer(address,uint256)");
// [0xa9, 0x05, 0x9c, 0xbb]
Memory Management (Zig)
In Zig, CallDataDecoded owns allocated memory:
pub fn processCallData(
allocator: std.mem.Allocator,
calldata: CallData,
) !void {
var decoded = try calldata.decode(allocator);
defer decoded.deinit(); // Free allocated memory
// Use decoded parameters
for (decoded.parameters) |param| {
std.debug.print("Param: {}\n", .{param});
}
}
Signature Resolution
Known Signatures
const KNOWN_SIGNATURES = {
"0xa9059cbb": "transfer(address,uint256)",
"0x095ea7b3": "approve(address,uint256)",
"0x70a08231": "balanceOf(address)",
};
function resolveSignature(calldata: CallData): string | null {
const selector = CallData.getSelector(calldata);
const selectorHex = Hex.fromBytes(selector);
return KNOWN_SIGNATURES[selectorHex] ?? null;
}
4byte.directory API
async function lookupSelector(selector: [4]u8): Promise<string[]> {
const selectorHex = Hex.fromBytes(selector);
const response = await fetch(
`https://www.4byte.directory/api/v1/signatures/?hex_signature=${selectorHex}`
);
const data = await response.json();
return data.results.map(r => r.text_signature);
}
See Also