Skip to main content
Tevm, Viem, and Ethers.js are all popular Ethereum libraries. This page compares how to perform common operations across all three libraries.

Address Operations

Creating and Checksumming Addresses

import { Address } from '@tevm/voltaire';

// Create and checksum
const address = Address.from('0x742d35cc6634c0532925a3b844bc9e7595f51e3e');
const checksummed = Address.toChecksummed(address);
// "0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e"

// Validate address
const isValid = Address.validate(address);
// true

// Compare addresses
const addr1 = Address.from('0x742d35cc6634c0532925a3b844bc9e7595f51e3e');
const addr2 = Address.from('0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e');
const areEqual = Address.equals(addr1, addr2);
// true (case-insensitive comparison)

Keccak256 Hashing

Hashing Strings and Bytes

import * as Keccak256 from '@tevm/voltaire/Keccak256';
import { Hex } from '@tevm/voltaire';

// Hash a string
const stringHash = Keccak256.hash(
  new TextEncoder().encode('hello world')
);
const hashHex = Hex.toHex(stringHash);
// "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"

// Hash hex-encoded data
const hexData = Hex.fromHex('0x1234');
const dataHash = Keccak256.hash(hexData);

// Hash for Ethereum signatures (with prefix)
const message = 'hello world';
const ethMessageHash = Keccak256.hashEthereumMessage(
  new TextEncoder().encode(message)
);

ABI Encoding & Decoding

Function Calls

import * as Abi from '@tevm/voltaire/Abi';

// Define ABI
const transferAbi = {
  type: 'function',
  name: 'transfer',
  inputs: [
    { name: 'to', type: 'address' },
    { name: 'amount', type: 'uint256' }
  ],
  outputs: [{ type: 'bool' }]
} as const;

// Encode function call
const encoded = Abi.Function.encodeParams(transferAbi, [
  '0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e',
  1000000000000000000n
]);
// Returns Uint8Array with encoded parameters

// Get function selector
const selector = Abi.Function.getSelector(transferAbi);
// "0xa9059cbb"

// Decode function result
const resultData = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
const decoded = Abi.Function.decodeResult(transferAbi, resultData);
// [true]

Event Logs

import * as Abi from '@tevm/voltaire/Abi';

// Define event ABI
const transferEvent = {
  type: 'event',
  name: 'Transfer',
  inputs: [
    { name: 'from', type: 'address', indexed: true },
    { name: 'to', type: 'address', indexed: true },
    { name: 'value', type: 'uint256', indexed: false }
  ]
} as const;

// Get event selector (topic0)
const topic0 = Abi.Event.getSelector(transferEvent);
// "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

// Encode event topics
const topics = Abi.Event.encodeTopics(transferEvent, {
  from: '0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e',
  to: '0x1234567890123456789012345678901234567890'
});

// Decode event log
const log = {
  topics: [
    '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
    '0x000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f51e3e',
    '0x0000000000000000000000001234567890123456789012345678901234567890'
  ],
  data: new Uint8Array(32) // uint256 value
};
const decoded = Abi.Event.decodeLog(transferEvent, log);

Quick Reference Table

OperationTevmViemEthers.js
Address checksummingAddress.toChecksummed()getAddress()ethers.getAddress()
Address validationAddress.validate()isAddress()ethers.isAddress()
Address comparisonAddress.equals()isAddressEqual()Manual comparison
Keccak256 hashKeccak256.hash()keccak256()ethers.keccak256()
Hash message (EIP-191)Keccak256.hashEthereumMessage()hashMessage()ethers.hashMessage()
Function encodingAbi.Function.encodeParams()encodeFunctionData()iface.encodeFunctionData()
Function decodingAbi.Function.decodeResult()decodeFunctionResult()iface.decodeFunctionResult()
Event topic encodingAbi.Event.encodeTopics()encodeEventTopics()Manual with topicHash
Event log decodingAbi.Event.decodeLog()decodeEventLog()iface.parseLog()
Function selectorAbi.Function.getSelector()Extract from encodeFunctionData()iface.getFunction().selector

Key Differences

Type System

Tevm uses branded types for compile-time type safety with zero runtime overhead:
type AddressType = Uint8Array & { readonly __tag: "Address" };
type HashType = Uint8Array & { readonly __tag: "Hash" };
Viem uses plain strings and hex types:
type Address = `0x${string}`;
type Hash = `0x${string}`;
Ethers.js primarily uses strings with runtime validation:
// Addresses and hashes are strings
const address: string = ethers.getAddress('0x...');
const hash: string = ethers.keccak256('0x...');

Tree-Shaking

Tevm provides granular tree-shakeable imports:
// Import only what you need
import { from, toHex } from '@tevm/voltaire/AddressType';
Viem has tree-shakeable functions by default:
// Functions are automatically tree-shakeable
import { getAddress, keccak256 } from 'viem';
Ethers.js v6 improved tree-shaking but still bundles more:
// Top-level imports bundle more code
import { ethers } from 'ethers';

Bundle Size

Approximate bundle sizes for common operations:
LibraryAddress + Keccak256With ABI encoding
Tevm~8 KB~25 KB
Viem~12 KB~35 KB
Ethers.js~95 KB~120 KB
Sizes are approximate and depend on tree-shaking effectiveness. Tevm and Viem are optimized for minimal bundle size.

WASM Acceleration

Tevm provides optional WASM acceleration for performance-critical operations:
import { Keccak256Wasm } from '@tevm/voltaire/crypto/keccak256.wasm';

await Keccak256Wasm.init();
const hash = Keccak256Wasm.hash(data); // 10-100x faster
Viem and Ethers.js use JavaScript implementations only.

See Also