In Solidity, delegatecall is a powerful tool that lets a contract (Contract A) execute code from another contract (Contract B) while preserving its own state. This feature enables upgradable contracts, but it also introduces one of the most common vulnerabilities in Web3 development.
What is Delegatecall?
In Solidity, delegatecall is a low-level function that allows one contract to call another contract and run its code within the context of the calling contract. This mechanism is commonly used in contract upgradeability patterns, proxy contracts, and when a contract needs to delegate functionality to another contract.
Key Characteristics Context
- Context: When a contract makes a delegatecall to another contract, the context is passed from the original contract to the target contract. This means that the target contract executes with the state and storage of the calling contract.
- State Variable Access: The target contract can read and write the state variables of the calling contract, but not its own state variables.
- Return Value: The delegatecall function returns a boolean status variable indicating whether the function call reverted or not, as well as any return value from the function call.
Vulnerability
The Risk When Contract A uses delegatecall to execute code from Contract B, it effectively hands over control of its own storage and state. If Contract B is untrusted or exploitable, it can manipulate Contract A’s state directly.
Real-World Example
Let’s say we have a smart contract that uses delegatecall to interact with another contract. Here’s how an attacker could exploit this: The attacker deploys a malicious Contract B with a function that overwrites Contract A’s storage (like changing the owner). The attacker then calls Contract A’s delegatecall, passing in the malicious contract’s address. Boom! Contract A’s owner is now the attacker, who can drain funds or change crucial variables.
Conclusion: How to Protect Your Smart Contracts
1. Limit delegatecall to trusted contracts: Don’t allow arbitrary addresses in delegate calls.
2. Verify storage layouts between contracts to avoid unintended data overwrites.
3. Audit your code carefully, especially if you’re using delegatecall for upgradability!