Ethereum Address Validation in Node.js — EIP-55 Checksum Explained
Ethereum addresses are 20-byte hex strings with an optional mixed-case checksum defined by EIP-55. A simple regex won't catch wrong-case typos that could send funds to an unrecoverable address. Here's how to validate them properly.
In this guide
1. What is an Ethereum address?
An Ethereum address is a 20-byte (160-bit) identifier that represents an account on the Ethereum network. It is derived from the public key of an elliptic-curve key pair: take the Keccak-256 hash of the uncompressed public key, then keep only the last 20 bytes.
When displayed, Ethereum addresses are encoded as 40 hexadecimal characters prefixed with 0x, giving a total length of 42 characters. For example:
Ethereum addresses are used for two types of accounts:
Externally Owned Accounts (EOAs)
Controlled by a private key held by a person or application. EOAs can sign transactions, send ETH, and interact with smart contracts.
Smart contract accounts
Deployed at a deterministic address derived from the deployer's address and nonce. Smart contracts contain executable code but do not have a private key.
2. The EIP-55 checksum
EIP-55 (Ethereum Improvement Proposal 55) introduced a backwards-compatible checksum by encoding it in the capitalisation of the hex characters. The mixed-case encoding acts as a checksum without adding any extra characters to the address.
Here is how the algorithm works, step by step, for the address 0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359:
Step 1 — Remove the 0x prefix and lowercase everything
fb6916095ca1df60bb79ce92ce3ea74c37c5d359
Step 2 — Compute the Keccak-256 hash of the lowercase address string (as ASCII)
6b51d0a3ee…(64 hex digits)
Each hex digit of this hash will control the case of the corresponding character in the address.
Step 3 — For each character at position i in the address
If the character is a digit (0-9), keep it as-is.
If the character is a letter (a-f), check the i-th hex digit of the hash:
- If the hash digit is 8, 9, a, b, c, d, e, or f (i.e. ≥ 8), uppercase the letter
- If the hash digit is 0, 1, 2, 3, 4, 5, 6, or 7 (i.e. < 8), keep it lowercase
Step 4 — Re-attach the 0x prefix
0xFb6916095cA1dF60bB79cE92cE3Ea74c37c5D359
Here is the EIP-55 algorithm in JavaScript:
// eip55.js — implements EIP-55 checksum encoding const { keccak256 } = require('js-sha3'); function toChecksumAddress(address) { // Step 1: lowercase, strip 0x const addr = address.toLowerCase().replace('0x', ''); // Step 2: hash the lowercase address const hash = keccak256(addr); // Step 3: capitalise where hash digit >= 8 let checksummed = '0x'; for (let i = 0; i < 40; i++) { const ch = addr[i]; if (/[a-f]/.test(ch)) { checksummed += parseInt(hash[i], 16) >= 8 ? ch.toUpperCase() : ch; } else { checksummed += ch; // digits stay as-is } } return checksummed; } // Validate: compare the input against the checksummed version function isValidChecksum(address) { return address === toChecksumAddress(address); } console.log(toChecksumAddress('0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359')); // → 0xFb6916095cA1dF60bB79cE92cE3Ea74c37c5D359
3. Why simple hex regex isn't enough
A common first attempt at Ethereum address validation looks like this:
// naive approach — DO NOT use in production const isEthAddress = (addr) => /^0x[0-9a-fA-F]{40}$/.test(addr); isEthAddress('0x0000000000000000000000000000000000000000'); // true — but is it real? isEthAddress('0xd8da6bf26964af9d7eed9e03e53415d37aa96045'); // true — but wrong checksum!
This regex confirms the string looks like an address, but misses critical problems:
The zero address passes
0x0000000000000000000000000000000000000000 is syntactically valid hex but is the Ethereum "burn address." Sending funds there is irreversible. A regex won't flag this.
Wrong-case mixed-case addresses slip through
A user copies an address and accidentally changes the case of one letter. The regex still matches, but the EIP-55 checksum is now broken. Without checksum verification, your application accepts a potentially garbled address.
Sending to the wrong address is irreversible
Unlike traditional banking where a wrong transfer can sometimes be reversed, sending ETH or tokens to an incorrect Ethereum address means the funds are gone forever. There is no chargeback, no dispute process, and no central authority that can intervene. Proper validation is your last line of defence.
No distinction between address types
A regex cannot tell you whether the address is an EOA or a smart contract. While this distinction does not change the address format, knowing the address type can be useful for user-facing warnings (e.g. "you are about to send tokens to a contract address").
4. The right solution
The IsValid Ethereum Address API performs full structural validation and EIP-55 checksum verification in a single GET request. The response includes the correctly checksummed address so you can store and display it confidently.
Get your free API key at isvalid.dev. The free tier includes 100 calls per day — enough for most development and low-volume production use.
Full parameter reference and response schema: Ethereum Address Validation API docs →
5. Node.js code example
Using the IsValid SDK or the native fetch API.
import { createClient } from '@isvalid-dev/sdk'; const iv = createClient({ apiKey: process.env.ISVALID_API_KEY }); // ── Example usage ──────────────────────────────────────────────────────────── const result = await iv.ethAddress('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'); if (!result.valid) { console.log('Invalid Ethereum address'); } else { console.log(`Valid address: ${result.address}`); console.log(`EIP-55 checksum valid: ${result.isChecksumValid}`); // → Valid address: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 // → EIP-55 checksum valid: true }
In a withdrawal handler, you might use it like this:
// routes/withdraw.js (Express) app.post('/withdraw', async (req, res) => { const { address, amount, token } = req.body; let addrCheck; try { addrCheck = await validateEthAddress(address); } catch { return res.status(502).json({ error: 'Address validation service unavailable' }); } if (!addrCheck.valid) { return res.status(400).json({ error: 'Invalid Ethereum address' }); } if (!addrCheck.isChecksumValid) { return res.status(400).json({ error: 'Address has invalid EIP-55 checksum — please double-check the address', expected: addrCheck.address, // correctly checksummed version }); } // Use the checksummed address from the API response for storage await initiateWithdrawal({ toAddress: addrCheck.address, amount, token, }); res.json({ success: true, toAddress: addrCheck.address }); });
address field. This ensures consistency in your database and makes it safe to compare addresses using simple string equality.6. cURL example
Validate a checksummed Ethereum address:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/eth-address?value=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
Validate an all-lowercase address (no checksum encoding):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/eth-address?value=0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
An invalid address (wrong length):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/eth-address?value=0xd8dA6BF26964aF9D7eEd9e03E534"
7. Understanding the response
Valid checksummed address:
{ "valid": true, "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "isChecksumValid": true }
Valid all-lowercase address (no checksum to verify):
{ "valid": true, "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "isChecksumValid": true }
Invalid address:
{ "valid": false }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Whether the address passes format validation (0x prefix, 40 hex characters) |
| address | string | The address with correct EIP-55 checksummed capitalisation (only present when valid) |
| isChecksumValid | boolean | Whether the EIP-55 mixed-case checksum is correct. Always true for all-lowercase or all-uppercase addresses because there is no checksum to verify (only present when valid) |
8. Edge cases to handle
All-lowercase addresses are valid
An address like 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 is perfectly valid. Since it is all-lowercase, there is no mixed-case checksum to verify, so isChecksumValid returns true. The same applies to all-uppercase addresses (excluding the 0x prefix). The API returns the correctly checksummed version in the address field regardless.
ENS names are not Ethereum addresses
Names like vitalik.eth are Ethereum Name Service (ENS) domains that resolve to an address via on-chain lookups. They are not validated by this endpoint. If your application accepts ENS names, resolve them to an address first (using a library like ethers.js or an ENS resolver API), then validate the resulting address.
// Resolve ENS name before validating const { ethers } = require('ethers'); const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); const ensName = 'vitalik.eth'; const resolved = await provider.resolveName(ensName); if (!resolved) { console.log('ENS name does not resolve'); } else { // Now validate the resolved address const result = await validateEthAddress(resolved); console.log(result); }
ICAP / IBAN-style Ethereum addresses
Ethereum defines an ICAP (Inter-exchange Client Address Protocol) encoding that represents addresses in an IBAN-like format, such as XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS. ICAP addresses are rarely used in practice and are not validated by this endpoint. If you encounter them, convert to the standard 0x-prefixed format before validation.
The zero address (0x000...0)
0x0000000000000000000000000000000000000000 is structurally valid and will pass format validation. However, it is commonly used as a "burn address" — tokens sent there are irrecoverable. Consider adding an application-level check to warn users before sending to the zero address.
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; const result = await validateEthAddress(address); if (result.valid && result.address === ZERO_ADDRESS) { console.warn('Warning: this is the zero/burn address'); }
Summary
See also
Validate Ethereum addresses instantly
Free tier includes 100 API calls per day. No credit card required. Full EIP-55 checksum verification with correctly cased address returned in the response.