Skip to content

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.0
pragma 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?