Part III · Chapter 7 of 43 · Homestead

EIP-7: DELEGATECALL — borrow the code, keep your context

Contracts wanted to reuse a shared library's logic without copy-pasting its bytecode into every one. But a normal CALL runs that logic in the library's own storage — useless for a library that's supposed to work on your data. DELEGATECALL runs borrowed code in the caller's context, and in doing so it quietly invented libraries, proxies, and upgradeable contracts.

Updated Jun 24, 2026 · 11 min
Assumed
  • the EVM & the call model
  • storage & context

We enter Part III at the beginning: Homestead, March 2016, Ethereum’s first planned upgrade. It shipped a small opcode with enormous consequences. By now (from the EVM chapter) you know a contract is code plus storage, and that one contract can call another. But calling raises a question the earliest EVM answered badly: if I want to reuse another contract’s code — a shared library of vetted logic — whose data does that code run on? Get the answer wrong and shared libraries are impossible; get it right and you unlock proxies and upgradeable contracts. EIP-7 is that right answer, DELEGATECALL. Let’s derive it.

1 step Reuse without copying

The problem: shared logic, without copy-paste

Deploying a contract costs gas proportional to its code size, and the same routines — safe math, token transfers, access control — get written again and again. Copy-pasting a library’s bytecode into every contract is wasteful and unmaintainable: fix a bug and you’d have to redeploy everyone. The obvious wish is a single, audited library contract deployed once, whose code many contracts can borrow.

everyone copies the same code 0x00… transfer() 0x11… transfer() 0x22… transfer() same bytecode, over and over share one implementation app Aapp Bapp C Library transfer() whose data does the shared code touch?
Every contract carrying its own copy of the same transfer logic is pure duplication — expensive to deploy and impossible to patch in one place. The wish: deploy the logic once as a shared library and have many contracts run it. But that raises the real question — whose storage does the shared code touch?

→ Step 2: watch a plain CALL land in the wrong storage.

2 step CALL runs in the wrong place

Why a normal CALL can’t build a library

When your contract does a plain CALL to the library, the EVM switches into the library’s execution context The account whose storage, balance, and address the currently-running code reads and writes, plus msg.sender/msg.value. A plain CALL switches context to the callee; the code runs 'as' the callee. . The library’s code executes, but every SSTORE it does writes to the library’s storage, address(this) is the library, and msg.sender is your contract. So a library method like balances[msg.sender] += amount dutifully updates the library’s balances mapping — not yours. Your storage is never touched.

CALL — run the library, in the library's context your contract storage: empty ✗ never written CALL Library balances[msg.sender] += x storage: WRITTEN here the write lands in the Library's storage — msg.sender is your contract useless: a library can't operate on your data
A plain CALL runs the library's code in the library's context: the write lands in the library's storage, and to the library, msg.sender is your contract. Your contract's own storage is never written. A library that mutates the caller's data is impossible this way.

The code is perfectly good; it just ran in the wrong account’s world. What we actually want is the opposite wiring: the library’s code, but our context.

→ Step 3: DELEGATECALL.

3 step Borrow code, keep context

DELEGATECALL: the callee’s code, the caller’s everything

DELEGATECALL An EVM call (EIP-7) that loads the callee's code and runs it entirely in the caller's context: the caller's storage and balance, the caller's address as this, and — unlike the older CALLCODE — the original msg.sender and msg.value preserved. 'Run this code as if it were mine.' is the exact wiring we wanted. It loads the target’s code but executes it in your context: reads and writes hit your storage, address(this) is you, the balance is yours. And it fixes CALLCODE’s flaw — it preserves the original msg.sender and msg.value, so the borrowed code sees the same caller and value your contract was called with. It is, precisely, “run this contract’s code as if it were part of mine.”

DELEGATECALL — borrow the code, keep your context your contract storage: WRITTEN ✓ your balance & caller runs as if it were yours borrow code runs here ↩ Library CODE balances[msg.sender] += x code only, no storage same opcodes, but they read & write YOUR storage, balance, and caller the fix over CALLCODE: it also preserves the original msg.sender & value
DELEGATECALL loads the library's code but runs it in your context: the write lands in YOUR storage, the balance and address are yours, and the original msg.sender/msg.value are preserved. The library becomes, in effect, extra methods bolted onto your contract.
Formula
DELEGATECALL: code = callee 1 · context = caller 2
  1. 1 the instructions executed are the target library's bytecode
  2. 2 storage, balance, address(this), and the original msg.sender/value are the caller's
Split what a call binds: take the code from there, keep the context here. That single split is the whole idea.

→ Step 4: separate state from logic, and upgrade.

4 step Proxies & upgrades

The payoff: libraries, proxies, upgradeable contracts

Once code and context are separable, patterns fall out. Solidity library calls compile to DELEGATECALL, so shared logic lives once on-chain. More powerfully, you can split a system into a thin proxy A minimal contract that holds all the state and, on every call, DELEGATECALLs to a separate implementation contract for the logic. Because state lives in the proxy, swapping the implementation address upgrades behaviour without moving data — and the address users know never changes. that holds all the state and a separate implementation that holds the logic. Every call to the proxy is delegated to the implementation, which runs against the proxy’s storage. To upgrade, you just point the proxy at a new implementation — the data stays put and the address everyone uses never changes.

state in the proxy, logic swappable Proxy · 0xYou holds all state address never changes DELEGATECALL Implementation v1 the logic today Implementation v2 swap in — same state upgrade the logic without moving the data — how upgradeable contracts work the same idea EIP-7702 later brings to plain EOAs
A proxy holds all the state and DELEGATECALLs its logic to an implementation contract. Swap the implementation from v1 to v2 and behaviour upgrades while the state — and the address users know — stay exactly where they are. This is how upgradeable contracts work.
04 Go Deeper Where to take it from here