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

# hasSelector

> Check if CallData matches a specific function selector

Checks whether calldata matches a specific function selector. Convenient helper for routing and filtering transactions.

## Signature

<Tabs>
  <Tab title="Namespace">
    ```typescript theme={null}
    function hasSelector(
      calldata: CallDataType,
      selector: string | Uint8Array | [4]u8
    ): boolean
    ```
  </Tab>

  <Tab title="Class">
    ```typescript theme={null}
    calldata.hasSelector(
      selector: string | Uint8Array | [4]u8
    ): boolean
    ```
  </Tab>
</Tabs>

## Parameters

* **calldata** - CallData instance to check
* **selector** - Expected selector to match against:
  * `string` - Hex string (e.g., `"0xa9059cbb"`)
  * `Uint8Array` - Byte array
  * `[4]u8` - 4-byte tuple

## Returns

`boolean` - `true` if selector matches, `false` otherwise

## Examples

<Tabs>
  <Tab title="Basic Usage">
    ```typescript theme={null}
    import { CallData } from '@tevm/voltaire';

    const calldata = CallData("0xa9059cbb...");

    // Check against hex string
    const isTransfer = CallData.hasSelector(calldata, "0xa9059cbb");
    console.log(isTransfer); // true

    // Check against different selector
    const isApprove = CallData.hasSelector(calldata, "0x095ea7b3");
    console.log(isApprove); // false
    ```
  </Tab>

  <Tab title="Function Routing">
    ```typescript theme={null}
    import { CallData } from '@tevm/voltaire';

    function routeERC20Call(calldata: CallDataType) {
      if (CallData.hasSelector(calldata, "0xa9059cbb")) {
        return handleTransfer(calldata);
      } else if (CallData.hasSelector(calldata, "0x095ea7b3")) {
        return handleApprove(calldata);
      } else if (CallData.hasSelector(calldata, "0x23b872dd")) {
        return handleTransferFrom(calldata);
      } else {
        throw new Error("Unknown function");
      }
    }
    ```
  </Tab>

  <Tab title="Byte Array Selector">
    ```typescript theme={null}
    import { CallData } from '@tevm/voltaire';

    const TRANSFER_SELECTOR = new Uint8Array([0xa9, 0x05, 0x9c, 0xbb]);

    const isTransfer = CallData.hasSelector(calldata, TRANSFER_SELECTOR);
    console.log(isTransfer);
    ```
  </Tab>

  <Tab title="Class API">
    ```typescript theme={null}
    import { CallData } from '@tevm/voltaire';

    const calldata = CallData("0xa9059cbb...");
    const isTransfer = calldata.hasSelector("0xa9059cbb"); // Method on instance
    ```
  </Tab>
</Tabs>

## Selector Formats

Accepts multiple input formats:

<Tabs>
  <Tab title="Hex String">
    ```typescript theme={null}
    // With 0x prefix
    CallData.hasSelector(calldata, "0xa9059cbb");

    // Without 0x prefix
    CallData.hasSelector(calldata, "a9059cbb");

    // Both work identically
    ```
  </Tab>

  <Tab title="Uint8Array">
    ```typescript theme={null}
    const selector = new Uint8Array([0xa9, 0x05, 0x9c, 0xbb]);
    CallData.hasSelector(calldata, selector);
    ```
  </Tab>

  <Tab title="Tuple">
    ```typescript theme={null}
    const selector: [4]u8 = [0xa9, 0x05, 0x9c, 0xbb];
    CallData.hasSelector(calldata, selector);
    ```
  </Tab>
</Tabs>

## Use Cases

### Transaction Filtering

```typescript theme={null}
import { CallData } from '@tevm/voltaire';

interface Transaction {
  hash: string;
  to: string;
  data: string;
}

// Filter transactions by function
function filterTransfers(txs: Transaction[]): Transaction[] {
  return txs.filter(tx => {
    try {
      const calldata = CallData(tx.data);
      return CallData.hasSelector(calldata, "0xa9059cbb");
    } catch {
      return false; // Invalid calldata
    }
  });
}
```

### Multi-Selector Matching

```typescript theme={null}
import { CallData } from '@tevm/voltaire';

const ERC20_WRITE_SELECTORS = [
  "0xa9059cbb", // transfer
  "0x095ea7b3", // approve
  "0x23b872dd", // transferFrom
];

function isERC20WriteCall(calldata: CallDataType): boolean {
  return ERC20_WRITE_SELECTORS.some(selector =>
    CallData.hasSelector(calldata, selector)
  );
}
```

