Simple proxy
Simple implementation
pragma solidity ^0.8.0;
contract Proxy { address public implementation;
constructor(address _implementation) { implementation = _implementation; }
fallback() external payable { // Forward the call to the implementation contract (bool success, ) = implementation.delegatecall(msg.data); require(success, "Delegatecall failed"); }}
contract ImplementationV1 { uint public num; address public implementation;
function setNum(uint _num) public { num = _num; }}
Try calling the proxy contract
with the following callData
0xcd16ecbf0000000000000000000000000000000000000000000000000000000000000002
Complex implementation
Ref - https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol
Proxy.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
pragma solidity ^0.8.20;
abstract contract Proxy { function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize())
// Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data. returndatacopy(0, 0, returndatasize())
switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } }
/** * @dev This is a virtual function that should be overridden so it returns the address to which the fallback * function and {_fallback} should delegate. */ function _implementation() internal view virtual returns (address);
/** * @dev Delegates the current call to the address returned by `_implementation()`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _fallback() internal virtual { _delegate(_implementation()); }
/** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. */ fallback() external payable virtual { _fallback(); }}
Using Proxy.sol
// SPDX-License-Identifier: GPL-3.0pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/Proxy.sol";
contract StorageProxy is Proxy { uint256 public num; address public implementation;
constructor(address _implementation) { implementation = _implementation; }
function _implementation() internal view virtual override returns (address) { return implementation; }}
contract ImplementationV1 { uint256 public num; address public implementation;
function setNum(uint256 _num) public { num = _num; }}
Problems with this approach -
How do we upgrade implementation (you might say I will add a new function called upgradeImplementation
but what if there is a similar function in the implementation
contract?