freecoding.school100% FREE · NO SIGNUP
The EVM CoreISSUE #26 of 48

abi encoding · encode · encodePacked · decode

SoliaVSThe Reentrancy Reaper
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

The Reentrancy Reaper attacks — common mistakes

Boss battleEncode (uint256, address, bool) with both methods and compare byte sizes.

Example code

<!doctype html><html><head><meta charset="utf-8"></head>
<body style="background:#06040d;color:#e6e0ff;font-family:monospace;padding:20px"><pre id="o"></pre>
<script>
// abi.encode: each arg → 32 bytes.  abi.encodePacked: tight bytes.
const args = { u256: 0xdeadbeef, addr: '0x1234567890abcdef1234567890abcdef12345678', flag: 1 };
const enc = {
  u256: args.u256.toString(16).padStart(64, '0'),
  addr: args.addr.slice(2).padStart(64, '0'),
  flag: args.flag.toString(16).padStart(64, '0')
};
const packed = {
  u256: args.u256.toString(16).padStart(64, '0'),    // u256 already 32 bytes
  addr: args.addr.slice(2).padStart(40, '0'),        // 20 bytes
  flag: args.flag.toString(16).padStart(2, '0')      // 1 byte
};
const encBytes    = 32 + 32 + 32;
const packedBytes = 32 + 20 + 1;
document.getElementById('o').textContent = [
  'args: (uint256 ' + args.u256 + ', address ' + args.addr + ', bool ' + args.flag + ')',
  '',
  'abi.encode (each padded to 32 bytes):',
  '  0x' + enc.u256,
  '  0x' + enc.addr + '   ← address left-padded to 32',
  '  0x' + enc.flag + '   ← bool left-padded to 32',
  '  total: ' + encBytes + ' bytes',
  '',
  'abi.encodePacked (tight):',
  '  0x' + packed.u256 + packed.addr + packed.flag,
  '  total: ' + packedBytes + ' bytes  (' + (encBytes - packedBytes) + ' bytes saved)',
  '',
  '⚠ encodePacked of two dynamic types can COLLIDE → never hash user input that way'
].join('\n');
</script></body></html>
▶ Open the interactive comic issue
‹ Yul · Inline Assembly FundamentalsFunction Selectors · 4-Byte Clashing ›