### Access Control

```typescript theme={null}
import { CallData } from '@tevm/voltaire';

const ALLOWED_FUNCTIONS = [
  "0xa9059cbb", // transfer
  "0x095ea7b3", // approve
];

function validateCall(calldata: CallDataType): void {
  const isAllowed = ALLOWED_FUNCTIONS.some(selector =>
    CallData.hasSelector(calldata, selector)
  );

  if (!isAllowed) {
    throw new Error("Function not allowed");
  }
}
```

### Event Handler Selection

```typescript theme={null}
import { CallData } from '@tevm/voltaire';

class TransactionMonitor {
  private handlers = new Map<string, (data: CallDataType) => void>();

  register(selector: string, handler: (data: CallDataType) => void) {
    this.handlers.set(selector, handler);
  }

  async process(tx: Transaction) {
    const calldata = CallData(tx.data);

    for (const [selector, handler] of this.handlers) {
      if (CallData.hasSelector(calldata, selector)) {
        await handler(calldata);
        return;
      }
    }

    console.log("No handler for selector");
  }
}

// Usage
const monitor = new TransactionMonitor();
monitor.register("0xa9059cbb", handleTransfer);
monitor.register("0x095ea7b3", handleApprove);
```

## Performance

Constant-time comparison (4-byte check):

```typescript theme={null}
// Benchmark: 10M iterations
const calldata = CallData("0xa9059cbb...");

console.time("hasSelector");
for (let i = 0; i < 10_000_000; i++) {
  CallData.hasSelector(calldata, "0xa9059cbb");
}
console.timeEnd("hasSelector");

// Pure JS: ~180ms
// WASM: ~95ms (1.9x faster)
// Extremely fast (just 4-byte comparison)
```

## Comparison with Manual Check

<Tabs>
  <Tab title="Using hasSelector">
    ```typescript theme={null}
    import { CallData } from '@tevm/voltaire';

    // Concise and clear
    if (CallData.hasSelector(calldata, "0xa9059cbb")) {
      handleTransfer(calldata);
    }
    ```

    **Advantages:**

    * Clear intent
    * Handles multiple selector formats
    * Optimized implementation
    * Type-safe
  </Tab>

  <Tab title="Manual Comparison">
    ```typescript theme={null}
    import { CallData, Hex } from '@tevm/voltaire';

    // Verbose
    const selector = CallData.getSelector(calldata);
    const selectorHex = Hex.fromBytes(selector);
    if (selectorHex === "0xa9059cbb") {
      handleTransfer(calldata);
    }
    ```

    **Disadvantages:**

    * More verbose
    * Manual hex conversion
    * Easy to make mistakes
    * Slower (string comparison)
  </Tab>
</Tabs>

Use `hasSelector` for cleaner, faster code.

## Constant-Time Comparison

Implementation uses constant-time comparison to prevent timing attacks:

```typescript theme={null}
// Pseudocode (actual implementation)
function hasSelector(calldata: CallDataType, expected: [4]u8): boolean {
  const actual = getSelector(calldata);

  // Constant-time: Always checks all 4 bytes
  let result = 0;
  for (let i = 0; i < 4; i++) {
    result |= actual[i] ^ expected[i];
  }

  return result === 0;
}
```

Early returns would leak timing information about which byte differs.

## Type Safety

Enforces CallData type at compile time:

```typescript theme={null}
import { CallData, type CallDataType } from '@tevm/voltaire';

// Type error: Uint8Array not CallDataType
const bytes = new Uint8Array([0xa9, 0x05, 0x9c, 0xbb]);
CallData.hasSelector(bytes, "0xa9059cbb"); // ❌

// Correct: Use CallData constructor
const calldata: CallDataType = CallData.fromBytes(bytes);
CallData.hasSelector(calldata, "0xa9059cbb"); // ✅
```

## Related

* [getSelector](/primitives/calldata/get-selector) - Extract selector
* [equals](/primitives/calldata/equals) - Compare full calldata
* [decode](/primitives/calldata/decode) - Decode parameters
* [Fundamentals](/primitives/calldata/fundamentals) - How selectors work
