Skip to main content

Try it Live

Run Address examples in the interactive playground
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
View the complete executable example at playground/src/examples/primitives/address/is-address.ts.

C FFI

primitives_address_is_valid(const PrimitivesAddress* addr): bool

Validate address pointer is non-null (size already enforced by struct). Parameters:
  • addr: const PrimitivesAddress* - Address pointer to check
Returns: bool - true if pointer is non-null Example:
#include "primitives.h"

// Validate before use
void processAddress(const PrimitivesAddress* addr) {
    if (!primitives_address_is_valid(addr)) {
        // Handle null pointer
        return;
    }

    // Safe to use addr
    char hex[43]; // 0x + 40 chars + null
    primitives_address_to_hex(addr, hex);
}

// Size enforced by struct (always 20 bytes)
PrimitivesAddress addr;
// sizeof(addr) == 20 guaranteed
Note: C struct definition enforces 20-byte size. Runtime check only validates pointer.

TypeScript Type Predicates

Type guards use TypeScript’s is operator to narrow types:
import * as Address from '@tevm/voltaire/Address'

// Type predicate signature
function is(value: unknown): value is AddressType {
  return value instanceof Uint8Array && value.length === 20
}

// Before type guard
function example1(value: unknown) {
  const hex = value.toHex() // Type error: unknown not assignable
}

// After type guard
function example2(value: unknown) {
  if (Address.is(value)) {
    // Type narrowed to AddressType
    const hex = value.toHex() // OK
    const checksummed = value.toChecksummed() // OK
  }
}
Benefits:
  • Type safety without type assertions
  • Catches errors at compile time
  • No runtime overhead (inline check)
  • Works with TypeScript’s control flow analysis

Type Narrowing

Type guards enable safe handling of unknown values:

Basic Narrowing

import * as Address from '@tevm/voltaire/Address'

function handleUnknown(value: unknown) {
  if (Address.is(value)) {
    // Type: AddressType
    console.log(value.length) // 20
    const hex = value.toHex()
  } else {
    // Type: unknown
    console.log("Not an address")
  }
}

Union Type Narrowing

import * as Address from '@tevm/voltaire/Address'
import * as Hash from '@tevm/voltaire/Hash'

type AddressOrHash = AddressType | Hash

function process(value: AddressOrHash) {
  if (Address.is(value)) {
    // Narrowed to AddressType
    console.log("Address:", value.toHex())
  } else {
    // Narrowed to Hash
    console.log("Hash:", value.toHex())
  }
}

Array Filtering

import * as Address from '@tevm/voltaire/Address'

const mixed: unknown[] = [
  Address(69n),
  "not an address",
  Address.zero(),
  42,
  Address("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3")
]

// Filter to only addresses
const addresses = mixed.filter(Address.is)
// Type: AddressType[]

addresses.forEach(addr => {
  console.log(addr.toHex()) // Type-safe
})

Defensive Programming

Type guards enable validation at system boundaries:

API Input Validation

import * as Address from '@tevm/voltaire/Address'

function transferTokens(to: unknown, amount: bigint) {
  if (!Address.is(to)) {
    throw new Error("Invalid recipient address")
  }

  // Type-safe operations
  if (to.equals(Address.zero())) {
    throw new Error("Cannot transfer to zero address")
  }

  // Process transfer...
}

User Input Sanitization

import * as Address from '@tevm/voltaire/Address'

function parseUserInput(input: string): AddressType {
  try {
    const addr = Address(input)

    if (!Address.is(addr)) {
      throw new Error("Invalid address format")
    }

    return addr
  } catch (e) {
    throw new Error(`Failed to parse address: ${e.message}`)
  }
}

External Data Validation

import * as Address from '@tevm/voltaire/Address'

interface Transaction {
  from: unknown
  to: unknown
  value: bigint
}

function validateTransaction(tx: Transaction): boolean {
  // Validate both addresses
  if (!Address.is(tx.from) || !Address.is(tx.to)) {
    return false
  }

  // Validate not zero addresses
  if (tx.from.equals(Address.zero())) {
    return false
  }

  return true
}

Implementation Details

Check logic:
export function is(value) {
  return value instanceof Uint8Array && value.length === 20
}
Two conditions:
  1. instanceof Uint8Array - Must be Uint8Array (not regular Array)
  2. length === 20 - Exactly 20 bytes (not 19, not 21)
What it accepts:
Address.is(new Uint8Array(20))           // true
Address.is(Address(69n))            // true
Address.is(Address.zero())               // true
What it rejects:
Address.is(new Uint8Array(19))           // false (wrong length)
Address.is(Bytes32())           // false (wrong length)
Address.is("0x742d35...")               // false (string)
Address.is(42n)                          // false (bigint)
Address.is(null)                         // false (null)
Address.is(undefined)                    // false (undefined)
Address.is({})                           // false (object)
Address.is(new Array(20).fill(0))       // false (Array not Uint8Array)

Use Cases

Function Overloading

import * as Address from '@tevm/voltaire/Address'

function normalize(value: unknown): AddressType {
  if (Address.is(value)) {
    // Already valid address
    return value
  }

  if (typeof value === 'string') {
    return Address(value)
  }

  if (typeof value === 'bigint' || typeof value === 'number') {
    return Address(value)
  }

  throw new Error("Cannot convert to address")
}

Safe Deserialization

import * as Address from '@tevm/voltaire/Address'

interface SerializedTransaction {
  from: Uint8Array
  to: Uint8Array
  value: string
}

function deserialize(data: unknown): Transaction | null {
  if (!isSerializedTransaction(data)) {
    return null
  }

  // Validate addresses
  if (!Address.is(data.from) || !Address.is(data.to)) {
    return null
  }

  return {
    from: data.from,
    to: data.to,
    value: BigInt(data.value)
  }
}

Collection Validation

import * as Address from '@tevm/voltaire/Address'

function validateWhitelist(addresses: unknown[]): AddressType[] {
  const valid: AddressType[] = []

  for (const addr of addresses) {
    if (Address.is(addr)) {
      valid.push(addr)
    } else {
      console.warn("Skipping invalid address:", addr)
    }
  }

  return valid
}

Comparison with Other Validators

vs Address.isValid(hex)

isValid validates hex string format:
Address.isValid("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3") // true
Address.is("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3")      // false (string)
is validates AddressType type:
const addr = Address("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3")
Address.is(addr)      // true (AddressType)
Address.isValid(addr) // false (not string)

vs Address.isValidChecksum(hex)

isValidChecksum validates EIP-55 checksumming:
Address.isValidChecksum("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3") // true
Address.isValidChecksum("0x742d35cc6634c0532925a3b844bc9e7595f51e3e") // false
is doesn’t validate checksum:
const lower = Address("0x742d35cc6634c0532925a3b844bc9e7595f51e3e")
const checksum = Address("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3")
Address.is(lower)    // true (valid AddressType)
Address.is(checksum) // true (valid AddressType)
Summary:
  • Address.is(value) - Runtime type guard for AddressType
  • Address.isValid(hex) - Validates hex string format
  • Address.isValidChecksum(hex) - Validates EIP-55 checksum

See Also