ConcreteFactory

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/factory/ConcreteFactory.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.24;

/**
 * @title ConcreteFactory
 * @notice Factory for deploying and managing Concrete vault proxies and implementations.
 *         Supports deterministic deployments (CREATE2) and UUPS upgradeability with ownership controls.
 *
 * @author Blueprint Finance
 * @custom:protocol Concrete Earn V2
 * @custom:oz-upgrades Uses UUPS + eip7201 storage layout
 * @custom:source on request
 * @custom:audits on request
 * @custom:license AGPL-3.0
 */

// ─────────────────────────────────────────────────────────────────────────────
// External dependencies
// ─────────────────────────────────────────────────────────────────────────────
import {OwnableUpgradeable} from "@openzeppelin-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Create2} from "@openzeppelin-contracts/utils/Create2.sol";
import {EnumerableSet} from "@openzeppelin-contracts/utils/structs/EnumerableSet.sol";

// ─────────────────────────────────────────────────────────────────────────────
// Protocol-facing interfaces
// ─────────────────────────────────────────────────────────────────────────────
import {IConcreteFactory} from "../interface/IConcreteFactory.sol";
import {IUpgradeableVault} from "../interface/IUpgradeableVault.sol";

// ─────────────────────────────────────────────────────────────────────────────
// Internal contracts
// ─────────────────────────────────────────────────────────────────────────────
import {IVaultProxy, VaultProxy} from "./VaultProxy.sol";

// ─────────────────────────────────────────────────────────────────────────────
// Storage layout libraries
// ─────────────────────────────────────────────────────────────────────────────
import {ConcreteFactoryBaseStorageLib as CFBSLib} from "../lib/storage/ConcreteFactoryBaseStorageLib.sol";

