--- name: nodejs-keccak256 description: Prevent Ethereum hashing bugs in JavaScript and TypeScript. Node's sha3-256 is NIST SHA3, not Ethereum Keccak-256, and silently breaks selectors, signatures, storage slots, and address derivation. origin: ECC direct-port adaptation version: "1.0.0" --- # Node.js Keccak-256 Ethereum uses Keccak-256, not the NIST-standardized SHA3 variant exposed by Node's `crypto.createHash('sha3-256')`. ## When to Use - Computing Ethereum function selectors or event topics - Building EIP-712, signature, Merkle, or storage-slot helpers in JS/TS - Reviewing any code that hashes Ethereum data with Node crypto directly ## How It Works The two algorithms produce different outputs for the same input, and Node will not warn you. ```javascript import crypto from 'crypto'; import { keccak256, toUtf8Bytes } from 'ethers'; const data = 'hello'; const nistSha3 = crypto.createHash('sha3-256').update(data).digest('hex'); const keccak = keccak256(toUtf8Bytes(data)).slice(2); console.log(nistSha3 === keccak); // false ``` ## Examples ### ethers v6 ```typescript import { keccak256, toUtf8Bytes, solidityPackedKeccak256, id } from 'ethers'; const hash = keccak256(new Uint8Array([0x01, 0x02])); const hash2 = keccak256(toUtf8Bytes('hello')); const topic = id('Transfer(address,address,uint256)'); const packed = solidityPackedKeccak256( ['address', 'uint256'], ['0x742d35Cc6634C0532925a3b8D4C9B569890FaC1c', 100n], ); ``` ### viem ```typescript import { keccak256, toBytes } from 'viem'; const hash = keccak256(toBytes('hello')); ``` ### web3.js ```javascript const hash = web3.utils.keccak256('hello'); const packed = web3.utils.soliditySha3( { type: 'address', value: '0x742d35Cc6634C0532925a3b8D4C9B569890FaC1c' }, { type: 'uint256', value: '100' }, ); ``` ### Common patterns ```typescript import { id, keccak256, AbiCoder } from 'ethers'; const selector = id('transfer(address,uint256)').slice(0, 10); const typeHash = keccak256(toUtf8Bytes('Transfer(address from,address to,uint256 value)')); function getMappingSlot(key: string, mappingSlot: number): string { return keccak256( AbiCoder.defaultAbiCoder().encode(['address', 'uint256'], [key, mappingSlot]), ); } ``` ### Address from public key ```typescript import { keccak256 } from 'ethers'; function pubkeyToAddress(pubkeyBytes: Uint8Array): string { const hash = keccak256(pubkeyBytes.slice(1)); return '0x' + hash.slice(-40); } ``` ### Audit your codebase ```bash grep -rn "createHash.*sha3" --include="*.ts" --include="*.js" --exclude-dir=node_modules . grep -rn "keccak256" --include="*.ts" --include="*.js" . | grep -v node_modules ``` ## Rule For Ethereum contexts, never use `crypto.createHash('sha3-256')`. Use Keccak-aware helpers from `ethers`, `viem`, `web3`, or another explicit Keccak implementation.