// Source: contracts/upgradable/FinalProxy.sol pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT // File contracts/deploy/CreateDeploy.sol /** * @title CreateDeploy Contract * @notice This contract deploys new contracts using the `CREATE` opcode and is used as part of * the `CREATE3` deployment method. */ contract CreateDeploy { /** * @dev Deploys a new contract with the specified bytecode using the `CREATE` opcode. * @param bytecode The bytecode of the contract to be deployed */ // slither-disable-next-line locked-ether function deploy(bytes memory bytecode) external payable { assembly { if iszero(create(0, add(bytecode, 32), mload(bytecode))) { revert(0, 0) } } } } // File contracts/deploy/Create3Address.sol /** * @title Create3Address contract * @notice This contract can be used to predict the deterministic deployment address of a contract deployed with the `CREATE3` technique. */ contract Create3Address { /// @dev bytecode hash of the CreateDeploy helper contract bytes32 internal immutable createDeployBytecodeHash; constructor() { createDeployBytecodeHash = keccak256(type(CreateDeploy).creationCode); } /** * @notice Compute the deployed address that will result from the `CREATE3` method. * @param deploySalt A salt to influence the contract address * @return deployed The deterministic contract address if it was deployed */ function _create3Address(bytes32 deploySalt) internal view returns (address deployed) { address deployer = address( uint160(uint256(keccak256(abi.encodePacked(hex'ff', address(this), deploySalt, createDeployBytecodeHash)))) ); deployed = address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', deployer, hex'01'))))); } } // File contracts/interfaces/IDeploy.sol /** * @title IDeploy Interface * @notice This interface defines the errors for a contract that is responsible for deploying new contracts. */ interface IDeploy { error EmptyBytecode(); error AlreadyDeployed(); error DeployFailed(); } // File contracts/libs/ContractAddress.sol library ContractAddress { function isContract(address contractAddress) internal view returns (bool) { bytes32 existingCodeHash = contractAddress.codehash; // https://eips.ethereum.org/EIPS/eip-1052 // keccak256('') == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 return existingCodeHash != bytes32(0) && existingCodeHash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; } } // File contracts/deploy/Create3.sol /** * @title Create3 contract * @notice This contract can be used to deploy a contract with a deterministic address that depends only on * the deployer address and deployment salt, not the contract bytecode and constructor parameters. */ contract Create3 is Create3Address, IDeploy { using ContractAddress for address; /** * @notice Deploys a new contract using the `CREATE3` method. * @dev This function first deploys the CreateDeploy contract using * the `CREATE2` opcode and then utilizes the CreateDeploy to deploy the * new contract with the `CREATE` opcode. * @param bytecode The bytecode of the contract to be deployed * @param deploySalt A salt to influence the contract address * @return deployed The address of the deployed contract */ function _create3(bytes memory bytecode, bytes32 deploySalt) internal returns (address deployed) { deployed = _create3Address(deploySalt); if (bytecode.length == 0) revert EmptyBytecode(); if (deployed.isContract()) revert AlreadyDeployed(); // Deploy using create2 CreateDeploy create = new CreateDeploy{ salt: deploySalt }(); if (address(create) == address(0)) revert DeployFailed(); // Deploy using create create.deploy(bytecode); } } // 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/IFinalProxy.sol // General interface for upgradable contracts interface IFinalProxy is IProxy { function isFinal() external view returns (bool); function finalUpgrade(bytes memory bytecode, bytes calldata setupParams) external returns (address); } // 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/Proxy.sol /** * @title Proxy Contract * @notice A proxy contract that delegates calls to a designated implementation contract. Inherits from BaseProxy. * @dev The constructor takes in the address of the implementation contract, the owner address, and any optional setup * parameters for the implementation contract. */ contract Proxy is BaseProxy { /** * @notice Constructs the proxy contract with the implementation address, owner address, and optional setup parameters. * @param implementationAddress The address of the implementation contract * @param owner The owner address * @param setupParams Optional parameters to setup the implementation contract * @dev The constructor verifies that the owner address is not the zero address and that the contract ID of the implementation is valid. * It then stores the implementation address and owner address in their designated storage slots and calls the setup function on the * implementation (if setup params exist). */ constructor( address implementationAddress, address owner, bytes memory setupParams ) { if (owner == address(0)) revert InvalidOwner(); 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, owner) } if (setupParams.length != 0) { (bool success, ) = implementationAddress.delegatecall( abi.encodeWithSelector(BaseProxy.setup.selector, setupParams) ); if (!success) revert SetupFailed(); } } function contractId() internal pure virtual override returns (bytes32) { return bytes32(0); } } // File contracts/upgradable/FinalProxy.sol /** * @title FinalProxy Contract * @notice The FinalProxy contract is a proxy that can be upgraded to a final implementation * that uses less gas than regular proxy calls. It inherits from the Proxy contract and implements * the IFinalProxy interface. */ contract FinalProxy is Create3, Proxy, IFinalProxy { // bytes32(uint256(keccak256('final-implementation')) - 1); bytes32 internal constant FINAL_IMPLEMENTATION_SALT = 0x80df4dfef2d6527a47431f6f203697684e26d83f81418443821420778d4c4e8c; /** * @dev Constructs a FinalProxy contract with a given implementation address, owner, and setup parameters. * @param implementationAddress The address of the implementation contract * @param owner The owner of the proxy contract * @param setupParams Parameters to setup the implementation contract */ constructor( address implementationAddress, address owner, bytes memory setupParams ) Proxy(implementationAddress, owner, setupParams) {} /** * @dev The final implementation address takes less gas to compute than reading an address from storage. That makes FinalProxy * more efficient when making delegatecalls to the implementation (assuming it is the final implementation). * @return implementation_ The address of the final implementation if it exists, otherwise the current implementation */ function implementation() public view override(BaseProxy, IProxy) returns (address implementation_) { implementation_ = _finalImplementation(); if (implementation_ == address(0)) { implementation_ = super.implementation(); } } /** * @dev Checks if the final implementation has been deployed. * @return bool True if the final implementation exists, false otherwise */ function isFinal() external view returns (bool) { return _finalImplementation() != address(0); } /** * @dev Computes the final implementation address. * @return implementation_ The address of the final implementation, or the zero address if the final implementation * has not yet been deployed */ function _finalImplementation() internal view virtual returns (address implementation_) { // Computing the address is cheaper than using storage implementation_ = _create3Address(FINAL_IMPLEMENTATION_SALT); if (implementation_.code.length == 0) implementation_ = address(0); } /** * @dev Upgrades the proxy to a final implementation. * @param bytecode The bytecode of the final implementation contract * @param setupParams The parameters to setup the final implementation contract * @return finalImplementation_ The address of the final implementation contract */ function finalUpgrade(bytes memory bytecode, bytes calldata setupParams) external returns (address finalImplementation_) { address owner; assembly { owner := sload(_OWNER_SLOT) } if (msg.sender != owner) revert NotOwner(); bytes32 id = contractId(); finalImplementation_ = _create3(bytecode, FINAL_IMPLEMENTATION_SALT); // Skipping the check if contractId() is not set by an inheriting proxy contract if (id != bytes32(0) && IContractIdentifier(finalImplementation_).contractId() != id) revert InvalidImplementation(); if (setupParams.length != 0) { (bool success, ) = finalImplementation_.delegatecall( abi.encodeWithSelector(BaseProxy.setup.selector, setupParams) ); if (!success) revert SetupFailed(); } } }