contract ConcreteFactory is IConcreteFactory, UUPSUpgradeable, OwnableUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /**
     * @dev A modifier to validate version
     * @param version implementation's version
     */
    modifier checkVersion(uint64 version) {
        require(version > 0 && version <= lastVersion(), InvalidVersion());
        _;
    }

    /**
     * @dev Constructor for the ConcreteFactory contract.
     */
    constructor() {
        _disableInitializers();
    }

    /**
     * @dev Initializes the ConcreteFactory contract.
     * @param owner_ The address of the owner of the factory.
     */
    function initialize(address owner_) external initializer {
        __Ownable_init_unchained(owner_);
        __UUPSUpgradeable_init_unchained();
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function approveImplementation(address implementation) external onlyOwner {
        require(IUpgradeableVault(implementation).factory() == address(this), InvalidImplementation());
        // load the storage variable
        CFBSLib.ConcreteFactoryBaseStorage storage $ = CFBSLib.fetch();
        require($.implementations.add(implementation), AlreadyApproved());

        emit ApprovedImplementation(implementation);
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function blockImplementation(uint64 version) external onlyOwner checkVersion(version) {
        CFBSLib.ConcreteFactoryBaseStorage storage $ = CFBSLib.fetch();
        require(!$.blocked[version], AlreadyBlocked());

        $.blocked[version] = true;

        emit Blocked(version);
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function setMigratable(uint64 fromVersion, uint64 toVersion)
        external
        onlyOwner
        checkVersion(fromVersion)
        checkVersion(toVersion)
    {
        require(fromVersion < toVersion, OldVersion());

        CFBSLib.ConcreteFactoryBaseStorage storage $ = CFBSLib.fetch();
        $.migratable[fromVersion][toVersion] = true;

        emit Migratable(fromVersion, toVersion);
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function create(uint64 version, address ownerAddr, bytes calldata data) external returns (address) {
        return create(version, ownerAddr, data, 0);
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function create(uint64 version, address ownerAddr, bytes calldata data, bytes32 salt) public returns (address) {
        address predictedAddress = predictVaultAddress(version, ownerAddr, data, salt);

        CFBSLib.fetch().vaults[predictedAddress] = true;
        bytes memory bytecode = _computeBytecode(version, ownerAddr, data);

        // Deploy using OpenZeppelin Create2
        address concreteVault = Create2.deploy(0, salt, bytecode);

        require(predictedAddress == concreteVault, InvalidVaultAddress());

        // Mark the vault as deployed by this factory

        emit Deployed(concreteVault, version, ownerAddr);

        return concreteVault;
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function predictVaultAddress(uint64 version, address ownerAddr, bytes calldata data)
        external
        view
        returns (address)
    {
        return predictVaultAddress(version, ownerAddr, data, 0);
    }

    function _computeBytecode(uint64 version, address ownerAddr, bytes calldata data)
        internal
        view
        returns (bytes memory)
    {
        require(!isBlocked(version), ImplementationBlocked());

        // Get the implementation address
        address implementation = getImplementationByVersion(version);

        // Encode the constructor parameters
        bytes memory constructorData = abi.encodeCall(IUpgradeableVault.initialize, (version, ownerAddr, data));

        // Create the bytecode for deployment
        return abi.encodePacked(type(VaultProxy).creationCode, abi.encode(implementation, constructorData));
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function predictVaultAddress(uint64 version, address ownerAddr, bytes calldata data, bytes32 salt)
        public
        view
        returns (address)
    {
        bytes memory bytecode = _computeBytecode(version, ownerAddr, data);

        // Compute CREATE2 address using OpenZeppelin
        return Create2.computeAddress(salt, keccak256(bytecode), address(this));
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function upgrade(address vault, uint64 newVersion, bytes calldata data) external {
        _upgrade(vault, newVersion, data);
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function batchUpgrade(address[] calldata vaultAddresses, uint64 newVersion, bytes[] calldata data) external {
        require(vaultAddresses.length == data.length, InvalidDataLength());
        for (uint256 i = 0; i < vaultAddresses.length; i++) {
            _upgrade(vaultAddresses[i], newVersion, data[i]);
        }
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function lastVersion() public view returns (uint64) {
        return uint64(CFBSLib.fetch().implementations.length());
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function getImplementationByVersion(uint64 version) public view checkVersion(version) returns (address) {
        return CFBSLib.fetch().implementations.at(version - 1);
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function isBlocked(uint64 version) public view checkVersion(version) returns (bool) {
        return CFBSLib.fetch().blocked[version];
    }

    /**
     * @inheritdoc IConcreteFactory
     */
    function isMigratable(uint64 fromVersion, uint64 toVersion)
        public
        view
        checkVersion(fromVersion)
        checkVersion(toVersion)
        returns (bool)
    {
        return CFBSLib.fetch().migratable[fromVersion][toVersion];
    }

    /**
     * @notice Check if a vault was deployed by this factory
     * @param vault The vault address to check
     * @return True if the vault was deployed by this factory, false otherwise
     */
    function isRegisteredVault(address vault) public view returns (bool) {
        return CFBSLib.fetch().vaults[vault];
    }

    /**
     * @dev Admin function to register an external vault proxy to be managed by this factory
     * useful in case of pre-v2 migrations
     * @param vault The vault address to register
     */
    function registerVault(address vault) external onlyOwner {
        require(vault != address(0), ZeroAddress());
        require(!CFBSLib.fetch().vaults[vault], VaultAlreadyRegistered());
        require(IUpgradeableVault(vault).factory() == address(this), InvalidImplementation());

        CFBSLib.fetch().vaults[vault] = true;

        emit VaultRegistered(vault);
    }

    /**
     * @dev Internal function to upgrade a vault to a new implementation version
     * @param vault The vault address to upgrade
     * @param newVersion The target implementation version
     * @param data Custom data to pass to the new implementation's upgrade function
     */
    function _upgrade(address vault, uint64 newVersion, bytes calldata data) internal {
        require(isRegisteredVault(vault), NotRegisteredVault());
        require(msg.sender == OwnableUpgradeable(vault).owner(), NotOwner());

        uint64 currentVaultVersion = IUpgradeableVault(vault).version();

        require(newVersion > currentVaultVersion, OldVersion());
        require(isMigratable(currentVaultVersion, newVersion), CanNotMigrate());
        require(!isBlocked(newVersion), ImplementationBlocked());

        IVaultProxy(vault)
            .upgradeToAndCall(
                getImplementationByVersion(newVersion), abi.encodeCall(IUpgradeableVault.upgrade, (newVersion, data))
            );

        emit Migrated(vault, newVersion);
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @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.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
    struct OwnableStorage {
        address _owner;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;

    function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
        assembly {
            $.slot := OwnableStorageLocation
        }
    }

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    function __Ownable_init(address initialOwner) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner);
    }

    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        OwnableStorage storage $ = _getOwnableStorage();
        return $._owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        OwnableStorage storage $ = _getOwnableStorage();
        address oldOwner = $._owner;
        $._owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.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 Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @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 ERC-1967) 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 ERC-1167 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();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {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 virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        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
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/Create2.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)

pragma solidity ^0.8.20;

import {Errors} from "./Errors.sol";

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev There's no code to deploy.
     */
    error Create2EmptyBytecode();

    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }
        if (bytecode.length == 0) {
            revert Create2EmptyBytecode();
        }
        assembly ("memory-safe") {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
            // if no address was created, and returndata is not empty, bubble revert
            if and(iszero(addr), not(iszero(returndatasize()))) {
                let p := mload(0x40)
                returndatacopy(p, 0, returndatasize())
                revert(p, returndatasize())
            }
        }
        if (addr == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
        assembly ("memory-safe") {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/structs/EnumerableSet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}
"
    },
    "src/interface/IConcreteFactory.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

interface IConcreteFactory {
    error AlreadyBlocked();
    error AlreadyApproved();
    error InvalidImplementation();
    error InvalidVersion();
    error NotOwner();
    error NotRegisteredVault();
    error OldVersion();
    error ImplementationBlocked();
    error CanNotMigrate();
    error InvalidFactoryOwner();
    error VaultAlreadyRegistered();
    error ZeroAddress();
    error InvalidVaultAddress();
    error InvalidDataLength();

    /**
     * @notice Emitted when a new vault is deployed.
     * @param vault address of the new vault
     * @param version version of the new vault
     * @param owner address of the owner of the new vault
     */
    event Deployed(address indexed vault, uint64 indexed version, address indexed owner);

    /**
     * @notice Emitted when a new implementation is approved.
     * @param implementation address of the new implementation
     */
    event ApprovedImplementation(address indexed implementation);

    /**
     * @notice Emitted when an implementation is blocked.
     * @param version version of the implementation that was blocked
     */
    event Blocked(uint64 indexed version);

    /**
     * @notice Emitted when a version is set as migratable to another version.
     * @param fromVersion version that was set as migratable
     * @param toVersion version that was set as migratable
     */
    event Migratable(uint64 indexed fromVersion, uint64 indexed toVersion);

    /**
     * @notice Emitted when an vault is migrated to a new version.
     * @param vault address of the vault
     * @param newVersion new version of the vault
     */
    event Migrated(address indexed vault, uint64 newVersion);

    /**
     * @notice Emitted when a vault is registered with the factory.
     * @param vault address of the vault that was registered
     */
    event VaultRegistered(address indexed vault);

    /**
     * @notice Approve a new implementation for using to deploy a proxy.
     * @param implementation address of the new implementation
     */
    function approveImplementation(address implementation) external;

    /**
     * @notice Block an implementation.
     * @param version version of the implementation to block
     */
    function blockImplementation(uint64 version) external;

    /**
     * @notice Set a version as migratable to another version.
     * @param fromVersion version to set as migratable
     * @param toVersion version to set as migratable
     */
    function setMigratable(uint64 fromVersion, uint64 toVersion) external;

    /**
     * @notice Deploy a new proxy vault.
     * @param version vault's version to use
     * @param owner initial owner of the vault
     * @param data initial data for the vault creation
     * @return address of the vault
     * @dev CREATE2 salt is constructed from the given parameters.
     */
    function create(uint64 version, address owner, bytes calldata data) external returns (address);

    /**
     * @notice Deploy a new proxy vault.
     * @param version vault's version to use
     * @param owner initial owner of the vault
     * @param data initial data for the vault creation
     * @return address of the vault
     * @dev CREATE2 salt is constructed from the given parameters.
     */
    function create(uint64 version, address owner, bytes calldata data, bytes32 salt) external returns (address);

    /**
     * @notice Predict the address of a vault that would be deployed with the given parameters.
     * @param version vault's version to use
     * @param ownerAddr initial owner of the vault
     * @param data initial data for the vault creation
     * @return predicted address of the vault
     */
    function predictVaultAddress(uint64 version, address ownerAddr, bytes calldata data) external view returns (address);

    /**
     * @notice Predict the address of a vault that would be deployed with the given parameters.
     * @param version vault's version to use
     * @param ownerAddr initial owner of the vault
     * @param data initial data for the vault creation
     * @param salt for CREATE2 deployment.
     * @return predicted address of the vault
     */
    function predictVaultAddress(uint64 version, address ownerAddr, bytes calldata data, bytes32 salt)
        external
        view
        returns (address);

    /**
     * @notice Upgrade a vault to a new implementation version.
     * @param vault address of the vault to upgrade
     * @param newVersion new version to upgrade to
     * @param data upgrade data
     * @dev Only the vault owner can initiate upgrade. The new version must be higher than the current version.
     */
    function upgrade(address vault, uint64 newVersion, bytes calldata data) external;

    /**
     * @notice Upgrade multiple vaults to a new implementation version in a single transaction.
     * @param vaultAddresses array of vault addresses to upgrade
     * @param newVersion new version to upgrade all vaults to
     * @param data upgrade data to use for all vault upgrades
     * @dev Only the vault owners can initiate upgrade for their respective vaults. The new version must be higher than the current version for each vault.
     */
    function batchUpgrade(address[] calldata vaultAddresses, uint64 newVersion, bytes[] calldata data) external;

    /**
     * @notice Get the last available version.
     * @return version of the last implementation
     * @dev If zero, no implementations are approved.
     */
    function lastVersion() external view returns (uint64);

    /**
     * @notice Get the implementation for a given version.
     * @param version version to get the implementation for
     * @return address of the implementation
     * @dev Reverts when an invalid version.
     */
    function getImplementationByVersion(uint64 version) external view returns (address);

    /**
     * @notice Get if an implementation is blocked
     * @param version version to check
     * @return bool true if the implementation is blocked
     */
    function isBlocked(uint64 version) external view returns (bool);

    /**
     * @notice Get if a version is migratable to another version
     * @param fromVersion version to check
     * @param toVersion version to check
     * @return bool true if the version is migratable
     */
    function isMigratable(uint64 fromVersion, uint64 toVersion) external view returns (bool);

    /**
     * @notice Check if a vault was deployed by this factory
     * @param vault The vault address to check
     * @return True if the vault was deployed by this factory, false otherwise
     */
    function isRegisteredVault(address vault) external view returns (bool);

    /**
     * @notice Register a previously deployed vault with this factory for management
     * @param vault The vault address to register
     * @dev Only the factory owner can register vaults. The vault must be a valid upgradeable vault.
     */
    function registerVault(address vault) external;
}
"
    },
    "src/interface/IUpgradeableVault.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

interface IUpgradeableVault {
    error AlreadyInitialized();
    error NotFactory();
    error NotInitialized();
    error InvalidFactoryOwner();

    /**
     * @notice Get the factory's address.
     * @return address of the factory
     */
    function factory() external view returns (address);

    /**
     * @notice Get the vault's version.
     * @return version of the vault
     * @dev Starts from 1.
     */
    function version() external view returns (uint64);

    /**
     * @notice Initialize `UpgradeableVaultProxy` contract by using a given data and setting a particular version and owner.
     * @param initialVersion initial version of the vault
     * @param owner initial owner of the vault
     * @param data some data to use
     */
    function initialize(uint64 initialVersion, address owner, bytes calldata data) external;

    /**
     * @notice Upgrade this vault to a specific newer version using a given data.
     * @param newVersion new version of the vault
     * @param data some data to use
     */
    function upgrade(uint64 newVersion, bytes calldata data) external;
}
"
    },
    "src/factory/VaultProxy.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {ERC1967Proxy} from "@openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {ERC1967Utils} from "@openzeppelin-contracts/proxy/ERC1967/ERC1967Utils.sol";
import {IVaultProxy} from "../interface/IVaultProxy.sol";

contract VaultProxy is ERC1967Proxy, IVaultProxy {
    // An immutable address for the admin to avoid unnecessary SLOADs before each call.
    address private immutable _admin;

    /**
     * @dev The proxy caller is not the current admin.
     */
    error CallerNotProxyAdmin();

    /**
     * @dev Initializes an upgradeable proxy managed by `msg.sender`,
     * backed by the implementation at `logic`, and optionally initialized with `data` as explained in
     * {ERC1967Proxy-constructor}.
     */
    constructor(address logic, bytes memory data) ERC1967Proxy(logic, data) {
        _admin = msg.sender;
        // Set the storage value and emit an event for ERC-1967 compatibility
        ERC1967Utils.changeAdmin(_proxyAdmin());
    }

    /**
     * @dev Simplified pattern - upgradeToAndCall will not fallback to implementation via this proxy
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external {
        if (msg.sender != _proxyAdmin()) {
            revert CallerNotProxyAdmin();
        }

        ERC1967Utils.upgradeToAndCall(newImplementation, data);
    }

    /**
     * @dev Returns the admin of this proxy.
     */
    function _proxyAdmin() internal view returns (address) {
        return _admin;
    }
}
"
    },
    "src/lib/storage/ConcreteFactoryBaseStorageLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {EnumerableSet} from "@openzeppelin-contracts/utils/structs/EnumerableSet.sol";

library ConcreteFactoryBaseStorageLib {
    struct ConcreteFactoryBaseStorage {
        /// @dev Set of approved implementation addresses that can be used to deploy new vaults
        // This Factory depends on EnumerableSet ordering.
        // MUST NOT call `remove`, as the order is only reliable when elements are never removed.
        EnumerableSet.AddressSet implementations;
        /// @dev Mapping of implementation addresses to boolean values indicating if the implementation is blocked
        mapping(uint64 version => bool blocked) blocked;
        /// @dev Mapping indicating if a version is migratable to another version
        mapping(uint64 fromVersion => mapping(uint64 toVersion => bool)) migratable;
        /// @dev Mapping to track deployed vaults from this factory
        mapping(address => bool) vaults;
    }

    // keccak256(abi.encode(uint256(keccak256("concrete.storage.ConcreteFactoryBaseStorage")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 constant CONCRETE_FACTORY_STORAGE_SLOT = 0x8e455bed11907189c537a36ae6131c14495d217d4f7af335ce892f553df45a00;

    function fetch() internal pure returns (ConcreteFactoryBaseStorage storage concreteFactoryBaseStorage) {
        bytes32 slot = CONCRETE_FACTORY_STORAGE_SLOT;
        assembly {
            concreteFactoryBaseStorage.slot := slot
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @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 {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        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. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * 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 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @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 {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @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);
}
"
    },
    "node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.22;

import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This library provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
 */
library ERC1967Utils {
    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC19

Tags:
Multisig, Swap, Upgradeable, Multi-Signature, Factory|addr:0x93629424a72ad246097f1afc74c64147b26cc9d4|verified:true|block:23727918|tx:0x2810f98c0b2ce083b0edb6abdc296fceee908d3dbc97ea7451230239eeca39f3|first_check:1762283454

Submitted on: 2025-11-04 20:10:54

Comments

Log in to comment.

No comments yet.