Skip to main content

Try it Live

Run Address examples in the interactive playground

Checksummed

EIP-55 checksummed address type providing mixed-case format for display and integrity verification.

Overview

Checksummed is a branded hex string type representing an Ethereum address with EIP-55 checksum encoding. Unlike AddressType (a Uint8Array), Checksummed is a string with mixed casing that encodes integrity verification in the character case. Key characteristics:
  • Type: Branded string (not Uint8Array)
  • Format: Mixed-case hex (e.g., 0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e)
  • Use cases: UI display, user input validation, QR codes, wallet integrations
  • Validation: Checksum encodes keccak256 hash for detecting typos

Type Definition

import type { Hex } from 'tevm'

type Checksummed = Hex.Sized<20> & {
  readonly __variant: 'Address'
  readonly __checksummed: true
}
Branded string with EIP-55 mixed-case checksum extending Hex.Sized<20>. Defined in: primitives/Address/AddressType/ChecksumAddress.js:8

ChecksumAddress API

ChecksumAddress.from(value)

Create checksummed address from any input format.Parameters:
  • value: number | bigint | string | Uint8Array - Input to convert
Returns: Checksummed - EIP-55 checksummed hex stringNote: Uses keccak256 internally. Import keccak256 hash function to use this API.Defined in: primitives/Address/AddressType/ChecksumAddress.js:23Example:
import * as ChecksumAddress from 'tevm/Address/ChecksumAddress'
import { hash as keccak256 } from 'tevm/crypto/Keccak256'

// Create factory function with crypto dependency
const from = ChecksumAddress.From({ keccak256 })

// Various input formats
from("0x742d35cc6634c0532925a3b844bc9e7595f51e3e")
// "0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e"

from(69n)
// "0x0000000000000000000000000000000000000045"

from(addr)  // AddressType
// "0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e"

ChecksumAddress.isValid(str)

Validate EIP-55 checksum of address string.Parameters:
  • str: string - Address string to validate
Returns: boolean - true if checksum is validNote: Uses keccak256 internally. Import keccak256 hash function to use this API.Defined in: primitives/Address/AddressType/ChecksumAddress.js:63EIP-55 Logic:
  • If all letters same case (all lower OR all upper), valid
  • If mixed case, each letter’s case must match keccak256-based checksum
  • Zero address with all same case always valid
Example:
import * as ChecksumAddress from 'tevm/Address/ChecksumAddress'
import { hash as keccak256 } from 'tevm/crypto/Keccak256'

// Create factory function with crypto dependency
const isValid = ChecksumAddress.IsValid({ keccak256 })

// Correct checksum
isValid("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")
// true

// Wrong case on one character
isValid("0x5aaeb6053F3E94C9b9A09f33669435E7Ef1BeAed")
// false (first 'A' should be uppercase)

