Solia saysabi.encode pads every argument to 32 bytes (standard ABI); abi.encodePacked drops the padding for tightness — but encodePacked breaks for dynamic types and hash collisions.
The Ethereum ABI is rigid: every static-type argument is padded to 32 bytes, every dynamic type gets a head offset plus a tail with length prefix. abi.encode follows this strictly, producing data that any contract can decode. abi.encodePacked drops the padding entirely, producing the tightest possible bytes — but it breaks for dynamic types (two different inputs can produce the same bytes, breaking hashes).
Use encode for anything you will send to another contract or store; use encodePacked only when you control both sides and the inputs are fixed-size — typically for keccak-based identifiers like signed-message hashes. The demo encodes a (uint256, address, bool) both ways.
Power-ups you unlock
abi.encode: every static arg padded to 32 bytes
abi.encode: dynamic args use head offset + tail
abi.encodePacked: tight bytes, no padding
encodePacked dangerous for dynamic types (hash collisions)
Use encode for ABI compatibility; encodePacked for fixed-size identifiers
The Reentrancy Reaper attacks — common mistakes
Hashing user-supplied dynamic types with encodePacked (collision risk)
Calling another contract with encodePacked bytes — it cannot decode them
Forgetting encode adds 32-byte head pointers for dynamic args
Mixing encode and encodePacked in the same signed-message scheme
Boss battleEncode (uint256, address, bool) with both methods and compare byte sizes.