// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IBeacon, IERC1967} from "./interfaces/IBeacon.sol";
import {Ownable} from "../external/Ownable.sol";
import {ERC1967Utils} from "./libraries/ERC1967Utils.sol";
/// @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
/// implementation contract, which is where they will delegate all function calls.
/// An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
contract Beacon is IBeacon, Ownable {
// =========================
// constructor
// =========================
/// @notice Sets the address of the initial implementation,
/// and the deployer account as the owner who can upgrade the beacon.
/// @param implementation_ address of the initial implementation
constructor(address implementation_) {
_transferOwnership(msg.sender);
ERC1967Utils.setImplementation(implementation_);
}
// =========================
// getters
// =========================
/// @inheritdoc IBeacon
function implementation() external view returns (address) {
return ERC1967Utils.getImplementation();
}
// =========================
// setters
// =========================
/// @notice Upgrades the beacon to a new implementation.
/// Emits an {Upgraded} event.
/// Requirements:
/// - msg.sender must be the owner of the contract.
/// - `newImplementation` must be a contract.
function upgradeTo(address newImplementation) external onlyOwner {
ERC1967Utils.setImplementation(newImplementation);
emit IERC1967.Upgraded(newImplementation);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IBeacon} from "./interfaces/IBeacon.sol";
import {IFactory} from "../interfaces/IFactory.sol";
/// @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
///
/// The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
/// conflict with the storage layout of the implementation behind the proxy.
contract BeaconProxy {
// =========================
// immutable storage
// =========================
/// @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
/// This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)).
bytes32 internal constant _BEACON_SLOT =
0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
// =========================
// errors
// =========================
/// @notice Throws when beacon address does not have a code.
error BeaconProxy_NewBeaconIsNotContract();
// =========================
// constructor
// =========================
/// @notice Initializes the proxy with `beacon`.
/// @dev Stores a new beacon in the EIP1967 beacon slot.
constructor() {
address beacon = IFactory(msg.sender).beacon();
assembly ("memory-safe") {
sstore(_BEACON_SLOT, beacon)
}
}
// =========================
// fallbacks
// =========================
/// @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 {
_fallback();
}
/// @dev Fallback function that delegates calls to the address returned by `_implementation()`.
/// Will run if call data is empty.
receive() external payable {
_fallback();
}
// =========================
// private functions
// =========================
/// @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() private {
address beaconAddress;
assembly ("memory-safe") {
beaconAddress := sload(_BEACON_SLOT)
}
address implementation = IBeacon(beaconAddress).implementation();
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())
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
library ERC1967Utils {
// =========================
// immutable storage
// =========================
/// @dev Storage slot with the address of the current implementation.
/// This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
bytes32 internal constant IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// =========================
// events
// =========================
/// @dev Emitted when the implementation is upgraded.
event Upgraded(address indexed implementation);
// =========================
// errors
// =========================
/// @dev The `implementation` of the proxy is invalid.
error ERC1967_InvalidImplementation(address implementation);
/// @dev An upgrade function sees `msg.value > 0` that may be lost.
error ERC1967_NonPayable();
/// @dev A call to an address target failed. The target may have reverted.
error ERC1967_FailedInnerCall();
// =========================
// main functions
// =========================
/// @dev Returns the current implementation address.
function getImplementation()
internal
view
returns (address implementation)
{
assembly ("memory-safe") {
implementation := sload(IMPLEMENTATION_SLOT)
}
}
/// @dev Stores a new address in the EIP1967 implementation slot.
function setImplementation(address newImplementation) internal {
assembly ("memory-safe") {
if iszero(extcodesize(newImplementation)) {
// "ERC1967_InvalidImplementation(address)" selector
mstore(0, 0x4a4a0aa2)
mstore(32, newImplementation)
revert(28, 36)
}
sstore(IMPLEMENTATION_SLOT, newImplementation)
}
}
/// @dev Performs implementation upgrade with additional setup call if data is nonempty.
/// This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
/// to avoid stuck value in the contract.
///
/// Emits an {IERC1967-Upgraded} event.
function upgradeToAndCall(
address newImplementation,
bytes memory data
) internal {
setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
(bool success, bytes memory returndata) = newImplementation
.delegatecall(data);
if (!success) {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
revert(add(32, returndata), mload(returndata))
}
} else {
revert ERC1967_FailedInnerCall();
}
}
} else {
_checkNonPayable();
}
}
// =========================
// private functions
// =========================
/// @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
/// if an upgrade doesn't perform an initialization call.
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967_NonPayable();
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {Initializable} from "./proxy/Initializable.sol";
import {UUPSUpgradeable} from "./proxy/UUPSUpgradeable.sol";
import {BeaconProxy} from "./proxy/BeaconProxy.sol";
import {Beacon} from "./proxy/Beacon.sol";
import {Ownable2Step} from "./external/Ownable2Step.sol";
import {IFactory} from "./interfaces/IFactory.sol";
/// @title Factory
/// @dev contract to create proxy instances, calls from which will pass through the beacon
contract Factory is UUPSUpgradeable, Initializable, Ownable2Step, IFactory {
// =========================
// immutable storage
// =========================
bytes32 private immutable _beaconProxyInitCodeHash;
// =========================
// storage
// =========================
Beacon private _beacon;
// =========================
// constructor
// =========================
constructor() {
bytes memory beaconProxyInitCode = type(BeaconProxy).creationCode;
bytes32 hash;
assembly ("memory-safe") {
hash := keccak256(
add(beaconProxyInitCode, 32),
mload(beaconProxyInitCode)
)
}
_beaconProxyInitCodeHash = hash;
_disableInitializers();
}
/// @inheritdoc IFactory
function initialize(address logic, address newOwner) external initializer {
_transferOwnership(newOwner);
_beacon = new Beacon(logic);
emit IFactory.LogicChanged(logic);
}
// =========================
// getters
// =========================
/// @inheritdoc IFactory
function getVersion() external view returns (uint8) {
return _getInitializedVersion();
}
/// @inheritdoc IFactory
function beacon() external view returns (address beaconAddress) {
beaconAddress = address(_beacon);
}
/// @inheritdoc IFactory
function beaconProxyInitCodeHash() external view returns (bytes32) {
return _beaconProxyInitCodeHash;
}
/// @inheritdoc IFactory
function implementation()
external
view
returns (address implementationAddress)
{
implementationAddress = _beacon.implementation();
}
/// @inheritdoc IFactory
function getProxyAddress(
uint256 userId
) external view returns (address proxyAddress) {
bytes32 salt;
assembly ("memory-safe") {
// compute the salt
mstore(0, userId)
salt := keccak256(0, 32)
// rewrite free memory pointer -> future allocation will be from beginning of the memory
mstore(64, 128)
}
// compute the address of the proxy
bytes32 _proxyAddress = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
_beaconProxyInitCodeHash
)
);
assembly ("memory-safe") {
// casting bytes32 value to address size (20 bytes)
proxyAddress := _proxyAddress
}
}
// =========================
// main functions
// =========================
/// @inheritdoc IFactory
function deploy(uint256 userId) external {
bytes memory beaconProxyInitCode = type(BeaconProxy).creationCode;
address proxy;
assembly ("memory-safe") {
// compute the salt
mstore(0, userId)
let salt := keccak256(0, 32)
proxy := create2(
0,
add(beaconProxyInitCode, 32),
mload(beaconProxyInitCode),
salt
)
if iszero(extcodesize(proxy)) {
// "Factory_DeployFailed()" selector
mstore(0, 0xd01b506e)
revert(28, 4)
}
}
emit IFactory.ProxyCreated(proxy, userId);
}
// =========================
// update functions
// =========================
/// @inheritdoc IFactory
function updateLogic(address _newLogic) external onlyOwner {
_beacon.upgradeTo(_newLogic);
emit IFactory.LogicChanged(_newLogic);
}
// =========================
// internal functions
// =========================
/// @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
/// {upgradeTo} and {upgradeToAndCall}.
///
/// Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
///
/// ```solidity
/// function _authorizeUpgrade(address) internal override onlyOwner {}
/// ```
function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC1967} from "./IERC1967.sol";
/// @dev This is the interface that {BeaconProxy} expects of its beacon.
interface IBeacon is IERC1967 {
// =========================
// getters
// =========================
/// @notice Must return an address that can be used as a delegate call target.
/// {BeaconProxy} will check that this address is a contract.
function implementation() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
/// proxy whose upgrades are fully controlled by the current implementation.
interface IERC1822Proxiable {
/// @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
/// address.
///
/// IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
/// bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
/// function revert if invoked through a proxy.
function proxiableUUID() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
interface IERC1967 {
/// @dev Emitted when the implementation is upgraded.
event Upgraded(address indexed implementation);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title IFactory - Factory interface
interface IFactory {
// =========================
// events
// =========================
/// @notice Emits when a new proxy is created
event ProxyCreated(address indexed newProxy, uint256 userId);
/// @notice Emits when the new logic is set
event LogicChanged(address newLogic);
// =========================
// errors
// =========================
/// @notice Throws when deploy proxy failed
error Factory_DeployFailed();
// =========================
// constructor
// =========================
/// @notice Deploys the `Beacon` contract and sets the first logic
/// @param logic address of a new logic implementation
/// @dev Reverts if a `_logic` is not a contract
function initialize(address logic, address newOwner) external;
// =========================
// getters
// =========================
/// @notice Returns the current proxy version
function getVersion() external view returns (uint8);
/// @notice Returns the address of the `Beacon` contract
function beacon() external view returns (address beaconAddress);
/// @notice Returns the beaconProxy's init code hash
function beaconProxyInitCodeHash() external view returns (bytes32);
/// @notice Returns the address of the current implementation
function implementation()
external
view
returns (address implementationAddress);
/// @notice Returns the address of the current implementation
function getProxyAddress(
uint256 userId
) external view returns (address proxyAddress);
// =========================
// main functions
// =========================
/// @notice Creates new proxy contract
/// @dev Reverts if `salt` already used
function deploy(uint256 userId) external;
// =========================
// update functions
// =========================
/// @notice Update logic contract for delegatecalls
/// @param _newLogic address of a new logic implementation
/// @dev Reverts if a `_newLogic` is not a contract
function updateLogic(address _newLogic) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title IOwnable - Ownable Interface
interface IOwnable {
// =========================
// events
// =========================
/// @notice Emits when ownership of the contract is transferred from `previousOwner`
/// to `newOwner`.
/// @param previousOwner The address of the previous owner.
/// @param newOwner The address of the new owner.
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
// =========================
// errors
// =========================
/// @notice Throws when the caller is not authorized to perform an operation.
/// @param sender The address of the sender trying to access a restricted function.
error Ownable_SenderIsNotOwner(address sender);
// =========================
// getters
// =========================
/// @notice Returns the address of the current owner.
/// @return The address of the current owner.
function owner() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "./IOwnable.sol";
/// @title IOwnable2Step - Ownable2Step Interface
interface IOwnable2Step is IOwnable {
// =========================
// events
// =========================
/// @notice Emits when the account ownership transfer to `newOwner` procedure begins.
/// @param previousOwner The address of the previous owner.
/// @param newOwner The address of the new owner.
event OwnershipTransferStarted(
address indexed previousOwner,
address indexed newOwner
);
// =========================
// errors
// =========================
/// @notice Throws when the new caller is not a pendingOwner.
error Ownable_CallerIsNotTheNewOwner(address caller);
/// @notice Throws when the new owner is not a valid owner account.
error Ownable_NewOwnerCannotBeAddressZero();
// =========================
// getters
// =========================
/// @notice Returns the address of the pending owner.
/// @return The address of the pending owner.
function pendingOwner() external view returns (address);
// =========================
// main functions
// =========================
/// @notice Starts the ownership transfer of the contract to a new account.
/// @dev Replaces the pending transfer if there is one.
/// Can only be called by the current owner.
function transferOwnership(address newOwner) external;
/// @notice The new owner accepts the ownership transfer.
function acceptOwnership() external;
/// @notice Leaves the contract without an owner. It will not be possible to call
/// `onlyOwner` functions anymore.
/// @dev Can only be called by the current owner.
function renounceOwnership() external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/// @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
/// behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
/// external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
/// function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
///
/// The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
/// reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
/// case an upgrade adds a module that needs to be initialized.
///
/// For example:
///
/// [.hljs-theme-light.nopadding]
/// ```solidity
/// contract MyToken is ERC20Upgradeable {
/// function initialize() initializer public {
/// __ERC20_init("MyToken", "MTK");
/// }
/// }
///
/// contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
/// function initializeV2() reinitializer(2) public {
/// __ERC20Permit_init("MyToken");
/// }
/// }
/// ```
///
/// TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
/// possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
///
/// CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
/// that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
///
/// [CAUTION]
/// ====
/// Avoid leaving a contract uninitialized.
///
/// An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
/// contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
/// the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
///
/// [.hljs-theme-light.nopadding]
/// ```
/// /// @custom:oz-upgrades-unsafe-allow constructor
/// constructor() {
/// _disableInitializers();
/// }
/// ```
/// ====
abstract contract Initializable {
// =========================
// storage
// =========================
/// @dev Indicates that the contract has been initialized.
uint8 private _initialized;
/// @dev Indicates that the contract is in the process of being initialized.
bool private _initializing;
// =========================
// events
// =========================
/// @dev Triggered when the contract has been initialized or reinitialized.
event Initialized(uint8 version);
// =========================
// errors
// =========================
/// @dev The contract is already initialized.
error InvalidInitialization();
/// @dev The contract is not initializing.
error NotInitializing();
// =========================
// modifiers
// =========================
/// @dev A modifier that defines a protected initializer function that can be invoked at most once.
///
/// Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
/// constructor.
///
/// Emits an {Initialized} event.
modifier initializer() {
bool isTopLevelCall = !_initializing;
uint64 initialized = _initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/// @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
/// contract hasn't been initialized to a greater version before.
///
/// A reinitializer may be used after the original initialization step. This is essential to configure modules that
/// are added through upgrades and that require initialization.
///
/// When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
/// cannot be nested. If one is invoked in the context of another, execution will revert.
///
/// Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
/// a contract, executing them in the right order is up to the developer or operator.
///
/// WARNING: setting the version to 255 will prevent any future reinitialization.
///
/// Emits an {Initialized} event.
modifier reinitializer(uint8 version) {
if (_initializing || _initialized >= version) {
revert InvalidInitialization();
}
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
// =========================
// internal functions
// =========================
/// @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
/// Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
/// to any version. It is recommended to use this to lock implementation contracts that are designed to be called
/// through proxies.
///
/// Emits an {Initialized} event the first time it is successfully executed.
function _disableInitializers() internal virtual {
if (_initializing) {
revert InvalidInitialization();
}
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/// @dev Returns the highest version that has been initialized. See {reinitializer}.
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IOwnable} from "./IOwnable.sol";
/// @title Ownable
/// @dev Contract module which provides a basic access control mechanism, where
/// there is an account (an owner) that can be granted exclusive access to
/// specific functions.
///
/// This module is used through inheritance. It will make available the modifier
/// `onlyOwner`, which can be applied to your functions to restrict their use to
/// the owner.
abstract contract Ownable is IOwnable {
// =========================
// storage
// =========================
/// @dev Private variable to store the owner's address.
address internal _owner;
// =========================
// modifiers
// =========================
/// @dev Throws if called by any account other than the owner.
modifier onlyOwner() {
_checkOwner();
_;
}
// =========================
// getters
// =========================
/// @inheritdoc IOwnable
function owner() external view returns (address) {
return _owner;
}
// =========================
// internal functions
// =========================
/// @dev Internal function to verify if the caller is the owner of the contract.
/// Errors:
/// - Thrown `Ownable_SenderIsNotOwner` if the caller is not the owner.
function _checkOwner() internal view {
if (_owner != msg.sender) {
revert Ownable_SenderIsNotOwner(msg.sender);
}
}
/// @dev Transfers ownership of the contract to a new account (`newOwner`).
/// @dev Emits an {OwnershipTransferred} event.
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit IOwnable.OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IOwnable2Step} from "./IOwnable2Step.sol";
import {Ownable} from "./Ownable.sol";
/// @title Ownable2Step
/// @dev Contract module which provides access control mechanism, where
/// there is an account (an owner) that can be granted exclusive access to
/// specific functions.
///
/// By default, the owner account will be the one that sets during deployment. This
/// can later be changed with {transferOwnership} and {acceptOwnership}.
///
/// This module is used through inheritance. It will make available the modifier
/// `onlyOwner`, which can be applied to your functions to restrict their use to
/// the owner.
abstract contract Ownable2Step is Ownable, IOwnable2Step {
// =========================
// storage
// =========================
/// @dev Private variable to store the pendingOwner's address.
address private _pendingOwner;
// =========================
// getters
// =========================
/// @inheritdoc IOwnable2Step
function pendingOwner() external view returns (address) {
return _pendingOwner;
}
// =========================
// main functions
// =========================
/// @inheritdoc IOwnable2Step
function transferOwnership(address newOwner) external onlyOwner {
_checkIsNotAddressZero(newOwner);
_pendingOwner = newOwner;
emit IOwnable2Step.OwnershipTransferStarted(_owner, newOwner);
}
/// @inheritdoc IOwnable2Step
function acceptOwnership() external {
if (msg.sender != _pendingOwner) {
revert Ownable_CallerIsNotTheNewOwner(msg.sender);
}
_transferOwnership(msg.sender);
}
/// @inheritdoc IOwnable2Step
function renounceOwnership() external onlyOwner {
_transferOwnership(address(0));
}
// =========================
// internal functions
// =========================
/// @dev Internal function to verify if the account is not the address zero.
function _checkIsNotAddressZero(address account) internal pure {
if (account == address(0)) {
revert Ownable_NewOwnerCannotBeAddressZero();
}
}
/// @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
/// Internal function without access restriction.
/// Emits an {OwnershipTransferred} event.
function _transferOwnership(address newOwner) internal override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IERC1822Proxiable} from "./interfaces/IERC1822Proxiable.sol";
import {IERC1967} from "./interfaces/IERC1967.sol";
import {ERC1967Utils} from "./libraries/ERC1967Utils.sol";
/// @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
/// {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
///
/// A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
/// reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
/// `UUPSUpgradeable` with a custom implementation of upgrades.
///
/// The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
abstract contract UUPSUpgradeable is IERC1822Proxiable, IERC1967 {
// =========================
// immutable storage
// =========================
address private immutable __self = address(this);
// =========================
// errors
// =========================
/// @dev The call is from an unauthorized context.
error UUPSUnauthorizedCallContext();
/// @dev The storage `slot` is unsupported as a UUID.
error UUPSUnsupportedProxiableUUID(bytes32 slot);
// =========================
// modifiers
// =========================
/// @dev Check that the execution is being performed through a delegatecall call and that the execution context is
/// a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
/// for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
/// function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
/// fail.
modifier onlyProxy() {
_checkProxy();
_;
}
/// @dev Check that the execution is not being performed through a delegate call. This allows a function to be
/// callable on the implementing contract but not through proxies.
modifier notDelegated() {
_checkNotDelegated();
_;
}
// =========================
// getters
// =========================
/// @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
/// implementation. It is used to validate the implementation's compatibility when performing an upgrade.
///
/// IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
/// bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
/// function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
function proxiableUUID() external view notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
// =========================
// setters
// =========================
/// @dev Upgrade the implementation of the proxy to `newImplementation`.
///
/// Calls {_authorizeUpgrade}.
///
/// Emits an {Upgraded} event.
function upgradeTo(address newImplementation) external onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToUUPS(newImplementation);
}
// =========================
// internal functions
// =========================
/// @dev Reverts if the execution is not performed via delegatecall or the execution
/// context is not of a proxy with an ERC1967-compliant implementation pointing to self.
/// See {_onlyProxy}.
function _checkProxy() internal view {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/// @dev Perform implementation upgrade
///
/// Emits an {Upgraded} event.
function _upgradeTo(address newImplementation) internal {
ERC1967Utils.setImplementation(newImplementation);
emit IERC1967.Upgraded(newImplementation);
}
/// @dev Reverts if the execution is performed via delegatecall.
/// See {notDelegated}.
function _checkNotDelegated() internal view {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/// @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
/// {upgradeTo} and {upgradeToAndCall}.
///
/// Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
///
/// ```solidity
/// function _authorizeUpgrade(address) internal override onlyOwner {}
/// ```
function _authorizeUpgrade(address newImplementation) internal virtual;
/// @dev Perform implementation upgrade with security checks for UUPS proxies.
///
/// Emits an {Upgraded} event.
function _upgradeToUUPS(address newImplementation) internal {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (
bytes32 slot
) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
_upgradeTo(newImplementation);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967_InvalidImplementation(
newImplementation
);
}
}
}
{
"compilationTarget": {
"src/Factory.sol": "Factory"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/"
]
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967_InvalidImplementation","type":"error"},{"inputs":[],"name":"Factory_DeployFailed","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"Ownable_CallerIsNotTheNewOwner","type":"error"},{"inputs":[],"name":"Ownable_NewOwnerCannotBeAddressZero","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"Ownable_SenderIsNotOwner","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newLogic","type":"address"}],"name":"LogicChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newProxy","type":"address"},{"indexed":false,"internalType":"uint256","name":"userId","type":"uint256"}],"name":"ProxyCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"beacon","outputs":[{"internalType":"address","name":"beaconAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beaconProxyInitCodeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"userId","type":"uint256"}],"name":"deploy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"userId","type":"uint256"}],"name":"getProxyAddress","outputs":[{"internalType":"address","name":"proxyAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementationAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"logic","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newLogic","type":"address"}],"name":"updateLogic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]