If you've ever hashed something in Python with hashlib.sha3_256 and got a different result from Solidity's keccak256, nothing is wrong with your code. You're using two different functions that share almost everything.
Same engine, different padding
Keccak won NIST's hash competition in 2012. When NIST published the final standard, FIPS 202, in 2015, it changed the padding: SHA-3 appends the bits 01 before the usual 10*1 padding (so the first padding byte is 0x06), while the original Keccak used 0x01. Ethereum had already adopted Keccak, so it kept the original.
One byte, completely different outputs:
| Input | Keccak-256 (Ethereum) | SHA3-256 (NIST) |
|---|---|---|
| "" | c5d24601...5d85a470 |
a7ffc6f8...80f8434a |
| "abc" | 4e03657a...a12d6c45 |
3a985da7...11431532 |
Quick check: if your empty-string hash starts with c5d24601, you have Keccak-256. If it starts with a7ffc6f8, you have SHA3-256.
Getting the one you need
import hashlib
hashlib.sha3_256(b"abc").hexdigest() # NIST SHA3-256
from Crypto.Hash import keccak # pip install pycryptodome
k = keccak.new(digest_bits=256); k.update(b"abc"); k.hexdigest() # Ethereum
import { createHash } from "node:crypto";
createHash("sha3-256").update("abc").digest("hex"); // NIST SHA3-256
import { keccak256, toUtf8Bytes } from "ethers";
keccak256(toUtf8Bytes("abc")); // Ethereum
Watch out for library names: Solidity's old sha3() was an alias of keccak256(), and some packages still say "sha3" when they mean Keccak.
Try it
I put every variant side by side in a free in-browser tool: sha3.si. Type transfer(address,uint256) and the first 8 hex characters of Keccak-256 give you the ERC-20 selector, a9059cbb. From the terminal: npx sha3-check -s "transfer(address,uint256)" -a keccak-256.
Top comments (0)