EIP-5656: the memory-copy opcode the EVM never had
Copying a range of memory is one of the most basic things a program does — yet for years the EVM had no instruction for it. Contracts either ran a verbose MLOAD/MSTORE loop or abused the identity precompile as a roundabout memcpy. EIP-5656 finally adds MCOPY: one opcode, one copy.
- the EVM's memory model
- precompiles
Cancun / Deneb, March 2024. Here’s a humbling gap. In almost any programming environment, copying a block of bytes from one place to another is a single, cheap primitive — memcpy. The EVM has memory — a flat, expandable byte array — and contracts copy ranges of it constantly: assembling calldata, ABI-encoding return values, shuffling buffers in a hash function. And yet, for its entire history, the EVM had no memory-to-memory copy instruction. Developers worked around the hole in two awkward ways, both wasteful. EIP-5656 closes it with a single new opcode. Let’s derive it.
The problem: the EVM had every way to copy memory except a good one
To copy N bytes of memory, a contract had two options, neither good. The first was a MLOAD/MSTORE loop Copying memory word by word: MLOAD reads 32 bytes onto the stack, MSTORE writes them to the destination, repeated for each word. It works, but it's many opcodes per copy — extra gas and extra bytecode for a trivial operation. : read 32 bytes with MLOAD, write them with MSTORE, and repeat, word by word. Correct, but it burns a pile of opcodes and gas and bloats the contract’s bytecode. The second was a notorious hack: the identity precompile The precompile at address 0x04 that returns its input unchanged. Because a call to it copies input to output, contracts abused it as a memcpy — paying full call overhead for a copy, and obscuring intent. at address 0x04 returns whatever you send it, so you could call it and use the returned data as a copy — paying call overhead to smuggle a memcpy out of a precompile that was never meant for it. That the ecosystem leaned on such tricks is the tell: a fundamental primitive was missing.
→ Step 2: add exactly that instruction.
EIP-5656: one opcode, one copy
The fix is as direct as the problem. EIP-5656 adds the MCOPY Opcode 0x5e. Pops three stack items — destination offset, source offset, and length — and copies that many bytes within memory in one instruction, with correct handling of overlapping source and destination (like memmove). opcode (0x5e). It pops three values — destination, source, and length — and copies that many bytes of memory from source to destination in a single instruction. It handles overlapping regions correctly (the way memmove does), and it’s priced like the EVM’s other copy operations: a small base cost plus 3 gas per 32-byte word, plus any memory-expansion cost. No loop, no call, no hack — just the primitive, at its honest price.
→ Step 3: existing contracts get cheaper without lifting a finger.
The payoff: a small opcode with broad reach
Because memory copying is everywhere — ABI encoding The standard way contracts lay out function arguments and return values in memory. It involves copying and relocating byte ranges constantly, so a cheaper memory copy makes a large share of ordinary contract execution cheaper. , string and array handling, and especially cryptographic code that shuffles buffers — a cheaper copy quietly makes a large fraction of contract execution cheaper and smaller. And the win is nearly automatic: compilers (Solidity, Vyper) emit MCOPY wherever they used to emit a copy loop, so contracts get cheaper and more compact simply by recompiling against a newer toolchain. The identity-precompile hack retires, intent becomes explicit in the bytecode, and the EVM finally has the memcpy it always should have. It’s the same spirit as PUSH0: give the machine the small, obvious primitive it lacked, and everything built on top gets a little leaner.