Quick answer
Does abi.encodePacked of a uint256 produce the same bytes as bytes32? Yes. abi.encodePacked(uint256 value) writes 32 bytes in big-endian order. abi.encodePacked(bytes32(value)) writes the same 32 bytes. The two calls produce byte-identical output because bytes32 and uint256 share the same EVM storage layout and abi.encodePacked does not add length prefixes to fixed-width types.
Section 01 · Encoding
What abi.encodePacked actually does
Two encoding functions exist in Solidity. For uint256 and bytes32, their output is identical — but they diverge the moment a dynamic type appears.
Two encoding functions are available in Solidity:
- abi.encode(...) is the canonical ABI encoder. Fixed-width types pad to 32 bytes; dynamic types get a length-prefix offset. Used by every
call(...)and event log. - abi.encodePacked(...) is the tight-pack encoder. Each value is written at its own natural byte width with no padding and no length prefix. Used when the developer wants the smallest possible byte sequence — typically for hashing.
For uint256 and bytes32, the natural byte width is identical (32 bytes), so the packed output is identical:
uint256 x = 0x000000000000000000000000000000000000000000000000000000000000002a; // 42
bytes memory a = abi.encodePacked(x); // 32 bytes, big-endian
bytes memory b = abi.encodePacked(bytes32(x)); // same 32 bytes
// keccak256(a) == keccak256(b) — always.This is why uint256 and bytes32 are interchangeable inside hash inputs. They occupy the same slot, big-endian.
Section 02 · Fixed-Width
When the equivalence holds
For any combination of fixed-width types, packed encoding is unambiguous and collision-free.
keccak256(abi.encodePacked(uint256 a, uint256 b))
// 64 bytes — first 32 are `a`, second 32 are `b`. Deterministic, no collision possible.uint8, uint16, ..., uint256, int* siblings, address (20 bytes), bytes1 through bytes32, and bool (1 byte) all pack at their natural width with no surprises.
Section 03 · Collision Risk
When the equivalence breaks
The moment a dynamic type appears in the call, packed encoding loses injectivity — and collisions become possible.
keccak256(abi.encodePacked("ab", "c")); // packs "abc" (3 bytes)
keccak256(abi.encodePacked("a", "bc")); // packs "abc" (3 bytes) — same hashBoth calls produce identical hash inputs because abi.encodePacked writes string contents without length prefixes. Two different logical inputs collide to the same hash.
Production exploit pattern
A signature scheme that hashes abi.encodePacked(user, message) is vulnerable to replay if the attacker can manipulate where user ends and message begins. This is the most common audit finding on custom signature validation logic.
This is the production exploit pattern: a signature scheme that hashes abi.encodePacked(user, message) is vulnerable to replay if the attacker can manipulate where user ends and message begins.
Section 04 · Safe Usage
The safe pattern
Three rules cover every safe usage of abi.encodePacked in production.
Three rules cover the safe usage:
- Use abi.encode, not abi.encodePacked, whenever a dynamic type is involved.
abi.encodeadds the length prefix and the encoding is unambiguous. - Use abi.encodePacked only when every input is fixed-width or when collisions are impossible by construction (for example, you control the input completely and never let user data into the pack).
- When you need both performance (tight packing) and safety, separate the dynamic parts with a fixed-width delimiter that the attacker cannot inject. For example,
abi.encodePacked(user, bytes1(0x00), message)is collision-resistant ifusercannot contain a null byte.
Pair this with the keccak256 hash generator tool and the ABI encoder decoder to test your encoding decisions interactively before committing them to a deployed contract.
Section 05 · Context
Why this query shows up in search
The unusually long query phrasing is a signal: the developer is mid-implementation and testing an assumption.
Developers pasting solidity abi.encodepacked uint256 bytes32 encoding 32 bytes big endian into Google are usually verifying a hash input mid-implementation. The query phrasing is unusually long because the developer is testing whether the two encodings are equivalent — they typed every fact they thought might matter into the search bar.
The short answer is: yes, equivalent for these two specific types. The longer answer (the one that matters for production) is: stop using abi.encodePacked with any dynamic input.
FAQ
Frequently asked questions
Common follow-up questions about abi.encodePacked encoding semantics in Solidity.
Is abi.encodePacked(uint256) the same as casting to bytes32 manually?
For the byte output, yes. Both produce a 32-byte big-endian sequence. The cast bytes32(uint256Value) does not change the bit pattern; it just reinterprets the storage slot.
Does endianness matter on the EVM?
The EVM is big-endian for stack and memory. uint256 is stored big-endian, so abi.encodePacked(uint256) writes the most-significant byte first. This matches Ethereum's wire format conventions and matters when interoperating with external systems that use little-endian (Solana, x86).
Why does OpenZeppelin's ECDSA.toEthSignedMessageHash use abi.encodePacked?
Because the inputs are all fixed-width: the prefix string is a constant of known length, and the hash is bytes32. No dynamic input means no collision risk.
When should I use abi.encode instead?
Whenever the function accepts arbitrary user data, especially string or bytes. The length prefix overhead is small (roughly 32 bytes per dynamic input) and the safety gain is large.