The delegatecall opcode is a cornerstone of upgradeable smart contract patterns and proxy architectures. Unlike a standard external call (call), which executes code in the context of the called contract, delegatecall executes the logic of the target contract as if it were running inside the calling contract. This means the executed code reads from and writes to the storage of the caller, not the callee. This behavior is critical for creating proxy contracts that can delegate their logic to a separate, updatable implementation contract, enabling system upgrades without migrating state.
Delegatecall
What is Delegatecall?
Delegatecall is a low-level function call in the Ethereum Virtual Machine (EVM) that allows a smart contract to execute code from another contract while preserving its own storage, `msg.sender`, and `msg.value` context.
Understanding the preserved context is essential for security. When Contract A performs a delegatecall to Contract B, the code from B runs, but msg.sender and msg.value remain the original values from the transaction that initiated the call to A. This allows for complex, modular designs where a single logic contract can serve multiple proxy instances, each with its own independent storage. However, this powerful feature introduces significant risks, most famously demonstrated by the 2016 DAO hack, where a malicious contract exploited a delegatecall to drain funds.
The primary use case for delegatecall is in proxy patterns like the EIP-1967 transparent proxy or the UUPS (Universal Upgradeable Proxy Standard). In these systems, a lightweight proxy contract stores all state variables and uses delegatecall to forward all logic execution to a separate implementation contract. To upgrade the system, the proxy's pointer to the implementation address is changed, instantly redirecting all future calls to new logic while preserving the accumulated user data and balances in the proxy's immutable storage.
Developers must exercise extreme caution with delegatecall. Because the called contract's code operates on the caller's storage, storage layout compatibility between the proxy and implementation is paramount. An upgrade that changes the order or types of state variables can catastrophically corrupt storage. Furthermore, the selfdestruct opcode within a delegate-called contract will destroy the calling (proxy) contract, not the logic contract. Audits and formal verification are strongly recommended for any system employing this low-level operation.
In practice, delegatecall is invoked in Solidity using the delegatecall() method on an address type, which returns a boolean success flag and bytes data. It is a state-changing call that consumes gas. A related but distinct opcode is staticcall, which is identical to delegatecall but prohibits any state modifications, making it suitable for view-like functions. Mastering the nuances of delegatecall is fundamental for architects building sophisticated, maintainable decentralized applications on Ethereum and compatible EVM chains.
How Delegatecall Works
A deep dive into the low-level EVM opcode that enables contract composability and upgradeable proxy patterns by executing code in the context of the caller.
Delegatecall is a low-level opcode in the Ethereum Virtual Machine (EVM) that allows a smart contract to execute code from another contract while preserving its own storage, msg.sender, and msg.value. Unlike a standard message call (call), which runs the target contract's code in its own context, delegatecall runs the target's code as if it were part of the calling contract. This means the executed code can read from and write to the storage of the caller, not its own, making it a foundational mechanism for creating modular and upgradeable contract systems like proxy patterns.
The primary technical distinction lies in the execution context. When Contract A performs a delegatecall to Contract B, the code from B is executed, but the persistent storage slots referenced (SLOAD, SSTORE) point to Contract A's storage. Furthermore, the identity of the original transaction sender (msg.sender) and the amount of Ether sent (msg.value) remain those from the initial call to A. This behavior is critical for proxy contracts, where a lightweight proxy holds the storage and delegates all logic execution to a separate, upgradeable implementation contract, enabling seamless upgrades without migrating state.
A major security consideration is that delegatecall introduces significant risks if not used carefully. Since the target contract's code operates on the caller's storage, any mismatch in storage layout between the caller and the target can lead to catastrophic state corruption. For example, if the implementation contract is upgraded and its variables are reordered, the proxy's storage will be interpreted incorrectly. Developers must ensure strict alignment in storage variable declarations and use established patterns like the EIP-1967 storage slot standard to mitigate these risks.
Beyond upgradeability, delegatecall enables powerful library patterns. Stateless libraries, which contain pure functions or functions that operate on the caller's storage, can be deployed once and reused by many contracts, reducing deployment gas costs. However, because delegatecall does not transfer Ether, libraries cannot have payable functions in the traditional sense; any Ether sent must be handled by the calling contract before or after the delegatecall.
In practice, delegatecall is invoked in Solidity using the delegatecall() method on an address, returning a boolean success flag and bytes data. Its correct use is a hallmark of advanced Ethereum development, enabling complex systems like decentralized autonomous organizations (DAOs) with modular governance, and metatransaction relayers that allow users to submit gasless transactions. Understanding its context-preserving nature is essential for both building secure systems and auditing smart contract code.
Key Features of Delegatecall
Delegatecall is a low-level EVM opcode that allows a contract to execute code from another contract while preserving its own storage, balance, and msg.sender context.
Preservation of Execution Context
The defining feature of delegatecall is that it executes the code of the target contract within the context of the calling contract. This means:
- Storage,
msg.sender, andmsg.valueremain those of the original caller. - The target contract's code acts as if it were part of the caller, enabling upgradeable contract patterns and library usage.
- It is the mechanism behind proxy contracts, where logic is separated from storage.
Comparison with Call and Staticcall
Delegatecall is one of three primary call operations in the EVM, each with distinct state-change permissions:
call: Executes code in another contract's context. Can modify that contract's storage and transfer value.staticcall: A read-onlycall. Guarantees no state modification (introduced in EIP-214).delegatecall: Executes another contract's code but writes to the caller's storage. Cannot transfer native value (msg.valueis preserved but cannot be sent).
Security Implications & Risks
Using delegatecall introduces critical security considerations:
- Storage Layout Collisions: The calling and target contracts must have identical storage variable layouts; mismatches can corrupt storage.
- Self-Destruct Vulnerability: If the target contract contains a
selfdestructopcode, it will destroy the caller contract, not itself. - Delegatecall Proxy Pattern: This pattern, while enabling upgrades, concentrates trust in the logic contract and requires rigorous initialization guards.
Primary Use Case: Upgradeable Proxies
The most significant application of delegatecall is in upgradeable proxy contracts. The system comprises:
- Proxy Contract: Holds all storage and user funds. Uses
delegatecallto execute logic. - Logic/Implementation Contract: Contains the business logic. Can be replaced without migrating storage.
- Proxy Admin: Controls the upgrade authorization. This pattern is foundational to major protocols like OpenZeppelin's UUPS and Transparent Proxy standards.
Library Linking Pattern
Delegatecall enables the creation of library contracts that provide reusable functions. Unlike regular internal libraries, these are deployed once and called by many contracts.
- Gas Efficiency: Code is deployed once, reducing deployment costs for users.
- Example: The canonical
LibStringorSafeMath(pre-Solidity 0.8) libraries usedelegatecallto perform operations like string manipulation or safe arithmetic on the caller's data. - The library contract must be deliberately written for
delegatecall, typically usinglibrarykeyword andinternalfunctions.
Technical Execution Flow
When delegatecall is invoked, the EVM performs a specific sequence:
- Context Load: The current
msg.sender,msg.value, and storage pointers are preserved. - Code Execution: Jumps to the code at the target contract address and executes it.
- State Modification: All storage writes (
SSTORE) update the caller's storage slot, not the target's. - Return: Control returns to the caller with any return data from the executed function. The
address(this)in the executed code refers to the caller.
Delegatecall Code Example
A practical demonstration of the delegatecall opcode, which allows a smart contract to execute code from another contract while preserving its own storage, msg.sender, and msg.value.
The delegatecall is a low-level Solidity function that enables a proxy pattern or upgradeable contract design. Unlike a standard call, delegatecall executes the code of the target contract within the context of the calling contract. This means the target's logic runs, but it reads from and writes to the storage layout of the caller. Crucially, the msg.sender and msg.value are also preserved, making the call appear to originate directly from the original user. This is a foundational mechanism for creating modular and upgradeable systems on Ethereum.
Example function signature: (bool success, bytes memory data) = _target.delegatecall(_calldata);
Consider a simple example where a Proxy contract delegates logic execution to a Logic contract. The Proxy's storage holds the owner address and a value. When a user calls Proxy.setValue(42), the Proxy uses delegatecall to the Logic contract's setValue function. The Logic code executes, but it modifies the value variable stored in the Proxy's storage slot, not its own. This separation allows the Logic contract's code to be replaced later (by updating the _target address in the Proxy) without migrating the application's state, enabling contract upgradability.
Key considerations when using delegatecall include storage layout compatibility and security. The calling and target contracts must have identically ordered storage variables; a mismatch can cause catastrophic data corruption. Furthermore, because the target contract's code is executed with the caller's privileges, a malicious or compromised target can perform any action the Proxy is authorized to do, making it a critical trust assumption. This opcode is central to patterns like EIP-1967 transparent proxies and many decentralized autonomous organization (DAO) frameworks.
Ecosystem Usage & Examples
The delegatecall opcode is a foundational mechanism for creating modular, upgradeable smart contracts. Its unique property of executing code from another contract within the context of the calling contract enables several key architectural patterns.
The Parity Wallet Hack (2017)
A critical vulnerability demonstrated the dangers of delegatecall. The Parity multi-sig wallet library contract was accidentally self-destructed by a user. Because hundreds of user wallets used delegatecall to this library, they all became permanently unusable, freezing over 500,000 ETH. This incident highlighted the state context risk: a delegatecall to a compromised or self-destructed contract can brick all dependent proxies.
Security Considerations & Best Practices
Using delegatecall requires strict security practices:
- Storage Layout: The calling and target contract must have identical storage layouts to prevent state corruption.
- Trusted Targets: Only delegatecall to immutable, audited contracts.
msg.senderandmsg.value: Understand thatmsg.senderandmsg.valueare preserved from the original call, which impacts access control and native token handling.- Testing: Rigorously test upgrade paths and storage collisions.
Security Considerations & Risks
The delegatecall opcode is a powerful but dangerous low-level function in the EVM that allows a contract to execute code from another contract while preserving its own storage, msg.sender, and msg.value. Its misuse is a root cause of major vulnerabilities.
Storage Collision & State Corruption
A delegatecall executes logic in the context of the calling contract's storage. If the storage layout (variable order and types) between the calling contract and the target contract do not match exactly, it can lead to catastrophic storage collisions. This can corrupt critical state variables, such as owner addresses or token balances, because the delegatecalled function will read/write to the wrong storage slots.
- Example: A library expecting its first variable at slot 0 will instead write to the calling contract's first variable, which could be the contract owner.
The Parity Wallet Multisig Hack
This infamous 2017 exploit, resulting in a loss of over $30 million, was caused by a vulnerable delegatecall in a library contract. An attacker was able to call a public initialization function via delegatecall, making themselves the owner of the library's logic contract. Because multiple wallets delegatecalled to this library, the attacker became the owner of all dependent wallets, allowing them to drain funds. This highlights the risk of delegatecall to uninitialized or improperly permissioned logic contracts.
msg.sender Preservation & Access Control Bypass
Unlike a regular call, delegatecall preserves the original msg.sender and msg.value. This can break access control if not carefully considered.
- A contract might use
tx.originormsg.senderchecks in a function that isdelegatecalled. Since themsg.senderis the original EOA or caller, not the intermediary contract, it can bypass intended permissions. - Selfdestruct Risk: If the delegatecalled code contains a
selfdestructopcode, it will destroy the calling contract, not the target library, as the execution context is the caller's.
Mitigation & Best Practices
To safely use delegatecall, developers should enforce strict conventions and audits.
- Storage Layout Management: Use inherited storage contracts or structured storage (EIP-1967) to guarantee alignment between proxy and logic contracts.
- Validate Target Address: Only
delegatecallto trusted, immutable contracts. Avoid letting users arbitrarily specify the target. - Use Established Patterns: Rely on audited, standard upgradeability frameworks like OpenZeppelin's Transparent or UUPS proxies, which handle these risks.
- Explicit Visibility & Initializers: Mark initialization functions and protect them with
initializermodifiers to prevent re-execution.
Delegatecall vs. Call vs. Staticcall
A technical comparison of the three primary EVM opcodes for executing external contract code, detailing their execution context and state-modifying capabilities.
| Feature / Context | Delegatecall | Call | Staticcall |
|---|---|---|---|
Execution Context (msg.sender) | Preserves the original caller | Sets caller to the calling contract | Sets caller to the calling contract |
Execution Context (this/address(this)) | Uses the caller's storage & balance | Uses the target contract's storage & balance | Uses the target contract's storage & balance |
Can Modify State | |||
Can Transfer Native Value (ETH) | |||
Primary Use Case | Upgradeable proxies, library patterns | Standard external interactions, value transfers | View/pure function calls, security checks |
Gas Cost (Relative) | Higher (preserves full context) | Standard | Lower (state changes reverted) |
Opcode Number (hex) | 0xf4 | 0xf1 | 0xfa |
Revert on State Mutation |
Common Misconceptions
delegatecall is a low-level EVM opcode that is fundamental to proxy patterns and upgradeable contracts, but its behavior is often misunderstood.
It Does Not Transfer Ether
A delegatecall does not forward native value (ETH) from the caller. The msg.value in the context of the delegated code is the value sent with the original transaction to the proxy, not a value passed with the call itself. This is a critical difference from a standard call.
- Example: If a user sends 1 ETH to a proxy contract that
delegatecalls to a logic contract, the logic contract's code executes as if it were the proxy, but themsg.valueis 1 ETH, not a separate transfer.
Storage Context is the Caller's
The most critical aspect of delegatecall is that the executed code modifies the storage of the calling contract, not its own. The logic contract's code runs in the context of the proxy's state.
- Misconception: "The logic contract's storage is used."
- Reality: The logic contract's storage layout must align perfectly with the proxy's, or catastrophic storage collisions will occur, as the opcode uses the caller's storage slots.
`msg.sender` and `msg.value` Are Preserved
The execution context (msg.sender, msg.value, block.* variables) is preserved from the original call to the proxy. The logic contract sees the original end-user (or caller) as msg.sender, not the proxy contract address.
This is why access control in upgradeable systems is often implemented in the proxy or via a dedicated function in the logic contract that checks permissions based on this preserved msg.sender.
It's Not Just for Upgrades
While delegatecall is the backbone of proxy upgrade patterns (like UUPS and Transparent Proxies), it has other uses. It enables library contracts, where reusable code is deployed once and its logic is executed in the context of many different calling contracts.
- Key Difference: A library uses
delegatecallto enable code reuse without copying it into every contract, while a proxy uses it to enable logic upgrades for a single contract instance.
Gas is Paid by the Original Caller
All gas consumed by the logic executed via delegatecall is charged to the original external transaction's gas limit and payer. The proxy contract does not need its own ETH balance to pay for execution.
- Implication: Gas estimation for transactions involving proxies must account for the full execution path of the logic contract, as the gas is consumed from the top-level call.
Technical Deep Dive
An exploration of the delegatecall opcode, a fundamental and powerful mechanism in Ethereum smart contract development that enables code reuse and upgradeable proxy patterns.
Delegatecall is a low-level opcode in the Ethereum Virtual Machine (EVM) that allows a smart contract to execute code from another contract's logic while preserving its own storage, msg.sender, and msg.value. It works by loading the bytecode of the target contract into the context of the calling contract. Unlike a standard call, which runs the target's code in its own context, delegatecall ensures the executed code acts as if it were part of the caller, modifying the caller's storage and using the caller's address for msg.sender. This is the core mechanism behind proxy patterns and upgradeable contracts.
Key Mechanism:
- Context Preservation: The caller's storage, balance, and address (
this) are used. - Logic Separation: The target contract contains only logic, while the proxy holds the state.
- Execution Flow:
delegatecall(target, data)loadstarget's code and executes it with the caller's context.
Frequently Asked Questions (FAQ)
A deep dive into the low-level EVM opcode that enables proxy patterns and upgradeable contracts, answering common developer questions.
Delegatecall is a low-level EVM opcode that allows a contract to execute code from another contract while preserving its own storage, msg.sender, and msg.value. Unlike a regular call, which runs the target contract's code in its own context, delegatecall runs the target's code within the context of the calling contract. This means the logic is borrowed, but all state changes (storage writes) occur in the storage of the caller. It is the foundational mechanism behind proxy patterns and upgradeable smart contracts, where a proxy contract delegates its logic execution to a separate, updatable implementation contract.
Get In Touch
today.
Our experts will offer a free quote and a 30min call to discuss your project.