Consider a proxy contract that delegates calls to an implementation contract. What will be the output of the following Solidity code snippet when calling getValue() through the proxy?
pragma solidity ^0.8.0; contract Implementation { uint public value; function setValue(uint _value) public { value = _value; } function getValue() public view returns (uint) { return value; } } contract Proxy { uint public value; address public implementation; constructor(address _impl) { implementation = _impl; } fallback() external payable { (bool success, ) = implementation.delegatecall(msg.data); require(success); } }
Remember that delegatecall runs code in the context of the calling contract's storage.
The proxy uses delegatecall, so the storage accessed is the proxy's own. Since value in the proxy is never set, it defaults to 0. Thus, getValue() returns 0.
Why is it important to carefully manage storage layout in upgradeable proxy contracts?
Think about how delegatecall works with storage.
During delegatecall, the implementation contract's code runs in the proxy's context, so both share the same storage slots. If storage layouts differ, variables can overwrite each other causing bugs.
The following proxy upgrade code fails to preserve state after upgrading the implementation. What is the main cause?
pragma solidity ^0.8.0; contract ImplementationV1 { uint public count; function increment() public { count += 1; } } contract ImplementationV2 { uint public count; uint public newVar; function increment() public { count += 1; } function setNewVar(uint _val) public { newVar = _val; } } contract Proxy { uint public count; address public implementation; constructor(address _impl) { implementation = _impl; } fallback() external payable { (bool success, ) = implementation.delegatecall(msg.data); require(success); } function upgrade(address _newImpl) public { implementation = _newImpl; } }
Check storage variables order and count in proxy and implementations.
Adding newVar in ImplementationV2 shifts storage layout, causing the proxy's storage to mismatch and lose state. Storage layout must remain compatible across upgrades.
Which option correctly implements a fallback function in Solidity 0.8+ for a proxy contract that forwards calls using delegatecall?
Recall the correct syntax for fallback functions in Solidity 0.8+.
In Solidity 0.8+, fallback functions are declared without the function keyword and can be marked external payable. Option A uses correct syntax and delegatecall.
A proxy contract has 2 storage variables: address implementation and uint256 owner. The implementation contract has 3 storage variables: uint256 count, bool active, and uint256 data. How many storage slots does the proxy contract use after upgrade if the implementation is called via delegatecall?
Remember that delegatecall uses the proxy's storage layout, so all variables must fit in proxy storage.
The proxy has 2 variables, and the implementation has 3 variables. Since delegatecall runs implementation code in proxy storage, the proxy must reserve storage for all 5 variables to avoid collisions.