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)
import {
getAddress,
isAddress,
isAddressEqual
} from 'viem';
// Create and checksum
const checksummed = getAddress('0x742d35cc6634c0532925a3b844bc9e7595f51e3e');
// "0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e"
// Validate address
const isValid = isAddress('0x742d35cc6634c0532925a3b844bc9e7595f51e3e');
// true
// Compare addresses
const areEqual = isAddressEqual(
'0x742d35cc6634c0532925a3b844bc9e7595f51e3e',
'0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e'
);
// true
import { ethers } from 'ethers';
// Create and checksum
const checksummed = ethers.getAddress('0x742d35cc6634c0532925a3b844bc9e7595f51e3e');
// "0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e"
// Validate address
const isValid = ethers.isAddress('0x742d35cc6634c0532925a3b844bc9e7595f51e3e');
// true
// Compare addresses (manual)
const addr1 = ethers.getAddress('0x742d35cc6634c0532925a3b844bc9e7595f51e3e');
const addr2 = ethers.getAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e');
const areEqual = addr1 === addr2;
// true
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)
);
import { keccak256, toHex, toBytes } from 'viem';
// Hash a string
const stringHash = keccak256(toHex('hello world'));
// "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"
// Hash hex-encoded data
const dataHash = keccak256('0x1234');
// Hash for Ethereum signatures (with prefix)
import { hashMessage } from 'viem';
const ethMessageHash = hashMessage('hello world');
// Automatically adds "\x19Ethereum Signed Message:\n" prefix
import { ethers } from 'ethers';
// Hash a string (UTF-8 encoded)
const stringHash = ethers.keccak256(ethers.toUtf8Bytes('hello world'));
// "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"
// Hash hex-encoded data
const dataHash = ethers.keccak256('0x1234');
// Hash for Ethereum signatures (with prefix)
const ethMessageHash = ethers.hashMessage('hello world');
// Automatically adds "\x19Ethereum Signed Message:\n" prefix
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]
import {
encodeFunctionData,
decodeFunctionResult,
parseAbi
} from 'viem';
// Define ABI
const abi = parseAbi([
'function transfer(address to, uint256 amount) returns (bool)'
]);
// Encode function call
const encoded = encodeFunctionData({
abi,
functionName: 'transfer',
args: [
'0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e',
1000000000000000000n
]
});
// "0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f51e3e0000000000000000000000000000000000000000000000000de0b6b3a7640000"
// Decode function result
const decoded = decodeFunctionResult({
abi,
functionName: 'transfer',
data: '0x0000000000000000000000000000000000000000000000000000000000000001'
});
// true
import { ethers } from 'ethers';
// Define ABI
const abi = [
'function transfer(address to, uint256 amount) returns (bool)'
];
const iface = new ethers.Interface(abi);
// Encode function call
const encoded = iface.encodeFunctionData('transfer', [
'0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e',
ethers.parseEther('1')
]);
// "0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f51e3e0000000000000000000000000000000000000000000000000de0b6b3a7640000"
// Decode function result
const decoded = iface.decodeFunctionResult(
'transfer',
'0x0000000000000000000000000000000000000000000000000000000000000001'
);
// [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);
import {
encodeEventTopics,
decodeEventLog,
parseAbi
} from 'viem';
// Define event ABI
const abi = parseAbi([
'event Transfer(address indexed from, address indexed to, uint256 value)'
]);
// Encode event topics (for filtering)
const topics = encodeEventTopics({
abi,
eventName: 'Transfer',
args: {
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e',
to: '0x1234567890123456789012345678901234567890'
}
});
// Decode event log
const decoded = decodeEventLog({
abi,
data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
topics: [
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f51e3e',
'0x0000000000000000000000001234567890123456789012345678901234567890'
]
});
// { eventName: 'Transfer', args: { from: '0x...', to: '0x...', value: 1000000000000000000n } }
import { ethers } from 'ethers';
// Define event ABI
const abi = [
'event Transfer(address indexed from, address indexed to, uint256 value)'
];
const iface = new ethers.Interface(abi);
// Get event topic (for filtering)
const topic0 = iface.getEvent('Transfer').topicHash;
// "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
// Encode event topics (manual)
const topics = [
topic0,
ethers.zeroPadValue('0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e', 32),
ethers.zeroPadValue('0x1234567890123456789012345678901234567890', 32)
];
// Decode event log
const decoded = iface.parseLog({
data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
topics: [
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f51e3e',
'0x0000000000000000000000001234567890123456789012345678901234567890'
]
});
// { name: 'Transfer', args: ['0x...', '0x...', 1000000000000000000n] }
Quick Reference Table
| Operation | Tevm | Viem | Ethers.js |
|---|
| Address checksumming | Address.toChecksummed() | getAddress() | ethers.getAddress() |
| Address validation | Address.validate() | isAddress() | ethers.isAddress() |
| Address comparison | Address.equals() | isAddressEqual() | Manual comparison |
| Keccak256 hash | Keccak256.hash() | keccak256() | ethers.keccak256() |
| Hash message (EIP-191) | Keccak256.hashEthereumMessage() | hashMessage() | ethers.hashMessage() |
| Function encoding | Abi.Function.encodeParams() | encodeFunctionData() | iface.encodeFunctionData() |
| Function decoding | Abi.Function.decodeResult() | decodeFunctionResult() | iface.decodeFunctionResult() |
| Event topic encoding | Abi.Event.encodeTopics() | encodeEventTopics() | Manual with topicHash |
| Event log decoding | Abi.Event.decodeLog() | decodeEventLog() | iface.parseLog() |
| Function selector | Abi.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:
| Library | Address + Keccak256 | With 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