Skip to main content
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

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..."

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