// All lowercase (no mixed case = no checksum requirement)
isValid("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
// true

// All uppercase (no mixed case = no checksum requirement)
isValid("0x5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED")
// true

EIP-55 Checksum Algorithm

The checksum encodes integrity verification in hexadecimal character casing:
  1. Convert address to lowercase hex (without 0x prefix)
  2. Compute keccak256 hash of lowercase hex string
  3. For each hex character:
    • If character is a letter (a-f):
      • If corresponding hash nibble >= 8: uppercase
      • Otherwise: lowercase
    • If character is a digit (0-9): unchanged
Pseudocode:
lowercase = address.toHex().slice(2)  // Remove 0x
hash = keccak256(lowercase)
result = "0x"
for i in 0..39:
  char = lowercase[i]
  if char is letter:
    hashNibble = hash[i/2] nibble at position (i % 2)
    if hashNibble >= 8:
      result += char.toUpperCase()
    else:
      result += char.toLowerCase()
  else:
    result += char

Use Cases

UI Display

Display addresses with integrity verification:
import { Address } from 'tevm'

function AddressDisplay({ address }: { address: Address }) {
  const checksummed = address.toChecksummed()

  return (
    <div>
      <span className="font-mono">{checksummed}</span>
      <button onClick={() => navigator.clipboard.writeText(checksummed)}>
        Copy
      </button>
    </div>
  )
}

User Input Validation

Validate user-provided addresses and detect typos:
import { Address } from 'tevm'
import * as ChecksumAddress from 'tevm/Address/ChecksumAddress'

function validateUserInput(input: string): { valid: boolean; error?: string; address?: Address } {
  const trimmed = input.trim()

  // Check basic format
  if (!Address.isValid(trimmed)) {
    return { valid: false, error: "Invalid address format" }
  }

  // Check checksum if mixed case
  const hasMixedCase = trimmed !== trimmed.toLowerCase() && trimmed !== trimmed.toUpperCase()
  if (hasMixedCase && !ChecksumAddress.isValid(trimmed)) {
    // Provide correct checksummed version
    const addr = Address(trimmed)
    const correct = addr.toChecksummed()
    return {
      valid: false,
      error: `Invalid checksum. Did you mean: ${correct}?`
    }
  }

  const address = Address(trimmed)
  return { valid: true, address }
}

// Usage
const result = validateUserInput("0x742d35cc6634C0532925a3b844Bc9e7595f51e3e")
if (!result.valid) {
  console.error(result.error)
  // "Invalid checksum. Did you mean: 0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e?"
}

QR Codes

Use checksummed addresses in QR codes for error detection:
import { Address } from 'tevm'
import QRCode from 'qrcode'

async function generateAddressQR(address: Address): Promise<string> {
  const checksummed = address.toChecksummed()
  const qr = await QRCode.toDataURL(checksummed)
  return qr
}

// Usage
const addr = Address("0x742d35cc6634c0532925a3b844bc9e7595f51e3e")
const qrDataUrl = await generateAddressQR(addr)

Wallet Integrations

Wallets expect checksummed addresses:
import { Address } from 'tevm'

async function requestPayment(recipient: Address, amount: bigint) {
  const checksummed = recipient.toChecksummed()

  await ethereum.request({
    method: 'eth_sendTransaction',
    params: [{
      to: checksummed,
      value: `0x${amount.toString(16)}`
    }]
  })
}

Storage

Store addresses with checksums for verification on retrieval:
import { Address } from 'tevm'
import * as ChecksumAddress from 'tevm/Address/ChecksumAddress'

// Store
function saveAddress(key: string, address: Address) {
  const checksummed = address.toChecksummed()
  localStorage.setItem(key, checksummed)
}

// Retrieve with validation
function loadAddress(key: string): Address | null {
  const stored = localStorage.getItem(key)
  if (!stored) return null

  // Validate checksum to detect corruption
  if (!ChecksumAddress.isValid(stored)) {
    console.error("Stored address checksum invalid - possible corruption")
    return null
  }

  return Address(stored)
}

Comparison with Other Formats

import { Address } from 'tevm'

const addr = Address("0x742d35cc6634c0532925a3b844bc9e7595f51e3e")

// Lowercase (toHex)
addr.toHex()
// "0x742d35cc6634c0532925a3b844bc9e7595f51e3e"

// Checksummed (toChecksummed)
addr.toChecksummed()
// "0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e"

// Uppercase (toUppercase)
addr.toUppercase()
// "0x742D35CC6634C0532925A3B844BC9E7595F51E3E"

// All represent the same address
Address(addr.toHex()).equals(Address(addr.toChecksummed()))
// true

Example Walkthrough

import { Address } from 'tevm'

// Input address (lowercase)
const addr = Address("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")

// Generate checksummed output
console.log(addr.toChecksummed())
// "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
//     ^  ^     ^  ^ ^^     ^  ^             ^
//     Letters capitalized based on keccak256 hash
Breakdown:
  1. Lowercase hex: 5aaeb6053f3e94c9b9a09f33669435e7ef1beaed
  2. Keccak256 hash of lowercase: ce1b9e8e5e10a90a16c6c3ae3f56c5cf99d82e39c703c1a2adcdf2bd7ad9d685
  3. For each letter character:
    • a at position 1: hash nibble = c (12) >= 8 → A
    • a at position 2: hash nibble = e (14) >= 8 → A
    • e at position 3: hash nibble = 1 (1) < 8 → e
    • And so on…

Performance

Time complexity: O(n) where n = 20 bytes (constant time) Overhead: One keccak256 hash computation For repeated conversions, consider caching:
import { Address } from 'tevm'
import type { Checksummed } from 'tevm/Address/ChecksumAddress'

class CachedAddress {
  private _checksumCache?: Checksummed

  constructor(private address: Address) {}

  toChecksummed(): Checksummed {
    if (!this._checksumCache) {
      this._checksumCache = this.address.toChecksummed()
    }
    return this._checksumCache
  }
}

Bundle Size Considerations

With the factory pattern, keccak256 is only bundled if you explicitly import it. Crypto dependencies are now explicit and tree-shakeable.
Tree-shaking:
  • ChecksumAddress.From({ keccak256 }) → Crypto dependencies explicit - only bundled if imported
  • ChecksumAddress.IsValid({ keccak256 }) → Crypto dependencies explicit - only bundled if imported
  • Address.toLowercase() → No crypto dependencies
  • Address.toUppercase() → No crypto dependencies
Selective imports for optimal bundle size:
// Minimal bundle (no crypto)
import { from, toHex } from 'tevm/Address'

// With checksumming - explicit crypto import
import { From } from 'tevm/Address/ChecksumAddress'
import { hash as keccak256 } from 'tevm/crypto/Keccak256'

const from = From({ keccak256 })