// Source: contracts/upgradable/InitProxy.sol pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT // File contracts/interfaces/IProxy.sol // General interface for upgradable contracts interface IProxy { error InvalidOwner(); error InvalidImplementation(); error SetupFailed(); error NotOwner(); error AlreadyInitialized(); function implementation() external view returns (address); function setup(bytes calldata setupParams) external; } // File contracts/interfaces/IInitProxy.sol // General interface for upgradable contracts interface IInitProxy is IProxy { function init( address implementationAddress, address newOwner, bytes memory params ) external; } // File contracts/upgradable/BaseProxy.sol /** * @title BaseProxy Contract * @dev This abstract contract implements a basic proxy that stores an implementation address. Fallback function * calls are delegated to the implementation. This contract is meant to be inherited by other proxy contracts. */ abstract contract BaseProxy is IProxy { // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; // keccak256('owner') bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; /** * @dev Returns the current implementation address. * @return implementation_ The address of the current implementation contract */ function implementation() public view virtual returns (address implementation_) { assembly { implementation_ := sload(_IMPLEMENTATION_SLOT) } } /** * @dev Shadows the setup function of the implementation contract so it can't be called directly via the proxy. * @param params The setup parameters for the implementation contract. */ function setup(bytes calldata params) external {} /** * @dev Returns the contract ID. It can be used as a check during upgrades. Meant to be implemented in derived contracts. * @return bytes32 The contract ID */ function contractId() internal pure virtual returns (bytes32); /** * @dev Fallback function. Delegates the call to the current implementation contract. */ fallback() external payable virtual { address implementation_ = implementation(); assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), implementation_, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev Payable fallback function. Can be overridden in derived contracts. */ receive() external payable virtual {} } // File contracts/interfaces/IContractIdentifier.sol // General interface for upgradable contracts interface IContractIdentifier { /** * @notice Returns the contract ID. It can be used as a check during upgrades. * @dev Meant to be overridden in derived contracts. * @return bytes32 The contract ID */ function contractId() external pure returns (bytes32); } // File contracts/upgradable/InitProxy.sol /** * @title InitProxy Contract * @notice A proxy contract that can be initialized to use a specified implementation and owner. Inherits from BaseProxy * and implements the IInitProxy interface. * @dev This proxy is constructed empty and then later initialized with the implementation contract address, new owner address, * and any optional setup parameters. */ contract InitProxy is BaseProxy, IInitProxy { /** * @dev Initializes the contract and sets the caller as the owner of the contract. */ constructor() { assembly { sstore(_OWNER_SLOT, caller()) } } function contractId() internal pure virtual override returns (bytes32) { return bytes32(0); } /** * @notice Initializes the proxy contract with the specified implementation, new owner, and any optional setup parameters. * @param implementationAddress The address of the implementation contract * @param newOwner The address of the new proxy owner * @param params Optional parameters to be passed to the setup function of the implementation contract * @dev This function is only callable by the owner of the proxy. If the proxy has already been initialized, it will revert. * If the contract ID of the implementation is incorrect, it will also revert. It then stores the implementation address and * new owner address in the designated storage slots and calls the setup function on the implementation (if setup params exist). */ function init( address implementationAddress, address newOwner, bytes memory params ) external { address owner; assembly { owner := sload(_OWNER_SLOT) } if (msg.sender != owner) revert NotOwner(); if (implementation() != address(0)) revert AlreadyInitialized(); bytes32 id = contractId(); // Skipping the check if contractId() is not set by an inheriting proxy contract if (id != bytes32(0) && IContractIdentifier(implementationAddress).contractId() != id) revert InvalidImplementation(); assembly { sstore(_IMPLEMENTATION_SLOT, implementationAddress) sstore(_OWNER_SLOT, newOwner) } if (params.length != 0) { (bool success, ) = implementationAddress.delegatecall( abi.encodeWithSelector(BaseProxy.setup.selector, params) ); if (!success) revert SetupFailed(); } } }